Skip to content

Commit cae2e58

Browse files
committed
feat(frontend): fix up where condition
1 parent 26e9358 commit cae2e58

File tree

3 files changed

+94
-30
lines changed

3 files changed

+94
-30
lines changed

frontend/src/components/input.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export const Input: FC<InputProps> = ({ value, setValue, type, placeholder, inpu
3131
}, [inputProps]);
3232

3333
return <input type={type} placeholder={placeholder}
34-
value={value} {...inputProps} onChange={handleChange} onKeyDown={handleKeyDown}
34+
{...inputProps} onChange={handleChange} onKeyDown={handleKeyDown} value={value}
3535
className={twMerge(classNames("appearance-none border border-gray-200 rounded-md w-full p-1 text-gray-700 leading-tight focus:outline-none focus:shadow-outline text-sm h-[34px] px-2 dark:text-neutral-300/100 dark:bg-white/10 dark:border-white/20", inputProps.className))} />
3636
}
3737

frontend/src/pages/storage-unit/explore-storage-unit-where-condition.tsx

Lines changed: 86 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { ActionButton, AnimatedButton } from "../../components/button";
55
import { createDropdownItem, Dropdown, IDropdownItem } from "../../components/dropdown";
66
import { Icons } from "../../components/icons";
77
import { Input, Label } from "../../components/input";
8+
import { twMerge } from "tailwind-merge";
89

910
export type IExploreStorageUnitWhereConditionFilter = {
1011
field: string;
@@ -13,58 +14,88 @@ export type IExploreStorageUnitWhereConditionFilter = {
1314
}
1415

1516
type IExploreStorageUnitWhereConditionProps = {
17+
defaultFilters?: IExploreStorageUnitWhereConditionFilter[];
1618
options: string[];
1719
operators: string[];
1820
onChange?: (filters: IExploreStorageUnitWhereConditionFilter[]) => void;
1921
}
2022

21-
export const ExploreStorageUnitWhereCondition: FC<IExploreStorageUnitWhereConditionProps> = ({ options, onChange, operators }) => {
22-
const [newFilter, setNewFilter] = useState<IExploreStorageUnitWhereConditionFilter>({ field: options[0], operator: operators[0], value: "" });
23+
export const ExploreStorageUnitWhereCondition: FC<IExploreStorageUnitWhereConditionProps> = ({ defaultFilters, options, onChange, operators }) => {
24+
const [currentFilter, setCurrentFilter] = useState<IExploreStorageUnitWhereConditionFilter>({ field: options[0], operator: operators[0], value: "" });
2325
const [filters, setFitlers] = useState<IExploreStorageUnitWhereConditionFilter[]>([]);
24-
const [show, setShow] = useState(false);
26+
const [newFilter, setNewFilter] = useState(false);
27+
const [editingFilter, setEditingFilter] = useState(-1);
2528

2629
const handleClick = useCallback(() => {
27-
setShow(s => !s);
28-
}, []);
30+
const shouldShow = !newFilter;
31+
if (shouldShow) {
32+
setEditingFilter(-1);
33+
setCurrentFilter({ field: currentFilter.field, operator: operators[0], value: "" });
34+
}
35+
setNewFilter(!newFilter);
36+
}, [currentFilter.field, operators, newFilter]);
2937

3038
const fieldsDropdownItems = useMemo(() => {
3139
return options.map(option => createDropdownItem(option));
3240
}, [options]);
3341

3442
const handleFieldSelect = useCallback((item: IDropdownItem) => {
35-
setNewFilter(val => ({
43+
setCurrentFilter(val => ({
3644
...val,
3745
field: item.id,
3846
}));
3947
}, []);
4048

4149
const handleOperatorSelector = useCallback((item: IDropdownItem) => {
42-
setNewFilter(val => ({
50+
setCurrentFilter(val => ({
4351
...val,
4452
operator: item.id,
4553
}));
4654
}, []);
4755

4856
const handleInputChange = useCallback((newValue: string) => {
49-
setNewFilter(val => ({
57+
setCurrentFilter(val => ({
5058
...val,
5159
value: newValue,
5260
}));
5361
}, []);
5462

5563
const handleAddFilter = useCallback(() => {
56-
const newFilters = [...filters, newFilter];
64+
const newFilters = [...filters, currentFilter];
5765
setFitlers(newFilters);
58-
setNewFilter({ field: newFilter.field, operator: operators[0], value: "" });
66+
setCurrentFilter({ field: currentFilter.field, operator: operators[0], value: "" });
5967
onChange?.(newFilters);
60-
}, [filters, newFilter, onChange, operators]);
68+
}, [filters, currentFilter, onChange, operators]);
6169

6270
const handleRemove = useCallback((index: number) => {
63-
setFitlers(oldFilters => oldFilters.filter((_, i) => i !== index));
64-
}, []);
71+
setEditingFilter(-1);
72+
const newFilters = filters.filter((_, i) => i !== index)
73+
setFitlers(newFilters);
74+
onChange?.(newFilters);
75+
}, [filters, onChange]);
76+
77+
const handleEdit = useCallback((index: number) => {
78+
if (editingFilter === index) {
79+
setEditingFilter(-1);
80+
setCurrentFilter({ field: currentFilter.field, operator: operators[0], value: "" });
81+
return;
82+
}
83+
setNewFilter(false);
84+
setCurrentFilter(filters[index]);
85+
setEditingFilter(index);
86+
}, [editingFilter, filters, currentFilter.field, operators]);
87+
88+
const handleSaveFilter = useCallback((index: number) => {
89+
const newFilters = [...filters];
90+
newFilters[index] = {...currentFilter};
91+
setFitlers(newFilters);
92+
setEditingFilter(-1);
93+
setCurrentFilter({ field: currentFilter.field, operator: operators[0], value: "" });
94+
onChange?.(newFilters);
95+
}, [currentFilter, filters, onChange, operators]);
6596

6697
useEffect(() => {
67-
setNewFilter(f => ({
98+
setCurrentFilter(f => ({
6899
...f,
69100
operator: operators[0],
70101
}));
@@ -73,28 +104,57 @@ export const ExploreStorageUnitWhereCondition: FC<IExploreStorageUnitWhereCondit
73104
const validOperators = useMemo(() => {
74105
return operators.map(operator => createDropdownItem(operator));
75106
}, [operators]);
76-
107+
108+
useEffect(() => {
109+
setFitlers(defaultFilters ?? []);
110+
}, [defaultFilters]);
111+
77112
return <div className="flex flex-col gap-1 h-full relative">
78113
<Label label="Where condition" />
79114
<div className="flex gap-1 items-center max-w-[min(500px,calc(100vw-20px))] flex-wrap">
80115
{
81116
filters.map((filter, i) => (
82-
<div className="group/filter-item flex gap-1 items-center text-xs px-2 py-1 rounded-2xl dark:bg-white/5 cursor-pointer relative overflow-hidden shadow-sm border border-neutral-100 dark:border-neutral-800"
83-
onClick={() => handleRemove(i)}>
84-
<div className="max-w-[350px] truncate dark:text-neutral-300">
117+
<div key={`explore-storage-unit-filter-${i}`} className="group/filter-item flex gap-1 items-center text-xs rounded-2xl dark:bg-white/5 cursor-pointer relative shadow-sm border border-neutral-100 dark:border-neutral-800">
118+
<div className={twMerge(classNames("px-2 py-1 h-full max-w-[350px] truncate dark:text-neutral-300 rounded-2xl", {
119+
"dark:bg-white/10": editingFilter === i,
120+
}))} onClick={() => handleEdit(i)}>
85121
{filter.field} {filter.operator} {filter.value}
86122
</div>
87-
<ActionButton icon={Icons.Cancel} containerClassName="hover:scale-125 absolute right-0 top-1/2 -translate-y-1/2 z-10 h-4 w-4 opacity-0 group-hover/filter-item:opacity-100" />
123+
<ActionButton icon={Icons.Cancel} containerClassName="hover:scale-125 absolute right-2 top-1/2 -translate-y-1/2 z-10 h-4 w-4 opacity-0 group-hover/filter-item:opacity-100"
124+
onClick={() => handleRemove(i)} />
125+
<AnimatePresence mode="wait">
126+
{
127+
editingFilter === i &&
128+
<motion.div className="flex gap-1 z-[5] py-2 px-4 absolute left-0 top-full mt-2 rounded-lg shadow-md border border-neutral-100 dark:border-white/5 dark:bg-white/20 dark:backdrop-blur-xl translate-y-full bg-white" initial={{
129+
y: -10,
130+
opacity: 0,
131+
}} animate={{
132+
y: 0,
133+
opacity: 1,
134+
}} exit={{
135+
y: -10,
136+
opacity: 0,
137+
}}>
138+
<Dropdown noItemsLabel="No fields found" className="min-w-[100px]" value={createDropdownItem(currentFilter.field)} items={fieldsDropdownItems.length === 0 ? [createDropdownItem(currentFilter.field)] : fieldsDropdownItems} onChange={handleFieldSelect} />
139+
<Dropdown noItemsLabel="No operators found" className="min-w-20" value={createDropdownItem(currentFilter.operator)} items={validOperators} onChange={handleOperatorSelector} />
140+
<Input inputProps={{
141+
className: "min-w-[150px]"
142+
}} placeholder="Enter filter value" value={currentFilter.value} setValue={handleInputChange} />
143+
<AnimatedButton className="dark:bg-white/5" icon={Icons.Cancel} label="Cancel" onClick={() => handleEdit(i)} />
144+
<AnimatedButton className="dark:bg-white/5" icon={Icons.CheckCircle} label="Save" onClick={() => handleSaveFilter(i)} />
145+
</motion.div>
146+
}
147+
</AnimatePresence>
88148
</div>
89149
))
90150
}
91151
<ActionButton className={classNames("transition-all", {
92-
"rotate-45": show,
152+
"rotate-45": newFilter,
93153
})} icon={Icons.Add} containerClassName="h-8 w-8" onClick={handleClick} />
94154
</div>
95155
<AnimatePresence mode="wait">
96156
{
97-
show &&
157+
newFilter &&
98158
<motion.div className="flex gap-1 z-[5] py-2 px-4 absolute top-full mt-1 rounded-lg shadow-md border border-neutral-100 dark:border-white/5 dark:bg-white/20 dark:backdrop-blur-xl translate-y-full bg-white" initial={{
99159
y: -10,
100160
opacity: 0,
@@ -105,11 +165,12 @@ export const ExploreStorageUnitWhereCondition: FC<IExploreStorageUnitWhereCondit
105165
y: -10,
106166
opacity: 0,
107167
}}>
108-
<Dropdown className="min-w-[100px]" value={createDropdownItem(newFilter.field)} items={fieldsDropdownItems} onChange={handleFieldSelect} />
109-
<Dropdown className="min-w-20" value={createDropdownItem(newFilter.operator)} items={validOperators} onChange={handleOperatorSelector} />
168+
<Dropdown noItemsLabel="No fields found" className="min-w-[100px]" value={createDropdownItem(currentFilter.field)} items={fieldsDropdownItems} onChange={handleFieldSelect} />
169+
<Dropdown noItemsLabel="No operators found" className="min-w-20" value={createDropdownItem(currentFilter.operator)} items={validOperators} onChange={handleOperatorSelector} />
110170
<Input inputProps={{
111-
className: "min-w-[150px]"
112-
}} placeholder="Enter filter value" value={newFilter.value} setValue={handleInputChange} />
171+
className: "min-w-[150px]",
172+
}} placeholder="Enter filter value" value={currentFilter.value} setValue={handleInputChange} />
173+
<AnimatedButton className="dark:bg-white/5" icon={Icons.Cancel} label="Cancel" onClick={handleClick} />
113174
<AnimatedButton className="dark:bg-white/5" icon={Icons.CheckCircle} label="Add" onClick={handleAddFilter} />
114175
</motion.div>
115176
}

frontend/src/pages/storage-unit/explore-storage-unit.tsx

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export const ExploreStorageUnit: FC = () => {
2020
const [bufferPageSize, setBufferPageSize] = useState("10");
2121
const [currentPage, setCurrentPage] = useState(0);
2222
const [whereCondition, setWhereCondition] = useState("");
23+
const [currentFilters, setCurrentFilters] = useState<IExploreStorageUnitWhereConditionFilter[]>([]);
2324
const [pageSize, setPageSize] = useState("");
2425
const unit: StorageUnit = useLocation().state?.unit;
2526
const schema = useAppSelector(state => state.database.schema);
@@ -135,7 +136,7 @@ export const ExploreStorageUnit: FC = () => {
135136
}
136137
const firstRow = rows?.Row.Rows?.[0];
137138
if (firstRow == null) {
138-
return dataColumns;
139+
return [];
139140
}
140141
return keys(JSON.parse(firstRow[0]));
141142
}, [rows?.Row.Columns, rows?.Row.Rows]);
@@ -145,7 +146,9 @@ export const ExploreStorageUnit: FC = () => {
145146
navigate(InternalRoutes.Dashboard.StorageUnit.path);
146147
}
147148
}, [navigate, unitName]);
149+
148150
const handleFilterChange = useCallback((filters: IExploreStorageUnitWhereConditionFilter[]) => {
151+
setCurrentFilters(filters);
149152
if (!current?.Type) {
150153
return;
151154
}
@@ -163,9 +166,9 @@ export const ExploreStorageUnit: FC = () => {
163166
case DatabaseType.ElasticSearch:
164167
const elasticSearchConditions: Record<string, Record<string, any>> = {};
165168
filters.forEach(filter => {
166-
elasticSearchConditions[filter.field] = { [filter.operator]: filter.value };
169+
elasticSearchConditions[filter.operator] = { [filter.field]: filter.value };
167170
});
168-
whereClause = JSON.stringify({ query: { bool: { must: Object.values(elasticSearchConditions) } } });
171+
whereClause = JSON.stringify({ query: { bool: { must: Object.entries(elasticSearchConditions).map(([field, condition]) => ({ [field]: condition })) } } });
169172
break;
170173
case DatabaseType.MongoDb:
171174
const mongoDbConditions: Record<string, Record<string, any>> = {};
@@ -228,7 +231,7 @@ export const ExploreStorageUnit: FC = () => {
228231
</div>
229232
<div className="flex gap-2">
230233
<InputWithlabel label="Page Size" value={bufferPageSize} setValue={setBufferPageSize} />
231-
{ current?.Type !== DatabaseType.Redis && <ExploreStorageUnitWhereCondition options={columns} operators={validOperators} onChange={handleFilterChange} /> }
234+
{ current?.Type !== DatabaseType.Redis && <ExploreStorageUnitWhereCondition defaultFilters={currentFilters} options={columns} operators={validOperators} onChange={handleFilterChange} /> }
232235
<AnimatedButton className="mt-5" type="lg" icon={Icons.CheckCircle} label="Query" onClick={handleQuery} />
233236
</div>
234237
<div className="grow">

0 commit comments

Comments
 (0)