fix(dashboard): breakdowns on profile properties
This commit is contained in:
@@ -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' && (
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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' ||
|
||||
|
||||
Reference in New Issue
Block a user