diff --git a/apps/dashboard/src/components/report/sidebar/filters/FiltersCombobox.tsx b/apps/dashboard/src/components/report/sidebar/PropertiesCombobox.tsx similarity index 85% rename from apps/dashboard/src/components/report/sidebar/filters/FiltersCombobox.tsx rename to apps/dashboard/src/components/report/sidebar/PropertiesCombobox.tsx index cf6372bd..d3615122 100644 --- a/apps/dashboard/src/components/report/sidebar/filters/FiltersCombobox.tsx +++ b/apps/dashboard/src/components/report/sidebar/PropertiesCombobox.tsx @@ -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>) => 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 /> ); } -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 ( - {}} value={search} /> - + {/* {}} value={search} /> */} + {/* */} { @@ -229,15 +217,7 @@ export function FiltersCombobox({ event }: FiltersComboboxProps) { setOpen(open); }} > - - - + {children(setOpen)} {state === 'index' && ( diff --git a/apps/dashboard/src/components/report/sidebar/ReportBreakdowns.tsx b/apps/dashboard/src/components/report/sidebar/ReportBreakdowns.tsx index ede3eabd..5636cd1f 100644 --- a/apps/dashboard/src/components/report/sidebar/ReportBreakdowns.tsx +++ b/apps/dashboard/src/components/report/sidebar/ReportBreakdowns.tsx @@ -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() {
{index} - { + { dispatch( changeBreakdown({ ...item, - name: value, + name: action.value, }), ); }} - items={properties} - placeholder="Select..." - /> + > + {(setOpen) => ( + + )} +
); })} - { + { dispatch( addBreakdown({ - name: value, + name: action.value, }), ); }} - items={properties} - placeholder="Select breakdown" - /> + > + {(setOpen) => ( + + )} + ); diff --git a/apps/dashboard/src/components/report/sidebar/ReportEvents.tsx b/apps/dashboard/src/components/report/sidebar/ReportEvents.tsx index 9f70a4bd..d98047ea 100644 --- a/apps/dashboard/src/components/report/sidebar/ReportEvents.tsx +++ b/apps/dashboard/src/components/report/sidebar/ReportEvents.tsx @@ -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({ )} - {showAddFilter && } + {showAddFilter && ( + { + dispatch( + changeEvent({ + ...event, + filters: [ + ...event.filters, + { + id: shortId(), + name: action.value, + operator: 'is', + value: [], + }, + ], + }), + ); + }} + > + {(setOpen) => ( + + )} + + )} {showSegment && (event.segment === 'property_average' || diff --git a/packages/db/src/services/chart.service.ts b/packages/db/src/services/chart.service.ts index 0bc47e93..5fa9a6e9 100644 --- a/packages/db/src/services/chart.service.ts +++ b/packages/db/src/services/chart.service.ts @@ -88,8 +88,11 @@ export function getChartSql({ const anyFilterOnProfile = event.filters.some((filter) => filter.name.startsWith('profile.'), ); + const anyBreakdownOnProfile = breakdowns.some((breakdown) => + breakdown.name.startsWith('profile.'), + ); - if (anyFilterOnProfile) { + if (anyFilterOnProfile || anyBreakdownOnProfile) { sb.joins.profiles = `LEFT ANY JOIN (SELECT * FROM ${TABLE_NAMES.profiles} FINAL WHERE project_id = ${escape(projectId)}) as profile on profile.id = profile_id`; } @@ -130,6 +133,7 @@ export function getChartSql({ sb.where.bar = `(${breakdowns.map((b) => getSelectPropertyKey(b.name)).join(',')}) IN ( SELECT ${breakdowns.map((b) => getSelectPropertyKey(b.name)).join(',')} FROM ${TABLE_NAMES.events} + ${getJoins()} ${getWhere()} GROUP BY ${breakdowns.map((b) => getSelectPropertyKey(b.name)).join(',')} ORDER BY count(*) DESC