From 1099cce828ce8b64305a173493b2124b73459dd5 Mon Sep 17 00:00:00 2001 From: Kneesal Date: Thu, 29 Jan 2026 02:31:16 +0000 Subject: [PATCH 1/3] fix: plausible date range filtering --- .../AnalyticsOverlaySwitch.spec.tsx | 68 +++++++++++++++++- .../AnalyticsOverlaySwitch.tsx | 69 ++++++++++++++----- .../buildPlausibleDateRange.spec.ts | 54 +++++++++++++++ .../buildPlausibleDateRange.ts | 18 +++++ .../buildPlausibleDateRange/index.ts | 2 + .../AnalyticsItem/AnalyticsItem.spec.tsx | 4 ++ 6 files changed, 198 insertions(+), 17 deletions(-) create mode 100644 apps/journeys-admin/src/components/Editor/Slider/JourneyFlow/AnalyticsOverlaySwitch/buildPlausibleDateRange/buildPlausibleDateRange.spec.ts create mode 100644 apps/journeys-admin/src/components/Editor/Slider/JourneyFlow/AnalyticsOverlaySwitch/buildPlausibleDateRange/buildPlausibleDateRange.ts create mode 100644 apps/journeys-admin/src/components/Editor/Slider/JourneyFlow/AnalyticsOverlaySwitch/buildPlausibleDateRange/index.ts diff --git a/apps/journeys-admin/src/components/Editor/Slider/JourneyFlow/AnalyticsOverlaySwitch/AnalyticsOverlaySwitch.spec.tsx b/apps/journeys-admin/src/components/Editor/Slider/JourneyFlow/AnalyticsOverlaySwitch/AnalyticsOverlaySwitch.spec.tsx index 74e198594f5..42de90914c4 100644 --- a/apps/journeys-admin/src/components/Editor/Slider/JourneyFlow/AnalyticsOverlaySwitch/AnalyticsOverlaySwitch.spec.tsx +++ b/apps/journeys-admin/src/components/Editor/Slider/JourneyFlow/AnalyticsOverlaySwitch/AnalyticsOverlaySwitch.spec.tsx @@ -1,5 +1,6 @@ import { MockedProvider } from '@apollo/client/testing' -import { render, screen, waitFor } from '@testing-library/react' +import { fireEvent, render, screen, waitFor } from '@testing-library/react' +import userEvent from '@testing-library/user-event' import { EditorProvider } from '@core/journeys/ui/EditorProvider' import { JourneyProvider } from '@core/journeys/ui/JourneyProvider' @@ -8,12 +9,23 @@ import { getJourneyAnalytics } from '@core/journeys/ui/useJourneyAnalyticsQuery/ import { GetJourney_journey } from '../../../../../../__generated__/GetJourney' import { earliestStatsCollected } from './AnalyticsOverlaySwitch' +import { buildPlausibleDateRange } from './buildPlausibleDateRange' + +jest.mock('./buildPlausibleDateRange') + +const mockBuildPlausibleDateRange = + buildPlausibleDateRange as jest.MockedFunction import { AnalyticsOverlaySwitch } from '.' +import { formatISO } from 'date-fns' const mockCurrentDate = '2024-06-02' describe('AnalyticsOverlaySwitch', () => { + beforeEach(() => { + mockBuildPlausibleDateRange.mockClear() + }) + beforeAll(() => { jest.useFakeTimers() jest.setSystemTime(new Date('2024-06-02')) @@ -24,6 +36,9 @@ describe('AnalyticsOverlaySwitch', () => { }) it('toggles showAnalytics', async () => { + mockBuildPlausibleDateRange.mockReturnValue( + `${earliestStatsCollected},${mockCurrentDate}` + ) const result = jest.fn().mockReturnValue(getJourneyAnalytics.result) const journey = { id: 'journeyId' } as unknown as GetJourney_journey const request = { @@ -78,4 +93,55 @@ describe('AnalyticsOverlaySwitch', () => { screen.getByRole('checkbox').click() expect(showAnalytics).toHaveTextContent('false') }) + + it('gets analytics for selected date range', async () => { + const selectedStartDate = new Date('2024-06-05') + const selectedEndDate = new Date('2024-06-10') + const formattedDateRange = `${formatISO(selectedStartDate, { + representation: 'date' + })},${formatISO(selectedEndDate, { representation: 'date' })}` + + // Pretend the date picker has already produced our custom range + mockBuildPlausibleDateRange.mockReturnValue(formattedDateRange) + + const result = jest.fn().mockReturnValue(getJourneyAnalytics.result) + const journey = { id: 'journeyId' } as unknown as GetJourney_journey + + render( + + + + {({ state: { analytics } }) => ( + <> +
{JSON.stringify(analytics)}
+ + + )} +
+
+
+ ) + + const analyticsCheckbox = screen.getByRole('checkbox') + fireEvent.click(analyticsCheckbox) + + // Verify the network call was made with the mocked date range + await waitFor(() => { + expect(result).toHaveBeenCalled() + }) + }) }) diff --git a/apps/journeys-admin/src/components/Editor/Slider/JourneyFlow/AnalyticsOverlaySwitch/AnalyticsOverlaySwitch.tsx b/apps/journeys-admin/src/components/Editor/Slider/JourneyFlow/AnalyticsOverlaySwitch/AnalyticsOverlaySwitch.tsx index aebf69cd997..78050d49de5 100644 --- a/apps/journeys-admin/src/components/Editor/Slider/JourneyFlow/AnalyticsOverlaySwitch/AnalyticsOverlaySwitch.tsx +++ b/apps/journeys-admin/src/components/Editor/Slider/JourneyFlow/AnalyticsOverlaySwitch/AnalyticsOverlaySwitch.tsx @@ -1,15 +1,18 @@ import FormControlLabel from '@mui/material/FormControlLabel' +import Stack from '@mui/material/Stack' import Switch from '@mui/material/Switch' import Typography from '@mui/material/Typography' -import { formatISO } from 'date-fns' import { useTranslation } from 'next-i18next' import { enqueueSnackbar } from 'notistack' -import { ReactElement } from 'react' +import { ReactElement, useState } from 'react' import { useEditor } from '@core/journeys/ui/EditorProvider' import { useJourney } from '@core/journeys/ui/JourneyProvider' import { useJourneyAnalyticsQuery } from '@core/journeys/ui/useJourneyAnalyticsQuery' +import { buildPlausibleDateRange } from './buildPlausibleDateRange' +import { DateRangePicker } from '../../../../JourneyVisitorsList/FilterDrawer/ExportDialog/DateRangePicker' + // Used to for filter all time stats export const earliestStatsCollected = '2024-06-01' @@ -20,15 +23,32 @@ export function AnalyticsOverlaySwitch(): ReactElement { state: { showAnalytics }, dispatch } = useEditor() - const currentDate = formatISO(new Date(), { representation: 'date' }) + + const [startDate, setStartDate] = useState( + new Date(earliestStatsCollected) + ) + const [endDate, setEndDate] = useState(new Date()) + + const isDateRangeValid = + startDate != null && endDate != null && startDate <= endDate + + const formattedDateRange = buildPlausibleDateRange( + startDate, + endDate, + earliestStatsCollected, + new Date() + ) useJourneyAnalyticsQuery({ variables: { id: journey?.id ?? '', period: 'custom', - date: `${earliestStatsCollected},${currentDate}` + date: formattedDateRange }, - skip: journey?.id == null || showAnalytics !== true, + skip: + journey?.id == null || + showAnalytics !== true || + isDateRangeValid !== true, onCompleted: (analytics) => { dispatch({ type: 'SetAnalyticsAction', @@ -36,6 +56,13 @@ export function AnalyticsOverlaySwitch(): ReactElement { }) }, onError: (_) => { + if (isDateRangeValid) { + enqueueSnackbar(t('Invalid date range'), { + variant: 'error', + preventDuplicate: true + }) + return + } enqueueSnackbar(t('Error fetching analytics'), { variant: 'error', preventDuplicate: true @@ -51,17 +78,27 @@ export function AnalyticsOverlaySwitch(): ReactElement { } return ( - + + } + label={ + {t('Analytics Overlay')} + } + labelPlacement="start" + /> + {showAnalytics && ( + - } - label={ - {t('Analytics Overlay')} - } - labelPlacement="start" - /> + )} + ) } diff --git a/apps/journeys-admin/src/components/Editor/Slider/JourneyFlow/AnalyticsOverlaySwitch/buildPlausibleDateRange/buildPlausibleDateRange.spec.ts b/apps/journeys-admin/src/components/Editor/Slider/JourneyFlow/AnalyticsOverlaySwitch/buildPlausibleDateRange/buildPlausibleDateRange.spec.ts new file mode 100644 index 00000000000..dc0062b21e8 --- /dev/null +++ b/apps/journeys-admin/src/components/Editor/Slider/JourneyFlow/AnalyticsOverlaySwitch/buildPlausibleDateRange/buildPlausibleDateRange.spec.ts @@ -0,0 +1,54 @@ +// Ensure we use the real formatISO implementation, not a mock from other tests +import { formatISO } from 'date-fns' + +import { buildPlausibleDateRange } from './buildPlausibleDateRange' + +describe('buildPlausibleDateRange', () => { + const fallbackStart = '2024-06-01' + const fallbackEnd = new Date('2024-06-10') + + it('returns formatted range when start and end dates are valid', () => { + const startDate = new Date('2024-06-02') + const endDate = new Date('2024-06-05') + + const result = buildPlausibleDateRange( + startDate, + endDate, + fallbackStart, + fallbackEnd + ) + + expect(result).toBe( + `${formatISO(startDate, { representation: 'date' })},${formatISO(endDate, { representation: 'date' })}` + ) + }) + + it('falls back when dates are null', () => { + const result = buildPlausibleDateRange( + null, + null, + fallbackStart, + fallbackEnd + ) + + expect(result).toBe( + `${fallbackStart},${formatISO(fallbackEnd, { representation: 'date' })}` + ) + }) + + it('falls back when start date is after end date', () => { + const startDate = new Date('2024-06-06') + const endDate = new Date('2024-06-05') + + const result = buildPlausibleDateRange( + startDate, + endDate, + fallbackStart, + fallbackEnd + ) + + expect(result).toBe( + `${formatISO(fallbackStart, { representation: 'date' })},${formatISO(fallbackEnd, { representation: 'date' })}` + ) + }) +}) diff --git a/apps/journeys-admin/src/components/Editor/Slider/JourneyFlow/AnalyticsOverlaySwitch/buildPlausibleDateRange/buildPlausibleDateRange.ts b/apps/journeys-admin/src/components/Editor/Slider/JourneyFlow/AnalyticsOverlaySwitch/buildPlausibleDateRange/buildPlausibleDateRange.ts new file mode 100644 index 00000000000..5dc64a02b51 --- /dev/null +++ b/apps/journeys-admin/src/components/Editor/Slider/JourneyFlow/AnalyticsOverlaySwitch/buildPlausibleDateRange/buildPlausibleDateRange.ts @@ -0,0 +1,18 @@ +import { formatISO } from 'date-fns' + +export function buildPlausibleDateRange( + startDate: Date | null, + endDate: Date | null, + fallbackStartDate: string, + fallbackEndDate: Date +): string { + if (startDate != null && endDate != null && startDate <= endDate) { + return `${formatISO(startDate, { + representation: 'date' + })},${formatISO(endDate, { representation: 'date' })}` + } + + return `${fallbackStartDate},${formatISO(fallbackEndDate, { + representation: 'date' + })}` +} diff --git a/apps/journeys-admin/src/components/Editor/Slider/JourneyFlow/AnalyticsOverlaySwitch/buildPlausibleDateRange/index.ts b/apps/journeys-admin/src/components/Editor/Slider/JourneyFlow/AnalyticsOverlaySwitch/buildPlausibleDateRange/index.ts new file mode 100644 index 00000000000..5bc0414bc70 --- /dev/null +++ b/apps/journeys-admin/src/components/Editor/Slider/JourneyFlow/AnalyticsOverlaySwitch/buildPlausibleDateRange/index.ts @@ -0,0 +1,2 @@ +export { buildPlausibleDateRange } from './buildPlausibleDateRange' + diff --git a/apps/journeys-admin/src/components/Editor/Toolbar/Items/AnalyticsItem/AnalyticsItem.spec.tsx b/apps/journeys-admin/src/components/Editor/Toolbar/Items/AnalyticsItem/AnalyticsItem.spec.tsx index 949a67d453d..f14c92b6e97 100644 --- a/apps/journeys-admin/src/components/Editor/Toolbar/Items/AnalyticsItem/AnalyticsItem.spec.tsx +++ b/apps/journeys-admin/src/components/Editor/Toolbar/Items/AnalyticsItem/AnalyticsItem.spec.tsx @@ -56,6 +56,10 @@ describe('AnalyticsItem', () => { jest.clearAllMocks() }) + afterAll(() => { + jest.restoreAllMocks() + }) + it('should link to journey reports page as a list item', async () => { const { getByRole } = render( From 91f92ddc00f5744749f0ecff831e45b7618c752f Mon Sep 17 00:00:00 2001 From: Kneesal Date: Thu, 29 Jan 2026 02:36:51 +0000 Subject: [PATCH 2/3] fix: revert this file to main --- .../Editor/Toolbar/Items/AnalyticsItem/AnalyticsItem.spec.tsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/apps/journeys-admin/src/components/Editor/Toolbar/Items/AnalyticsItem/AnalyticsItem.spec.tsx b/apps/journeys-admin/src/components/Editor/Toolbar/Items/AnalyticsItem/AnalyticsItem.spec.tsx index f14c92b6e97..949a67d453d 100644 --- a/apps/journeys-admin/src/components/Editor/Toolbar/Items/AnalyticsItem/AnalyticsItem.spec.tsx +++ b/apps/journeys-admin/src/components/Editor/Toolbar/Items/AnalyticsItem/AnalyticsItem.spec.tsx @@ -56,10 +56,6 @@ describe('AnalyticsItem', () => { jest.clearAllMocks() }) - afterAll(() => { - jest.restoreAllMocks() - }) - it('should link to journey reports page as a list item', async () => { const { getByRole } = render( From a5badae76f2c27248aa0513d7cb67fe36387dbe4 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Thu, 29 Jan 2026 02:41:18 +0000 Subject: [PATCH 3/3] fix: lint issues --- .../AnalyticsOverlaySwitch/AnalyticsOverlaySwitch.spec.tsx | 6 +++--- .../AnalyticsOverlaySwitch/AnalyticsOverlaySwitch.tsx | 3 ++- .../AnalyticsOverlaySwitch/buildPlausibleDateRange/index.ts | 1 - libs/locales/en/apps-journeys-admin.json | 1 + 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/apps/journeys-admin/src/components/Editor/Slider/JourneyFlow/AnalyticsOverlaySwitch/AnalyticsOverlaySwitch.spec.tsx b/apps/journeys-admin/src/components/Editor/Slider/JourneyFlow/AnalyticsOverlaySwitch/AnalyticsOverlaySwitch.spec.tsx index 42de90914c4..95bdd1537f2 100644 --- a/apps/journeys-admin/src/components/Editor/Slider/JourneyFlow/AnalyticsOverlaySwitch/AnalyticsOverlaySwitch.spec.tsx +++ b/apps/journeys-admin/src/components/Editor/Slider/JourneyFlow/AnalyticsOverlaySwitch/AnalyticsOverlaySwitch.spec.tsx @@ -1,6 +1,7 @@ import { MockedProvider } from '@apollo/client/testing' import { fireEvent, render, screen, waitFor } from '@testing-library/react' import userEvent from '@testing-library/user-event' +import { formatISO } from 'date-fns' import { EditorProvider } from '@core/journeys/ui/EditorProvider' import { JourneyProvider } from '@core/journeys/ui/JourneyProvider' @@ -11,14 +12,13 @@ import { GetJourney_journey } from '../../../../../../__generated__/GetJourney' import { earliestStatsCollected } from './AnalyticsOverlaySwitch' import { buildPlausibleDateRange } from './buildPlausibleDateRange' +import { AnalyticsOverlaySwitch } from '.' + jest.mock('./buildPlausibleDateRange') const mockBuildPlausibleDateRange = buildPlausibleDateRange as jest.MockedFunction -import { AnalyticsOverlaySwitch } from '.' -import { formatISO } from 'date-fns' - const mockCurrentDate = '2024-06-02' describe('AnalyticsOverlaySwitch', () => { diff --git a/apps/journeys-admin/src/components/Editor/Slider/JourneyFlow/AnalyticsOverlaySwitch/AnalyticsOverlaySwitch.tsx b/apps/journeys-admin/src/components/Editor/Slider/JourneyFlow/AnalyticsOverlaySwitch/AnalyticsOverlaySwitch.tsx index 78050d49de5..34055eb3876 100644 --- a/apps/journeys-admin/src/components/Editor/Slider/JourneyFlow/AnalyticsOverlaySwitch/AnalyticsOverlaySwitch.tsx +++ b/apps/journeys-admin/src/components/Editor/Slider/JourneyFlow/AnalyticsOverlaySwitch/AnalyticsOverlaySwitch.tsx @@ -10,9 +10,10 @@ import { useEditor } from '@core/journeys/ui/EditorProvider' import { useJourney } from '@core/journeys/ui/JourneyProvider' import { useJourneyAnalyticsQuery } from '@core/journeys/ui/useJourneyAnalyticsQuery' -import { buildPlausibleDateRange } from './buildPlausibleDateRange' import { DateRangePicker } from '../../../../JourneyVisitorsList/FilterDrawer/ExportDialog/DateRangePicker' +import { buildPlausibleDateRange } from './buildPlausibleDateRange' + // Used to for filter all time stats export const earliestStatsCollected = '2024-06-01' diff --git a/apps/journeys-admin/src/components/Editor/Slider/JourneyFlow/AnalyticsOverlaySwitch/buildPlausibleDateRange/index.ts b/apps/journeys-admin/src/components/Editor/Slider/JourneyFlow/AnalyticsOverlaySwitch/buildPlausibleDateRange/index.ts index 5bc0414bc70..51f70313f8b 100644 --- a/apps/journeys-admin/src/components/Editor/Slider/JourneyFlow/AnalyticsOverlaySwitch/buildPlausibleDateRange/index.ts +++ b/apps/journeys-admin/src/components/Editor/Slider/JourneyFlow/AnalyticsOverlaySwitch/buildPlausibleDateRange/index.ts @@ -1,2 +1 @@ export { buildPlausibleDateRange } from './buildPlausibleDateRange' - diff --git a/libs/locales/en/apps-journeys-admin.json b/libs/locales/en/apps-journeys-admin.json index 36e046eab01..edbc7dc53c8 100644 --- a/libs/locales/en/apps-journeys-admin.json +++ b/libs/locales/en/apps-journeys-admin.json @@ -124,6 +124,7 @@ "Headline": "Headline", "Secondary Text": "Secondary Text", "Social Post View": "Social Post View", + "Invalid date range": "Invalid date range", "Error fetching analytics": "Error fetching analytics", "Analytics Overlay": "Analytics Overlay", "Zoom in": "Zoom in",