Skip to content

Commit 0d3c841

Browse files
committed
feat: add specify feature for events
1 parent 47f3506 commit 0d3c841

File tree

4 files changed

+84
-40
lines changed

4 files changed

+84
-40
lines changed

src/client/components/insights/BreakdownParamsBlock.tsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,16 @@ export const BreakdownParamsBlock: React.FC<BreakdownParamsBlockProps> =
2626
const { t } = useTranslation();
2727
const [filterText, setFilterText] = useState('');
2828

29-
const filteredMetrics = props.list
30-
.filter((item) => item.type !== 'array') // TODO: not support array type yet
31-
.filter((item) => item.name.includes(filterText));
29+
const metrics = props.list.filter((item) => item.type !== 'array'); // TODO: not support array type yet
3230

3331
return (
3432
<div className="flex w-full cursor-pointer flex-col gap-1 rounded-lg border border-zinc-300 px-2 py-1 dark:border-zinc-700">
3533
{/* Params */}
3634
<DropdownSelect
3735
dropdownSize="default"
3836
defaultIsOpen={props.info === null}
39-
list={filteredMetrics}
37+
filterText={filterText}
38+
list={metrics}
4039
value={props.info?.value ?? ''}
4140
onSelect={(name, item) => {
4241
props.onSelect({

src/client/components/insights/DropdownSelect.tsx

Lines changed: 76 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { PropsWithChildren, useState } from 'react';
1+
import React, { PropsWithChildren, useState, useMemo } from 'react';
22
import {
33
Popover,
44
PopoverContent,
@@ -24,13 +24,51 @@ interface DropdownSelectProps<T> extends PropsWithChildren {
2424
onSelect: (name: string, item: T) => void;
2525
onSelectEmpty?: () => void;
2626
renderItem?: (item: T) => React.ReactNode;
27+
filterText?: string; // Optional filter text for internal filtering
28+
allowCustomInput?: boolean;
2729
}
2830
export const DropdownSelect = <T extends BasicListItem>(
2931
props: DropdownSelectProps<T>
3032
) => {
3133
const [isOpen, setIsOpen] = useState(props.defaultIsOpen ?? false);
3234
const { t } = useTranslation();
3335
const dropdownSize = props.dropdownSize ?? 'default';
36+
const allowCustomInput = props.allowCustomInput ?? true;
37+
38+
// Filter list based on filterText if provided
39+
const filteredList = useMemo(() => {
40+
if (!props.filterText) {
41+
return props.list;
42+
}
43+
return props.list.filter(
44+
(item) =>
45+
item.name.toLowerCase().includes(props.filterText!.toLowerCase()) ||
46+
(item.label &&
47+
item.label.toLowerCase().includes(props.filterText!.toLowerCase()))
48+
);
49+
}, [props.list, props.filterText]);
50+
51+
// Check if we should show the specify option
52+
const shouldShowSpecify = useMemo(() => {
53+
return (
54+
allowCustomInput &&
55+
props.filterText &&
56+
props.filterText.trim().length > 0 &&
57+
filteredList.length === 0
58+
);
59+
}, [allowCustomInput, props.filterText, filteredList.length]);
60+
61+
const handleSpecifySelect = () => {
62+
if (shouldShowSpecify) {
63+
// Create a custom item with the filterText as name
64+
const customItem = {
65+
name: props.filterText!.trim(),
66+
label: props.filterText!.trim(),
67+
} as T;
68+
props.onSelect(props.filterText!.trim(), customItem);
69+
setIsOpen(false);
70+
}
71+
};
3472

3573
return (
3674
<Popover
@@ -62,35 +100,46 @@ export const DropdownSelect = <T extends BasicListItem>(
62100
<div>{props.dropdownHeader}</div>
63101

64102
<ScrollArea className="flex-1">
65-
{props.list.length === 0 && (
103+
{shouldShowSpecify ? (
104+
<div
105+
className="hover:bg-muted flex cursor-pointer items-center gap-2 rounded px-2 py-1 text-sm transition-all"
106+
onClick={handleSpecifySelect}
107+
>
108+
<span className="text-primary">
109+
{t('Specify')}: <strong>{props.filterText!.trim()}</strong>
110+
</span>
111+
</div>
112+
) : filteredList.length === 0 ? (
66113
<div className="mt-4 text-center opacity-80">
67-
{t('No any item availabled.')}
114+
{props.filterText
115+
? t('No items match your search.')
116+
: t('No any item availabled.')}
117+
</div>
118+
) : (
119+
<div className="flex flex-col gap-0.5">
120+
{filteredList.map((item, i) => {
121+
return (
122+
<div
123+
key={i}
124+
className={cn(
125+
'hover:bg-muted flex cursor-pointer items-center gap-2 rounded px-2 py-1 text-sm transition-all',
126+
props.value === item.name && 'bg-muted'
127+
)}
128+
onClick={() => {
129+
props.onSelect(item.name, item);
130+
setIsOpen(false);
131+
}}
132+
>
133+
{props.renderItem ? (
134+
props.renderItem(item)
135+
) : (
136+
<span>{item.label ?? item.name}</span>
137+
)}
138+
</div>
139+
);
140+
})}
68141
</div>
69142
)}
70-
71-
<div className="flex flex-col gap-0.5">
72-
{props.list.map((item, i) => {
73-
return (
74-
<div
75-
key={i}
76-
className={cn(
77-
'hover:bg-muted flex cursor-pointer items-center gap-2 rounded px-2 py-1 text-sm transition-all',
78-
props.value === item.name && 'bg-muted'
79-
)}
80-
onClick={() => {
81-
props.onSelect(item.name, item);
82-
setIsOpen(false);
83-
}}
84-
>
85-
{props.renderItem ? (
86-
props.renderItem(item)
87-
) : (
88-
<span>{item.label ?? item.name}</span>
89-
)}
90-
</div>
91-
);
92-
})}
93-
</div>
94143
</ScrollArea>
95144
</div>
96145
</PopoverContent>

src/client/components/insights/FilterParamsBlock.tsx

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,7 @@ export const FilterParamsBlock: React.FC<FilterParamsBlockProps> = React.memo(
3131
const { t } = useTranslation();
3232
const [filterText, setFilterText] = useState('');
3333

34-
const filteredMetrics = props.list
35-
.filter((item) => item.type !== 'array') // TODO: not support array type yet
36-
.filter((item) => item.name.includes(filterText));
34+
const metrics = props.list.filter((item) => item.type !== 'array'); // TODO: not support array type yet
3735

3836
const moreEl = (
3937
<div>
@@ -74,7 +72,8 @@ export const FilterParamsBlock: React.FC<FilterParamsBlockProps> = React.memo(
7472
<DropdownSelect
7573
dropdownSize="default"
7674
defaultIsOpen={props.info === null}
77-
list={filteredMetrics}
75+
filterText={filterText}
76+
list={metrics}
7877
value={props.info?.name ?? ''}
7978
onSelect={(name, item) => {
8079
props.onSelect({

src/client/components/insights/MetricsBlock.tsx

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,17 +47,14 @@ export const MetricsBlock: React.FC<MetricsBlockProps> = React.memo((props) => {
4747
mathMethod.find((m) => m.name === props.info?.math)?.label ??
4848
mathMethod[0].label;
4949

50-
const filteredMetrics = props.list.filter((item) =>
51-
item.name.includes(filterText)
52-
);
53-
5450
return (
5551
<div className="flex w-full cursor-pointer flex-col gap-1 rounded-lg border border-zinc-300 px-2 py-1 dark:border-zinc-700">
5652
{/* Event */}
5753
<DropdownSelect
5854
dropdownSize="lg"
5955
defaultIsOpen={props.info === null}
60-
list={filteredMetrics}
56+
filterText={filterText}
57+
list={props.list}
6158
value={props.info?.name ?? ''}
6259
onSelect={(name: string) => {
6360
props.onSelect({

0 commit comments

Comments
 (0)