improve(dashboard): better event selector and other improvements

This commit is contained in:
Carl-Gerhard Lindesvärd
2025-06-05 11:28:06 +02:00
parent cd5dce02b8
commit f59bcfba3c
17 changed files with 470 additions and 145 deletions

View File

@@ -60,7 +60,7 @@ export function ReportSegment({
<Button
variant="outline"
icon={Icons[value]}
className={cn('justify-start', className)}
className={cn('justify-start text-sm', className)}
>
{items.find((item) => item.value === value)?.label}
</Button>

View File

@@ -94,6 +94,17 @@ export const reportSlice = createSlice({
...action.payload,
});
},
duplicateEvent: (state, action: PayloadAction<Omit<IChartEvent, 'id'>>) => {
state.dirty = true;
state.events.push({
...action.payload,
filters: action.payload.filters.map((filter) => ({
...filter,
id: shortId(),
})),
id: shortId(),
});
},
removeEvent: (
state,
action: PayloadAction<{
@@ -270,6 +281,7 @@ export const {
setName,
addEvent,
removeEvent,
duplicateEvent,
changeEvent,
addBreakdown,
removeBreakdown,

View File

@@ -1,40 +1,17 @@
import { Button } from '@/components/ui/button';
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
} from '@/components/ui/command';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuShortcut,
DropdownMenuSub,
DropdownMenuSubContent,
DropdownMenuSubTrigger,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import { Filter, MoreHorizontal, Tags, Trash } from 'lucide-react';
import { CopyIcon, MoreHorizontal, TrashIcon } from 'lucide-react';
import * as React from 'react';
const labels = [
'feature',
'bug',
'enhancement',
'documentation',
'design',
'question',
'maintenance',
];
export interface ReportEventMoreProps {
onClick: (action: 'remove') => void;
onClick: (action: 'remove' | 'duplicate') => void;
}
export function ReportEventMore({ onClick }: ReportEventMoreProps) {
@@ -49,12 +26,16 @@ export function ReportEventMore({ onClick }: ReportEventMoreProps) {
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-[200px]">
<DropdownMenuGroup>
<DropdownMenuSeparator />
<DropdownMenuItem onClick={() => onClick('duplicate')}>
<CopyIcon className="mr-2 h-4 w-4" />
Duplicate
<DropdownMenuShortcut></DropdownMenuShortcut>
</DropdownMenuItem>
<DropdownMenuItem
className="text-red-600"
onClick={() => onClick('remove')}
>
<Trash className="mr-2 h-4 w-4" />
<TrashIcon className="mr-2 h-4 w-4" />
Delete
<DropdownMenuShortcut></DropdownMenuShortcut>
</DropdownMenuItem>

View File

@@ -1,8 +1,7 @@
'use client';
import { ColorSquare } from '@/components/color-square';
import { Combobox } from '@/components/ui/combobox';
import { ComboboxAdvanced } from '@/components/ui/combobox-advanced';
import { ComboboxEvents } from '@/components/ui/combobox-events';
import { Input } from '@/components/ui/input';
import { useAppParams } from '@/hooks/useAppParams';
import { useDebounceFn } from '@/hooks/useDebounceFn';
@@ -27,11 +26,12 @@ 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 } from 'lucide-react';
import { FilterIcon, HandIcon } from 'lucide-react';
import { ReportSegment } from '../ReportSegment';
import {
addEvent,
changeEvent,
duplicateEvent,
removeEvent,
reorderEvents,
} from '../reportSlice';
@@ -146,6 +146,7 @@ export function ReportEvents() {
const eventNames = useEventNames({
projectId,
});
const showSegment = !['retention', 'funnel'].includes(chartType);
const showAddFilter = !['retention'].includes(chartType);
const showDisplayNameInput = !['retention'].includes(chartType);
@@ -181,6 +182,9 @@ export function ReportEvents() {
case 'remove': {
return dispatch(removeEvent(event));
}
case 'duplicate': {
return dispatch(duplicateEvent(event));
}
}
};
@@ -211,54 +215,42 @@ export function ReportEvents() {
isSelectManyEvents={isSelectManyEvents}
className="rounded-lg border bg-def-100"
>
{isSelectManyEvents ? (
<ComboboxAdvanced
className="flex-1"
value={event.filters[0]?.value ?? []}
onChange={(value) => {
dispatch(
changeEvent({
id: event.id,
segment: 'user',
filters: [
{
name: 'name',
operator: 'is',
value: value,
<ComboboxEvents
className="flex-1"
searchable
multiple={isSelectManyEvents as false}
value={
(isSelectManyEvents
? (event.filters[0]?.value ?? [])
: event.name) as any
}
onChange={(value) => {
dispatch(
changeEvent(
Array.isArray(value)
? {
id: event.id,
segment: 'user',
filters: [
{
name: 'name',
operator: 'is',
value: value,
},
],
name: '*',
}
: {
...event,
name: value,
filters: [],
},
],
name: '*',
}),
);
}}
items={eventNames.map((item) => ({
label: item.name,
value: item.name,
}))}
placeholder="Select event"
/>
) : (
<Combobox
icon={GanttChartIcon}
className="flex-1"
searchable
value={event.name}
onChange={(value) => {
dispatch(
changeEvent({
...event,
name: value,
filters: [],
}),
);
}}
items={eventNames.map((item) => ({
label: item.name,
value: item.name,
}))}
placeholder="Select event"
/>
)}
),
);
}}
items={eventNames}
placeholder="Select event"
/>
{showDisplayNameInput && (
<Input
placeholder={
@@ -280,9 +272,8 @@ export function ReportEvents() {
);
})}
<Combobox
<ComboboxEvents
disabled={isAddEventDisabled}
icon={GanttChartIcon}
value={''}
searchable
onChange={(value) => {
@@ -310,11 +301,8 @@ export function ReportEvents() {
);
}
}}
items={eventNames.map((item) => ({
label: item.name,
value: item.name,
}))}
placeholder="Select event"
items={eventNames}
/>
</div>
</SortableContext>