Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
dd9a5e5
feat: Added the new menus and menu items for viewing security audit logs
adeiji May 6, 2026
1a56e9c
chore: added and updated tests to work with new menu updates
adeiji May 6, 2026
4685110
wip: added the audit logs export popup UI
adeiji May 7, 2026
36f4828
chore: added tests for DateRangePicker
adeiji May 8, 2026
45733c4
refactor: Replace ExportSettingsPopup date logic with shared DateRang…
adeiji May 8, 2026
c949200
chore: removed security audit logs export from team members action menu
adeiji May 9, 2026
93c7e35
chore: refactored exportTemplate and moved logging into separate func…
adeiji May 13, 2026
efea553
chore: added documentation for exportEncryptedDataSucceed and exportD…
adeiji May 13, 2026
4fd52f1
feat: implement audit logs export with paginated fetch and progress m…
adeiji May 13, 2026
ad2e950
chore: moved audit logs export popup file to different folder
adeiji May 13, 2026
0f1947b
chore: clean up and refactor of AuditLogsExportSetting.tsx file
adeiji May 13, 2026
a81bb29
chore: added documentation and small fixes
adeiji May 13, 2026
c8628a7
fix: audit logs export - correct API types, date format, and per-page…
adeiji May 18, 2026
8909d82
chore: fixed lint issues
adeiji May 18, 2026
6183018
fix: removed unused signal parameter from getExportAuditsLogApi
adeiji May 18, 2026
ea9f107
fix: updated documentation to match current architecture
adeiji May 18, 2026
43a5937
chore: removed unecessary documentation
adeiji May 18, 2026
668fc5a
fix: flatten array values and display as comma delimited values
adeiji May 18, 2026
9a47c04
fix: lint issues
adeiji May 18, 2026
aa1ed1a
fix: now only admins and managers can download Security Audit logs
adeiji May 21, 2026
1028347
fix: remove erroneous appletId from AuditLogsExportPopup props
adeiji May 22, 2026
52f689e
feat: Add Mixpanel tracking events for audit logs export flow
adeiji May 29, 2026
abe21e2
fix: add AppletId to ExportAuditLogsClick and ExportAuditLogsDownload…
adeiji May 29, 2026
f02fce2
feat: send utc datetime in request instead of date only (M2-10832)
adeiji Jun 1, 2026
0d30d9a
feat: changed API parameters from fromDate and toDate to fromDatetime…
adeiji Jun 4, 2026
ecc7923
chore: add timezone-independent test for formatDateAsUTC
adeiji Jun 4, 2026
90eff2d
fix: add null type to user.id in AuditEvent to match backend
adeiji Jun 4, 2026
e479fcd
Merge branch 'feat/audit-logs' into feat/audit-logs-mixpanel-events-1…
adeiji Jun 4, 2026
0bda8a7
Merge branch 'develop' into feat/audit-logs-mixpanel-events-10770
adeiji Jun 12, 2026
8a542e1
chore: trigger Amplify build
adeiji Jul 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { renderWithProviders } from 'shared/utils/renderWithProviders';
import { mockedApplet, mockedCurrentWorkspace } from 'shared/mock';
import { Roles } from 'shared/consts';
import { initialStateData } from 'shared/state';
import { Mixpanel, MixpanelEventType, MixpanelProps } from 'shared/utils';

import { HeaderOptions } from './HeaderOptions';

Expand Down Expand Up @@ -47,8 +48,11 @@ vi.mock('react-router-dom', async () => {
};
});

const spyMixpanelTrack = vi.spyOn(Mixpanel, 'track');

describe('HeaderOptions', () => {
beforeEach(() => {
spyMixpanelTrack.mockReset();
renderWithProviders(<HeaderOptions />, { preloadedState: getPreloadedState() });
});

Expand All @@ -70,6 +74,16 @@ describe('HeaderOptions', () => {
expect(screen.queryByTestId('header-option-audit-logs-button')).toBeInTheDocument();
});

test('should track ExportAuditLogsClick when audit logs option is clicked', () => {
fireEvent.click(screen.getByTestId('header-option-export-button'));
fireEvent.click(screen.getByTestId('header-option-audit-logs-button'));

expect(spyMixpanelTrack).toHaveBeenCalledWith({
action: MixpanelEventType.ExportAuditLogsClick,
[MixpanelProps.AppletId]: testAppletId,
});
});

test('should contain link to settings page', () => {
expect(screen.getByTestId('header-option-settings-link')).toHaveAttribute(
'href',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
checkIfCanAccessData,
checkIfCanEdit,
MixpanelEventType,
MixpanelProps,
checkIfFullAccess,
} from 'shared/utils';
import { workspaces } from 'shared/state';
Expand Down Expand Up @@ -42,7 +43,10 @@ export const HeaderOptions = () => {

const handleOpenAuditLogs = () => {
setIsAuditLogsExportOpen(true);
Mixpanel.track({ action: MixpanelEventType.ExportAuditLogsClick });
Mixpanel.track({
action: MixpanelEventType.ExportAuditLogsClick,
[MixpanelProps.AppletId]: appletId,
});
};

const handleOpenResponseData = () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { vi } from 'vitest';
import { initialStateData } from 'redux/modules';
import { mockedApplet } from 'shared/mock';
import { renderWithProviders } from 'shared/utils/renderWithProviders';
import { Mixpanel, MixpanelEventType, MixpanelProps } from 'shared/utils';

import { AuditLogsExportSetting } from './AuditLogsExportSetting';

Expand All @@ -29,13 +30,16 @@ vi.mock('shared/utils/exportTemplate', () => ({
exportTemplate: vi.fn().mockResolvedValue(true),
}));

const spyMixpanelTrack = vi.spyOn(Mixpanel, 'track');

const dataTestId = 'audit-logs-export';

describe('AuditLogsExportSetting', () => {
beforeEach(() => {
vi.useFakeTimers({ shouldAdvanceTime: true });
vi.setSystemTime(mockedNow);
mockedExportAuditLogsApi.mockClear();
spyMixpanelTrack.mockReset();
});

afterEach(() => {
Expand Down Expand Up @@ -169,4 +173,75 @@ describe('AuditLogsExportSetting', () => {
expect(mockedExportAuditLogsApi).toHaveBeenCalledTimes(2);
});
});

it('should track ExportAuditLogsDownload when the download button is clicked', async () => {
mockedExportAuditLogsApi.mockResolvedValueOnce({
data: { result: [], count: 0 },
});

renderWithProviders(
<AuditLogsExportSetting
isExportSettingsOpen
onExportSettingsClose={vi.fn()}
onExportPopupClose={vi.fn()}
data-testid={dataTestId}
/>,
{ preloadedState },
);

fireEvent.click(screen.getByTestId(`${dataTestId}-settings-download-button`));

expect(spyMixpanelTrack).toHaveBeenCalledWith({
action: MixpanelEventType.ExportAuditLogsDownload,
[MixpanelProps.AppletId]: mockedApplet.id,
});
});

it('should track ExportAuditLogsSuccessful when the export completes', async () => {
mockedExportAuditLogsApi.mockResolvedValueOnce({
data: { result: [], count: 0 },
});

renderWithProviders(
<AuditLogsExportSetting
isExportSettingsOpen
onExportSettingsClose={vi.fn()}
onExportPopupClose={vi.fn()}
data-testid={dataTestId}
/>,
{ preloadedState },
);

fireEvent.click(screen.getByTestId(`${dataTestId}-settings-download-button`));

await waitFor(() => {
expect(spyMixpanelTrack).toHaveBeenCalledWith({
action: MixpanelEventType.ExportAuditLogsSuccessful,
[MixpanelProps.AppletId]: mockedApplet.id,
});
});
});

it('should track ExportAuditLogsFailed when the export fails', async () => {
mockedExportAuditLogsApi.mockRejectedValueOnce(new Error('Network error'));

renderWithProviders(
<AuditLogsExportSetting
isExportSettingsOpen
onExportSettingsClose={vi.fn()}
onExportPopupClose={vi.fn()}
data-testid={dataTestId}
/>,
{ preloadedState },
);

fireEvent.click(screen.getByTestId(`${dataTestId}-settings-download-button`));

await waitFor(() => {
expect(spyMixpanelTrack).toHaveBeenCalledWith({
action: MixpanelEventType.ExportAuditLogsFailed,
[MixpanelProps.AppletId]: mockedApplet.id,
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { ObjectSchema } from 'yup';
import { getNormalizedTimezoneDate } from 'shared/utils/dateTimezone';
import { DateRangePickerType } from 'shared/components/DateRangePicker';
import { applet } from 'shared/state/Applet';
import { Mixpanel, MixpanelEventType, MixpanelProps } from 'shared/utils';

import {
AuditLogsExportFormValues,
Expand Down Expand Up @@ -66,6 +67,10 @@ export const AuditLogsExportSetting = ({
onExportSettingsClose();
}}
onExport={() => {
Mixpanel.track({
action: MixpanelEventType.ExportAuditLogsDownload,
[MixpanelProps.AppletId]: appletId,
});
setDataIsExporting(true);
onExportSettingsClose();
}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { getExportAuditLogsApi } from 'api';
import { getExportPageAmount } from 'modules/Dashboard/api/api.utils';
import { DateRangePickerType } from 'shared/components/DateRangePicker';
import { DateFormats } from 'shared/consts';
import { Mixpanel, MixpanelEventType, MixpanelProps } from 'shared/utils';
import { getLast24hUTCRange } from 'shared/utils';

import { AuditLogsExportFormValues } from '../../AuditLogsExportSetting.types';
Expand Down Expand Up @@ -71,8 +72,16 @@ export const useAuditLogsExport = (
setCurrentPage(page);
}

Mixpanel.track({
action: MixpanelEventType.ExportAuditLogsSuccessful,
[MixpanelProps.AppletId]: appletId,
});
handlePopupCloseRef.current();
} catch (e) {
Mixpanel.track({
action: MixpanelEventType.ExportAuditLogsFailed,
[MixpanelProps.AppletId]: appletId,
});
setError(e as Error);
} finally {
isExportingRef.current = false;
Expand Down
18 changes: 18 additions & 0 deletions src/shared/utils/mixpanel/mixpanel.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ export enum MixpanelEventType {
AppletCreatedSuccessfully = 'Applet Created Successfully',
ExportDataClick = 'Export Data click',
ExportAuditLogsClick = 'Export Audit Logs click',
ExportAuditLogsDownload = 'Export Audit Logs download',
ExportAuditLogsSuccessful = 'Export Audit Logs Successful',
ExportAuditLogsFailed = 'Export Audit Logs Failed',
TakeNowDialogClosed = 'Take Now dialogue closed',
MultiInformantStartActivityClick = 'Multi-informant Start Activity click',
ProvidingResponsesDropdownOpened = '"Who will be providing responses" dropdown opened',
Expand Down Expand Up @@ -249,6 +252,18 @@ export type ExportAuditLogsClickEvent = WithAppletId<{
action: MixpanelEventType.ExportAuditLogsClick;
}>;

export type ExportAuditLogsDownloadEvent = WithAppletId<{
action: MixpanelEventType.ExportAuditLogsDownload;
}>;

export type ExportAuditLogsSuccessfulEvent = WithAppletId<{
action: MixpanelEventType.ExportAuditLogsSuccessful;
}>;

export type ExportAuditLogsFailedEvent = WithAppletId<{
action: MixpanelEventType.ExportAuditLogsFailed;
}>;

type TakeNowEvent = WithFeature<
WithAppletId<{
[MixpanelProps.MultiInformantAssessmentId]?: string | null;
Expand Down Expand Up @@ -571,6 +586,9 @@ export type MixpanelEvent =
| AppletCreatedSuccessfullyEvent
| ExportDataClickEvent
| ExportAuditLogsClickEvent
| ExportAuditLogsDownloadEvent
| ExportAuditLogsSuccessfulEvent
| ExportAuditLogsFailedEvent
| TakeNowDialogClosedEvent
| MultiInformantStartActivityClickEvent
| ProvidingResponsesDropdownOpenedEvent
Expand Down
Loading