fix(dashboard): improved funnels visually and removed bugs
This commit is contained in:
@@ -10,11 +10,12 @@ import { getChartColor } from '@/utils/theme';
|
||||
import { AlertCircleIcon } from 'lucide-react';
|
||||
import { last } from 'ramda';
|
||||
|
||||
import { getPreviousMetric } from '@openpanel/common';
|
||||
import { getPreviousMetric, round } from '@openpanel/common';
|
||||
import { alphabetIds } from '@openpanel/constants';
|
||||
|
||||
import { PreviousDiffIndicator } from '../common/previous-diff-indicator';
|
||||
import { useReportChartContext } from '../context';
|
||||
import { MetricCardNumber } from '../metric/metric-card';
|
||||
|
||||
const findMostDropoffs = (
|
||||
steps: RouterOutputs['chart']['funnel']['current']['steps'],
|
||||
@@ -41,65 +42,74 @@ export function Chart({
|
||||
const mostDropoffs = findMostDropoffs(steps);
|
||||
const lastStep = last(steps)!;
|
||||
const prevLastStep = last(previous.steps);
|
||||
const withWidget = (children: React.ReactNode) => {
|
||||
if (isEditMode) {
|
||||
return (
|
||||
<Widget>
|
||||
<WidgetBody>{children}</WidgetBody>
|
||||
</Widget>
|
||||
);
|
||||
}
|
||||
|
||||
return children;
|
||||
};
|
||||
|
||||
return withWidget(
|
||||
<div className="flex flex-col gap-4 @container">
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'flex flex-col gap-4 @container',
|
||||
isEditMode ? 'card' : '-m-4',
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
'border border-border',
|
||||
!isEditMode && 'border-0 border-b',
|
||||
'border-b border-border bg-def-100',
|
||||
isEditMode && 'rounded-t-md',
|
||||
)}
|
||||
>
|
||||
<div className="flex items-center gap-8 p-4">
|
||||
<div className="flex items-center gap-8 p-4 px-8">
|
||||
<div className="flex flex-1 items-center gap-8">
|
||||
<MetricCardNumber
|
||||
label="Converted"
|
||||
value={lastStep.count}
|
||||
enhancer={
|
||||
<PreviousDiffIndicator
|
||||
size="lg"
|
||||
{...getPreviousMetric(lastStep.count, prevLastStep?.count)}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<MetricCardNumber
|
||||
label="Percent"
|
||||
value={`${round((lastStep.count / totalSessions) * 100, 1)}%`}
|
||||
enhancer={
|
||||
<PreviousDiffIndicator
|
||||
size="lg"
|
||||
{...getPreviousMetric(lastStep.count, prevLastStep?.count)}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<MetricCardNumber
|
||||
label="Most dropoffs"
|
||||
value={mostDropoffs.event.displayName}
|
||||
enhancer={
|
||||
<PreviousDiffIndicator
|
||||
size="lg"
|
||||
{...getPreviousMetric(lastStep.count, prevLastStep?.count)}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div className="hidden shrink-0 gap-2 @xl:flex">
|
||||
{steps.map((step) => {
|
||||
return (
|
||||
<div
|
||||
className="flex h-20 w-8 items-end overflow-hidden rounded bg-def-200"
|
||||
className="flex h-16 w-4 items-end overflow-hidden rounded bg-def-200"
|
||||
key={step.event.id}
|
||||
>
|
||||
<div
|
||||
className="w-full bg-def-400"
|
||||
className={cn(
|
||||
'bg-def-400 w-full',
|
||||
step.event.id === mostDropoffs.event.id && 'bg-rose-500',
|
||||
)}
|
||||
style={{ height: `${step.percent}%` }}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div className="flex flex-1 items-center gap-4">
|
||||
<div className="flex flex-1 flex-col">
|
||||
<div className="text-2xl">
|
||||
<span className="font-bold">
|
||||
{lastStep.count} of {totalSessions}
|
||||
</span>{' '}
|
||||
sessions{' '}
|
||||
</div>
|
||||
<div className="text-xl text-muted-foreground">
|
||||
Last period:{' '}
|
||||
<span className="font-semibold">
|
||||
{prevLastStep?.count} of {previous.totalSessions}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<PreviousDiffIndicator
|
||||
size="lg"
|
||||
{...getPreviousMetric(lastStep.count, prevLastStep?.count)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col gap-1 divide-y">
|
||||
<div className="flex flex-col divide-y divide-def-200">
|
||||
{steps.map((step, index) => {
|
||||
const percent = (step.count / totalSessions) * 100;
|
||||
const isMostDropoffs = mostDropoffs.event.id === step.event.id;
|
||||
@@ -112,8 +122,8 @@ export function Chart({
|
||||
<ColorSquare className="absolute left-0 top-0.5">
|
||||
{alphabetIds[index]}
|
||||
</ColorSquare>
|
||||
<div className="font-semibold capitalize">
|
||||
{step.event.displayName.replace(/_/g, ' ')}
|
||||
<div className="font-semibold mt-1">
|
||||
{step.event.displayName}
|
||||
</div>
|
||||
<div className="flex items-center gap-8 text-sm">
|
||||
<TooltipComplete
|
||||
@@ -122,7 +132,7 @@ export function Chart({
|
||||
<div className="flex items-center gap-2">
|
||||
<span>
|
||||
Last period:{' '}
|
||||
<span className="font-semibold">
|
||||
<span className="font-mono">
|
||||
{previous.steps[index]?.previousCount}
|
||||
</span>
|
||||
</span>
|
||||
@@ -140,7 +150,7 @@ export function Chart({
|
||||
Total:
|
||||
</span>
|
||||
<div className="flex items-center gap-4">
|
||||
<span className="text-lg font-bold">
|
||||
<span className="text-lg font-mono">
|
||||
{step.previousCount}
|
||||
</span>
|
||||
</div>
|
||||
@@ -152,7 +162,7 @@ export function Chart({
|
||||
<div className="flex items-center gap-2">
|
||||
<span>
|
||||
Last period:{' '}
|
||||
<span className="font-semibold">
|
||||
<span className="font-mono">
|
||||
{previous.steps[index]?.dropoffCount}
|
||||
</span>
|
||||
</span>
|
||||
@@ -173,7 +183,7 @@ export function Chart({
|
||||
<div className="flex items-center gap-4">
|
||||
<span
|
||||
className={cn(
|
||||
'flex items-center gap-1 text-lg font-bold',
|
||||
'flex items-center gap-1 text-lg font-mono',
|
||||
isMostDropoffs && 'text-rose-500',
|
||||
)}
|
||||
>
|
||||
@@ -189,7 +199,7 @@ export function Chart({
|
||||
<div className="flex items-center gap-2">
|
||||
<span>
|
||||
Last period:{' '}
|
||||
<span className="font-semibold">
|
||||
<span className="font-mono">
|
||||
{previous.steps[index]?.count}
|
||||
</span>
|
||||
</span>
|
||||
@@ -207,7 +217,7 @@ export function Chart({
|
||||
Current:
|
||||
</span>
|
||||
<div className="flex items-center gap-4">
|
||||
<span className="text-lg font-bold">{step.count}</span>
|
||||
<span className="text-lg font-mono">{step.count}</span>
|
||||
{/* <button type="button"
|
||||
className="ml-2 underline"
|
||||
onClick={() =>
|
||||
@@ -226,14 +236,13 @@ export function Chart({
|
||||
</div>
|
||||
<Progress
|
||||
size="lg"
|
||||
className="w-full @2xl:w-1/2"
|
||||
color={getChartColor(index)}
|
||||
className="w-full @2xl:w-1/2 text-white bg-def-200 mt-0.5 dark:text-black"
|
||||
value={percent}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>,
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -104,24 +104,40 @@ export function MetricCard({
|
||||
)}
|
||||
</AutoSizer>
|
||||
</div>
|
||||
<div className="col relative gap-2">
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<div className="flex min-w-0 items-center gap-2 text-left">
|
||||
<span className="truncate text-muted-foreground">
|
||||
<SerieName name={serie.names} />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-end justify-between">
|
||||
<div className="truncate font-mono text-3xl font-bold">
|
||||
{renderValue(serie.metrics[metric], 'ml-1 font-light text-xl')}
|
||||
</div>
|
||||
<MetricCardNumber
|
||||
label={<SerieName name={serie.names} />}
|
||||
value={renderValue(serie.metrics[metric], 'ml-1 font-light text-xl')}
|
||||
enhancer={
|
||||
<PreviousDiffIndicator
|
||||
{...previous}
|
||||
className="text-sm text-muted-foreground"
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function MetricCardNumber({
|
||||
label,
|
||||
value,
|
||||
enhancer,
|
||||
}: {
|
||||
label: React.ReactNode;
|
||||
value: React.ReactNode;
|
||||
enhancer?: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<div className="flex min-w-0 flex-col gap-2">
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<div className="flex min-w-0 items-center gap-2 text-left">
|
||||
<span className="truncate text-muted-foreground">{label}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-end justify-between">
|
||||
<div className="truncate font-mono text-3xl font-bold">{value}</div>
|
||||
{enhancer}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user