fix(dashboard): breakdowns on profile properties

This commit is contained in:
Carl-Gerhard Lindesvärd
2025-04-16 20:52:22 +02:00
parent bfa1ee70e6
commit e2254e78a9
4 changed files with 105 additions and 67 deletions

View File

@@ -10,18 +10,20 @@ import {
import { Input } from '@/components/ui/input';
import { useAppParams } from '@/hooks/useAppParams';
import { useEventProperties } from '@/hooks/useEventProperties';
import { useDispatch } from '@/redux';
import { shortId } from '@openpanel/common';
import type { IChartEvent } from '@openpanel/validation';
import { AnimatePresence, motion } from 'framer-motion';
import { FilterIcon } from 'lucide-react';
import { ArrowLeftIcon, DatabaseIcon, UserIcon } from 'lucide-react';
import VirtualList from 'rc-virtual-list';
import { useEffect, useState } from 'react';
import { changeEvent } from '../../reportSlice';
import { type Dispatch, type SetStateAction, useEffect, useState } from 'react';
interface FiltersComboboxProps {
event: IChartEvent;
interface PropertiesComboboxProps {
event?: IChartEvent;
children: (setOpen: Dispatch<SetStateAction<boolean>>) => React.ReactNode;
onSelect: (action: {
value: string;
label: string;
description: string;
}) => void;
}
function SearchHeader({
@@ -44,24 +46,23 @@ function SearchHeader({
placeholder="Search"
value={value}
onChange={(e) => onSearch(e.target.value)}
autoFocus
/>
</div>
);
}
export function FiltersCombobox({ event }: FiltersComboboxProps) {
const dispatch = useDispatch();
export function PropertiesCombobox({
event,
children,
onSelect,
}: PropertiesComboboxProps) {
const { projectId } = useAppParams();
const [open, setOpen] = useState(false);
const properties = useEventProperties(
{
event: event.name,
projectId,
},
{
enabled: !!event.name,
},
);
const properties = useEventProperties({
event: event?.name,
projectId,
});
const [state, setState] = useState<'index' | 'event' | 'profile'>('index');
const [search, setSearch] = useState('');
const [direction, setDirection] = useState<'forward' | 'backward'>('forward');
@@ -99,27 +100,14 @@ export function FiltersCombobox({ event }: FiltersComboboxProps) {
description: string;
}) => {
setOpen(false);
dispatch(
changeEvent({
...event,
filters: [
...event.filters,
{
id: shortId(),
name: action.value,
operator: 'is',
value: [],
},
],
}),
);
onSelect(action);
};
const renderIndex = () => {
return (
<DropdownMenuGroup>
<SearchHeader onSearch={() => {}} value={search} />
<DropdownMenuSeparator />
{/* <SearchHeader onSearch={() => {}} value={search} /> */}
{/* <DropdownMenuSeparator /> */}
<DropdownMenuItem
className="group justify-between"
onClick={(e) => {
@@ -229,15 +217,7 @@ export function FiltersCombobox({ event }: FiltersComboboxProps) {
setOpen(open);
}}
>
<DropdownMenuTrigger asChild>
<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"
onClick={() => setOpen((p) => !p)}
>
<FilterIcon size={12} /> Add filter
</button>
</DropdownMenuTrigger>
<DropdownMenuTrigger asChild>{children(setOpen)}</DropdownMenuTrigger>
<DropdownMenuContent className="max-w-80" align="start">
<AnimatePresence mode="wait" initial={false}>
{state === 'index' && (

View File

@@ -5,11 +5,13 @@ import { Combobox } from '@/components/ui/combobox';
import { useAppParams } from '@/hooks/useAppParams';
import { useEventProperties } from '@/hooks/useEventProperties';
import { useDispatch, useSelector } from '@/redux';
import { SplitIcon } from 'lucide-react';
import { ChevronsUpDownIcon, SplitIcon } from 'lucide-react';
import type { IChartBreakdown } from '@openpanel/validation';
import { Button } from '@/components/ui/button';
import { addBreakdown, changeBreakdown, removeBreakdown } from '../reportSlice';
import { PropertiesCombobox } from './PropertiesCombobox';
import { ReportBreakdownMore } from './ReportBreakdownMore';
import type { ReportEventMoreProps } from './ReportEventMore';
@@ -46,42 +48,63 @@ export function ReportBreakdowns() {
<div key={item.name} className="rounded-lg border bg-def-100">
<div className="flex items-center gap-2 p-2 px-4">
<ColorSquare>{index}</ColorSquare>
<Combobox
icon={SplitIcon}
className="flex-1"
searchable
value={item.name}
onChange={(value) => {
<PropertiesCombobox
onSelect={(action) => {
dispatch(
changeBreakdown({
...item,
name: value,
name: action.value,
}),
);
}}
items={properties}
placeholder="Select..."
/>
>
{(setOpen) => (
<Button
variant={'outline'}
onClick={() => setOpen((prev) => !prev)}
size={'sm'}
autoHeight
className="flex-1"
>
<div className="row w-full gap-2 items-center">
<SplitIcon className="size-4" />
{item.name}
</div>
<ChevronsUpDownIcon className="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
)}
</PropertiesCombobox>
<ReportBreakdownMore onClick={handleMore(item)} />
</div>
</div>
);
})}
<Combobox
icon={SplitIcon}
searchable
value={''}
onChange={(value) => {
<PropertiesCombobox
onSelect={(action) => {
dispatch(
addBreakdown({
name: value,
name: action.value,
}),
);
}}
items={properties}
placeholder="Select breakdown"
/>
>
{(setOpen) => (
<Button
variant={'outline'}
onClick={() => setOpen((prev) => !prev)}
size={'sm'}
autoHeight
className="flex-1"
>
<div className="row w-full gap-2 items-center">
<SplitIcon className="size-4" />
Select breakdown
</div>
<ChevronsUpDownIcon className="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
)}
</PropertiesCombobox>
</div>
</div>
);

View File

@@ -25,9 +25,10 @@ import {
verticalListSortingStrategy,
} from '@dnd-kit/sortable';
import { CSS } from '@dnd-kit/utilities';
import { shortId } from '@openpanel/common';
import { alphabetIds } from '@openpanel/constants';
import type { IChartEvent } from '@openpanel/validation';
import { GanttChartIcon, HandIcon, Users } from 'lucide-react';
import { FilterIcon, GanttChartIcon, HandIcon, Users } from 'lucide-react';
import {
addEvent,
changeEvent,
@@ -35,9 +36,9 @@ import {
reorderEvents,
} from '../reportSlice';
import { EventPropertiesCombobox } from './EventPropertiesCombobox';
import { PropertiesCombobox } from './PropertiesCombobox';
import type { ReportEventMoreProps } from './ReportEventMore';
import { ReportEventMore } from './ReportEventMore';
import { FiltersCombobox } from './filters/FiltersCombobox';
import { FiltersList } from './filters/FiltersList';
function SortableEvent({
@@ -158,7 +159,37 @@ function SortableEvent({
</button>
</DropdownMenuComposed>
)}
{showAddFilter && <FiltersCombobox event={event} />}
{showAddFilter && (
<PropertiesCombobox
event={event}
onSelect={(action) => {
dispatch(
changeEvent({
...event,
filters: [
...event.filters,
{
id: shortId(),
name: action.value,
operator: 'is',
value: [],
},
],
}),
);
}}
>
{(setOpen) => (
<button
onClick={() => setOpen((p) => !p)}
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"
>
<FilterIcon size={12} /> Add filter
</button>
)}
</PropertiesCombobox>
)}
{showSegment &&
(event.segment === 'property_average' ||