'use client'; 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'; import { useEventNames } from '@/hooks/useEventNames'; import { useDispatch, useSelector } from '@/redux'; import { DndContext, type DragEndEvent, KeyboardSensor, PointerSensor, closestCenter, useSensor, useSensors, } from '@dnd-kit/core'; import { SortableContext, sortableKeyboardCoordinates, useSortable, 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 { FilterIcon, GanttChartIcon, HandIcon, Users } from 'lucide-react'; import { addEvent, changeEvent, removeEvent, reorderEvents, } from '../reportSlice'; import { EventPropertiesCombobox } from './EventPropertiesCombobox'; import { PropertiesCombobox } from './PropertiesCombobox'; import type { ReportEventMoreProps } from './ReportEventMore'; import { ReportEventMore } from './ReportEventMore'; import { FiltersList } from './filters/FiltersList'; function SortableEvent({ event, index, showSegment, showAddFilter, isSelectManyEvents, ...props }: { event: IChartEvent; index: number; showSegment: boolean; showAddFilter: boolean; isSelectManyEvents: boolean; } & React.HTMLAttributes) { const dispatch = useDispatch(); const { attributes, listeners, setNodeRef, transform, transition } = useSortable({ id: event.id ?? '' }); const style = { transform: CSS.Transform.toString(transform), transition, }; return (
{props.children}
{/* Segment and Filter buttons */} {(showSegment || showAddFilter) && (
{showSegment && ( { dispatch( changeEvent({ ...event, segment, }), ); }} 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" > )} {showAddFilter && ( { dispatch( changeEvent({ ...event, filters: [ ...event.filters, { id: shortId(), name: action.value, operator: 'is', value: [], }, ], }), ); }} > {(setOpen) => ( )} )} {showSegment && (event.segment === 'property_average' || event.segment === 'property_sum') && ( )}
)} {/* Filters */} {!isSelectManyEvents && }
); } export function ReportEvents() { const selectedEvents = useSelector((state) => state.report.events); const chartType = useSelector((state) => state.report.chartType); const dispatch = useDispatch(); const { projectId } = useAppParams(); const eventNames = useEventNames({ projectId, }); const showSegment = !['retention', 'funnel'].includes(chartType); const showAddFilter = !['retention'].includes(chartType); const showDisplayNameInput = !['retention'].includes(chartType); const isAddEventDisabled = (chartType === 'retention' || chartType === 'conversion') && selectedEvents.length >= 2; const dispatchChangeEvent = useDebounceFn((event: IChartEvent) => { dispatch(changeEvent(event)); }); const isSelectManyEvents = chartType === 'retention'; const sensors = useSensors( useSensor(PointerSensor), useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates, }), ); const handleDragEnd = (event: DragEndEvent) => { const { active, over } = event; if (over && active.id !== over.id) { const oldIndex = selectedEvents.findIndex((e) => e.id === active.id); const newIndex = selectedEvents.findIndex((e) => e.id === over.id); dispatch(reorderEvents({ fromIndex: oldIndex, toIndex: newIndex })); } }; const handleMore = (event: IChartEvent) => { const callback: ReportEventMoreProps['onClick'] = (action) => { switch (action) { case 'remove': { return dispatch(removeEvent(event)); } } }; return callback; }; return (

Events

({ id: e.id ?? '' }))} strategy={verticalListSortingStrategy} >
{selectedEvents.map((event, index) => { return ( {isSelectManyEvents ? ( { dispatch( changeEvent({ id: event.id, segment: 'user', filters: [ { name: 'name', operator: 'is', value: value, }, ], name: '*', }), ); }} items={eventNames.map((item) => ({ label: item.name, value: item.name, }))} placeholder="Select event" /> ) : ( { dispatch( changeEvent({ ...event, name: value, filters: [], }), ); }} items={eventNames.map((item) => ({ label: item.name, value: item.name, }))} placeholder="Select event" /> )} {showDisplayNameInput && ( { dispatchChangeEvent({ ...event, displayName: e.target.value, }); }} /> )} ); })} { if (isSelectManyEvents) { dispatch( addEvent({ segment: 'user', name: value, filters: [ { name: 'name', operator: 'is', value: [value], }, ], }), ); } else { dispatch( addEvent({ name: value, segment: 'event', filters: [], }), ); } }} items={eventNames.map((item) => ({ label: item.name, value: item.name, }))} placeholder="Select event" />
); }