Skip to content

Commit 827e958

Browse files
authored
feat: Add Mixpanel tracking events for audit logs export flow (M2-10770) (#2225)
🔗 [Jira Ticket M2-10770](https://mindlogger.atlassian.net/browse/M2-10770) Adds Mixpanel tracking events across the full audit logs export flow. Changes include: - Add `ExportAuditLogsDownload` event, tracked when the user clicks the Export button in the audit logs settings popup - Add `ExportAuditLogsSuccessful` event, tracked when the audit logs CSV download completes - Add `ExportAuditLogsFailed` event, tracked when the export encounters an error - Add test coverage for all audit log Mixpanel events
1 parent 826f4d2 commit 827e958

6 files changed

Lines changed: 126 additions & 1 deletion

File tree

src/modules/Dashboard/components/HeaderOptions/HeaderOptions.test.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { renderWithProviders } from 'shared/utils/renderWithProviders';
55
import { mockedApplet, mockedCurrentWorkspace } from 'shared/mock';
66
import { Roles } from 'shared/consts';
77
import { initialStateData } from 'shared/state';
8+
import { Mixpanel, MixpanelEventType, MixpanelProps } from 'shared/utils';
89

910
import { HeaderOptions } from './HeaderOptions';
1011

@@ -47,8 +48,11 @@ vi.mock('react-router-dom', async () => {
4748
};
4849
});
4950

51+
const spyMixpanelTrack = vi.spyOn(Mixpanel, 'track');
52+
5053
describe('HeaderOptions', () => {
5154
beforeEach(() => {
55+
spyMixpanelTrack.mockReset();
5256
renderWithProviders(<HeaderOptions />, { preloadedState: getPreloadedState() });
5357
});
5458

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

77+
test('should track ExportAuditLogsClick when audit logs option is clicked', () => {
78+
fireEvent.click(screen.getByTestId('header-option-export-button'));
79+
fireEvent.click(screen.getByTestId('header-option-audit-logs-button'));
80+
81+
expect(spyMixpanelTrack).toHaveBeenCalledWith({
82+
action: MixpanelEventType.ExportAuditLogsClick,
83+
[MixpanelProps.AppletId]: testAppletId,
84+
});
85+
});
86+
7387
test('should contain link to settings page', () => {
7488
expect(screen.getByTestId('header-option-settings-link')).toHaveAttribute(
7589
'href',

src/modules/Dashboard/components/HeaderOptions/HeaderOptions.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
checkIfCanAccessData,
1313
checkIfCanEdit,
1414
MixpanelEventType,
15+
MixpanelProps,
1516
checkIfFullAccess,
1617
} from 'shared/utils';
1718
import { workspaces } from 'shared/state';
@@ -42,7 +43,10 @@ export const HeaderOptions = () => {
4243

4344
const handleOpenAuditLogs = () => {
4445
setIsAuditLogsExportOpen(true);
45-
Mixpanel.track({ action: MixpanelEventType.ExportAuditLogsClick });
46+
Mixpanel.track({
47+
action: MixpanelEventType.ExportAuditLogsClick,
48+
[MixpanelProps.AppletId]: appletId,
49+
});
4650
};
4751

4852
const handleOpenResponseData = () => {

src/shared/features/AppletSettings/AuditLogsExportSetting/AuditLogsExportSetting.test.tsx

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { vi } from 'vitest';
44
import { initialStateData } from 'redux/modules';
55
import { mockedApplet } from 'shared/mock';
66
import { renderWithProviders } from 'shared/utils/renderWithProviders';
7+
import { Mixpanel, MixpanelEventType, MixpanelProps } from 'shared/utils';
78

89
import { AuditLogsExportSetting } from './AuditLogsExportSetting';
910

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

33+
const spyMixpanelTrack = vi.spyOn(Mixpanel, 'track');
34+
3235
const dataTestId = 'audit-logs-export';
3336

3437
describe('AuditLogsExportSetting', () => {
3538
beforeEach(() => {
3639
vi.useFakeTimers({ shouldAdvanceTime: true });
3740
vi.setSystemTime(mockedNow);
3841
mockedExportAuditLogsApi.mockClear();
42+
spyMixpanelTrack.mockReset();
3943
});
4044

4145
afterEach(() => {
@@ -169,4 +173,75 @@ describe('AuditLogsExportSetting', () => {
169173
expect(mockedExportAuditLogsApi).toHaveBeenCalledTimes(2);
170174
});
171175
});
176+
177+
it('should track ExportAuditLogsDownload when the download button is clicked', async () => {
178+
mockedExportAuditLogsApi.mockResolvedValueOnce({
179+
data: { result: [], count: 0 },
180+
});
181+
182+
renderWithProviders(
183+
<AuditLogsExportSetting
184+
isExportSettingsOpen
185+
onExportSettingsClose={vi.fn()}
186+
onExportPopupClose={vi.fn()}
187+
data-testid={dataTestId}
188+
/>,
189+
{ preloadedState },
190+
);
191+
192+
fireEvent.click(screen.getByTestId(`${dataTestId}-settings-download-button`));
193+
194+
expect(spyMixpanelTrack).toHaveBeenCalledWith({
195+
action: MixpanelEventType.ExportAuditLogsDownload,
196+
[MixpanelProps.AppletId]: mockedApplet.id,
197+
});
198+
});
199+
200+
it('should track ExportAuditLogsSuccessful when the export completes', async () => {
201+
mockedExportAuditLogsApi.mockResolvedValueOnce({
202+
data: { result: [], count: 0 },
203+
});
204+
205+
renderWithProviders(
206+
<AuditLogsExportSetting
207+
isExportSettingsOpen
208+
onExportSettingsClose={vi.fn()}
209+
onExportPopupClose={vi.fn()}
210+
data-testid={dataTestId}
211+
/>,
212+
{ preloadedState },
213+
);
214+
215+
fireEvent.click(screen.getByTestId(`${dataTestId}-settings-download-button`));
216+
217+
await waitFor(() => {
218+
expect(spyMixpanelTrack).toHaveBeenCalledWith({
219+
action: MixpanelEventType.ExportAuditLogsSuccessful,
220+
[MixpanelProps.AppletId]: mockedApplet.id,
221+
});
222+
});
223+
});
224+
225+
it('should track ExportAuditLogsFailed when the export fails', async () => {
226+
mockedExportAuditLogsApi.mockRejectedValueOnce(new Error('Network error'));
227+
228+
renderWithProviders(
229+
<AuditLogsExportSetting
230+
isExportSettingsOpen
231+
onExportSettingsClose={vi.fn()}
232+
onExportPopupClose={vi.fn()}
233+
data-testid={dataTestId}
234+
/>,
235+
{ preloadedState },
236+
);
237+
238+
fireEvent.click(screen.getByTestId(`${dataTestId}-settings-download-button`));
239+
240+
await waitFor(() => {
241+
expect(spyMixpanelTrack).toHaveBeenCalledWith({
242+
action: MixpanelEventType.ExportAuditLogsFailed,
243+
[MixpanelProps.AppletId]: mockedApplet.id,
244+
});
245+
});
246+
});
172247
});

src/shared/features/AppletSettings/AuditLogsExportSetting/AuditLogsExportSetting.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { ObjectSchema } from 'yup';
77
import { getNormalizedTimezoneDate } from 'shared/utils/dateTimezone';
88
import { DateRangePickerType } from 'shared/components/DateRangePicker';
99
import { applet } from 'shared/state/Applet';
10+
import { Mixpanel, MixpanelEventType, MixpanelProps } from 'shared/utils';
1011

1112
import {
1213
AuditLogsExportFormValues,
@@ -66,6 +67,10 @@ export const AuditLogsExportSetting = ({
6667
onExportSettingsClose();
6768
}}
6869
onExport={() => {
70+
Mixpanel.track({
71+
action: MixpanelEventType.ExportAuditLogsDownload,
72+
[MixpanelProps.AppletId]: appletId,
73+
});
6974
setDataIsExporting(true);
7075
onExportSettingsClose();
7176
}}

src/shared/features/AppletSettings/AuditLogsExportSetting/Popups/AuditLogsExportPopup/useAuditLogsExport.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { getExportAuditLogsApi } from 'api';
66
import { getExportPageAmount } from 'modules/Dashboard/api/api.utils';
77
import { DateRangePickerType } from 'shared/components/DateRangePicker';
88
import { DateFormats } from 'shared/consts';
9+
import { Mixpanel, MixpanelEventType, MixpanelProps } from 'shared/utils';
910
import { getLast24hUTCRange } from 'shared/utils';
1011

1112
import { AuditLogsExportFormValues } from '../../AuditLogsExportSetting.types';
@@ -71,8 +72,16 @@ export const useAuditLogsExport = (
7172
setCurrentPage(page);
7273
}
7374

75+
Mixpanel.track({
76+
action: MixpanelEventType.ExportAuditLogsSuccessful,
77+
[MixpanelProps.AppletId]: appletId,
78+
});
7479
handlePopupCloseRef.current();
7580
} catch (e) {
81+
Mixpanel.track({
82+
action: MixpanelEventType.ExportAuditLogsFailed,
83+
[MixpanelProps.AppletId]: appletId,
84+
});
7685
setError(e as Error);
7786
} finally {
7887
isExportingRef.current = false;

src/shared/utils/mixpanel/mixpanel.types.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@ export enum MixpanelEventType {
6565
AppletCreatedSuccessfully = 'Applet Created Successfully',
6666
ExportDataClick = 'Export Data click',
6767
ExportAuditLogsClick = 'Export Audit Logs click',
68+
ExportAuditLogsDownload = 'Export Audit Logs download',
69+
ExportAuditLogsSuccessful = 'Export Audit Logs Successful',
70+
ExportAuditLogsFailed = 'Export Audit Logs Failed',
6871
TakeNowDialogClosed = 'Take Now dialogue closed',
6972
MultiInformantStartActivityClick = 'Multi-informant Start Activity click',
7073
ProvidingResponsesDropdownOpened = '"Who will be providing responses" dropdown opened',
@@ -249,6 +252,18 @@ export type ExportAuditLogsClickEvent = WithAppletId<{
249252
action: MixpanelEventType.ExportAuditLogsClick;
250253
}>;
251254

255+
export type ExportAuditLogsDownloadEvent = WithAppletId<{
256+
action: MixpanelEventType.ExportAuditLogsDownload;
257+
}>;
258+
259+
export type ExportAuditLogsSuccessfulEvent = WithAppletId<{
260+
action: MixpanelEventType.ExportAuditLogsSuccessful;
261+
}>;
262+
263+
export type ExportAuditLogsFailedEvent = WithAppletId<{
264+
action: MixpanelEventType.ExportAuditLogsFailed;
265+
}>;
266+
252267
type TakeNowEvent = WithFeature<
253268
WithAppletId<{
254269
[MixpanelProps.MultiInformantAssessmentId]?: string | null;
@@ -571,6 +586,9 @@ export type MixpanelEvent =
571586
| AppletCreatedSuccessfullyEvent
572587
| ExportDataClickEvent
573588
| ExportAuditLogsClickEvent
589+
| ExportAuditLogsDownloadEvent
590+
| ExportAuditLogsSuccessfulEvent
591+
| ExportAuditLogsFailedEvent
574592
| TakeNowDialogClosedEvent
575593
| MultiInformantStartActivityClickEvent
576594
| ProvidingResponsesDropdownOpenedEvent

0 commit comments

Comments
 (0)