Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
9 changes: 8 additions & 1 deletion apps/start/src/components/report-chart/area/chart.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { useRechartDataModel } from '@/hooks/use-rechart-data-model';
import { useVisibleSeries } from '@/hooks/use-visible-series';
import { useTRPC } from '@/integrations/trpc/react';
import { changeVisibleSeries } from '@/components/report/reportSlice';
import { useDispatch } from '@/redux';
import { pushModal } from '@/modals';
import type { IChartData } from '@/trpc/client';
import { cn } from '@/utils/cn';
Expand Down Expand Up @@ -52,10 +54,12 @@ export function Chart({ data }: Props) {
lineType,
series: reportSeries,
breakdowns,
visibleSeries: savedVisibleSeries,
},
isEditMode,
options: { hideXAxis, hideYAxis },
} = useReportChartContext();
const dispatch = useDispatch();
const trpc = useTRPC();
const references = useQuery(
trpc.reference.getChartReferences.queryOptions(
Expand All @@ -68,7 +72,10 @@ export function Chart({ data }: Props) {
{},
),
);
const { series, setVisibleSeries } = useVisibleSeries(data);
const { series, setVisibleSeries } = useVisibleSeries(data, undefined, {
initialSeries: savedVisibleSeries,
onChange: (ids) => dispatch(changeVisibleSeries(ids)),
});
const rechartData = useRechartDataModel(series);

let dotIndex = undefined;
Expand Down
9 changes: 8 additions & 1 deletion apps/start/src/components/report-chart/histogram/chart.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { useRechartDataModel } from '@/hooks/use-rechart-data-model';
import { useTheme } from '@/hooks/use-theme';
import { useVisibleSeries } from '@/hooks/use-visible-series';
import { useTRPC } from '@/integrations/trpc/react';
import { changeVisibleSeries } from '@/components/report/reportSlice';
import { useDispatch } from '@/redux';
import { pushModal } from '@/modals';
import type { IChartData } from '@/trpc/client';
import { cn } from '@/utils/cn';
Expand Down Expand Up @@ -61,9 +63,11 @@ export function Chart({ data }: Props) {
range,
series: reportSeries,
breakdowns,
visibleSeries: savedVisibleSeries,
},
options: { hideXAxis, hideYAxis },
} = useReportChartContext();
const dispatch = useDispatch();
const trpc = useTRPC();
const references = useQuery(
trpc.reference.getChartReferences.queryOptions(
Expand All @@ -76,7 +80,10 @@ export function Chart({ data }: Props) {
{},
),
);
const { series, setVisibleSeries } = useVisibleSeries(data);
const { series, setVisibleSeries } = useVisibleSeries(data, undefined, {
initialSeries: savedVisibleSeries,
onChange: (ids) => dispatch(changeVisibleSeries(ids)),
});
const rechartData = useRechartDataModel(series);
const yAxisProps = useYAxisProps({
hide: hideYAxis,
Expand Down
9 changes: 8 additions & 1 deletion apps/start/src/components/report-chart/line/chart.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { useRechartDataModel } from '@/hooks/use-rechart-data-model';
import { useVisibleSeries } from '@/hooks/use-visible-series';
import { useTRPC } from '@/integrations/trpc/react';
import { changeVisibleSeries } from '@/components/report/reportSlice';
import { useDispatch } from '@/redux';
import { pushModal } from '@/modals';
import type { IChartData } from '@/trpc/client';
import { cn } from '@/utils/cn';
Expand Down Expand Up @@ -51,10 +53,12 @@ export function Chart({ data }: Props) {
lineType,
series: reportSeries,
breakdowns,
visibleSeries: savedVisibleSeries,
},
isEditMode,
options: { hideXAxis, hideYAxis, maxDomain },
} = useReportChartContext();
const dispatch = useDispatch();
const dataLength = data.series[0]?.data?.length || 0;
const trpc = useTRPC();
const references = useQuery(
Expand All @@ -68,7 +72,10 @@ export function Chart({ data }: Props) {
{},
),
);
const { series, setVisibleSeries } = useVisibleSeries(data);
const { series, setVisibleSeries } = useVisibleSeries(data, undefined, {
initialSeries: savedVisibleSeries,
onChange: (ids) => dispatch(changeVisibleSeries(ids)),
});
const rechartData = useRechartDataModel(series);

let dotIndex = undefined;
Expand Down
13 changes: 11 additions & 2 deletions apps/start/src/components/report-chart/pie/chart.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { changeVisibleSeries } from '@/components/report/reportSlice';
import { useVisibleSeries } from '@/hooks/use-visible-series';
import { useDispatch } from '@/redux';
import type { IChartData } from '@/trpc/client';
import { cn } from '@/utils/cn';
import { round } from '@/utils/math';
Expand Down Expand Up @@ -64,8 +66,15 @@ const PieTooltip = (props: { payload?: any[] }) => {
};

export function Chart({ data }: Props) {
const { isEditMode } = useReportChartContext();
const { series, setVisibleSeries } = useVisibleSeries(data);
const {
isEditMode,
report: { visibleSeries: savedVisibleSeries },
} = useReportChartContext();
const dispatch = useDispatch();
const { series, setVisibleSeries } = useVisibleSeries(data, undefined, {
initialSeries: savedVisibleSeries,
onChange: (ids) => dispatch(changeVisibleSeries(ids)),
});

const sum = series.reduce((acc, serie) => acc + serie.metrics.sum, 0);
const pieData = series.map((serie) => ({
Expand Down
9 changes: 9 additions & 0 deletions apps/start/src/components/report/reportSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ const initialState: InitialState = {
criteria: 'on_or_after',
funnelGroup: undefined,
funnelWindow: undefined,
visibleSeries: undefined,
};

export const reportSlice = createSlice({
Expand Down Expand Up @@ -272,6 +273,13 @@ export const reportSlice = createSlice({
state.dirty = true;
state.funnelWindow = action.payload || undefined;
},
changeVisibleSeries(
state,
action: PayloadAction<string[] | undefined>,
) {
state.dirty = true;
state.visibleSeries = action.payload;
},
reorderEvents(
state,
action: PayloadAction<{ fromIndex: number; toIndex: number }>,
Expand Down Expand Up @@ -312,6 +320,7 @@ export const {
changeUnit,
changeFunnelGroup,
changeFunnelWindow,
changeVisibleSeries,
reorderEvents,
} = reportSlice.actions;

Expand Down
56 changes: 45 additions & 11 deletions apps/start/src/hooks/use-visible-series.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,53 @@
import type { IChartData } from '@/trpc/client';
import { useEffect, useMemo, useState } from 'react';
import { useCallback, useEffect, useMemo, useState } from 'react';

type VisibleSeriesOptions = {
initialSeries?: string[];
onChange?: (ids: string[]) => void;
};

export type IVisibleSeries = ReturnType<typeof useVisibleSeries>['series'];
export function useVisibleSeries(data: IChartData, limit?: number | undefined) {
export function useVisibleSeries(
data: IChartData,
limit?: number | undefined,
options?: VisibleSeriesOptions,
) {
const max = limit ?? 5;
const [visibleSeries, setVisibleSeries] = useState<string[]>(
data?.series?.slice(0, max).map((serie) => serie.id) ?? [],
);
const { initialSeries, onChange } = options ?? {};

useEffect(() => {
setVisibleSeries(
data?.series?.slice(0, max).map((serie) => serie.id) ?? [],
);
const getDefaultIds = useCallback(() => {
if (initialSeries !== undefined) {
const validIds = initialSeries.filter((id) =>
data?.series?.some((s) => s.id === id),
);
return validIds.length > 0
? validIds
: data?.series?.slice(0, max).map((s) => s.id) ?? [];
}
return data?.series?.slice(0, max).map((serie) => serie.id) ?? [];
// initialSeries is stable (comes from saved report config)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [data, max]);

const [visibleSeries, setVisibleSeries] = useState<string[]>(getDefaultIds);

useEffect(() => {
setVisibleSeries(getDefaultIds());
}, [getDefaultIds]);

const handleSetVisibleSeries: typeof setVisibleSeries = useCallback(
(value) => {
setVisibleSeries((prev) => {
const next = typeof value === 'function' ? value(prev) : value;
onChange?.(next);
return next;
});
},
// onChange is stable (dispatch function from Redux)
// eslint-disable-next-line react-hooks/exhaustive-deps
[],
);

return useMemo(() => {
return {
series: data.series
Expand All @@ -22,7 +56,7 @@ export function useVisibleSeries(data: IChartData, limit?: number | undefined) {
index,
}))
.filter((serie) => visibleSeries.includes(serie.id)),
setVisibleSeries,
setVisibleSeries: handleSetVisibleSeries,
} as const;
}, [visibleSeries, data.series]);
}, [visibleSeries, data.series, handleSetVisibleSeries]);
}
5 changes: 3 additions & 2 deletions packages/db/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -321,8 +321,9 @@ model Report {
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
previous Boolean @default(false)
criteria String?
funnelGroup String?
funnelWindow Float?
funnelGroup String?
funnelWindow Float?
visibleSeries Json?

dashboardId String
dashboard Dashboard @relation(fields: [dashboardId], references: [id], onDelete: Cascade)
Expand Down
1 change: 1 addition & 0 deletions packages/db/src/services/reports.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ export function transformReport(
criteria: (report.criteria as ICriteria) ?? undefined,
funnelGroup: report.funnelGroup ?? undefined,
funnelWindow: report.funnelWindow ?? undefined,
visibleSeries: (report.visibleSeries as string[] | null) ?? undefined,
layout: report.layout ?? undefined,
};
}
Expand Down
2 changes: 2 additions & 0 deletions packages/trpc/src/routers/report.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export const reportRouter = createTRPCRouter({
metric: report.metric === 'count' ? 'sum' : report.metric,
funnelGroup: report.funnelGroup,
funnelWindow: report.funnelWindow,
visibleSeries: report.visibleSeries ?? null,
},
});
}),
Expand Down Expand Up @@ -104,6 +105,7 @@ export const reportRouter = createTRPCRouter({
metric: report.metric === 'count' ? 'sum' : report.metric,
funnelGroup: report.funnelGroup,
funnelWindow: report.funnelWindow,
visibleSeries: report.visibleSeries ?? null,
},
});
}),
Expand Down
4 changes: 4 additions & 0 deletions packages/validation/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,10 @@ export const zReportInput = zChartInputBase.extend({
.describe(
"Optional unit of measurement for the chart's Y-axis (e.g., $, %, users)",
),
visibleSeries: z
.array(z.string())
.optional()
.describe('IDs of series that are visible in the chart'),
});

export const zChartInputAI = zReportInput
Expand Down