fix(api): filter empty values when using sum and average, added min and max
This commit is contained in:
92
apps/dashboard/src/components/report/ReportSegment.tsx
Normal file
92
apps/dashboard/src/components/report/ReportSegment.tsx
Normal file
@@ -0,0 +1,92 @@
|
||||
import {
|
||||
ActivityIcon,
|
||||
ClockIcon,
|
||||
EqualApproximatelyIcon,
|
||||
type LucideIcon,
|
||||
SigmaIcon,
|
||||
TrendingDownIcon,
|
||||
TrendingUpIcon,
|
||||
UserCheck2Icon,
|
||||
UserCheckIcon,
|
||||
UsersIcon,
|
||||
} from 'lucide-react';
|
||||
|
||||
import { chartSegments } from '@openpanel/constants';
|
||||
import { type IChartEventSegment, mapKeys } from '@openpanel/validation';
|
||||
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuGroup,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuShortcut,
|
||||
DropdownMenuTrigger,
|
||||
} from '@/components/ui/dropdown-menu';
|
||||
import { cn } from '@/utils/cn';
|
||||
import { Button } from '../ui/button';
|
||||
|
||||
interface ReportChartTypeProps {
|
||||
className?: string;
|
||||
value: IChartEventSegment;
|
||||
onChange: (segment: IChartEventSegment) => void;
|
||||
}
|
||||
export function ReportSegment({
|
||||
className,
|
||||
value,
|
||||
onChange,
|
||||
}: ReportChartTypeProps) {
|
||||
const items = mapKeys(chartSegments).map((key) => ({
|
||||
label: chartSegments[key],
|
||||
value: key,
|
||||
}));
|
||||
|
||||
const Icons: Record<IChartEventSegment, LucideIcon> = {
|
||||
event: ActivityIcon,
|
||||
user: UsersIcon,
|
||||
session: ClockIcon,
|
||||
user_average: UserCheck2Icon,
|
||||
one_event_per_user: UserCheckIcon,
|
||||
property_sum: SigmaIcon,
|
||||
property_average: EqualApproximatelyIcon,
|
||||
property_max: TrendingUpIcon,
|
||||
property_min: TrendingDownIcon,
|
||||
};
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
icon={Icons[value]}
|
||||
className={cn('justify-start', className)}
|
||||
>
|
||||
{items.find((item) => item.value === value)?.label}
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent className="w-56">
|
||||
<DropdownMenuLabel>Available charts</DropdownMenuLabel>
|
||||
<DropdownMenuSeparator />
|
||||
|
||||
<DropdownMenuGroup>
|
||||
{items.map((item) => {
|
||||
const Icon = Icons[item.value];
|
||||
return (
|
||||
<DropdownMenuItem
|
||||
key={item.value}
|
||||
onClick={() => onChange(item.value)}
|
||||
className="group"
|
||||
>
|
||||
{item.label}
|
||||
<DropdownMenuShortcut>
|
||||
<Icon className="size-4 group-hover:text-blue-500 group-hover:scale-125 transition-all group-hover:rotate-12" />
|
||||
</DropdownMenuShortcut>
|
||||
</DropdownMenuItem>
|
||||
);
|
||||
})}
|
||||
</DropdownMenuGroup>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
}
|
||||
@@ -3,7 +3,6 @@
|
||||
import { ColorSquare } from '@/components/color-square';
|
||||
import { Combobox } from '@/components/ui/combobox';
|
||||
import { ComboboxAdvanced } from '@/components/ui/combobox-advanced';
|
||||
import { DropdownMenuComposed } from '@/components/ui/dropdown-menu';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { useAppParams } from '@/hooks/useAppParams';
|
||||
import { useDebounceFn } from '@/hooks/useDebounceFn';
|
||||
@@ -28,7 +27,8 @@ import { CSS } from '@dnd-kit/utilities';
|
||||
import { shortId } from '@openpanel/common';
|
||||
import { alphabetIds } from '@openpanel/constants';
|
||||
import type { IChartEvent } from '@openpanel/validation';
|
||||
import { FilterIcon, GanttChartIcon, HandIcon, Users } from 'lucide-react';
|
||||
import { FilterIcon, GanttChartIcon, HandIcon } from 'lucide-react';
|
||||
import { ReportSegment } from '../ReportSegment';
|
||||
import {
|
||||
addEvent,
|
||||
changeEvent,
|
||||
@@ -82,7 +82,8 @@ function SortableEvent({
|
||||
{(showSegment || showAddFilter) && (
|
||||
<div className="flex gap-2 p-2 pt-0">
|
||||
{showSegment && (
|
||||
<DropdownMenuComposed
|
||||
<ReportSegment
|
||||
value={event.segment}
|
||||
onChange={(segment) => {
|
||||
dispatch(
|
||||
changeEvent({
|
||||
@@ -91,73 +92,7 @@ function SortableEvent({
|
||||
}),
|
||||
);
|
||||
}}
|
||||
items={[
|
||||
{
|
||||
value: 'event',
|
||||
label: 'All events',
|
||||
},
|
||||
{
|
||||
value: 'user',
|
||||
label: 'Unique users',
|
||||
},
|
||||
{
|
||||
value: 'session',
|
||||
label: 'Unique sessions',
|
||||
},
|
||||
{
|
||||
value: 'user_average',
|
||||
label: 'Average event per user',
|
||||
},
|
||||
{
|
||||
value: 'one_event_per_user',
|
||||
label: 'One event per user',
|
||||
},
|
||||
{
|
||||
value: 'property_sum',
|
||||
label: 'Sum of property',
|
||||
},
|
||||
{
|
||||
value: 'property_average',
|
||||
label: 'Average of property',
|
||||
},
|
||||
]}
|
||||
label="Segment"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
className="flex items-center gap-1 rounded-md border border-border bg-card p-1 px-2 text-sm font-medium leading-none"
|
||||
>
|
||||
{event.segment === 'user' ? (
|
||||
<>
|
||||
<Users size={12} /> Unique users
|
||||
</>
|
||||
) : event.segment === 'session' ? (
|
||||
<>
|
||||
<Users size={12} /> Unique sessions
|
||||
</>
|
||||
) : event.segment === 'user_average' ? (
|
||||
<>
|
||||
<Users size={12} /> Average event per user
|
||||
</>
|
||||
) : event.segment === 'one_event_per_user' ? (
|
||||
<>
|
||||
<Users size={12} /> One event per user
|
||||
</>
|
||||
) : event.segment === 'property_sum' ? (
|
||||
<>
|
||||
<Users size={12} /> Sum of property
|
||||
</>
|
||||
) : event.segment === 'property_average' ? (
|
||||
<>
|
||||
<Users size={12} /> Average of property
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<GanttChartIcon size={12} /> All events
|
||||
</>
|
||||
)}
|
||||
</button>
|
||||
</DropdownMenuComposed>
|
||||
/>
|
||||
)}
|
||||
{showAddFilter && (
|
||||
<PropertiesCombobox
|
||||
|
||||
Reference in New Issue
Block a user