diff --git a/apps/dashboard/src/components/report-chart/common/previous-diff-indicator.tsx b/apps/dashboard/src/components/report-chart/common/previous-diff-indicator.tsx index e33bb3f9..9bda730c 100644 --- a/apps/dashboard/src/components/report-chart/common/previous-diff-indicator.tsx +++ b/apps/dashboard/src/components/report-chart/common/previous-diff-indicator.tsx @@ -29,7 +29,7 @@ interface PreviousDiffIndicatorProps { children?: React.ReactNode; inverted?: boolean; className?: string; - size?: 'sm' | 'lg'; + size?: 'sm' | 'lg' | 'md'; } export function PreviousDiffIndicator({ @@ -80,6 +80,7 @@ export function PreviousDiffIndicator({ 'flex size-4 items-center justify-center rounded-full', variant, size === 'lg' && 'size-8', + size === 'md' && 'size-6', )} > {renderIcon()} diff --git a/apps/dashboard/src/components/report-chart/funnel/chart.tsx b/apps/dashboard/src/components/report-chart/funnel/chart.tsx index 1502a36a..e09215bd 100644 --- a/apps/dashboard/src/components/report-chart/funnel/chart.tsx +++ b/apps/dashboard/src/components/report-chart/funnel/chart.tsx @@ -39,15 +39,10 @@ export function Chart({ const { isEditMode } = useReportChartContext(); const mostDropoffs = findMostDropoffs(steps); const lastStep = last(steps)!; - const prevLastStep = last(previous.steps); + const prevLastStep = previous?.steps ? last(previous.steps) : null; return ( -
+
-
+
} /> } /> } />
-
- {steps.map((step) => { - return ( -
-
-
- ); - })} -
-
+
{steps.map((step, index) => { const percent = (step.count / totalSessions) * 100; const isMostDropoffs = mostDropoffs.event.id === step.event.id; return (
@@ -123,27 +101,27 @@ export function Chart({
{step.event.displayName}
-
+
Last period:{' '} - {previous.steps[index]?.previousCount} + {previous?.steps?.[index]?.previousCount}
} > -
+
Total: @@ -155,26 +133,26 @@ export function Chart({
Last period:{' '} - {previous.steps[index]?.dropoffCount} + {previous?.steps?.[index]?.dropoffCount}
} > -
+
Dropoff: @@ -192,25 +170,25 @@ export function Chart({
Last period:{' '} - {previous.steps[index]?.count} + {previous?.steps?.[index]?.count}
} > -
+
Current: @@ -219,12 +197,42 @@ export function Chart({
+ + + Last period:{' '} + + {previous?.steps?.[index]?.count} + + + +
+ } + > +
+ + Percent: + +
+ + {Number.isNaN(percent) ? 0 : round(percent, 2)}% + +
+
+
+
{label}
-
+
{value}
{enhancer}
diff --git a/apps/dashboard/src/components/report/ReportChartType.tsx b/apps/dashboard/src/components/report/ReportChartType.tsx index ad7e48cc..8e7a5fb9 100644 --- a/apps/dashboard/src/components/report/ReportChartType.tsx +++ b/apps/dashboard/src/components/report/ReportChartType.tsx @@ -1,10 +1,32 @@ import { useDispatch, useSelector } from '@/redux'; -import { LineChartIcon } from 'lucide-react'; +import { + AreaChartIcon, + ChartBarIcon, + ChartColumnIncreasingIcon, + ConeIcon, + GaugeIcon, + Globe2Icon, + LineChartIcon, + type LucideIcon, + PieChartIcon, + UsersIcon, +} from 'lucide-react'; import { chartTypes } from '@openpanel/constants'; import { objectToZodEnums } from '@openpanel/validation'; -import { Combobox } from '../ui/combobox'; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuGroup, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuShortcut, + DropdownMenuTrigger, +} from '@/components/ui/dropdown-menu'; +import { cn } from '@/utils/cn'; +import { Button } from '../ui/button'; import { changeChartType } from './reportSlice'; interface ReportChartTypeProps { @@ -13,20 +35,57 @@ interface ReportChartTypeProps { export function ReportChartType({ className }: ReportChartTypeProps) { const dispatch = useDispatch(); const type = useSelector((state) => state.report.chartType); + const items = objectToZodEnums(chartTypes).map((key) => ({ + label: chartTypes[key], + value: key, + })); + + const Icons: Record = { + area: AreaChartIcon, + bar: ChartBarIcon, + pie: PieChartIcon, + funnel: ((props) => ( + + )) as LucideIcon, + histogram: ChartColumnIncreasingIcon, + linear: LineChartIcon, + metric: GaugeIcon, + retention: UsersIcon, + map: Globe2Icon, + }; return ( - { - dispatch(changeChartType(value)); - }} - value={type} - items={objectToZodEnums(chartTypes).map((key) => ({ - label: chartTypes[key], - value: key, - }))} - /> + + + + + + Available charts + + + + {items.map((item) => { + const Icon = Icons[item.value]; + return ( + dispatch(changeChartType(item.value))} + > + {item.label} + + + + + ); + })} + + + ); } diff --git a/apps/dashboard/src/components/tooltip-complete.tsx b/apps/dashboard/src/components/tooltip-complete.tsx index 20022a8c..f17d2397 100644 --- a/apps/dashboard/src/components/tooltip-complete.tsx +++ b/apps/dashboard/src/components/tooltip-complete.tsx @@ -1,3 +1,4 @@ +import { TooltipPortal } from '@radix-ui/react-tooltip'; import { Tooltip, TooltipContent, TooltipTrigger } from './ui/tooltip'; interface TooltipCompleteProps { @@ -15,12 +16,17 @@ export function TooltipComplete({ }: TooltipCompleteProps) { return ( - + {children} - - {content} - + + + {content} + + ); } diff --git a/apps/dashboard/src/components/ui/progress.tsx b/apps/dashboard/src/components/ui/progress.tsx index 7aa20a5e..34bfc144 100644 --- a/apps/dashboard/src/components/ui/progress.tsx +++ b/apps/dashboard/src/components/ui/progress.tsx @@ -30,11 +30,7 @@ const Progress = React.forwardRef< }} /> {value && size !== 'sm' && ( -
+
{round(value, 2)}%
)} diff --git a/packages/trpc/src/routers/chart.ts b/packages/trpc/src/routers/chart.ts index 80227a4d..d8cc1afd 100644 --- a/packages/trpc/src/routers/chart.ts +++ b/packages/trpc/src/routers/chart.ts @@ -32,7 +32,6 @@ import { getChartPrevStartEndDate, getChartStartEndDate, getFunnelData, - getFunnelStep, } from './chart.helpers'; function utc(date: string | Date) { @@ -87,9 +86,12 @@ export const chartRouter = createTRPCRouter({ .map((item) => item.replace(/\.([0-9]+)/g, '[*]')) .map((item) => `properties.${item}`); + if (event === '*') { + properties.push('name'); + } + properties.push( 'has_profile', - 'name', 'path', 'origin', 'referrer', @@ -184,7 +186,9 @@ export const chartRouter = createTRPCRouter({ const [current, previous] = await Promise.all([ getFunnelData({ ...input, ...currentPeriod }), - getFunnelData({ ...input, ...previousPeriod }), + input.previous + ? getFunnelData({ ...input, ...previousPeriod }) + : Promise.resolve(null), ]); return { @@ -193,17 +197,6 @@ export const chartRouter = createTRPCRouter({ }; }), - funnelStep: protectedProcedure - .input( - zChartInput.extend({ - step: z.number(), - }), - ) - .query(async ({ input }) => { - const currentPeriod = getChartStartEndDate(input); - return getFunnelStep({ ...input, ...currentPeriod }); - }), - chart: publicProcedure.input(zChartInput).query(async ({ input, ctx }) => { if (ctx.session.userId) { const access = await getProjectAccessCached({