From b9830f0c26fa0f1720874156d5fa9e5aadfd15eb Mon Sep 17 00:00:00 2001 From: shivani170 Date: Thu, 16 Apr 2026 09:59:29 +0530 Subject: [PATCH 01/17] chore: audit log ui --- .../AuditLogs/AuditLogDetail.tsx | 118 ++++++++++ .../AuditLogs/AuditLogsList.tsx | 97 ++++++++ .../AuditLogs/AuditLogsRouter.tsx | 15 ++ .../AuditLogs/AuditLogsTableWrapper.tsx | 111 +++++++++ src/Pages-Devtron-2.0/AuditLogs/auditLog.scss | 6 + src/Pages-Devtron-2.0/AuditLogs/index.ts | 1 + src/Pages-Devtron-2.0/AuditLogs/service.tsx | 217 ++++++++++++++++++ src/Pages-Devtron-2.0/AuditLogs/types.ts | 63 +++++ src/Pages-Devtron-2.0/AuditLogs/utils.tsx | 106 +++++++++ src/Pages/Shared/CommandBar/utils.tsx | 71 +++--- src/components/Navigation/Navigation.tsx | 70 ++++-- src/components/Navigation/constants.ts | 76 +++--- src/components/Navigation/utils.ts | 13 ++ .../common/navigation/NavigationRoutes.tsx | 6 + 14 files changed, 887 insertions(+), 83 deletions(-) create mode 100644 src/Pages-Devtron-2.0/AuditLogs/AuditLogDetail.tsx create mode 100644 src/Pages-Devtron-2.0/AuditLogs/AuditLogsList.tsx create mode 100644 src/Pages-Devtron-2.0/AuditLogs/AuditLogsRouter.tsx create mode 100644 src/Pages-Devtron-2.0/AuditLogs/AuditLogsTableWrapper.tsx create mode 100644 src/Pages-Devtron-2.0/AuditLogs/auditLog.scss create mode 100644 src/Pages-Devtron-2.0/AuditLogs/index.ts create mode 100644 src/Pages-Devtron-2.0/AuditLogs/service.tsx create mode 100644 src/Pages-Devtron-2.0/AuditLogs/types.ts create mode 100644 src/Pages-Devtron-2.0/AuditLogs/utils.tsx diff --git a/src/Pages-Devtron-2.0/AuditLogs/AuditLogDetail.tsx b/src/Pages-Devtron-2.0/AuditLogs/AuditLogDetail.tsx new file mode 100644 index 0000000000..47b00bc556 --- /dev/null +++ b/src/Pages-Devtron-2.0/AuditLogs/AuditLogDetail.tsx @@ -0,0 +1,118 @@ +import { useParams } from 'react-router-dom' +import dayjs from 'dayjs' + +import { + BreadCrumb, + CodeEditor, + DATE_TIME_FORMATS, + Icon, + MODES, + PageHeader, + Progressing, + ROUTER_URLS, + useBreadcrumb, + useQuery, +} from '@devtron-labs/devtron-fe-common-lib' + +import { getAuditLogDetail } from './service' +import { AuditLogParamsType } from './types' + +import './auditLog.scss' + +const AuditLogDetail = () => { + const { auditLogId = '' } = useParams() + + const { data: auditLog, isFetching } = useQuery({ + queryFn: async () => { + const response = await getAuditLogDetail(auditLogId) + return response + }, + queryKey: ['audit-log-detail', auditLogId], + enabled: !!auditLogId, + }) + + const { breadcrumbs } = useBreadcrumb( + ROUTER_URLS.AUDIT_LOGS_DETAIL, + { + alias: { + 'audit-logs': { + component: , + linked: true, + }, + ':auditLogId': { + component: {auditLog?.action || 'Details'}, + linked: false, + }, + }, + }, + [auditLog?.action], + ) + + if (isFetching) { + return ( +
+ +
+ ) + } + + const breadCrumbs = () => + + return ( +
+ + +
+
+
+ {auditLog.action} +
+
+

Timestamp

+

+ {dayjs(auditLog.timestamp).format(DATE_TIME_FORMATS.TWELVE_HOURS_FORMAT)} +

+
+
+

Type

+

{auditLog.type}

+
+
+

Module

+

{auditLog.module}

+
+
+

Action

+

{auditLog.action}

+
+
+

User

+

{auditLog.user}

+
+
+

Resource

+

{auditLog.resource}

+
+
+ +
+
+

Payload

+
+ +
+ +
+
+
+
+ ) +} + +export default AuditLogDetail diff --git a/src/Pages-Devtron-2.0/AuditLogs/AuditLogsList.tsx b/src/Pages-Devtron-2.0/AuditLogs/AuditLogsList.tsx new file mode 100644 index 0000000000..46a4968ffd --- /dev/null +++ b/src/Pages-Devtron-2.0/AuditLogs/AuditLogsList.tsx @@ -0,0 +1,97 @@ +import { useMemo } from 'react' +import { useParams } from 'react-router-dom' + +import { + BreadCrumb, + ErrorScreenManager, + FiltersTypeEnum, + PageHeader, + PaginationEnum, + Progressing, + ROUTER_URLS, + Table, + useBreadcrumb, + useQuery, +} from '@devtron-labs/devtron-fe-common-lib' + +import AuditLogsTableWrapper from './AuditLogsTableWrapper' +import { getAuditLogFilterOptions, getAuditLogList } from './service' +import { AuditLogRowType, AuditLogTableAdditionalProps } from './types' +import { getAuditLogColumns, parseAuditLogURLParams, useGetAuditLogDetailsResponse } from './utils' + +const EMPTY_FILTER_OPTIONS: AuditLogTableAdditionalProps['filterOptions'] = { + typeOptions: [], + moduleOptions: [], +} + +const AuditLogsList = () => { + const columns = useMemo(() => getAuditLogColumns(), []) + const { auditLogId } = useParams() + + const { auditLogLoading, auditLogError, reloadAuditLog } = useGetAuditLogDetailsResponse(auditLogId) + + const { breadcrumbs } = useBreadcrumb(ROUTER_URLS.AUDIT_LOGS, { + alias: { + 'audit-logs': { + component: Audit Logs, + linked: false, + }, + }, + }) + + const { data: filterOptions = EMPTY_FILTER_OPTIONS } = useQuery< + Awaited>, + AuditLogTableAdditionalProps['filterOptions'], + ['audit-log-filter-options'], + false + >({ + queryFn: getAuditLogFilterOptions, + queryKey: ['audit-log-filter-options'], + select: (data) => data, + }) + + if (auditLogLoading) { + return ( +
+ +
+ ) + } + + if (auditLogError) { + return + } + + const auditLogBreadcrumb = () => + + return ( +
+ + + + id="table__audit-logs" + columns={columns} + ViewWrapper={AuditLogsTableWrapper} + filtersVariant={FiltersTypeEnum.URL} + additionalFilterProps={{ + initialSortKey: 'timestamp', + parseSearchParams: parseAuditLogURLParams, + defaultPageSize: 5, + }} + paginationVariant={PaginationEnum.PAGINATED} + getRows={getAuditLogList} + emptyStateConfig={{ + noRowsConfig: { + title: 'No audit logs found', + subTitle: 'Audit trail entries will show up here once actions are performed.', + noImage: true, + }, + }} + filter={null} + additionalProps={{ filterOptions }} + /> +
+ ) +} + +export default AuditLogsList diff --git a/src/Pages-Devtron-2.0/AuditLogs/AuditLogsRouter.tsx b/src/Pages-Devtron-2.0/AuditLogs/AuditLogsRouter.tsx new file mode 100644 index 0000000000..7847aa23e2 --- /dev/null +++ b/src/Pages-Devtron-2.0/AuditLogs/AuditLogsRouter.tsx @@ -0,0 +1,15 @@ +import { Route, Routes } from 'react-router-dom' + +import { BASE_ROUTES } from '@devtron-labs/devtron-fe-common-lib' + +import AuditLogDetail from './AuditLogDetail' +import AuditLogsList from './AuditLogsList' + +const AuditLogsRouter = () => ( + + } /> + } /> + +) + +export default AuditLogsRouter diff --git a/src/Pages-Devtron-2.0/AuditLogs/AuditLogsTableWrapper.tsx b/src/Pages-Devtron-2.0/AuditLogs/AuditLogsTableWrapper.tsx new file mode 100644 index 0000000000..e4b125ecec --- /dev/null +++ b/src/Pages-Devtron-2.0/AuditLogs/AuditLogsTableWrapper.tsx @@ -0,0 +1,111 @@ +import { + FilterChips, + GroupedFilterSelectPicker, + GroupedFilterSelectPickerProps, + SearchBar, +} from '@devtron-labs/devtron-fe-common-lib' + +import { AuditLogFilterKeys, AuditLogFiltersType, AuditLogsTableWrapperProps } from './types' +import { getAuditLogFilterLabel } from './utils' + +const AuditLogsTableWrapper = ({ + children, + searchKey, + handleSearch, + updateSearchParams, + clearFilters, + filterOptions, + areRowsLoading, + filteredRows, + areFiltersApplied, + ...restProps +}: AuditLogsTableWrapperProps) => { + const hideFilters = !areRowsLoading && filteredRows?.length === 0 && !areFiltersApplied + + const appliedFilters: AuditLogFiltersType = { + [AuditLogFilterKeys.TYPE]: restProps[AuditLogFilterKeys.TYPE] ?? [], + [AuditLogFilterKeys.MODULE]: restProps[AuditLogFilterKeys.MODULE] ?? [], + } + + const handleUpdateFilters = (filterKey: AuditLogFilterKeys) => (selectedOptions) => { + updateSearchParams({ [filterKey]: selectedOptions.map((option) => String(option.value)) }) + } + + const filterSelectPickerPropsMap: GroupedFilterSelectPickerProps['filterSelectPickerPropsMap'] = + { + [AuditLogFilterKeys.TYPE]: { + placeholder: 'Type', + inputId: 'audit-logs-type-filter', + options: filterOptions.typeOptions, + appliedFilterOptions: filterOptions.typeOptions.filter((option) => + appliedFilters[AuditLogFilterKeys.TYPE].includes(String(option.value)), + ), + handleApplyFilter: handleUpdateFilters(AuditLogFilterKeys.TYPE), + isDisabled: false, + isLoading: false, + }, + [AuditLogFilterKeys.MODULE]: { + placeholder: 'Module', + inputId: 'audit-logs-module-filter', + options: filterOptions.moduleOptions, + appliedFilterOptions: filterOptions.moduleOptions.filter((option) => + appliedFilters[AuditLogFilterKeys.MODULE].includes(String(option.value)), + ), + handleApplyFilter: handleUpdateFilters(AuditLogFilterKeys.MODULE), + isDisabled: false, + isLoading: false, + }, + } + + return ( +
+
+ {!hideFilters && ( + <> + + + + id="audit-log-filters" + width={220} + isFilterApplied={ + !!appliedFilters[AuditLogFilterKeys.TYPE].length || + !!appliedFilters[AuditLogFilterKeys.MODULE].length + } + options={[ + { + groupLabel: 'Filter by', + items: [ + { id: AuditLogFilterKeys.TYPE, label: 'Type' }, + { id: AuditLogFilterKeys.MODULE, label: 'Module' }, + ], + }, + ]} + filterSelectPickerPropsMap={filterSelectPickerPropsMap} + /> + + )} +
+ + {!hideFilters && ( + + filterConfig={appliedFilters} + onRemoveFilter={updateSearchParams} + clearFilters={clearFilters} + className="px-20" + getFormattedLabel={getAuditLogFilterLabel} + getFormattedValue={(_filterKey, filterValue: string) => filterValue} + /> + )} + + {children} +
+ ) +} + +export default AuditLogsTableWrapper diff --git a/src/Pages-Devtron-2.0/AuditLogs/auditLog.scss b/src/Pages-Devtron-2.0/AuditLogs/auditLog.scss new file mode 100644 index 0000000000..da087b7d4b --- /dev/null +++ b/src/Pages-Devtron-2.0/AuditLogs/auditLog.scss @@ -0,0 +1,6 @@ +.audit-logs { + &__grid { + grid-template-columns: 150px 1fr; + } + +} \ No newline at end of file diff --git a/src/Pages-Devtron-2.0/AuditLogs/index.ts b/src/Pages-Devtron-2.0/AuditLogs/index.ts new file mode 100644 index 0000000000..8b96a6dd43 --- /dev/null +++ b/src/Pages-Devtron-2.0/AuditLogs/index.ts @@ -0,0 +1 @@ +export { default as AuditLogs } from './AuditLogsRouter' diff --git a/src/Pages-Devtron-2.0/AuditLogs/service.tsx b/src/Pages-Devtron-2.0/AuditLogs/service.tsx new file mode 100644 index 0000000000..8084e17d3e --- /dev/null +++ b/src/Pages-Devtron-2.0/AuditLogs/service.tsx @@ -0,0 +1,217 @@ +import { ResponseType, SortingOrder } from '@devtron-labs/devtron-fe-common-lib' + +import { + AuditLogDetailType, + AuditLogFilterKeys, + AuditLogFilterOptionsType, + AuditLogRowType, + AuditLogSortableKeys, + GetAuditLogListProps, +} from './types' + +const MOCK_AUDIT_LOGS: AuditLogDetailType[] = [ + { + auditLogId: 101, + timestamp: '2026-04-08T10:15:00Z', + action: 'Application updated', + type: 'UPDATE', + user: 'shivani@devtron.ai', + resource: 'frontend-service', + module: 'Application Management', + payload: { before: { replicas: 2 }, after: { replicas: 3 }, env: 'production' }, + }, + { + auditLogId: 102, + timestamp: '2026-04-08T09:42:00Z', + action: 'Deployment triggered', + type: 'DEPLOY', + user: 'rahul@devtron.ai', + resource: 'payments-service', + module: 'Application Management', + payload: { image: 'payments:v2.4.1', pipelineId: 98, triggerType: 'manual' }, + }, + { + auditLogId: 103, + timestamp: '2026-04-08T08:30:00Z', + action: 'Policy enabled', + type: 'CONFIGURE', + user: 'sre@devtron.ai', + resource: 'restrict-prod-deployments', + module: 'Security Center', + payload: { policy: 'restrict-prod-deployments', state: 'enabled' }, + }, + { + auditLogId: 104, + timestamp: '2026-04-07T16:10:00Z', + action: 'Cluster added', + type: 'CREATE', + user: 'infra@devtron.ai', + resource: 'gke-prod-cluster', + module: 'Infrastructure Management', + payload: { clusterName: 'gke-prod-cluster', provider: 'gke', region: 'asia-south1' }, + }, + { + auditLogId: 105, + timestamp: '2026-04-07T15:00:00Z', + action: 'Chart version promoted', + type: 'PROMOTE', + user: 'release@devtron.ai', + resource: 'customer-portal', + module: 'Software Release Management', + payload: { chartVersion: '1.4.0', release: 'spring-release', target: 'staging' }, + }, + { + auditLogId: 106, + timestamp: '2026-04-07T13:25:00Z', + action: 'Backup scheduled', + type: 'CREATE', + user: 'ops@devtron.ai', + resource: 'nightly-backup', + module: 'Data Protection Management', + payload: { schedule: '0 1 * * *', storageLocation: 's3-prod-backups' }, + }, + { + auditLogId: 107, + timestamp: '2026-04-07T12:05:00Z', + action: 'Restore triggered', + type: 'RESTORE', + user: 'ops@devtron.ai', + resource: 'checkout-db', + module: 'Data Protection Management', + payload: { backupName: 'checkout-db-2026-04-07', destinationCluster: 'dr-cluster' }, + }, + { + auditLogId: 108, + timestamp: '2026-04-07T11:15:00Z', + action: 'Build triggered', + type: 'TRIGGER', + user: 'automation@devtron.ai', + resource: 'catalog-service', + module: 'Automation & Enablement', + payload: { pipeline: 'catalog-build', branch: 'main', commit: 'a1b2c3d' }, + }, + { + auditLogId: 109, + timestamp: '2026-04-06T18:40:00Z', + action: 'Cost configuration updated', + type: 'UPDATE', + user: 'finops@devtron.ai', + resource: 'aws-prod', + module: 'Cost Visibility', + payload: { currency: 'USD', idleCostThreshold: 20 }, + }, + { + auditLogId: 110, + timestamp: '2026-04-06T14:10:00Z', + action: 'Role mapping updated', + type: 'UPDATE', + user: 'platform-admin@devtron.ai', + resource: 'sso-role-map', + module: 'Global Configurations', + payload: { role: 'super-admin', groups: ['platform-team', 'sre'] }, + }, + { + auditLogId: 111, + timestamp: '2026-04-05T09:55:00Z', + action: 'Webhook deleted', + type: 'DELETE', + user: 'notifications@devtron.ai', + resource: 'slack-prod-alerts', + module: 'Global Configurations', + payload: { webhookName: 'slack-prod-alerts', integration: 'Slack' }, + }, + { + auditLogId: 112, + timestamp: '2026-04-04T07:20:00Z', + action: 'Vulnerability policy acknowledged', + type: 'ACKNOWLEDGE', + user: 'security@devtron.ai', + resource: 'cve-policy-2026-04', + module: 'Security Center', + payload: { severity: 'critical', justification: 'Mitigated by network policy' }, + }, +] + +const matchesSearch = (auditLog: AuditLogDetailType, searchKey: string) => { + if (!searchKey) { + return true + } + + const normalizedSearchKey = searchKey.toLowerCase() + + return [auditLog.action, auditLog.user, auditLog.resource, auditLog.module, auditLog.type] + .join(' ') + .toLowerCase() + .includes(normalizedSearchKey) +} + +const getSortedAuditLogs = ( + auditLogs: AuditLogDetailType[], + sortBy: string | undefined, + sortOrder: SortingOrder | undefined, +) => { + const sortingKey = (sortBy as AuditLogSortableKeys) || AuditLogSortableKeys.TIMESTAMP + const effectiveSortOrder = sortOrder || SortingOrder.DESC + + return [...auditLogs].sort((first, second) => { + const firstValue = first[sortingKey] + const secondValue = second[sortingKey] + + const comparison = + sortingKey === AuditLogSortableKeys.TIMESTAMP + ? new Date(firstValue).getTime() - new Date(secondValue).getTime() + : String(firstValue).localeCompare(String(secondValue)) + + return effectiveSortOrder === SortingOrder.ASC ? comparison : -comparison + }) +} + +// TODO: Remove later +export const getAuditListResponse = async (): Promise> => { + const response = { + status: 'OK', + code: 200, + result: MOCK_AUDIT_LOGS, + } + return response +} + +export const getAuditLogList = async ({ + offset, + pageSize, + searchKey, + sortBy, + sortOrder, + [AuditLogFilterKeys.TYPE]: type = [], + [AuditLogFilterKeys.MODULE]: module = [], +}: GetAuditLogListProps): Promise<{ rows: { id: string; data: AuditLogRowType }[]; totalRows: number }> => { + const filteredAuditLogs = (await getAuditListResponse()).result?.filter( + (auditLog) => + matchesSearch(auditLog, searchKey) && + (!type.length || type.includes(auditLog.type)) && + (!module.length || module.includes(auditLog.module)), + ) + + const sortedAuditLogs = getSortedAuditLogs(filteredAuditLogs, sortBy, sortOrder) + const paginatedAuditLogs = sortedAuditLogs.slice(offset, offset + pageSize) + + return { + rows: paginatedAuditLogs.map(({ ...auditLog }) => ({ + id: String(auditLog.auditLogId), + data: auditLog, + })), + totalRows: filteredAuditLogs.length, + } +} + +export const getAuditLogFilterOptions = async (): Promise => ({ + typeOptions: [...new Set(MOCK_AUDIT_LOGS.map(({ type }) => type))] + .sort((first, second) => first.localeCompare(second)) + .map((value) => ({ label: value, value })), + moduleOptions: [...new Set(MOCK_AUDIT_LOGS.map(({ module }) => module))] + .sort((first, second) => first.localeCompare(second)) + .map((value) => ({ label: value, value })), +}) + +export const getAuditLogDetail = async (auditLogId: string): Promise => + MOCK_AUDIT_LOGS.find((auditLog) => String(auditLog.auditLogId) === auditLogId) ?? null diff --git a/src/Pages-Devtron-2.0/AuditLogs/types.ts b/src/Pages-Devtron-2.0/AuditLogs/types.ts new file mode 100644 index 0000000000..fc78714afa --- /dev/null +++ b/src/Pages-Devtron-2.0/AuditLogs/types.ts @@ -0,0 +1,63 @@ +import { + FiltersTypeEnum, + SelectPickerOptionType, + TableProps, + TableViewWrapperProps, +} from '@devtron-labs/devtron-fe-common-lib' + +export enum AuditLogFilterKeys { + TYPE = 'type', + MODULE = 'module', +} + +export enum AuditLogSortableKeys { + TIMESTAMP = 'timestamp', + ACTION = 'action', + TYPE = 'type', + USER = 'user', + RESOURCE = 'resource', + MODULE = 'module', +} + +export type AuditLogFiltersType = { + [AuditLogFilterKeys.TYPE]: string[] + [AuditLogFilterKeys.MODULE]: string[] +} + +export interface AuditLogRowType { + auditLogId: number + timestamp: string + action: string + type: string + user: string + resource: string + module: string +} + +export interface AuditLogDetailType extends AuditLogRowType { + payload: Record +} + +export interface AuditLogParamsType extends Record<'auditLogId', string> {} + +export type AuditLogFilterOptionsType = { + typeOptions: SelectPickerOptionType[] + moduleOptions: SelectPickerOptionType[] +} + +export type AuditLogTableAdditionalProps = { + filterOptions: AuditLogFilterOptionsType +} + +export type AuditLogsTableProps = TableProps + +export type GetAuditLogListProps = AuditLogFiltersType & + Parameters>[0] & { + signal: AbortSignal + } + +export type AuditLogsTableWrapperProps = TableViewWrapperProps< + AuditLogRowType, + FiltersTypeEnum.URL, + AuditLogFiltersType & AuditLogTableAdditionalProps +> diff --git a/src/Pages-Devtron-2.0/AuditLogs/utils.tsx b/src/Pages-Devtron-2.0/AuditLogs/utils.tsx new file mode 100644 index 0000000000..d7e936b03c --- /dev/null +++ b/src/Pages-Devtron-2.0/AuditLogs/utils.tsx @@ -0,0 +1,106 @@ +import { generatePath, NavLink } from 'react-router-dom' +import dayjs from 'dayjs' + +import { + DATE_TIME_FORMATS, + FiltersTypeEnum, + getAlphabetIcon, + ROUTER_URLS, + TableCellComponentProps, + useAsync, +} from '@devtron-labs/devtron-fe-common-lib' + +import { getAuditLogDetail } from './service' +import { AuditLogFilterKeys, AuditLogFiltersType, AuditLogRowType, AuditLogsTableProps } from './types' + +const TimestampCellComponent = ({ value }: TableCellComponentProps) => ( +
+ + {value ? dayjs(String(value)).format(DATE_TIME_FORMATS.TWELVE_HOURS_FORMAT) : '-'} + +
+) + +const ActionCellComponent = ({ row }: TableCellComponentProps) => ( + + {row.data.action} + +) + +const UserCellComponent = ({ row }: TableCellComponentProps) => ( +
+ + {getAlphabetIcon(row.data.user, 'dc__no-shrink m-0-imp icon-dim-20')} + {row.data.user} + +
+) + +export const getAuditLogColumns = (): AuditLogsTableProps['columns'] => [ + { + field: 'timestamp', + label: 'Timestamp', + size: { fixed: 220 }, + isSortable: true, + CellComponent: TimestampCellComponent, + }, + { + field: 'action', + label: 'Action', + size: null, + isSortable: true, + CellComponent: ActionCellComponent, + }, + { + field: 'type', + label: 'Type', + size: { fixed: 130 }, + isSortable: true, + }, + { + field: 'user', + label: 'User', + size: { fixed: 180 }, + isSortable: true, + CellComponent: UserCellComponent, + }, + { + field: 'resource', + label: 'Resource', + size: { fixed: 180 }, + isSortable: true, + }, + { + field: 'module', + label: 'Module', + size: { fixed: 190 }, + isSortable: true, + }, +] + +export const parseAuditLogURLParams = (searchParams: URLSearchParams): AuditLogFiltersType => ({ + [AuditLogFilterKeys.TYPE]: searchParams.getAll(AuditLogFilterKeys.TYPE), + [AuditLogFilterKeys.MODULE]: searchParams.getAll(AuditLogFilterKeys.MODULE), +}) + +export const getAuditLogFilterLabel = (filterKey: string) => { + switch (filterKey) { + case AuditLogFilterKeys.TYPE: + return 'Type' + case AuditLogFilterKeys.MODULE: + return 'Module' + default: + return filterKey + } +} + +export const useGetAuditLogDetailsResponse = (auditLogId) => { + const [auditLogLoading, auditLog, auditLogError, reloadAuditLog] = useAsync(() => getAuditLogDetail(auditLogId), []) + return { auditLogLoading, auditLog, auditLogError, reloadAuditLog } +} diff --git a/src/Pages/Shared/CommandBar/utils.tsx b/src/Pages/Shared/CommandBar/utils.tsx index 704b32919e..23defe29dc 100644 --- a/src/Pages/Shared/CommandBar/utils.tsx +++ b/src/Pages/Shared/CommandBar/utils.tsx @@ -11,6 +11,7 @@ import { import { QueryParams as ChartStoreQueryParams } from '@Components/charts/constants' import { getNavigationList } from '@Components/Navigation' +import { hasNavigationGroupItems } from '@Components/Navigation/utils' import { getClusterChangeRedirectionUrl } from '@Components/ResourceBrowser/Utils' import { URLS } from '@Config/routes' @@ -103,36 +104,46 @@ const getNavItemBreakdownItems = ( export const getNavigationGroups = (serverMode: SERVER_MODE, isSuperAdmin: boolean): CommandBarGroupType[] => getNavigationList(serverMode).map((group) => { - const parsedItems = group.items.flatMap( - ({ hasSubMenu, subItems, title, href, id, icon, keywords }) => { - if (hasSubMenu && subItems?.length) { - return subItems.map((subItem) => ({ - title: `${title} / ${subItem.title}`, - id: subItem.id, - // Since icon is not present for some subItems, using from group - icon: NAV_SUB_ITEMS_ICON_MAPPING[id] || group.icon, - // TODO: No href present for some subItems - href: subItem.href ?? null, - keywords: subItem.keywords || [], - })) - } - - const breakdownItems = getNavItemBreakdownItems(id, serverMode, isSuperAdmin) - - if (breakdownItems.length) { - return breakdownItems - } - - return { - title, - id, - icon: icon || 'ic-arrow-right', - // TODO: No href present for some items - href: href ?? null, - keywords: keywords || [], - } - }, - ) + const parsedItems = hasNavigationGroupItems(group) + ? group.items.flatMap( + ({ hasSubMenu, subItems, title, href, id, icon, keywords }) => { + if (hasSubMenu && subItems?.length) { + return subItems.map((subItem) => ({ + title: `${title} / ${subItem.title}`, + id: subItem.id, + // Since icon is not present for some subItems, using from group + icon: NAV_SUB_ITEMS_ICON_MAPPING[id] || group.icon, + // TODO: No href present for some subItems + href: subItem.href ?? null, + keywords: subItem.keywords || [], + })) + } + + const breakdownItems = getNavItemBreakdownItems(id, serverMode, isSuperAdmin) + + if (breakdownItems.length) { + return breakdownItems + } + + return { + title, + id, + icon: icon || 'ic-arrow-right', + // TODO: No href present for some items + href: href ?? null, + keywords: keywords || [], + } + }, + ) + : [ + { + title: group.title, + id: group.id, + icon: group.icon, + href: group.href, + keywords: [], + } as CommandBarGroupType['items'][number], + ] return { title: group.title, diff --git a/src/components/Navigation/Navigation.tsx b/src/components/Navigation/Navigation.tsx index 1b5a7c9f8e..f6ae1c4732 100644 --- a/src/components/Navigation/Navigation.tsx +++ b/src/components/Navigation/Navigation.tsx @@ -42,7 +42,13 @@ import { NavGroup } from './NavGroup' import { NavigationLogo, NavigationLogoExpanded } from './NavigationLogo' import { NavItem } from './NavItem' import { NavGroupProps, NavigationProps } from './types' -import { doesNavigationItemMatchPath, filterNavigationItems, findActiveNavigationItemOfNavGroup } from './utils' +import { + doesNavigationGroupMatchPath, + doesNavigationItemMatchPath, + filterNavigationItems, + findActiveNavigationItemOfNavGroup, + hasNavigationGroupItems, +} from './utils' import './styles.scss' @@ -143,17 +149,23 @@ export const Navigation = ({ const NAVIGATION_LIST = useMemo(() => getNavigationList(serverMode), [serverMode]) const selectedNavGroup = useMemo( - () => NAVIGATION_LIST.find(({ items }) => items.some((item) => doesNavigationItemMatchPath(item, pathname))), - [pathname], + () => NAVIGATION_LIST.find((group) => doesNavigationGroupMatchPath(group, pathname)), + [NAVIGATION_LIST, pathname], + ) + + const selectedExpandableNavGroup = useMemo( + () => (hasNavigationGroupItems(selectedNavGroup) ? selectedNavGroup : null), + [selectedNavGroup], ) // The current navigation group is the one that is hovered or the one that is active, \ // this is used to determine which nav group items are to be shown in expanded state. - const currentNavGroup = hoveredNavGroup || selectedNavGroup + const currentNavGroup = hoveredNavGroup || selectedExpandableNavGroup const isExpanded = !!hoveredNavGroup const navItems = useMemo( - () => (currentNavGroup ? filterNavigationItems(currentNavGroup.items, searchText) : []), + () => + hasNavigationGroupItems(currentNavGroup) ? filterNavigationItems(currentNavGroup.items, searchText) : [], [currentNavGroup, searchText], ) @@ -164,6 +176,7 @@ export const Navigation = ({ // Prevent navigation, if the item is already active if ( selectedNavGroup?.id === navItem.id && + hasNavigationGroupItems(selectedNavGroup) && doesNavigationItemMatchPath(findActiveNavigationItemOfNavGroup(selectedNavGroup.items), pathname) ) { e.preventDefault() @@ -172,6 +185,13 @@ export const Navigation = ({ category: 'Navigation', action: `nav-${navItem.id}`, }) + + if (!hasNavigationGroupItems(navItem)) { + setHoveredNavGroup(null) + setSearchText('') + return + } + setHoveredNavGroup(navItem) setSearchText('') } @@ -186,25 +206,27 @@ export const Navigation = ({ setSearchText('') } - const handleNavGroupHover = (navGroup: typeof hoveredNavGroup) => (isHovered: boolean) => { - clearTimeout(timeoutRef.current) + const handleNavGroupHover = + (navGroup: NavigationGroupType & { items: NonNullable }) => + (isHovered: boolean) => { + clearTimeout(timeoutRef.current) - if (isHovered) { - if (!hoveredNavGroup) { - setHoveredNavGroup(navGroup) - return - } + if (isHovered) { + if (!hoveredNavGroup) { + setHoveredNavGroup(navGroup) + return + } - timeoutRef.current = setTimeout(() => { - setHoveredNavGroup(navGroup) - setSearchText('') - }, 50) + timeoutRef.current = setTimeout(() => { + setHoveredNavGroup(navGroup) + setSearchText('') + }, 50) + } } - } const handleOpenExpandedNavigation = (e: MouseEvent) => { if (!hoveredNavGroup && e.target === e.currentTarget) { - setHoveredNavGroup(selectedNavGroup) + setHoveredNavGroup(selectedExpandableNavGroup) } } @@ -253,8 +275,16 @@ export const Navigation = ({ isExpanded={isExpanded} isSelected={hoveredNavGroup?.id === item.id || selectedNavGroup?.id === item.id} onClick={handleNavGroupClick(item)} - to={findActiveNavigationItemOfNavGroup(item.items)?.href} - onHover={handleNavGroupHover(item)} + to={ + hasNavigationGroupItems(item) + ? findActiveNavigationItemOfNavGroup(item.items)?.href + : item.href + } + onHover={ + hasNavigationGroupItems(item) + ? handleNavGroupHover(item) + : handleCloseExpandedNavigation(true) + } showTooltip={item.disabled} /> ))} diff --git a/src/components/Navigation/constants.ts b/src/components/Navigation/constants.ts index 2e7908400c..52d9a87ab3 100644 --- a/src/components/Navigation/constants.ts +++ b/src/components/Navigation/constants.ts @@ -2,7 +2,7 @@ import { NavigationGroupType, NavigationItemType, ROUTER_URLS, SERVER_MODE } fro import { importComponentFromFELibrary } from '@Components/common' -import { filterNavGroupAndItem } from './utils' +import { filterNavGroupAndItem, hasNavigationGroupItems } from './utils' const APPLICATION_MANAGEMENT_POLICIES_NAV_ITEM: NavigationItemType = importComponentFromFELibrary( 'APPLICATION_MANAGEMENT_POLICIES_NAV_ITEM', @@ -244,6 +244,12 @@ const NAVIGATION_LIST: NavigationGroupType[] = [ ], }, ...(DATA_PROTECTION_MANAGEMENT_NAV_GROUP ? [DATA_PROTECTION_MANAGEMENT_NAV_GROUP] : []), + { + id: 'audit-logs', + title: 'Audit logs', + icon: 'ic-file-log-search', + href: ROUTER_URLS.AUDIT_LOGS, + }, { id: 'global-configuration', title: 'Global Configuration', @@ -325,37 +331,41 @@ export const getNavigationList = (serverMode: SERVER_MODE): NavigationGroupType[ ), ) - const filteredNavItems = filteredNavGroup.map((group) => { - const filteredItems = group.items.filter((item) => - filterNavGroupAndItem( - { - forceHideEnvKey: item.forceHideEnvKey, - hideNav: item.hideNav, - isAvailableInEA: item.isAvailableInEA, - }, - serverMode, - ), - ) - return { ...group, items: filteredItems } - }) + return filteredNavGroup.map((group) => { + if (!hasNavigationGroupItems(group)) { + return group + } + + const filteredItems = group.items + .filter((item) => + filterNavGroupAndItem( + { + forceHideEnvKey: item.forceHideEnvKey, + hideNav: item.hideNav, + isAvailableInEA: item.isAvailableInEA, + }, + serverMode, + ), + ) + .map((item) => { + if (item.hasSubMenu && item.subItems) { + const filteredSubItems = item.subItems.filter((subItem) => + filterNavGroupAndItem( + { + forceHideEnvKey: subItem.forceHideEnvKey, + hideNav: subItem.hideNav, + isAvailableInEA: subItem.isAvailableInEA, + }, + serverMode, + ), + ) - return filteredNavItems.map((group) => ({ - ...group, - items: group.items.map((item) => { - if (item.hasSubMenu && item.subItems) { - const filteredSubItems = item.subItems.filter((subItem) => - filterNavGroupAndItem( - { - forceHideEnvKey: subItem.forceHideEnvKey, - hideNav: subItem.hideNav, - isAvailableInEA: subItem.isAvailableInEA, - }, - serverMode, - ), - ) - return { ...item, subItems: filteredSubItems } - } - return item - }), - })) + return { ...item, subItems: filteredSubItems } + } + + return item + }) + + return { ...group, items: filteredItems } as NavigationGroupType + }) } diff --git a/src/components/Navigation/utils.ts b/src/components/Navigation/utils.ts index 27a89bff0b..5b2706f9c3 100644 --- a/src/components/Navigation/utils.ts +++ b/src/components/Navigation/utils.ts @@ -1,5 +1,6 @@ import { CommonNavigationItemType, + NavigationGroupType, NavigationItemType, SERVER_MODE, TreeNode, @@ -108,6 +109,10 @@ export const doesNavigationItemMatchPath = ( return !navItem.disabled && item.href && isSubPath(item.href, pathname) } +export const hasNavigationGroupItems = ( + group: NavigationGroupType | null | undefined, +): group is NavigationGroupType & { items: NavigationItemType[] } => Array.isArray(group?.items) + /** * Finds the first enabled navigation item within a group. * @param items The navigation item group to search. @@ -116,6 +121,14 @@ export const doesNavigationItemMatchPath = ( export const findActiveNavigationItemOfNavGroup = (items: NavigationItemType[]) => items.find(({ disabled }) => !disabled) +export const doesNavigationGroupMatchPath = (group: NavigationGroupType, pathname: string): boolean => { + if (hasNavigationGroupItems(group)) { + return group.items.some((item) => doesNavigationItemMatchPath(item, pathname)) + } + + return !group.disabled && isSubPath(group.href, pathname) +} + export const filterNavGroupAndItem = ( item: Pick, serverMode: SERVER_MODE, diff --git a/src/components/common/navigation/NavigationRoutes.tsx b/src/components/common/navigation/NavigationRoutes.tsx index c567310628..5ef88500a0 100644 --- a/src/components/common/navigation/NavigationRoutes.tsx +++ b/src/components/common/navigation/NavigationRoutes.tsx @@ -70,6 +70,7 @@ import { getUserRole } from '@Pages/GlobalConfigurations/Authorization/authoriza import EditClusterDrawerContent from '@Pages/GlobalConfigurations/ClustersAndEnvironments/EditClusterDrawerContent' import { ReleaseConfigurations } from '@Pages/Releases/Detail' import { ApplicationManagementRouter } from '@PagesDevtron2.0/ApplicationManagement' +import AuditLogsRouter from '@PagesDevtron2.0/AuditLogs/AuditLogsList' import { InfrastructureManagementRouter } from '@PagesDevtron2.0/InfrastructureManagement' import { SERVER_MODE, ViewType } from '../../../config' @@ -562,6 +563,11 @@ const NavigationRoutes = ({ reloadVersionConfig }: Readonly} /> + } + /> {!window._env_.K8S_CLIENT ? [ ...(serverMode === SERVER_MODE.FULL From c99d0f650f78df19ef824468b8dbfb0ce4733ece Mon Sep 17 00:00:00 2001 From: shivani170 Date: Wed, 22 Apr 2026 17:33:31 +0530 Subject: [PATCH 02/17] chore: css fixes --- .../AuditLogs/AuditLogDetail.tsx | 106 ++++------ .../AuditLogs/AuditLogsList.tsx | 40 ++-- .../AuditLogs/AuditLogsRouter.tsx | 4 - src/Pages-Devtron-2.0/AuditLogs/service.tsx | 184 ++++-------------- src/Pages-Devtron-2.0/AuditLogs/types.ts | 17 +- src/Pages-Devtron-2.0/AuditLogs/utils.tsx | 52 ++--- 6 files changed, 139 insertions(+), 264 deletions(-) diff --git a/src/Pages-Devtron-2.0/AuditLogs/AuditLogDetail.tsx b/src/Pages-Devtron-2.0/AuditLogs/AuditLogDetail.tsx index 47b00bc556..dbc7ae508a 100644 --- a/src/Pages-Devtron-2.0/AuditLogs/AuditLogDetail.tsx +++ b/src/Pages-Devtron-2.0/AuditLogs/AuditLogDetail.tsx @@ -1,97 +1,75 @@ -import { useParams } from 'react-router-dom' import dayjs from 'dayjs' import { - BreadCrumb, + Button, + ButtonStyleType, + ButtonVariantType, CodeEditor, + ComponentSizeType, DATE_TIME_FORMATS, + Drawer, Icon, MODES, - PageHeader, - Progressing, - ROUTER_URLS, - useBreadcrumb, - useQuery, } from '@devtron-labs/devtron-fe-common-lib' -import { getAuditLogDetail } from './service' -import { AuditLogParamsType } from './types' +import { AuditLogDetailType } from './types' import './auditLog.scss' -const AuditLogDetail = () => { - const { auditLogId = '' } = useParams() - - const { data: auditLog, isFetching } = useQuery({ - queryFn: async () => { - const response = await getAuditLogDetail(auditLogId) - return response - }, - queryKey: ['audit-log-detail', auditLogId], - enabled: !!auditLogId, - }) - - const { breadcrumbs } = useBreadcrumb( - ROUTER_URLS.AUDIT_LOGS_DETAIL, - { - alias: { - 'audit-logs': { - component: , - linked: true, - }, - ':auditLogId': { - component: {auditLog?.action || 'Details'}, - linked: false, - }, - }, - }, - [auditLog?.action], - ) +interface AuditLogDetailProps { + auditLog: AuditLogDetailType + onClose: () => void +} - if (isFetching) { - return ( -
- +const AuditLogDetail = ({ auditLog, onClose }: AuditLogDetailProps) => ( + +
+
+
+ +

{auditLog.action}

+
+
- ) - } - - const breadCrumbs = () => - - return ( -
-
-
- {auditLog.action} -

Timestamp

- {dayjs(auditLog.timestamp).format(DATE_TIME_FORMATS.TWELVE_HOURS_FORMAT)} + {auditLog.timeStamp + ? dayjs(auditLog.timeStamp).format(DATE_TIME_FORMATS.TWELVE_HOURS_FORMAT) + : '-'}

Type

-

{auditLog.type}

+

{auditLog.requestMethod || '-'}

Module

-

{auditLog.module}

+

{auditLog.module || '-'}

-

Action

-

{auditLog.action}

+

User

+

{auditLog.user || '-'}

-

User

-

{auditLog.user}

+

Resource Name

+

{auditLog.resourceName || '-'}

-

Resource

-

{auditLog.resource}

+

Resource Type

+

{auditLog.resourceType || '-'}

@@ -105,14 +83,14 @@ const AuditLogDetail = () => { readOnly mode={MODES.JSON} noParsing - value={JSON.stringify(auditLog.payload, null, 2)} + value={JSON.stringify(auditLog.payload ?? {}, null, 2)} height="fitToParent" />
- ) -} + +) export default AuditLogDetail diff --git a/src/Pages-Devtron-2.0/AuditLogs/AuditLogsList.tsx b/src/Pages-Devtron-2.0/AuditLogs/AuditLogsList.tsx index 46a4968ffd..29c512c876 100644 --- a/src/Pages-Devtron-2.0/AuditLogs/AuditLogsList.tsx +++ b/src/Pages-Devtron-2.0/AuditLogs/AuditLogsList.tsx @@ -1,23 +1,22 @@ -import { useMemo } from 'react' -import { useParams } from 'react-router-dom' +import { useCallback, useMemo, useState } from 'react' import { BreadCrumb, - ErrorScreenManager, FiltersTypeEnum, PageHeader, PaginationEnum, - Progressing, ROUTER_URLS, Table, + TableProps, useBreadcrumb, useQuery, } from '@devtron-labs/devtron-fe-common-lib' +import AuditLogDetail from './AuditLogDetail' import AuditLogsTableWrapper from './AuditLogsTableWrapper' import { getAuditLogFilterOptions, getAuditLogList } from './service' -import { AuditLogRowType, AuditLogTableAdditionalProps } from './types' -import { getAuditLogColumns, parseAuditLogURLParams, useGetAuditLogDetailsResponse } from './utils' +import { AuditLogDetailType, AuditLogRowType, AuditLogTableAdditionalProps } from './types' +import { getAuditLogColumns, parseAuditLogURLParams } from './utils' const EMPTY_FILTER_OPTIONS: AuditLogTableAdditionalProps['filterOptions'] = { typeOptions: [], @@ -26,9 +25,7 @@ const EMPTY_FILTER_OPTIONS: AuditLogTableAdditionalProps['filterOptions'] = { const AuditLogsList = () => { const columns = useMemo(() => getAuditLogColumns(), []) - const { auditLogId } = useParams() - - const { auditLogLoading, auditLogError, reloadAuditLog } = useGetAuditLogDetailsResponse(auditLogId) + const [selectedAuditLog, setSelectedAuditLog] = useState(null) const { breadcrumbs } = useBreadcrumb(ROUTER_URLS.AUDIT_LOGS, { alias: { @@ -50,17 +47,17 @@ const AuditLogsList = () => { select: (data) => data, }) - if (auditLogLoading) { - return ( -
- -
- ) - } + const handleSelectAuditLog = useCallback((auditLog: AuditLogDetailType) => { + setSelectedAuditLog(auditLog) + }, []) + + const handleCloseDetail = useCallback(() => { + setSelectedAuditLog(null) + }, []) - if (auditLogError) { - return - } + const handleRowClick = useCallback< + NonNullable['onRowClick']> + >((row) => handleSelectAuditLog(row.data as AuditLogDetailType), [handleSelectAuditLog]) const auditLogBreadcrumb = () => @@ -80,6 +77,7 @@ const AuditLogsList = () => { }} paginationVariant={PaginationEnum.PAGINATED} getRows={getAuditLogList} + onRowClick={handleRowClick} emptyStateConfig={{ noRowsConfig: { title: 'No audit logs found', @@ -88,8 +86,10 @@ const AuditLogsList = () => { }, }} filter={null} - additionalProps={{ filterOptions }} + additionalProps={{ filterOptions, onSelectAuditLog: handleSelectAuditLog }} /> + + {selectedAuditLog && } ) } diff --git a/src/Pages-Devtron-2.0/AuditLogs/AuditLogsRouter.tsx b/src/Pages-Devtron-2.0/AuditLogs/AuditLogsRouter.tsx index 7847aa23e2..1c2feb9a37 100644 --- a/src/Pages-Devtron-2.0/AuditLogs/AuditLogsRouter.tsx +++ b/src/Pages-Devtron-2.0/AuditLogs/AuditLogsRouter.tsx @@ -1,14 +1,10 @@ import { Route, Routes } from 'react-router-dom' -import { BASE_ROUTES } from '@devtron-labs/devtron-fe-common-lib' - -import AuditLogDetail from './AuditLogDetail' import AuditLogsList from './AuditLogsList' const AuditLogsRouter = () => ( } /> - } /> ) diff --git a/src/Pages-Devtron-2.0/AuditLogs/service.tsx b/src/Pages-Devtron-2.0/AuditLogs/service.tsx index 8084e17d3e..0048fb00a5 100644 --- a/src/Pages-Devtron-2.0/AuditLogs/service.tsx +++ b/src/Pages-Devtron-2.0/AuditLogs/service.tsx @@ -1,6 +1,7 @@ -import { ResponseType, SortingOrder } from '@devtron-labs/devtron-fe-common-lib' +import { get, SortingOrder } from '@devtron-labs/devtron-fe-common-lib' import { + AuditLogApiResponse, AuditLogDetailType, AuditLogFilterKeys, AuditLogFilterOptionsType, @@ -9,128 +10,12 @@ import { GetAuditLogListProps, } from './types' -const MOCK_AUDIT_LOGS: AuditLogDetailType[] = [ - { - auditLogId: 101, - timestamp: '2026-04-08T10:15:00Z', - action: 'Application updated', - type: 'UPDATE', - user: 'shivani@devtron.ai', - resource: 'frontend-service', - module: 'Application Management', - payload: { before: { replicas: 2 }, after: { replicas: 3 }, env: 'production' }, - }, - { - auditLogId: 102, - timestamp: '2026-04-08T09:42:00Z', - action: 'Deployment triggered', - type: 'DEPLOY', - user: 'rahul@devtron.ai', - resource: 'payments-service', - module: 'Application Management', - payload: { image: 'payments:v2.4.1', pipelineId: 98, triggerType: 'manual' }, - }, - { - auditLogId: 103, - timestamp: '2026-04-08T08:30:00Z', - action: 'Policy enabled', - type: 'CONFIGURE', - user: 'sre@devtron.ai', - resource: 'restrict-prod-deployments', - module: 'Security Center', - payload: { policy: 'restrict-prod-deployments', state: 'enabled' }, - }, - { - auditLogId: 104, - timestamp: '2026-04-07T16:10:00Z', - action: 'Cluster added', - type: 'CREATE', - user: 'infra@devtron.ai', - resource: 'gke-prod-cluster', - module: 'Infrastructure Management', - payload: { clusterName: 'gke-prod-cluster', provider: 'gke', region: 'asia-south1' }, - }, - { - auditLogId: 105, - timestamp: '2026-04-07T15:00:00Z', - action: 'Chart version promoted', - type: 'PROMOTE', - user: 'release@devtron.ai', - resource: 'customer-portal', - module: 'Software Release Management', - payload: { chartVersion: '1.4.0', release: 'spring-release', target: 'staging' }, - }, - { - auditLogId: 106, - timestamp: '2026-04-07T13:25:00Z', - action: 'Backup scheduled', - type: 'CREATE', - user: 'ops@devtron.ai', - resource: 'nightly-backup', - module: 'Data Protection Management', - payload: { schedule: '0 1 * * *', storageLocation: 's3-prod-backups' }, - }, - { - auditLogId: 107, - timestamp: '2026-04-07T12:05:00Z', - action: 'Restore triggered', - type: 'RESTORE', - user: 'ops@devtron.ai', - resource: 'checkout-db', - module: 'Data Protection Management', - payload: { backupName: 'checkout-db-2026-04-07', destinationCluster: 'dr-cluster' }, - }, - { - auditLogId: 108, - timestamp: '2026-04-07T11:15:00Z', - action: 'Build triggered', - type: 'TRIGGER', - user: 'automation@devtron.ai', - resource: 'catalog-service', - module: 'Automation & Enablement', - payload: { pipeline: 'catalog-build', branch: 'main', commit: 'a1b2c3d' }, - }, - { - auditLogId: 109, - timestamp: '2026-04-06T18:40:00Z', - action: 'Cost configuration updated', - type: 'UPDATE', - user: 'finops@devtron.ai', - resource: 'aws-prod', - module: 'Cost Visibility', - payload: { currency: 'USD', idleCostThreshold: 20 }, - }, - { - auditLogId: 110, - timestamp: '2026-04-06T14:10:00Z', - action: 'Role mapping updated', - type: 'UPDATE', - user: 'platform-admin@devtron.ai', - resource: 'sso-role-map', - module: 'Global Configurations', - payload: { role: 'super-admin', groups: ['platform-team', 'sre'] }, - }, - { - auditLogId: 111, - timestamp: '2026-04-05T09:55:00Z', - action: 'Webhook deleted', - type: 'DELETE', - user: 'notifications@devtron.ai', - resource: 'slack-prod-alerts', - module: 'Global Configurations', - payload: { webhookName: 'slack-prod-alerts', integration: 'Slack' }, - }, - { - auditLogId: 112, - timestamp: '2026-04-04T07:20:00Z', - action: 'Vulnerability policy acknowledged', - type: 'ACKNOWLEDGE', - user: 'security@devtron.ai', - resource: 'cve-policy-2026-04', - module: 'Security Center', - payload: { severity: 'critical', justification: 'Mitigated by network policy' }, - }, -] +const AUDIT_LOG_ENDPOINT = 'audit-log' + +const fetchAuditLogs = async (signal?: AbortSignal): Promise => { + const response = await get(AUDIT_LOG_ENDPOINT, { signal }) + return response.result?.auditLogs?.data ?? [] +} const matchesSearch = (auditLog: AuditLogDetailType, searchKey: string) => { if (!searchKey) { @@ -139,7 +24,15 @@ const matchesSearch = (auditLog: AuditLogDetailType, searchKey: string) => { const normalizedSearchKey = searchKey.toLowerCase() - return [auditLog.action, auditLog.user, auditLog.resource, auditLog.module, auditLog.type] + return [ + auditLog.action, + auditLog.user, + auditLog.resourceName, + auditLog.resourceType, + auditLog.module, + auditLog.requestMethod, + ] + .filter(Boolean) .join(' ') .toLowerCase() .includes(normalizedSearchKey) @@ -166,29 +59,22 @@ const getSortedAuditLogs = ( }) } -// TODO: Remove later -export const getAuditListResponse = async (): Promise> => { - const response = { - status: 'OK', - code: 200, - result: MOCK_AUDIT_LOGS, - } - return response -} - export const getAuditLogList = async ({ offset, pageSize, searchKey, sortBy, sortOrder, + signal, [AuditLogFilterKeys.TYPE]: type = [], [AuditLogFilterKeys.MODULE]: module = [], }: GetAuditLogListProps): Promise<{ rows: { id: string; data: AuditLogRowType }[]; totalRows: number }> => { - const filteredAuditLogs = (await getAuditListResponse()).result?.filter( + const auditLogs = await fetchAuditLogs(signal) + + const filteredAuditLogs = auditLogs.filter( (auditLog) => matchesSearch(auditLog, searchKey) && - (!type.length || type.includes(auditLog.type)) && + (!type.length || type.includes(auditLog.requestMethod)) && (!module.length || module.includes(auditLog.module)), ) @@ -196,7 +82,7 @@ export const getAuditLogList = async ({ const paginatedAuditLogs = sortedAuditLogs.slice(offset, offset + pageSize) return { - rows: paginatedAuditLogs.map(({ ...auditLog }) => ({ + rows: paginatedAuditLogs.map((auditLog) => ({ id: String(auditLog.auditLogId), data: auditLog, })), @@ -204,14 +90,16 @@ export const getAuditLogList = async ({ } } -export const getAuditLogFilterOptions = async (): Promise => ({ - typeOptions: [...new Set(MOCK_AUDIT_LOGS.map(({ type }) => type))] - .sort((first, second) => first.localeCompare(second)) - .map((value) => ({ label: value, value })), - moduleOptions: [...new Set(MOCK_AUDIT_LOGS.map(({ module }) => module))] - .sort((first, second) => first.localeCompare(second)) - .map((value) => ({ label: value, value })), -}) - -export const getAuditLogDetail = async (auditLogId: string): Promise => - MOCK_AUDIT_LOGS.find((auditLog) => String(auditLog.auditLogId) === auditLogId) ?? null +export const getAuditLogFilterOptions = async (): Promise => { + const auditLogs = await fetchAuditLogs() + + const toOptions = (values: string[]) => + [...new Set(values.filter(Boolean))] + .sort((first, second) => first.localeCompare(second)) + .map((value) => ({ label: value, value })) + + return { + typeOptions: toOptions(auditLogs.map(({ requestMethod: type }) => type)), + moduleOptions: toOptions(auditLogs.map(({ module }) => module)), + } +} diff --git a/src/Pages-Devtron-2.0/AuditLogs/types.ts b/src/Pages-Devtron-2.0/AuditLogs/types.ts index fc78714afa..6eaf19a6c0 100644 --- a/src/Pages-Devtron-2.0/AuditLogs/types.ts +++ b/src/Pages-Devtron-2.0/AuditLogs/types.ts @@ -15,7 +15,8 @@ export enum AuditLogSortableKeys { ACTION = 'action', TYPE = 'type', USER = 'user', - RESOURCE = 'resource', + RESOURCE_NAME = 'resourceName', + RESOURCE_TYPE = 'resourceType', MODULE = 'module', } @@ -26,11 +27,12 @@ export type AuditLogFiltersType = { export interface AuditLogRowType { auditLogId: number - timestamp: string + timeStamp: string action: string - type: string + requestMethod: string user: string - resource: string + resourceName: string + resourceType: string module: string } @@ -38,7 +40,11 @@ export interface AuditLogDetailType extends AuditLogRowType { payload: Record } -export interface AuditLogParamsType extends Record<'auditLogId', string> {} +export interface AuditLogApiResponse { + auditLogs: { + data: AuditLogDetailType[] + } +} export type AuditLogFilterOptionsType = { typeOptions: SelectPickerOptionType[] @@ -47,6 +53,7 @@ export type AuditLogFilterOptionsType = { export type AuditLogTableAdditionalProps = { filterOptions: AuditLogFilterOptionsType + onSelectAuditLog: (auditLog: AuditLogDetailType) => void } export type AuditLogsTableProps = TableProps diff --git a/src/Pages-Devtron-2.0/AuditLogs/utils.tsx b/src/Pages-Devtron-2.0/AuditLogs/utils.tsx index d7e936b03c..383abcc70c 100644 --- a/src/Pages-Devtron-2.0/AuditLogs/utils.tsx +++ b/src/Pages-Devtron-2.0/AuditLogs/utils.tsx @@ -1,36 +1,27 @@ -import { generatePath, NavLink } from 'react-router-dom' import dayjs from 'dayjs' import { + capitalizeFirstLetter, DATE_TIME_FORMATS, FiltersTypeEnum, getAlphabetIcon, - ROUTER_URLS, TableCellComponentProps, - useAsync, } from '@devtron-labs/devtron-fe-common-lib' -import { getAuditLogDetail } from './service' import { AuditLogFilterKeys, AuditLogFiltersType, AuditLogRowType, AuditLogsTableProps } from './types' const TimestampCellComponent = ({ value }: TableCellComponentProps) => (
- {value ? dayjs(String(value)).format(DATE_TIME_FORMATS.TWELVE_HOURS_FORMAT) : '-'} + {value ? dayjs(String(value)).format(DATE_TIME_FORMATS.TWELVE_HOURS_FORMAT) : 'test'}
) const ActionCellComponent = ({ row }: TableCellComponentProps) => ( - - {row.data.action} - +
+ {`${capitalizeFirstLetter(row.data.action)}ed ${row.data.resourceName} ${row.data.resourceType} `} +
) const UserCellComponent = ({ row }: TableCellComponentProps) => ( @@ -42,9 +33,21 @@ const UserCellComponent = ({ row }: TableCellComponentProps ) +const ModuleCellComponent = ({ row }: TableCellComponentProps) => ( +
+ {capitalizeFirstLetter(row.data.module)} +
+) + +const TypeCellComponent = ({ row }: TableCellComponentProps) => ( +
+ {capitalizeFirstLetter(row.data.requestMethod)} +
+) + export const getAuditLogColumns = (): AuditLogsTableProps['columns'] => [ { - field: 'timestamp', + field: 'timeStamp', label: 'Timestamp', size: { fixed: 220 }, isSortable: true, @@ -58,10 +61,11 @@ export const getAuditLogColumns = (): AuditLogsTableProps['columns'] => [ CellComponent: ActionCellComponent, }, { - field: 'type', + field: 'requestMethod', label: 'Type', size: { fixed: 130 }, isSortable: true, + CellComponent: TypeCellComponent, }, { field: 'user', @@ -71,16 +75,23 @@ export const getAuditLogColumns = (): AuditLogsTableProps['columns'] => [ CellComponent: UserCellComponent, }, { - field: 'resource', - label: 'Resource', + field: 'resourceType', + label: 'Resource Type', size: { fixed: 180 }, isSortable: true, }, + { + field: 'resourceName', + label: 'Resource Name', + size: { fixed: 200 }, + isSortable: true, + }, { field: 'module', label: 'Module', size: { fixed: 190 }, isSortable: true, + CellComponent: ModuleCellComponent, }, ] @@ -99,8 +110,3 @@ export const getAuditLogFilterLabel = (filterKey: string) => { return filterKey } } - -export const useGetAuditLogDetailsResponse = (auditLogId) => { - const [auditLogLoading, auditLog, auditLogError, reloadAuditLog] = useAsync(() => getAuditLogDetail(auditLogId), []) - return { auditLogLoading, auditLog, auditLogError, reloadAuditLog } -} From 98f084a06aab4d9df588dbf72d830b5bf11c0828 Mon Sep 17 00:00:00 2001 From: shivani170 Date: Tue, 5 May 2026 08:52:41 +0530 Subject: [PATCH 03/17] chore: audit log fixes --- .../AuditLogs/AuditLogDetail.tsx | 141 +++++++++++------- .../AuditLogs/AuditLogsList.tsx | 48 ++++-- src/Pages-Devtron-2.0/AuditLogs/service.tsx | 140 ++++++++--------- src/Pages-Devtron-2.0/AuditLogs/types.ts | 34 ++--- src/Pages-Devtron-2.0/AuditLogs/utils.tsx | 14 +- 5 files changed, 219 insertions(+), 158 deletions(-) diff --git a/src/Pages-Devtron-2.0/AuditLogs/AuditLogDetail.tsx b/src/Pages-Devtron-2.0/AuditLogs/AuditLogDetail.tsx index dbc7ae508a..ea7eb3b2d9 100644 --- a/src/Pages-Devtron-2.0/AuditLogs/AuditLogDetail.tsx +++ b/src/Pages-Devtron-2.0/AuditLogs/AuditLogDetail.tsx @@ -21,76 +21,101 @@ interface AuditLogDetailProps { onClose: () => void } -const AuditLogDetail = ({ auditLog, onClose }: AuditLogDetailProps) => ( - -
-
-
- -

{auditLog.action}

-
-
+const getFormattedPayload = (value: AuditLogDetailType['jsonFormatLog'] | AuditLogDetailType['payload']) => { + if (!value) { + return '' + } -
-
-
-

Timestamp

-

- {auditLog.timeStamp - ? dayjs(auditLog.timeStamp).format(DATE_TIME_FORMATS.TWELVE_HOURS_FORMAT) - : '-'} -

-
-
-

Type

-

{auditLog.requestMethod || '-'}

-
-
-

Module

-

{auditLog.module || '-'}

-
-
-

User

-

{auditLog.user || '-'}

-
-
-

Resource Name

-

{auditLog.resourceName || '-'}

-
-
-

Resource Type

-

{auditLog.resourceType || '-'}

+ if (typeof value !== 'string') { + return JSON.stringify(value, null, 2) + } + + try { + return JSON.stringify(JSON.parse(value), null, 2) + } catch { + return value + } +} + +const getAuditLogPayload = ({ jsonFormatLog, payload }: AuditLogDetailType) => { + const formattedJsonLog = getFormattedPayload(jsonFormatLog) + + return formattedJsonLog || getFormattedPayload(payload) +} + +const AuditLogDetail = ({ auditLog, onClose }: AuditLogDetailProps) => { + const payload = getAuditLogPayload(auditLog) + + return ( + +
+
+
+ +

{auditLog.action}

+
-
-
-

Payload

+
+
+
+

Timestamp

+

+ {auditLog.timeStamp + ? dayjs(auditLog.timeStamp).format(DATE_TIME_FORMATS.TWELVE_HOURS_FORMAT) + : '-'} +

+
+
+

Type

+

{auditLog.requestMethod || '-'}

+
+
+

Module

+

{auditLog.module || '-'}

+
+
+

User

+

{auditLog.user || '-'}

+
+
+

Resource Name

+

{auditLog.resourceName || '-'}

+
+
+

Resource Type

+

{auditLog.resourceType || '-'}

+
-
+ -
+ > + +

Payload

+
+ +
-
- -) + + ) +} export default AuditLogDetail diff --git a/src/Pages-Devtron-2.0/AuditLogs/AuditLogsList.tsx b/src/Pages-Devtron-2.0/AuditLogs/AuditLogsList.tsx index 29c512c876..7c919173ee 100644 --- a/src/Pages-Devtron-2.0/AuditLogs/AuditLogsList.tsx +++ b/src/Pages-Devtron-2.0/AuditLogs/AuditLogsList.tsx @@ -1,4 +1,5 @@ import { useCallback, useMemo, useState } from 'react' +import { useNavigate } from 'react-router-dom' import { BreadCrumb, @@ -14,10 +15,16 @@ import { import AuditLogDetail from './AuditLogDetail' import AuditLogsTableWrapper from './AuditLogsTableWrapper' -import { getAuditLogFilterOptions, getAuditLogList } from './service' -import { AuditLogDetailType, AuditLogRowType, AuditLogTableAdditionalProps } from './types' +import { getAuditLogFilterOptions, getAuditLogList, getAuditLogs } from './service' +import { + AuditLogDetailType, + AuditLogRowType, + AuditLogTableAdditionalProps, + NormalizedAuditLogApiResponse, +} from './types' import { getAuditLogColumns, parseAuditLogURLParams } from './utils' +const EMPTY_AUDIT_LOGS: AuditLogDetailType[] = [] const EMPTY_FILTER_OPTIONS: AuditLogTableAdditionalProps['filterOptions'] = { typeOptions: [], moduleOptions: [], @@ -26,6 +33,7 @@ const EMPTY_FILTER_OPTIONS: AuditLogTableAdditionalProps['filterOptions'] = { const AuditLogsList = () => { const columns = useMemo(() => getAuditLogColumns(), []) const [selectedAuditLog, setSelectedAuditLog] = useState(null) + const navigate = useNavigate() const { breadcrumbs } = useBreadcrumb(ROUTER_URLS.AUDIT_LOGS, { alias: { @@ -36,17 +44,22 @@ const AuditLogsList = () => { }, }) - const { data: filterOptions = EMPTY_FILTER_OPTIONS } = useQuery< - Awaited>, - AuditLogTableAdditionalProps['filterOptions'], - ['audit-log-filter-options'], + const { data: auditLogs = EMPTY_AUDIT_LOGS, isLoading: areAuditLogsLoading } = useQuery< + NormalizedAuditLogApiResponse, + AuditLogDetailType[], + ['audit-logs'], false >({ - queryFn: getAuditLogFilterOptions, - queryKey: ['audit-log-filter-options'], - select: (data) => data, + queryFn: ({ signal }) => getAuditLogs({}, signal), + queryKey: ['audit-logs'], + select: (response) => response.data, }) + const filterOptions = useMemo( + () => (auditLogs.length ? getAuditLogFilterOptions(auditLogs) : EMPTY_FILTER_OPTIONS), + [auditLogs], + ) + const handleSelectAuditLog = useCallback((auditLog: AuditLogDetailType) => { setSelectedAuditLog(auditLog) }, []) @@ -59,6 +72,10 @@ const AuditLogsList = () => { NonNullable['onRowClick']> >((row) => handleSelectAuditLog(row.data as AuditLogDetailType), [handleSelectAuditLog]) + const clearFilters = useCallback(() => { + navigate(ROUTER_URLS.AUDIT_LOGS) + }, [navigate]) + const auditLogBreadcrumb = () => return ( @@ -71,21 +88,26 @@ const AuditLogsList = () => { ViewWrapper={AuditLogsTableWrapper} filtersVariant={FiltersTypeEnum.URL} additionalFilterProps={{ - initialSortKey: 'timestamp', + initialSortKey: 'timeStamp', parseSearchParams: parseAuditLogURLParams, - defaultPageSize: 5, }} paginationVariant={PaginationEnum.PAGINATED} getRows={getAuditLogList} + filter={null} + loading={areAuditLogsLoading} onRowClick={handleRowClick} + clearFilters={clearFilters} emptyStateConfig={{ noRowsConfig: { title: 'No audit logs found', subTitle: 'Audit trail entries will show up here once actions are performed.', - noImage: true, + }, + noRowsForFilterConfig: { + title: 'No audit logs found', + subTitle: 'Try adjusting your search or filters', + clearFilters, }, }} - filter={null} additionalProps={{ filterOptions, onSelectAuditLog: handleSelectAuditLog }} /> diff --git a/src/Pages-Devtron-2.0/AuditLogs/service.tsx b/src/Pages-Devtron-2.0/AuditLogs/service.tsx index 0048fb00a5..48faa3f6d4 100644 --- a/src/Pages-Devtron-2.0/AuditLogs/service.tsx +++ b/src/Pages-Devtron-2.0/AuditLogs/service.tsx @@ -1,98 +1,100 @@ -import { get, SortingOrder } from '@devtron-labs/devtron-fe-common-lib' +import { get, getUrlWithSearchParams } from '@devtron-labs/devtron-fe-common-lib' import { AuditLogApiResponse, AuditLogDetailType, AuditLogFilterKeys, AuditLogFilterOptionsType, - AuditLogRowType, - AuditLogSortableKeys, - GetAuditLogListProps, + AuditLogsTableProps, + GetAuditLogsParams, + NormalizedAuditLogApiResponse, } from './types' const AUDIT_LOG_ENDPOINT = 'audit-log' -const fetchAuditLogs = async (signal?: AbortSignal): Promise => { - const response = await get(AUDIT_LOG_ENDPOINT, { signal }) - return response.result?.auditLogs?.data ?? [] -} +const normalizeAuditLogResponse = (response?: AuditLogApiResponse): NormalizedAuditLogApiResponse => { + const { + data = response?.data ?? [], + offset = response?.offset ?? 0, + size = response?.size ?? data.length, + totalCount = response?.totalCount ?? data.length, + } = response?.auditLogs ?? {} -const matchesSearch = (auditLog: AuditLogDetailType, searchKey: string) => { - if (!searchKey) { - return true + return { + data, + offset, + size, + totalCount, } - - const normalizedSearchKey = searchKey.toLowerCase() - - return [ - auditLog.action, - auditLog.user, - auditLog.resourceName, - auditLog.resourceType, - auditLog.module, - auditLog.requestMethod, - ] - .filter(Boolean) - .join(' ') - .toLowerCase() - .includes(normalizedSearchKey) } -const getSortedAuditLogs = ( - auditLogs: AuditLogDetailType[], - sortBy: string | undefined, - sortOrder: SortingOrder | undefined, -) => { - const sortingKey = (sortBy as AuditLogSortableKeys) || AuditLogSortableKeys.TIMESTAMP - const effectiveSortOrder = sortOrder || SortingOrder.DESC - - return [...auditLogs].sort((first, second) => { - const firstValue = first[sortingKey] - const secondValue = second[sortingKey] - - const comparison = - sortingKey === AuditLogSortableKeys.TIMESTAMP - ? new Date(firstValue).getTime() - new Date(secondValue).getTime() - : String(firstValue).localeCompare(String(secondValue)) - - return effectiveSortOrder === SortingOrder.ASC ? comparison : -comparison +export const getAuditLogs = async ( + params?: Partial, + signal?: AbortSignal, +): Promise => { + const { + offset = 0, + pageSize, + searchKey, + sortBy, + sortOrder, + [AuditLogFilterKeys.TYPE]: type, + [AuditLogFilterKeys.MODULE]: module, + } = params ?? {} + + const url = getUrlWithSearchParams(AUDIT_LOG_ENDPOINT, { + offset, + size: pageSize, + searchKey, + sortBy, + sortOrder, + type, + module, }) + const { result } = await get(url, { signal }) + + return normalizeAuditLogResponse(result) } -export const getAuditLogList = async ({ - offset, - pageSize, - searchKey, - sortBy, - sortOrder, +export const getAuditLogList: AuditLogsTableProps['getRows'] = async ( + { + offset, + pageSize, + searchKey, + sortBy, + sortOrder, + [AuditLogFilterKeys.TYPE]: type = [], + [AuditLogFilterKeys.MODULE]: module = [], + }: GetAuditLogsParams, signal, - [AuditLogFilterKeys.TYPE]: type = [], - [AuditLogFilterKeys.MODULE]: module = [], -}: GetAuditLogListProps): Promise<{ rows: { id: string; data: AuditLogRowType }[]; totalRows: number }> => { - const auditLogs = await fetchAuditLogs(signal) - - const filteredAuditLogs = auditLogs.filter( - (auditLog) => - matchesSearch(auditLog, searchKey) && - (!type.length || type.includes(auditLog.requestMethod)) && - (!module.length || module.includes(auditLog.module)), +) => { + const auditLogResponse = await getAuditLogs( + { + offset, + pageSize, + searchKey, + sortBy, + sortOrder, + [AuditLogFilterKeys.TYPE]: type, + [AuditLogFilterKeys.MODULE]: module, + }, + signal, ) - - const sortedAuditLogs = getSortedAuditLogs(filteredAuditLogs, sortBy, sortOrder) - const paginatedAuditLogs = sortedAuditLogs.slice(offset, offset + pageSize) + const pageAuditLogs = + auditLogResponse.data.length > pageSize + ? auditLogResponse.data.slice(offset, offset + pageSize) + : auditLogResponse.data return { - rows: paginatedAuditLogs.map((auditLog) => ({ - id: String(auditLog.auditLogId), + rows: pageAuditLogs.map((auditLog, index) => ({ + id: `${auditLog.auditLogId}-${offset + index}`, data: auditLog, })), - totalRows: filteredAuditLogs.length, + totalRows: auditLogResponse.totalCount, } } -export const getAuditLogFilterOptions = async (): Promise => { - const auditLogs = await fetchAuditLogs() - +export const getAuditLogFilterOptions = (auditLogs: AuditLogDetailType[]): AuditLogFilterOptionsType => { const toOptions = (values: string[]) => [...new Set(values.filter(Boolean))] .sort((first, second) => first.localeCompare(second)) diff --git a/src/Pages-Devtron-2.0/AuditLogs/types.ts b/src/Pages-Devtron-2.0/AuditLogs/types.ts index 6eaf19a6c0..64b199b124 100644 --- a/src/Pages-Devtron-2.0/AuditLogs/types.ts +++ b/src/Pages-Devtron-2.0/AuditLogs/types.ts @@ -10,16 +10,6 @@ export enum AuditLogFilterKeys { MODULE = 'module', } -export enum AuditLogSortableKeys { - TIMESTAMP = 'timestamp', - ACTION = 'action', - TYPE = 'type', - USER = 'user', - RESOURCE_NAME = 'resourceName', - RESOURCE_TYPE = 'resourceType', - MODULE = 'module', -} - export type AuditLogFiltersType = { [AuditLogFilterKeys.TYPE]: string[] [AuditLogFilterKeys.MODULE]: string[] @@ -37,15 +27,27 @@ export interface AuditLogRowType { } export interface AuditLogDetailType extends AuditLogRowType { - payload: Record + jsonFormatLog?: string + payload?: Record | string } export interface AuditLogApiResponse { - auditLogs: { - data: AuditLogDetailType[] + data?: AuditLogDetailType[] + offset?: number + size?: number + totalCount?: number + auditLogs?: { + data?: AuditLogDetailType[] + offset?: number + size?: number + totalCount?: number } } +export type NormalizedAuditLogApiResponse = Required< + Pick +> + export type AuditLogFilterOptionsType = { typeOptions: SelectPickerOptionType[] moduleOptions: SelectPickerOptionType[] @@ -58,10 +60,8 @@ export type AuditLogTableAdditionalProps = { export type AuditLogsTableProps = TableProps -export type GetAuditLogListProps = AuditLogFiltersType & - Parameters>[0] & { - signal: AbortSignal - } +export type GetAuditLogsParams = Partial & + Parameters>[0] export type AuditLogsTableWrapperProps = TableViewWrapperProps< AuditLogRowType, diff --git a/src/Pages-Devtron-2.0/AuditLogs/utils.tsx b/src/Pages-Devtron-2.0/AuditLogs/utils.tsx index 383abcc70c..0871088f29 100644 --- a/src/Pages-Devtron-2.0/AuditLogs/utils.tsx +++ b/src/Pages-Devtron-2.0/AuditLogs/utils.tsx @@ -20,7 +20,7 @@ const TimestampCellComponent = ({ value }: TableCellComponentProps) => (
- {`${capitalizeFirstLetter(row.data.action)}ed ${row.data.resourceName} ${row.data.resourceType} `} + {`${capitalizeFirstLetter(row.data.action)}d ${row.data.resourceName} ${row.data.resourceType} `}
) @@ -45,12 +45,18 @@ const TypeCellComponent = ({ row }: TableCellComponentProps ) +const compareDates = (first: unknown, second: unknown) => + new Date(String(first ?? '')).getTime() - new Date(String(second ?? '')).getTime() + +const compareStrings = (first: unknown, second: unknown) => String(first ?? '').localeCompare(String(second ?? '')) + export const getAuditLogColumns = (): AuditLogsTableProps['columns'] => [ { field: 'timeStamp', label: 'Timestamp', size: { fixed: 220 }, isSortable: true, + comparator: compareDates, CellComponent: TimestampCellComponent, }, { @@ -58,6 +64,7 @@ export const getAuditLogColumns = (): AuditLogsTableProps['columns'] => [ label: 'Action', size: null, isSortable: true, + comparator: compareStrings, CellComponent: ActionCellComponent, }, { @@ -65,6 +72,7 @@ export const getAuditLogColumns = (): AuditLogsTableProps['columns'] => [ label: 'Type', size: { fixed: 130 }, isSortable: true, + comparator: compareStrings, CellComponent: TypeCellComponent, }, { @@ -72,6 +80,7 @@ export const getAuditLogColumns = (): AuditLogsTableProps['columns'] => [ label: 'User', size: { fixed: 180 }, isSortable: true, + comparator: compareStrings, CellComponent: UserCellComponent, }, { @@ -79,18 +88,21 @@ export const getAuditLogColumns = (): AuditLogsTableProps['columns'] => [ label: 'Resource Type', size: { fixed: 180 }, isSortable: true, + comparator: compareStrings, }, { field: 'resourceName', label: 'Resource Name', size: { fixed: 200 }, isSortable: true, + comparator: compareStrings, }, { field: 'module', label: 'Module', size: { fixed: 190 }, isSortable: true, + comparator: compareStrings, CellComponent: ModuleCellComponent, }, ] From 71603abe192eb54704c858c6b85734f3c247c3d8 Mon Sep 17 00:00:00 2001 From: shivani170 Date: Tue, 5 May 2026 10:15:42 +0530 Subject: [PATCH 04/17] chore: move audit log code --- .../AuditLogs/AuditLogDetail.tsx | 121 ----------------- .../AuditLogs/AuditLogsList.tsx | 119 ----------------- .../AuditLogs/AuditLogsRouter.tsx | 11 -- .../AuditLogs/AuditLogsTableWrapper.tsx | 111 ---------------- src/Pages-Devtron-2.0/AuditLogs/auditLog.scss | 6 - src/Pages-Devtron-2.0/AuditLogs/index.ts | 1 - src/Pages-Devtron-2.0/AuditLogs/service.tsx | 107 --------------- src/Pages-Devtron-2.0/AuditLogs/types.ts | 70 ---------- src/Pages-Devtron-2.0/AuditLogs/utils.tsx | 124 ------------------ .../common/navigation/NavigationRoutes.tsx | 15 ++- 10 files changed, 9 insertions(+), 676 deletions(-) delete mode 100644 src/Pages-Devtron-2.0/AuditLogs/AuditLogDetail.tsx delete mode 100644 src/Pages-Devtron-2.0/AuditLogs/AuditLogsList.tsx delete mode 100644 src/Pages-Devtron-2.0/AuditLogs/AuditLogsRouter.tsx delete mode 100644 src/Pages-Devtron-2.0/AuditLogs/AuditLogsTableWrapper.tsx delete mode 100644 src/Pages-Devtron-2.0/AuditLogs/auditLog.scss delete mode 100644 src/Pages-Devtron-2.0/AuditLogs/index.ts delete mode 100644 src/Pages-Devtron-2.0/AuditLogs/service.tsx delete mode 100644 src/Pages-Devtron-2.0/AuditLogs/types.ts delete mode 100644 src/Pages-Devtron-2.0/AuditLogs/utils.tsx diff --git a/src/Pages-Devtron-2.0/AuditLogs/AuditLogDetail.tsx b/src/Pages-Devtron-2.0/AuditLogs/AuditLogDetail.tsx deleted file mode 100644 index ea7eb3b2d9..0000000000 --- a/src/Pages-Devtron-2.0/AuditLogs/AuditLogDetail.tsx +++ /dev/null @@ -1,121 +0,0 @@ -import dayjs from 'dayjs' - -import { - Button, - ButtonStyleType, - ButtonVariantType, - CodeEditor, - ComponentSizeType, - DATE_TIME_FORMATS, - Drawer, - Icon, - MODES, -} from '@devtron-labs/devtron-fe-common-lib' - -import { AuditLogDetailType } from './types' - -import './auditLog.scss' - -interface AuditLogDetailProps { - auditLog: AuditLogDetailType - onClose: () => void -} - -const getFormattedPayload = (value: AuditLogDetailType['jsonFormatLog'] | AuditLogDetailType['payload']) => { - if (!value) { - return '' - } - - if (typeof value !== 'string') { - return JSON.stringify(value, null, 2) - } - - try { - return JSON.stringify(JSON.parse(value), null, 2) - } catch { - return value - } -} - -const getAuditLogPayload = ({ jsonFormatLog, payload }: AuditLogDetailType) => { - const formattedJsonLog = getFormattedPayload(jsonFormatLog) - - return formattedJsonLog || getFormattedPayload(payload) -} - -const AuditLogDetail = ({ auditLog, onClose }: AuditLogDetailProps) => { - const payload = getAuditLogPayload(auditLog) - - return ( - -
-
-
- -

{auditLog.action}

-
-
- -
-
-
-

Timestamp

-

- {auditLog.timeStamp - ? dayjs(auditLog.timeStamp).format(DATE_TIME_FORMATS.TWELVE_HOURS_FORMAT) - : '-'} -

-
-
-

Type

-

{auditLog.requestMethod || '-'}

-
-
-

Module

-

{auditLog.module || '-'}

-
-
-

User

-

{auditLog.user || '-'}

-
-
-

Resource Name

-

{auditLog.resourceName || '-'}

-
-
-

Resource Type

-

{auditLog.resourceType || '-'}

-
-
- - - - -

Payload

-
-
-
-
-
-
- ) -} - -export default AuditLogDetail diff --git a/src/Pages-Devtron-2.0/AuditLogs/AuditLogsList.tsx b/src/Pages-Devtron-2.0/AuditLogs/AuditLogsList.tsx deleted file mode 100644 index 7c919173ee..0000000000 --- a/src/Pages-Devtron-2.0/AuditLogs/AuditLogsList.tsx +++ /dev/null @@ -1,119 +0,0 @@ -import { useCallback, useMemo, useState } from 'react' -import { useNavigate } from 'react-router-dom' - -import { - BreadCrumb, - FiltersTypeEnum, - PageHeader, - PaginationEnum, - ROUTER_URLS, - Table, - TableProps, - useBreadcrumb, - useQuery, -} from '@devtron-labs/devtron-fe-common-lib' - -import AuditLogDetail from './AuditLogDetail' -import AuditLogsTableWrapper from './AuditLogsTableWrapper' -import { getAuditLogFilterOptions, getAuditLogList, getAuditLogs } from './service' -import { - AuditLogDetailType, - AuditLogRowType, - AuditLogTableAdditionalProps, - NormalizedAuditLogApiResponse, -} from './types' -import { getAuditLogColumns, parseAuditLogURLParams } from './utils' - -const EMPTY_AUDIT_LOGS: AuditLogDetailType[] = [] -const EMPTY_FILTER_OPTIONS: AuditLogTableAdditionalProps['filterOptions'] = { - typeOptions: [], - moduleOptions: [], -} - -const AuditLogsList = () => { - const columns = useMemo(() => getAuditLogColumns(), []) - const [selectedAuditLog, setSelectedAuditLog] = useState(null) - const navigate = useNavigate() - - const { breadcrumbs } = useBreadcrumb(ROUTER_URLS.AUDIT_LOGS, { - alias: { - 'audit-logs': { - component: Audit Logs, - linked: false, - }, - }, - }) - - const { data: auditLogs = EMPTY_AUDIT_LOGS, isLoading: areAuditLogsLoading } = useQuery< - NormalizedAuditLogApiResponse, - AuditLogDetailType[], - ['audit-logs'], - false - >({ - queryFn: ({ signal }) => getAuditLogs({}, signal), - queryKey: ['audit-logs'], - select: (response) => response.data, - }) - - const filterOptions = useMemo( - () => (auditLogs.length ? getAuditLogFilterOptions(auditLogs) : EMPTY_FILTER_OPTIONS), - [auditLogs], - ) - - const handleSelectAuditLog = useCallback((auditLog: AuditLogDetailType) => { - setSelectedAuditLog(auditLog) - }, []) - - const handleCloseDetail = useCallback(() => { - setSelectedAuditLog(null) - }, []) - - const handleRowClick = useCallback< - NonNullable['onRowClick']> - >((row) => handleSelectAuditLog(row.data as AuditLogDetailType), [handleSelectAuditLog]) - - const clearFilters = useCallback(() => { - navigate(ROUTER_URLS.AUDIT_LOGS) - }, [navigate]) - - const auditLogBreadcrumb = () => - - return ( -
- - - - id="table__audit-logs" - columns={columns} - ViewWrapper={AuditLogsTableWrapper} - filtersVariant={FiltersTypeEnum.URL} - additionalFilterProps={{ - initialSortKey: 'timeStamp', - parseSearchParams: parseAuditLogURLParams, - }} - paginationVariant={PaginationEnum.PAGINATED} - getRows={getAuditLogList} - filter={null} - loading={areAuditLogsLoading} - onRowClick={handleRowClick} - clearFilters={clearFilters} - emptyStateConfig={{ - noRowsConfig: { - title: 'No audit logs found', - subTitle: 'Audit trail entries will show up here once actions are performed.', - }, - noRowsForFilterConfig: { - title: 'No audit logs found', - subTitle: 'Try adjusting your search or filters', - clearFilters, - }, - }} - additionalProps={{ filterOptions, onSelectAuditLog: handleSelectAuditLog }} - /> - - {selectedAuditLog && } -
- ) -} - -export default AuditLogsList diff --git a/src/Pages-Devtron-2.0/AuditLogs/AuditLogsRouter.tsx b/src/Pages-Devtron-2.0/AuditLogs/AuditLogsRouter.tsx deleted file mode 100644 index 1c2feb9a37..0000000000 --- a/src/Pages-Devtron-2.0/AuditLogs/AuditLogsRouter.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import { Route, Routes } from 'react-router-dom' - -import AuditLogsList from './AuditLogsList' - -const AuditLogsRouter = () => ( - - } /> - -) - -export default AuditLogsRouter diff --git a/src/Pages-Devtron-2.0/AuditLogs/AuditLogsTableWrapper.tsx b/src/Pages-Devtron-2.0/AuditLogs/AuditLogsTableWrapper.tsx deleted file mode 100644 index e4b125ecec..0000000000 --- a/src/Pages-Devtron-2.0/AuditLogs/AuditLogsTableWrapper.tsx +++ /dev/null @@ -1,111 +0,0 @@ -import { - FilterChips, - GroupedFilterSelectPicker, - GroupedFilterSelectPickerProps, - SearchBar, -} from '@devtron-labs/devtron-fe-common-lib' - -import { AuditLogFilterKeys, AuditLogFiltersType, AuditLogsTableWrapperProps } from './types' -import { getAuditLogFilterLabel } from './utils' - -const AuditLogsTableWrapper = ({ - children, - searchKey, - handleSearch, - updateSearchParams, - clearFilters, - filterOptions, - areRowsLoading, - filteredRows, - areFiltersApplied, - ...restProps -}: AuditLogsTableWrapperProps) => { - const hideFilters = !areRowsLoading && filteredRows?.length === 0 && !areFiltersApplied - - const appliedFilters: AuditLogFiltersType = { - [AuditLogFilterKeys.TYPE]: restProps[AuditLogFilterKeys.TYPE] ?? [], - [AuditLogFilterKeys.MODULE]: restProps[AuditLogFilterKeys.MODULE] ?? [], - } - - const handleUpdateFilters = (filterKey: AuditLogFilterKeys) => (selectedOptions) => { - updateSearchParams({ [filterKey]: selectedOptions.map((option) => String(option.value)) }) - } - - const filterSelectPickerPropsMap: GroupedFilterSelectPickerProps['filterSelectPickerPropsMap'] = - { - [AuditLogFilterKeys.TYPE]: { - placeholder: 'Type', - inputId: 'audit-logs-type-filter', - options: filterOptions.typeOptions, - appliedFilterOptions: filterOptions.typeOptions.filter((option) => - appliedFilters[AuditLogFilterKeys.TYPE].includes(String(option.value)), - ), - handleApplyFilter: handleUpdateFilters(AuditLogFilterKeys.TYPE), - isDisabled: false, - isLoading: false, - }, - [AuditLogFilterKeys.MODULE]: { - placeholder: 'Module', - inputId: 'audit-logs-module-filter', - options: filterOptions.moduleOptions, - appliedFilterOptions: filterOptions.moduleOptions.filter((option) => - appliedFilters[AuditLogFilterKeys.MODULE].includes(String(option.value)), - ), - handleApplyFilter: handleUpdateFilters(AuditLogFilterKeys.MODULE), - isDisabled: false, - isLoading: false, - }, - } - - return ( -
-
- {!hideFilters && ( - <> - - - - id="audit-log-filters" - width={220} - isFilterApplied={ - !!appliedFilters[AuditLogFilterKeys.TYPE].length || - !!appliedFilters[AuditLogFilterKeys.MODULE].length - } - options={[ - { - groupLabel: 'Filter by', - items: [ - { id: AuditLogFilterKeys.TYPE, label: 'Type' }, - { id: AuditLogFilterKeys.MODULE, label: 'Module' }, - ], - }, - ]} - filterSelectPickerPropsMap={filterSelectPickerPropsMap} - /> - - )} -
- - {!hideFilters && ( - - filterConfig={appliedFilters} - onRemoveFilter={updateSearchParams} - clearFilters={clearFilters} - className="px-20" - getFormattedLabel={getAuditLogFilterLabel} - getFormattedValue={(_filterKey, filterValue: string) => filterValue} - /> - )} - - {children} -
- ) -} - -export default AuditLogsTableWrapper diff --git a/src/Pages-Devtron-2.0/AuditLogs/auditLog.scss b/src/Pages-Devtron-2.0/AuditLogs/auditLog.scss deleted file mode 100644 index da087b7d4b..0000000000 --- a/src/Pages-Devtron-2.0/AuditLogs/auditLog.scss +++ /dev/null @@ -1,6 +0,0 @@ -.audit-logs { - &__grid { - grid-template-columns: 150px 1fr; - } - -} \ No newline at end of file diff --git a/src/Pages-Devtron-2.0/AuditLogs/index.ts b/src/Pages-Devtron-2.0/AuditLogs/index.ts deleted file mode 100644 index 8b96a6dd43..0000000000 --- a/src/Pages-Devtron-2.0/AuditLogs/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as AuditLogs } from './AuditLogsRouter' diff --git a/src/Pages-Devtron-2.0/AuditLogs/service.tsx b/src/Pages-Devtron-2.0/AuditLogs/service.tsx deleted file mode 100644 index 48faa3f6d4..0000000000 --- a/src/Pages-Devtron-2.0/AuditLogs/service.tsx +++ /dev/null @@ -1,107 +0,0 @@ -import { get, getUrlWithSearchParams } from '@devtron-labs/devtron-fe-common-lib' - -import { - AuditLogApiResponse, - AuditLogDetailType, - AuditLogFilterKeys, - AuditLogFilterOptionsType, - AuditLogsTableProps, - GetAuditLogsParams, - NormalizedAuditLogApiResponse, -} from './types' - -const AUDIT_LOG_ENDPOINT = 'audit-log' - -const normalizeAuditLogResponse = (response?: AuditLogApiResponse): NormalizedAuditLogApiResponse => { - const { - data = response?.data ?? [], - offset = response?.offset ?? 0, - size = response?.size ?? data.length, - totalCount = response?.totalCount ?? data.length, - } = response?.auditLogs ?? {} - - return { - data, - offset, - size, - totalCount, - } -} - -export const getAuditLogs = async ( - params?: Partial, - signal?: AbortSignal, -): Promise => { - const { - offset = 0, - pageSize, - searchKey, - sortBy, - sortOrder, - [AuditLogFilterKeys.TYPE]: type, - [AuditLogFilterKeys.MODULE]: module, - } = params ?? {} - - const url = getUrlWithSearchParams(AUDIT_LOG_ENDPOINT, { - offset, - size: pageSize, - searchKey, - sortBy, - sortOrder, - type, - module, - }) - const { result } = await get(url, { signal }) - - return normalizeAuditLogResponse(result) -} - -export const getAuditLogList: AuditLogsTableProps['getRows'] = async ( - { - offset, - pageSize, - searchKey, - sortBy, - sortOrder, - [AuditLogFilterKeys.TYPE]: type = [], - [AuditLogFilterKeys.MODULE]: module = [], - }: GetAuditLogsParams, - signal, -) => { - const auditLogResponse = await getAuditLogs( - { - offset, - pageSize, - searchKey, - sortBy, - sortOrder, - [AuditLogFilterKeys.TYPE]: type, - [AuditLogFilterKeys.MODULE]: module, - }, - signal, - ) - const pageAuditLogs = - auditLogResponse.data.length > pageSize - ? auditLogResponse.data.slice(offset, offset + pageSize) - : auditLogResponse.data - - return { - rows: pageAuditLogs.map((auditLog, index) => ({ - id: `${auditLog.auditLogId}-${offset + index}`, - data: auditLog, - })), - totalRows: auditLogResponse.totalCount, - } -} - -export const getAuditLogFilterOptions = (auditLogs: AuditLogDetailType[]): AuditLogFilterOptionsType => { - const toOptions = (values: string[]) => - [...new Set(values.filter(Boolean))] - .sort((first, second) => first.localeCompare(second)) - .map((value) => ({ label: value, value })) - - return { - typeOptions: toOptions(auditLogs.map(({ requestMethod: type }) => type)), - moduleOptions: toOptions(auditLogs.map(({ module }) => module)), - } -} diff --git a/src/Pages-Devtron-2.0/AuditLogs/types.ts b/src/Pages-Devtron-2.0/AuditLogs/types.ts deleted file mode 100644 index 64b199b124..0000000000 --- a/src/Pages-Devtron-2.0/AuditLogs/types.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { - FiltersTypeEnum, - SelectPickerOptionType, - TableProps, - TableViewWrapperProps, -} from '@devtron-labs/devtron-fe-common-lib' - -export enum AuditLogFilterKeys { - TYPE = 'type', - MODULE = 'module', -} - -export type AuditLogFiltersType = { - [AuditLogFilterKeys.TYPE]: string[] - [AuditLogFilterKeys.MODULE]: string[] -} - -export interface AuditLogRowType { - auditLogId: number - timeStamp: string - action: string - requestMethod: string - user: string - resourceName: string - resourceType: string - module: string -} - -export interface AuditLogDetailType extends AuditLogRowType { - jsonFormatLog?: string - payload?: Record | string -} - -export interface AuditLogApiResponse { - data?: AuditLogDetailType[] - offset?: number - size?: number - totalCount?: number - auditLogs?: { - data?: AuditLogDetailType[] - offset?: number - size?: number - totalCount?: number - } -} - -export type NormalizedAuditLogApiResponse = Required< - Pick -> - -export type AuditLogFilterOptionsType = { - typeOptions: SelectPickerOptionType[] - moduleOptions: SelectPickerOptionType[] -} - -export type AuditLogTableAdditionalProps = { - filterOptions: AuditLogFilterOptionsType - onSelectAuditLog: (auditLog: AuditLogDetailType) => void -} - -export type AuditLogsTableProps = TableProps - -export type GetAuditLogsParams = Partial & - Parameters>[0] - -export type AuditLogsTableWrapperProps = TableViewWrapperProps< - AuditLogRowType, - FiltersTypeEnum.URL, - AuditLogFiltersType & AuditLogTableAdditionalProps -> diff --git a/src/Pages-Devtron-2.0/AuditLogs/utils.tsx b/src/Pages-Devtron-2.0/AuditLogs/utils.tsx deleted file mode 100644 index 0871088f29..0000000000 --- a/src/Pages-Devtron-2.0/AuditLogs/utils.tsx +++ /dev/null @@ -1,124 +0,0 @@ -import dayjs from 'dayjs' - -import { - capitalizeFirstLetter, - DATE_TIME_FORMATS, - FiltersTypeEnum, - getAlphabetIcon, - TableCellComponentProps, -} from '@devtron-labs/devtron-fe-common-lib' - -import { AuditLogFilterKeys, AuditLogFiltersType, AuditLogRowType, AuditLogsTableProps } from './types' - -const TimestampCellComponent = ({ value }: TableCellComponentProps) => ( -
- - {value ? dayjs(String(value)).format(DATE_TIME_FORMATS.TWELVE_HOURS_FORMAT) : 'test'} - -
-) - -const ActionCellComponent = ({ row }: TableCellComponentProps) => ( -
- {`${capitalizeFirstLetter(row.data.action)}d ${row.data.resourceName} ${row.data.resourceType} `} -
-) - -const UserCellComponent = ({ row }: TableCellComponentProps) => ( -
- - {getAlphabetIcon(row.data.user, 'dc__no-shrink m-0-imp icon-dim-20')} - {row.data.user} - -
-) - -const ModuleCellComponent = ({ row }: TableCellComponentProps) => ( -
- {capitalizeFirstLetter(row.data.module)} -
-) - -const TypeCellComponent = ({ row }: TableCellComponentProps) => ( -
- {capitalizeFirstLetter(row.data.requestMethod)} -
-) - -const compareDates = (first: unknown, second: unknown) => - new Date(String(first ?? '')).getTime() - new Date(String(second ?? '')).getTime() - -const compareStrings = (first: unknown, second: unknown) => String(first ?? '').localeCompare(String(second ?? '')) - -export const getAuditLogColumns = (): AuditLogsTableProps['columns'] => [ - { - field: 'timeStamp', - label: 'Timestamp', - size: { fixed: 220 }, - isSortable: true, - comparator: compareDates, - CellComponent: TimestampCellComponent, - }, - { - field: 'action', - label: 'Action', - size: null, - isSortable: true, - comparator: compareStrings, - CellComponent: ActionCellComponent, - }, - { - field: 'requestMethod', - label: 'Type', - size: { fixed: 130 }, - isSortable: true, - comparator: compareStrings, - CellComponent: TypeCellComponent, - }, - { - field: 'user', - label: 'User', - size: { fixed: 180 }, - isSortable: true, - comparator: compareStrings, - CellComponent: UserCellComponent, - }, - { - field: 'resourceType', - label: 'Resource Type', - size: { fixed: 180 }, - isSortable: true, - comparator: compareStrings, - }, - { - field: 'resourceName', - label: 'Resource Name', - size: { fixed: 200 }, - isSortable: true, - comparator: compareStrings, - }, - { - field: 'module', - label: 'Module', - size: { fixed: 190 }, - isSortable: true, - comparator: compareStrings, - CellComponent: ModuleCellComponent, - }, -] - -export const parseAuditLogURLParams = (searchParams: URLSearchParams): AuditLogFiltersType => ({ - [AuditLogFilterKeys.TYPE]: searchParams.getAll(AuditLogFilterKeys.TYPE), - [AuditLogFilterKeys.MODULE]: searchParams.getAll(AuditLogFilterKeys.MODULE), -}) - -export const getAuditLogFilterLabel = (filterKey: string) => { - switch (filterKey) { - case AuditLogFilterKeys.TYPE: - return 'Type' - case AuditLogFilterKeys.MODULE: - return 'Module' - default: - return filterKey - } -} diff --git a/src/components/common/navigation/NavigationRoutes.tsx b/src/components/common/navigation/NavigationRoutes.tsx index 5ef88500a0..4b22363de5 100644 --- a/src/components/common/navigation/NavigationRoutes.tsx +++ b/src/components/common/navigation/NavigationRoutes.tsx @@ -70,7 +70,6 @@ import { getUserRole } from '@Pages/GlobalConfigurations/Authorization/authoriza import EditClusterDrawerContent from '@Pages/GlobalConfigurations/ClustersAndEnvironments/EditClusterDrawerContent' import { ReleaseConfigurations } from '@Pages/Releases/Detail' import { ApplicationManagementRouter } from '@PagesDevtron2.0/ApplicationManagement' -import AuditLogsRouter from '@PagesDevtron2.0/AuditLogs/AuditLogsList' import { InfrastructureManagementRouter } from '@PagesDevtron2.0/InfrastructureManagement' import { SERVER_MODE, ViewType } from '../../../config' @@ -133,6 +132,8 @@ const CostVisibilityRouter = importComponentFromFELibrary('CostVisibilityRouter' const AIRecommendations = importComponentFromFELibrary('AIRecommendations', null, 'function') const AIChatProvider = importComponentFromFELibrary('AIChatProvider', null, 'function') +const AuditLogsRouter = importComponentFromFELibrary('AuditLogsRouter', null, 'function') + const NavigationRoutes = ({ reloadVersionConfig }: Readonly) => { const navigate = useNavigate() const location = useLocation() @@ -563,11 +564,13 @@ const NavigationRoutes = ({ reloadVersionConfig }: Readonly} /> - } - /> + {serverMode === SERVER_MODE.FULL && AuditLogsRouter && ( + } + /> + )} {!window._env_.K8S_CLIENT ? [ ...(serverMode === SERVER_MODE.FULL From 1aea63a49b90e8fc5078a0b569e2fed92912bb1d Mon Sep 17 00:00:00 2001 From: shivani170 Date: Wed, 6 May 2026 16:15:06 +0530 Subject: [PATCH 05/17] chore: update target environment URL and sync AuditLogs component path and constant definition --- src/components/common/navigation/NavigationRoutes.tsx | 2 +- src/config/constants.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/common/navigation/NavigationRoutes.tsx b/src/components/common/navigation/NavigationRoutes.tsx index 4b22363de5..dcfc0e88d3 100644 --- a/src/components/common/navigation/NavigationRoutes.tsx +++ b/src/components/common/navigation/NavigationRoutes.tsx @@ -132,7 +132,7 @@ const CostVisibilityRouter = importComponentFromFELibrary('CostVisibilityRouter' const AIRecommendations = importComponentFromFELibrary('AIRecommendations', null, 'function') const AIChatProvider = importComponentFromFELibrary('AIChatProvider', null, 'function') -const AuditLogsRouter = importComponentFromFELibrary('AuditLogsRouter', null, 'function') +const AuditLogsRouter = importComponentFromFELibrary('AuditLogs', null, 'function') const NavigationRoutes = ({ reloadVersionConfig }: Readonly) => { const navigate = useNavigate() diff --git a/src/config/constants.ts b/src/config/constants.ts index 354bc1e9c4..84e517037a 100644 --- a/src/config/constants.ts +++ b/src/config/constants.ts @@ -89,6 +89,7 @@ export const Routes = { EPHEMERAL_CONTAINERS: 'k8s/resources/ephemeralContainers', APP_EDIT: 'app/edit', APPLICATION_EXTERNAL_HELM_RELEASE: 'application/external-helm-release', + AUDIT_LOG: 'audit-log', JOB_CI_DETAIL: 'job/ci-pipeline/list', From c6de2c33d747cadd9e655d13fc901dddfc832e82 Mon Sep 17 00:00:00 2001 From: shivani170 Date: Fri, 8 May 2026 14:05:53 +0530 Subject: [PATCH 06/17] chore: update @devtron-labs/devtron-fe-common-lib to 4.0.4-beta-7 --- package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index bb5bb8e629..efd1770db0 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "private": true, "homepage": "/dashboard", "dependencies": { - "@devtron-labs/devtron-fe-common-lib": "4.0.4-pre-0", + "@devtron-labs/devtron-fe-common-lib": "4.0.4-beta-7", "@esbuild-plugins/node-globals-polyfill": "0.2.3", "@sentry/browser": "7.119.1", "@sentry/integrations": "7.50.0", diff --git a/yarn.lock b/yarn.lock index 29d7531bd3..6310855c8d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1738,9 +1738,9 @@ __metadata: languageName: node linkType: hard -"@devtron-labs/devtron-fe-common-lib@npm:4.0.4-pre-0": - version: 4.0.4-pre-0 - resolution: "@devtron-labs/devtron-fe-common-lib@npm:4.0.4-pre-0" +"@devtron-labs/devtron-fe-common-lib@npm:4.0.4-beta-7": + version: 4.0.4-beta-7 + resolution: "@devtron-labs/devtron-fe-common-lib@npm:4.0.4-beta-7" dependencies: "@codemirror/autocomplete": "npm:6.18.6" "@codemirror/lang-json": "npm:6.0.1" @@ -1794,7 +1794,7 @@ __metadata: react-select: 5.8.0 rxjs: ^7.8.1 yaml: ^2.8.3 - checksum: 10c0/ae699b1673b72454475d699a6f593658bcb9f4fa5e8443a4c794429795ac060ef91fb1fdb371fd12b496fca9e146674585cd31cfd1d26b48f4fa7aae5d363ffc + checksum: 10c0/762f4e6482f0c504b6ee3e7b167304088b296d2b7ad0ee9f962a4dc5c9fc0891e73b388b87671363f5c94cffb35179a8a5db90d09ef1862e7954b78fce81b009 languageName: node linkType: hard @@ -5529,7 +5529,7 @@ __metadata: version: 0.0.0-use.local resolution: "dashboard@workspace:." dependencies: - "@devtron-labs/devtron-fe-common-lib": "npm:4.0.4-pre-0" + "@devtron-labs/devtron-fe-common-lib": "npm:4.0.4-beta-7" "@esbuild-plugins/node-globals-polyfill": "npm:0.2.3" "@playwright/test": "npm:^1.32.1" "@sentry/browser": "npm:7.119.1" From 699a1a3b8dde03975fb1d6bf8ae0fbe92c1aa1e3 Mon Sep 17 00:00:00 2001 From: shivani170 Date: Thu, 14 May 2026 13:23:38 +0530 Subject: [PATCH 07/17] feat: position of audit icon --- src/components/Navigation/constants.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/Navigation/constants.ts b/src/components/Navigation/constants.ts index 9920790182..75ff8f58d4 100644 --- a/src/components/Navigation/constants.ts +++ b/src/components/Navigation/constants.ts @@ -251,12 +251,6 @@ const NAVIGATION_LIST: NavigationGroupType[] = [ ], }, ...(DATA_PROTECTION_MANAGEMENT_NAV_GROUP ? [DATA_PROTECTION_MANAGEMENT_NAV_GROUP] : []), - { - id: 'audit-logs', - title: 'Audit logs', - icon: 'ic-file-log-search', - href: ROUTER_URLS.AUDIT_LOGS, - }, { id: 'global-configuration', title: 'Global Configuration', @@ -328,6 +322,12 @@ const NAVIGATION_LIST: NavigationGroupType[] = [ ], isAvailableInEA: true, }, + { + id: 'audit-logs', + title: 'Audit logs', + icon: 'ic-file-log-search', + href: ROUTER_URLS.AUDIT_LOGS, + }, ] export const getNavigationList = (serverMode: SERVER_MODE): NavigationGroupType[] => { From f5f2ae3a843c588e66c2e4580a066df0f8e75dda Mon Sep 17 00:00:00 2001 From: Arun Jain Date: Fri, 19 Jun 2026 16:36:13 +0530 Subject: [PATCH 08/17] fix: update navigation path for linked CI details in CINode component --- src/components/workflowEditor/nodes/CINode.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/workflowEditor/nodes/CINode.tsx b/src/components/workflowEditor/nodes/CINode.tsx index 0c90efede0..ff79ca6201 100644 --- a/src/components/workflowEditor/nodes/CINode.tsx +++ b/src/components/workflowEditor/nodes/CINode.tsx @@ -36,7 +36,7 @@ export class CINode extends Component { // stopPropagation to stop redirection to ci-details e.stopPropagation() e.preventDefault() - this.props.navigate(`${URLS.LINKED_CI_DETAILS}/${this.props.id}`) + this.props.navigate(`${URLS.APP_WORKFLOW_CONFIG}/${URLS.LINKED_CI_DETAILS}/${this.props.id}`) } onClickAddNode = (event: any) => { From 1633a0934c7bd29bba14f80e568633409d4ad203 Mon Sep 17 00:00:00 2001 From: Arun Jain Date: Wed, 24 Jun 2026 13:51:51 +0530 Subject: [PATCH 09/17] chore: update @devtron-labs/devtron-fe-common-lib to version 4.0.7 --- package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 779f653cd9..8818ffef1f 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "private": true, "homepage": "/dashboard", "dependencies": { - "@devtron-labs/devtron-fe-common-lib": "4.0.6", + "@devtron-labs/devtron-fe-common-lib": "4.0.7", "@esbuild-plugins/node-globals-polyfill": "0.2.3", "@sentry/browser": "7.119.1", "@sentry/integrations": "7.50.0", diff --git a/yarn.lock b/yarn.lock index f89470704b..92fd9137f1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1738,9 +1738,9 @@ __metadata: languageName: node linkType: hard -"@devtron-labs/devtron-fe-common-lib@npm:4.0.6": - version: 4.0.6 - resolution: "@devtron-labs/devtron-fe-common-lib@npm:4.0.6" +"@devtron-labs/devtron-fe-common-lib@npm:4.0.7": + version: 4.0.7 + resolution: "@devtron-labs/devtron-fe-common-lib@npm:4.0.7" dependencies: "@codemirror/autocomplete": "npm:6.18.6" "@codemirror/lang-json": "npm:6.0.1" @@ -1794,7 +1794,7 @@ __metadata: react-select: 5.8.0 rxjs: ^7.8.1 yaml: ^2.8.3 - checksum: 10c0/355dc92aeeda6528c340e15704ff8de3278ba06203a72dd74ed3ec2355a8c15f93332105cb702163bdfda6fb38689ce015bb2ce5862f12bde35e5650b8956102 + checksum: 10c0/2bb098d1357a412da7075097992096d343b8edca0b88e79848befbffc28ac6eb750c52f1a4cbb41841d29ec6761e391c6dbfa905b791fb42d70e9f4c01c1e7ba languageName: node linkType: hard @@ -5529,7 +5529,7 @@ __metadata: version: 0.0.0-use.local resolution: "dashboard@workspace:." dependencies: - "@devtron-labs/devtron-fe-common-lib": "npm:4.0.6" + "@devtron-labs/devtron-fe-common-lib": "npm:4.0.7" "@esbuild-plugins/node-globals-polyfill": "npm:0.2.3" "@playwright/test": "npm:^1.32.1" "@sentry/browser": "npm:7.119.1" From 9a15ed1138c56896502da56d5e576e460d4a7df2 Mon Sep 17 00:00:00 2001 From: Arun Jain Date: Thu, 25 Jun 2026 15:36:26 +0530 Subject: [PATCH 10/17] fix: update routing structure in DevtronStackManager --- .../DevtronStackManager.tsx | 220 ++++++++++-------- 1 file changed, 119 insertions(+), 101 deletions(-) diff --git a/src/components/v2/devtronStackManager/DevtronStackManager.tsx b/src/components/v2/devtronStackManager/DevtronStackManager.tsx index c42fb702fd..75aefe8b73 100644 --- a/src/components/v2/devtronStackManager/DevtronStackManager.tsx +++ b/src/components/v2/devtronStackManager/DevtronStackManager.tsx @@ -406,9 +406,9 @@ export default function DevtronStackManager({ navigate({ pathname: fromDiscoverModules - ? ROUTER_URLS.STACK_MANAGER.DISCOVER_MODULES_DETAILS - : ROUTER_URLS.STACK_MANAGER.INSTALLED_MODULES_DETAILS, - search: `?${queryParams.toString()}`, + ? ROUTER_URLS.STACK_MANAGER.DISCOVER_MODULES_DETAILS + : ROUTER_URLS.STACK_MANAGER.INSTALLED_MODULES_DETAILS, + search: `?${queryParams.toString()}`, }) } @@ -437,104 +437,122 @@ export default function DevtronStackManager({ const Body = () => { return ( - - - - - - - - - - - - - - - - - - + + } + /> + + } + /> + + } + /> + + } + /> + + } + /> + + } + /> } /> ) From 64a76836c5c2f5e806d75dc3dd6535690fb363c6 Mon Sep 17 00:00:00 2001 From: Arun Jain Date: Tue, 30 Jun 2026 15:38:00 +0530 Subject: [PATCH 11/17] fix: redirection from empty state in api token listing --- .../Authorization/APITokens/ApiTokens.component.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Pages/GlobalConfigurations/Authorization/APITokens/ApiTokens.component.tsx b/src/Pages/GlobalConfigurations/Authorization/APITokens/ApiTokens.component.tsx index e4d3d5f515..89282f5d97 100644 --- a/src/Pages/GlobalConfigurations/Authorization/APITokens/ApiTokens.component.tsx +++ b/src/Pages/GlobalConfigurations/Authorization/APITokens/ApiTokens.component.tsx @@ -153,7 +153,7 @@ const ApiTokens = () => { ) const redirectToCreate = () => { - navigate(`../create`) + navigate('create') } const renderGenerateButton = () => ( From 087cf689391e2183032a467bbf3270c57e9a4b9f Mon Sep 17 00:00:00 2001 From: Arun Date: Fri, 10 Jul 2026 16:57:56 +0530 Subject: [PATCH 12/17] chore: fix resource navigation URL construction in NodeDetails component --- src/components/ClusterNodes/NodeDetails.tsx | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/src/components/ClusterNodes/NodeDetails.tsx b/src/components/ClusterNodes/NodeDetails.tsx index 2133daf92e..8eb96eed32 100644 --- a/src/components/ClusterNodes/NodeDetails.tsx +++ b/src/components/ClusterNodes/NodeDetails.tsx @@ -708,18 +708,13 @@ const NodeDetails = ({ lowercaseKindToResourceGroupMap, updateTabUrl }: ClusterL const handleResourceClick = (e) => { const { name, tab = ResourceBrowserActionMenuEnum.manifest, namespace } = e.currentTarget.dataset - navigate( - getUrlWithSearchParams( - generatePath(RESOURCE_BROWSER_ROUTES.K8S_RESOURCE_DETAIL, { - clusterId, - group: selectedResource?.gvk.Group.toLowerCase() || K8S_EMPTY_GROUP, - kind: 'pod', - name, - namespace, - }), - { tab }, - ), - ) + navigate(generatePath(`${RESOURCE_BROWSER_ROUTES.K8S_RESOURCE_DETAIL}/${tab}`, { + clusterId, + group: selectedResource?.gvk.Group.toLowerCase() || K8S_EMPTY_GROUP, + kind: 'pod', + name, + namespace, + })) } const getTriggerSortingHandler = From 9b99c061104c1cb628c7951a5815039152781160 Mon Sep 17 00:00:00 2001 From: shivani170 Date: Wed, 15 Jul 2026 18:00:47 +0530 Subject: [PATCH 13/17] chore: fix timestamp order for audit logs --- src/components/Navigation/constants.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Navigation/constants.ts b/src/components/Navigation/constants.ts index 75ff8f58d4..eac044e89a 100644 --- a/src/components/Navigation/constants.ts +++ b/src/components/Navigation/constants.ts @@ -326,7 +326,7 @@ const NAVIGATION_LIST: NavigationGroupType[] = [ id: 'audit-logs', title: 'Audit logs', icon: 'ic-file-log-search', - href: ROUTER_URLS.AUDIT_LOGS, + href: `${ROUTER_URLS.AUDIT_LOGS}?sortBy=timeStamp&sortOrder=DESC`, }, ] From 1a55a1eb9047c0d15151594b49f2c86fc3b8fc11 Mon Sep 17 00:00:00 2001 From: shivani170 Date: Wed, 22 Jul 2026 20:34:16 +0530 Subject: [PATCH 14/17] Dependabot alerts --- package.json | 4 +- vite.config.mts | 2 +- yarn.lock | 236 +++++++++++++++++++++++++----------------------- 3 files changed, 126 insertions(+), 116 deletions(-) diff --git a/package.json b/package.json index bb5bb8e629..048d20b4a3 100644 --- a/package.json +++ b/package.json @@ -92,7 +92,7 @@ "svgo": "^3.3.2", "ts-node": "10.9.2", "typescript": "5.5.4", - "vite": "8.0.8", + "vite": "8.0.16", "vite-plugin-compression2": "2.4.0", "vite-plugin-pwa": "1.2.0", "vite-plugin-require-transform": "1.0.21", @@ -110,7 +110,7 @@ "packageManager": "yarn@4.9.2", "overrides": { "vite-plugin-svgr": { - "vite": "8.0.8" + "vite": "8.0.16" } } } diff --git a/vite.config.mts b/vite.config.mts index ec327b44c2..7d25b45b36 100644 --- a/vite.config.mts +++ b/vite.config.mts @@ -30,7 +30,7 @@ import { VitePWA } from 'vite-plugin-pwa' import { compression, defineAlgorithm } from 'vite-plugin-compression2' const WRONG_CODE = `import { bpfrpt_proptype_WindowScroller } from "../WindowScroller.js";` -const TARGET_URL = 'https://preview.devtron.ai/' +const TARGET_URL = 'https://devtron-ent-5.devtron.info/' function reactVirtualized(): PluginOption { return { diff --git a/yarn.lock b/yarn.lock index 29d7531bd3..f48a8b3ddc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1798,22 +1798,22 @@ __metadata: languageName: node linkType: hard -"@emnapi/core@npm:1.9.2": - version: 1.9.2 - resolution: "@emnapi/core@npm:1.9.2" +"@emnapi/core@npm:1.10.0": + version: 1.10.0 + resolution: "@emnapi/core@npm:1.10.0" dependencies: "@emnapi/wasi-threads": "npm:1.2.1" tslib: "npm:^2.4.0" - checksum: 10c0/5500393f953951bad0768fafaa9191f2d938956b20c6d6a79e5ab696a613a25ce6ad23422bc18e86e6ce8deb147619d8d0d7d413a69f84adc01a6633cc353cd9 + checksum: 10c0/f51d08227857b60632de7714d708124f0e100a1462dde6df8221760939aa3204a73193830371830fac0716f3ccd2129f2cac1b17cd7d7958bc4da9018a296edb languageName: node linkType: hard -"@emnapi/runtime@npm:1.9.2": - version: 1.9.2 - resolution: "@emnapi/runtime@npm:1.9.2" +"@emnapi/runtime@npm:1.10.0": + version: 1.10.0 + resolution: "@emnapi/runtime@npm:1.10.0" dependencies: tslib: "npm:^2.4.0" - checksum: 10c0/61c3a59e0c36784558b8d58eb02bd04815aa5fb0dbfbaf84d1b3050a78aa0cc63ea129ae806bd1e48062bfeb7fc36eb0e5431740d62f64ea51bdf426404b8caa + checksum: 10c0/953f14991d1aefb92ee6f8eb27dea725e484791a53a0cb5f47d9e0087b9a2c929ff2e92adf95af15d6ad456db6300c6b761ebf72b50a875b874a83520b3ba093 languageName: node linkType: hard @@ -2429,15 +2429,15 @@ __metadata: languageName: node linkType: hard -"@napi-rs/wasm-runtime@npm:^1.1.3": - version: 1.1.3 - resolution: "@napi-rs/wasm-runtime@npm:1.1.3" +"@napi-rs/wasm-runtime@npm:^1.1.4": + version: 1.1.6 + resolution: "@napi-rs/wasm-runtime@npm:1.1.6" dependencies: - "@tybys/wasm-util": "npm:^0.10.1" + "@tybys/wasm-util": "npm:^0.10.3" peerDependencies: "@emnapi/core": ^1.7.1 "@emnapi/runtime": ^1.7.1 - checksum: 10c0/745bb32a023b95095a18d93658bf4564403c2283ca0500a043afcf566ac6082bd0611792f14636276bab07dc2ce6d862591c8aabddae02ec697245b05bc6f144 + checksum: 10c0/344518bf3ef65051dda4c00969f293aa4a21ab7dc7822b3f48519b17cd5eaa3f0bc34898d115d50ba59b1817a0cb905d46f7a7223c8249239cd14c28db388e10 languageName: node linkType: hard @@ -2490,10 +2490,10 @@ __metadata: languageName: node linkType: hard -"@oxc-project/types@npm:=0.124.0": - version: 0.124.0 - resolution: "@oxc-project/types@npm:0.124.0" - checksum: 10c0/9564ee3ce41f4b87802ffd0d62a7602d27f4503fbd39c1bedab98d54fde06e2ac254a8f85d8f679af1281a26e8fc7aa053fadbb3e09e786b38178eb38a8e2fb3 +"@oxc-project/types@npm:=0.133.0": + version: 0.133.0 + resolution: "@oxc-project/types@npm:0.133.0" + checksum: 10c0/70c57ba58644f7ec217b670c301801f4d06995f4ccdba6b2bd106ea3e5ee49d616573e6ef8d55530b87571a960696543687f3850e87ad173d3f88965c30cdd63 languageName: node linkType: hard @@ -2607,122 +2607,115 @@ __metadata: languageName: node linkType: hard -"@rolldown/binding-android-arm64@npm:1.0.0-rc.15": - version: 1.0.0-rc.15 - resolution: "@rolldown/binding-android-arm64@npm:1.0.0-rc.15" +"@rolldown/binding-android-arm64@npm:1.0.3": + version: 1.0.3 + resolution: "@rolldown/binding-android-arm64@npm:1.0.3" conditions: os=android & cpu=arm64 languageName: node linkType: hard -"@rolldown/binding-darwin-arm64@npm:1.0.0-rc.15": - version: 1.0.0-rc.15 - resolution: "@rolldown/binding-darwin-arm64@npm:1.0.0-rc.15" +"@rolldown/binding-darwin-arm64@npm:1.0.3": + version: 1.0.3 + resolution: "@rolldown/binding-darwin-arm64@npm:1.0.3" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@rolldown/binding-darwin-x64@npm:1.0.0-rc.15": - version: 1.0.0-rc.15 - resolution: "@rolldown/binding-darwin-x64@npm:1.0.0-rc.15" +"@rolldown/binding-darwin-x64@npm:1.0.3": + version: 1.0.3 + resolution: "@rolldown/binding-darwin-x64@npm:1.0.3" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@rolldown/binding-freebsd-x64@npm:1.0.0-rc.15": - version: 1.0.0-rc.15 - resolution: "@rolldown/binding-freebsd-x64@npm:1.0.0-rc.15" +"@rolldown/binding-freebsd-x64@npm:1.0.3": + version: 1.0.3 + resolution: "@rolldown/binding-freebsd-x64@npm:1.0.3" conditions: os=freebsd & cpu=x64 languageName: node linkType: hard -"@rolldown/binding-linux-arm-gnueabihf@npm:1.0.0-rc.15": - version: 1.0.0-rc.15 - resolution: "@rolldown/binding-linux-arm-gnueabihf@npm:1.0.0-rc.15" +"@rolldown/binding-linux-arm-gnueabihf@npm:1.0.3": + version: 1.0.3 + resolution: "@rolldown/binding-linux-arm-gnueabihf@npm:1.0.3" conditions: os=linux & cpu=arm languageName: node linkType: hard -"@rolldown/binding-linux-arm64-gnu@npm:1.0.0-rc.15": - version: 1.0.0-rc.15 - resolution: "@rolldown/binding-linux-arm64-gnu@npm:1.0.0-rc.15" +"@rolldown/binding-linux-arm64-gnu@npm:1.0.3": + version: 1.0.3 + resolution: "@rolldown/binding-linux-arm64-gnu@npm:1.0.3" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@rolldown/binding-linux-arm64-musl@npm:1.0.0-rc.15": - version: 1.0.0-rc.15 - resolution: "@rolldown/binding-linux-arm64-musl@npm:1.0.0-rc.15" +"@rolldown/binding-linux-arm64-musl@npm:1.0.3": + version: 1.0.3 + resolution: "@rolldown/binding-linux-arm64-musl@npm:1.0.3" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@rolldown/binding-linux-ppc64-gnu@npm:1.0.0-rc.15": - version: 1.0.0-rc.15 - resolution: "@rolldown/binding-linux-ppc64-gnu@npm:1.0.0-rc.15" +"@rolldown/binding-linux-ppc64-gnu@npm:1.0.3": + version: 1.0.3 + resolution: "@rolldown/binding-linux-ppc64-gnu@npm:1.0.3" conditions: os=linux & cpu=ppc64 & libc=glibc languageName: node linkType: hard -"@rolldown/binding-linux-s390x-gnu@npm:1.0.0-rc.15": - version: 1.0.0-rc.15 - resolution: "@rolldown/binding-linux-s390x-gnu@npm:1.0.0-rc.15" +"@rolldown/binding-linux-s390x-gnu@npm:1.0.3": + version: 1.0.3 + resolution: "@rolldown/binding-linux-s390x-gnu@npm:1.0.3" conditions: os=linux & cpu=s390x & libc=glibc languageName: node linkType: hard -"@rolldown/binding-linux-x64-gnu@npm:1.0.0-rc.15": - version: 1.0.0-rc.15 - resolution: "@rolldown/binding-linux-x64-gnu@npm:1.0.0-rc.15" +"@rolldown/binding-linux-x64-gnu@npm:1.0.3": + version: 1.0.3 + resolution: "@rolldown/binding-linux-x64-gnu@npm:1.0.3" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@rolldown/binding-linux-x64-musl@npm:1.0.0-rc.15": - version: 1.0.0-rc.15 - resolution: "@rolldown/binding-linux-x64-musl@npm:1.0.0-rc.15" +"@rolldown/binding-linux-x64-musl@npm:1.0.3": + version: 1.0.3 + resolution: "@rolldown/binding-linux-x64-musl@npm:1.0.3" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@rolldown/binding-openharmony-arm64@npm:1.0.0-rc.15": - version: 1.0.0-rc.15 - resolution: "@rolldown/binding-openharmony-arm64@npm:1.0.0-rc.15" +"@rolldown/binding-openharmony-arm64@npm:1.0.3": + version: 1.0.3 + resolution: "@rolldown/binding-openharmony-arm64@npm:1.0.3" conditions: os=openharmony & cpu=arm64 languageName: node linkType: hard -"@rolldown/binding-wasm32-wasi@npm:1.0.0-rc.15": - version: 1.0.0-rc.15 - resolution: "@rolldown/binding-wasm32-wasi@npm:1.0.0-rc.15" +"@rolldown/binding-wasm32-wasi@npm:1.0.3": + version: 1.0.3 + resolution: "@rolldown/binding-wasm32-wasi@npm:1.0.3" dependencies: - "@emnapi/core": "npm:1.9.2" - "@emnapi/runtime": "npm:1.9.2" - "@napi-rs/wasm-runtime": "npm:^1.1.3" + "@emnapi/core": "npm:1.10.0" + "@emnapi/runtime": "npm:1.10.0" + "@napi-rs/wasm-runtime": "npm:^1.1.4" conditions: cpu=wasm32 languageName: node linkType: hard -"@rolldown/binding-win32-arm64-msvc@npm:1.0.0-rc.15": - version: 1.0.0-rc.15 - resolution: "@rolldown/binding-win32-arm64-msvc@npm:1.0.0-rc.15" +"@rolldown/binding-win32-arm64-msvc@npm:1.0.3": + version: 1.0.3 + resolution: "@rolldown/binding-win32-arm64-msvc@npm:1.0.3" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@rolldown/binding-win32-x64-msvc@npm:1.0.0-rc.15": - version: 1.0.0-rc.15 - resolution: "@rolldown/binding-win32-x64-msvc@npm:1.0.0-rc.15" +"@rolldown/binding-win32-x64-msvc@npm:1.0.3": + version: 1.0.3 + resolution: "@rolldown/binding-win32-x64-msvc@npm:1.0.3" conditions: os=win32 & cpu=x64 languageName: node linkType: hard -"@rolldown/pluginutils@npm:1.0.0-rc.15": - version: 1.0.0-rc.15 - resolution: "@rolldown/pluginutils@npm:1.0.0-rc.15" - checksum: 10c0/15eef6a65ee6b2d07405c16999c2333c40d8aeea60bbc35e04957992fe6477c7b278d3f02679688bb928ad2ef3fbd3a6149c116d7dc9928ebf8d1434a0591674 - languageName: node - linkType: hard - "@rolldown/pluginutils@npm:1.0.0-rc.7": version: 1.0.0-rc.7 resolution: "@rolldown/pluginutils@npm:1.0.0-rc.7" @@ -2730,6 +2723,13 @@ __metadata: languageName: node linkType: hard +"@rolldown/pluginutils@npm:^1.0.0": + version: 1.0.1 + resolution: "@rolldown/pluginutils@npm:1.0.1" + checksum: 10c0/99d9b06d90196823e4d8c841f258db7a16e5dbba5824a2962b05d907b79f1ba929d56f22dd744fd530936e568c865ee56a719dc31e57e13bc0a8eb4764a8d8dd + languageName: node + linkType: hard + "@rollup/plugin-babel@npm:^5.2.0": version: 5.3.1 resolution: "@rollup/plugin-babel@npm:5.3.1" @@ -3561,12 +3561,12 @@ __metadata: languageName: node linkType: hard -"@tybys/wasm-util@npm:^0.10.1": - version: 0.10.1 - resolution: "@tybys/wasm-util@npm:0.10.1" +"@tybys/wasm-util@npm:^0.10.3": + version: 0.10.3 + resolution: "@tybys/wasm-util@npm:0.10.3" dependencies: tslib: "npm:^2.4.0" - checksum: 10c0/b255094f293794c6d2289300c5fbcafbb5532a3aed3a5ffd2f8dc1828e639b88d75f6a376dd8f94347a44813fd7a7149d8463477a9a49525c8b2dcaa38c2d1e8 + checksum: 10c0/fd2bd2a79c6cd8c79ed1cf7a0fa375c64589264c88a27acaf9756d556b453ea222b62a4f68dd2fbb8b3a78b6bab3b1f4fb2431b6afc6aeda8344b53a521a1cd3 languageName: node linkType: hard @@ -5592,7 +5592,7 @@ __metadata: tippy.js: "npm:^6.3.7" ts-node: "npm:10.9.2" typescript: "npm:5.5.4" - vite: "npm:8.0.8" + vite: "npm:8.0.16" vite-plugin-compression2: "npm:2.4.0" vite-plugin-pwa: "npm:1.2.0" vite-plugin-require-transform: "npm:1.0.21" @@ -10315,14 +10315,14 @@ __metadata: languageName: node linkType: hard -"postcss@npm:^8.5.8": - version: 8.5.8 - resolution: "postcss@npm:8.5.8" +"postcss@npm:^8.5.15": + version: 8.5.22 + resolution: "postcss@npm:8.5.22" dependencies: - nanoid: "npm:^3.3.11" + nanoid: "npm:^3.3.16" picocolors: "npm:^1.1.1" source-map-js: "npm:^1.2.1" - checksum: 10c0/dd918f7127ee7c60a0295bae2e72b3787892296e1d1c3c564d7a2a00c68d8df83cadc3178491259daa19ccc54804fb71ed8c937c6787e08d8bd4bedf8d17044c + checksum: 10c0/9e143ee457988049d5f187116fd37f2750ae9d8d71cc99eae690b776e549e134b34086cbaa2f0360ecd1729b15d918227bd0fc3a2ffbe341f7212d0827080793 languageName: node linkType: hard @@ -11232,27 +11232,27 @@ __metadata: languageName: node linkType: hard -"rolldown@npm:1.0.0-rc.15": - version: 1.0.0-rc.15 - resolution: "rolldown@npm:1.0.0-rc.15" - dependencies: - "@oxc-project/types": "npm:=0.124.0" - "@rolldown/binding-android-arm64": "npm:1.0.0-rc.15" - "@rolldown/binding-darwin-arm64": "npm:1.0.0-rc.15" - "@rolldown/binding-darwin-x64": "npm:1.0.0-rc.15" - "@rolldown/binding-freebsd-x64": "npm:1.0.0-rc.15" - "@rolldown/binding-linux-arm-gnueabihf": "npm:1.0.0-rc.15" - "@rolldown/binding-linux-arm64-gnu": "npm:1.0.0-rc.15" - "@rolldown/binding-linux-arm64-musl": "npm:1.0.0-rc.15" - "@rolldown/binding-linux-ppc64-gnu": "npm:1.0.0-rc.15" - "@rolldown/binding-linux-s390x-gnu": "npm:1.0.0-rc.15" - "@rolldown/binding-linux-x64-gnu": "npm:1.0.0-rc.15" - "@rolldown/binding-linux-x64-musl": "npm:1.0.0-rc.15" - "@rolldown/binding-openharmony-arm64": "npm:1.0.0-rc.15" - "@rolldown/binding-wasm32-wasi": "npm:1.0.0-rc.15" - "@rolldown/binding-win32-arm64-msvc": "npm:1.0.0-rc.15" - "@rolldown/binding-win32-x64-msvc": "npm:1.0.0-rc.15" - "@rolldown/pluginutils": "npm:1.0.0-rc.15" +"rolldown@npm:1.0.3": + version: 1.0.3 + resolution: "rolldown@npm:1.0.3" + dependencies: + "@oxc-project/types": "npm:=0.133.0" + "@rolldown/binding-android-arm64": "npm:1.0.3" + "@rolldown/binding-darwin-arm64": "npm:1.0.3" + "@rolldown/binding-darwin-x64": "npm:1.0.3" + "@rolldown/binding-freebsd-x64": "npm:1.0.3" + "@rolldown/binding-linux-arm-gnueabihf": "npm:1.0.3" + "@rolldown/binding-linux-arm64-gnu": "npm:1.0.3" + "@rolldown/binding-linux-arm64-musl": "npm:1.0.3" + "@rolldown/binding-linux-ppc64-gnu": "npm:1.0.3" + "@rolldown/binding-linux-s390x-gnu": "npm:1.0.3" + "@rolldown/binding-linux-x64-gnu": "npm:1.0.3" + "@rolldown/binding-linux-x64-musl": "npm:1.0.3" + "@rolldown/binding-openharmony-arm64": "npm:1.0.3" + "@rolldown/binding-wasm32-wasi": "npm:1.0.3" + "@rolldown/binding-win32-arm64-msvc": "npm:1.0.3" + "@rolldown/binding-win32-x64-msvc": "npm:1.0.3" + "@rolldown/pluginutils": "npm:^1.0.0" dependenciesMeta: "@rolldown/binding-android-arm64": optional: true @@ -11285,8 +11285,8 @@ __metadata: "@rolldown/binding-win32-x64-msvc": optional: true bin: - rolldown: bin/cli.mjs - checksum: 10c0/95df21125dafd2a0ce6ae9a89d926540e47900684023126c84632e18123371020da8f6b3235a188c45af0e4f9a5b963235de33bd9658ee5db9f3ff5862200eed + rolldown: ./bin/cli.mjs + checksum: 10c0/5f9dd47b7abf203b16bc600db68542f245e974c800e59ff50b76157d1dada1403657690435b036fabca88e93d13a67c31abe5cfaa6f61ce33717f61720204cdf languageName: node linkType: hard @@ -12348,6 +12348,16 @@ __metadata: languageName: node linkType: hard +"tinyglobby@npm:^0.2.17": + version: 0.2.17 + resolution: "tinyglobby@npm:0.2.17" + dependencies: + fdir: "npm:^6.5.0" + picomatch: "npm:^4.0.4" + checksum: 10c0/7f7bb0f197c88bc4b20c231e0deca4240ca3bf313a88f5a7fee93a872b84966a4d50220947c0455ad07a60b3b360961c5b7fd979222aeb716a9f99b412002e4c + languageName: node + linkType: hard + "tippy.js@npm:^6.3.1, tippy.js@npm:^6.3.7": version: 6.3.7 resolution: "tippy.js@npm:6.3.7" @@ -13024,19 +13034,19 @@ __metadata: languageName: node linkType: hard -"vite@npm:8.0.8": - version: 8.0.8 - resolution: "vite@npm:8.0.8" +"vite@npm:8.0.16": + version: 8.0.16 + resolution: "vite@npm:8.0.16" dependencies: fsevents: "npm:~2.3.3" lightningcss: "npm:^1.32.0" picomatch: "npm:^4.0.4" - postcss: "npm:^8.5.8" - rolldown: "npm:1.0.0-rc.15" - tinyglobby: "npm:^0.2.15" + postcss: "npm:^8.5.15" + rolldown: "npm:1.0.3" + tinyglobby: "npm:^0.2.17" peerDependencies: "@types/node": ^20.19.0 || >=22.12.0 - "@vitejs/devtools": ^0.1.0 + "@vitejs/devtools": ^0.1.18 esbuild: ^0.27.0 || ^0.28.0 jiti: ">=1.21.0" less: ^4.0.0 @@ -13077,7 +13087,7 @@ __metadata: optional: true bin: vite: bin/vite.js - checksum: 10c0/63474b399612ccf087d0aa025d7eb5c0d675012b6257b7f64332ff39579d4af4d5d7f0ac330906fc99b101abbf592c756adf143bb5748a02aec08f7d3639054d + checksum: 10c0/d75be3fbe2f63e6a8145325970338afaf0dd4d96ba9175c13f9a286fd5f95afc489401b693e4fa6c0899a4dd0e137be91cdf9401a40a635563911ad5036e3467 languageName: node linkType: hard From 31e32e30d39c2bf423b2dc2074ad89945e6ca657 Mon Sep 17 00:00:00 2001 From: shivani170 Date: Thu, 23 Jul 2026 17:32:00 +0530 Subject: [PATCH 15/17] chore: version bump --- package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 2e84b67194..a2967a05a5 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "private": true, "homepage": "/dashboard", "dependencies": { - "@devtron-labs/devtron-fe-common-lib": "4.0.7-beta-11", + "@devtron-labs/devtron-fe-common-lib": "4.0.8", "@esbuild-plugins/node-globals-polyfill": "0.2.3", "@sentry/browser": "7.119.1", "@sentry/integrations": "7.50.0", diff --git a/yarn.lock b/yarn.lock index 210bdc531f..5bb2cc8603 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1738,9 +1738,9 @@ __metadata: languageName: node linkType: hard -"@devtron-labs/devtron-fe-common-lib@npm:4.0.7-beta-11": - version: 4.0.7-beta-11 - resolution: "@devtron-labs/devtron-fe-common-lib@npm:4.0.7-beta-11" +"@devtron-labs/devtron-fe-common-lib@npm:4.0.8": + version: 4.0.8 + resolution: "@devtron-labs/devtron-fe-common-lib@npm:4.0.8" dependencies: "@codemirror/autocomplete": "npm:6.18.6" "@codemirror/lang-json": "npm:6.0.1" @@ -1794,7 +1794,7 @@ __metadata: react-select: 5.8.0 rxjs: ^7.8.1 yaml: ^2.8.3 - checksum: 10c0/c178b18248aecafeaa95c82f00388f33e4ceed4fa5bab42e6e0f2e6dd46615190decf2b92a1b3267b8497744cbd84b129e0c7c4ead418771de7bdb9eb2fab138 + checksum: 10c0/d230776db27eeecc022f60de6aeed6b3e745ac15201df9d84c4f35030584f77add22172e918ce0e003501c61f284e0111f61a0a5c4fa959e82bdf0b213a54bab languageName: node linkType: hard @@ -5529,7 +5529,7 @@ __metadata: version: 0.0.0-use.local resolution: "dashboard@workspace:." dependencies: - "@devtron-labs/devtron-fe-common-lib": "npm:4.0.7-beta-11" + "@devtron-labs/devtron-fe-common-lib": "npm:4.0.8" "@esbuild-plugins/node-globals-polyfill": "npm:0.2.3" "@playwright/test": "npm:^1.32.1" "@sentry/browser": "npm:7.119.1" From c06df126c8e4653c2a921f7d278225701d62d6ef Mon Sep 17 00:00:00 2001 From: shivani170 Date: Fri, 24 Jul 2026 17:36:02 +0530 Subject: [PATCH 16/17] chore: version update --- package.json | 2 +- vite.config.mts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 2ec7b5c0af..b153d4fbba 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "private": true, "homepage": "/dashboard", "dependencies": { - "@devtron-labs/devtron-fe-common-lib": "4.0.8", + "@devtron-labs/devtron-fe-common-lib": "4.0.9", "@esbuild-plugins/node-globals-polyfill": "0.2.3", "@sentry/browser": "7.119.1", "@sentry/integrations": "7.50.0", diff --git a/vite.config.mts b/vite.config.mts index 7d25b45b36..ec327b44c2 100644 --- a/vite.config.mts +++ b/vite.config.mts @@ -30,7 +30,7 @@ import { VitePWA } from 'vite-plugin-pwa' import { compression, defineAlgorithm } from 'vite-plugin-compression2' const WRONG_CODE = `import { bpfrpt_proptype_WindowScroller } from "../WindowScroller.js";` -const TARGET_URL = 'https://devtron-ent-5.devtron.info/' +const TARGET_URL = 'https://preview.devtron.ai/' function reactVirtualized(): PluginOption { return { From c8fc00cb1e8214fc323dcdd62487736cd23c2008 Mon Sep 17 00:00:00 2001 From: shivani170 Date: Fri, 24 Jul 2026 17:44:59 +0530 Subject: [PATCH 17/17] chore: version bump --- yarn.lock | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/yarn.lock b/yarn.lock index 73a2631d51..97e5c859fd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1738,9 +1738,9 @@ __metadata: languageName: node linkType: hard -"@devtron-labs/devtron-fe-common-lib@npm:4.0.8": - version: 4.0.8 - resolution: "@devtron-labs/devtron-fe-common-lib@npm:4.0.8" +"@devtron-labs/devtron-fe-common-lib@npm:4.0.9": + version: 4.0.9 + resolution: "@devtron-labs/devtron-fe-common-lib@npm:4.0.9" dependencies: "@codemirror/autocomplete": "npm:6.18.6" "@codemirror/lang-json": "npm:6.0.1" @@ -1794,7 +1794,7 @@ __metadata: react-select: 5.8.0 rxjs: ^7.8.1 yaml: ^2.8.3 - checksum: 10c0/d230776db27eeecc022f60de6aeed6b3e745ac15201df9d84c4f35030584f77add22172e918ce0e003501c61f284e0111f61a0a5c4fa959e82bdf0b213a54bab + checksum: 10c0/ed2232582299f86b23b1fd27d8deab05cfb904fa12133c87a74aa41256588b1ba6cb72b6cb4c8b813b578e51f04a695cc61e030c652b5841e6388d478d8a3820 languageName: node linkType: hard @@ -5529,7 +5529,7 @@ __metadata: version: 0.0.0-use.local resolution: "dashboard@workspace:." dependencies: - "@devtron-labs/devtron-fe-common-lib": "npm:4.0.8" + "@devtron-labs/devtron-fe-common-lib": "npm:4.0.9" "@esbuild-plugins/node-globals-polyfill": "npm:0.2.3" "@playwright/test": "npm:^1.32.1" "@sentry/browser": "npm:7.119.1"