diff --git a/web/src/components/list-filter-bar/filter-popover.tsx b/web/src/components/list-filter-bar/filter-popover.tsx index 500be93bd..31c5408e3 100644 --- a/web/src/components/list-filter-bar/filter-popover.tsx +++ b/web/src/components/list-filter-bar/filter-popover.tsx @@ -15,7 +15,7 @@ import { useForm } from 'react-hook-form'; import { z, ZodArray, ZodString } from 'zod'; import { Button } from '@/components/ui/button'; -import { Input } from '@/components/ui/input'; +import { Input, SearchInput } from '@/components/ui/input'; import { Form, FormItem, FormLabel, FormMessage } from '@/components/ui/form'; import { t } from 'i18next'; @@ -249,23 +249,23 @@ function CheckboxFormMultiple({ return (
-
+
{x.label} {x.canSearch && ( - handleSearchChange(x.field, e.target.value) } - className="h-8 w-32 ml-2" + className="h-8 w-full" /> )}
-
+
{!!filteredItem.list?.length && filteredItem.list.map((item) => { return ( diff --git a/web/src/components/metadata-filter/index.tsx b/web/src/components/metadata-filter/index.tsx index c4360881a..10c7b1e6b 100644 --- a/web/src/components/metadata-filter/index.tsx +++ b/web/src/components/metadata-filter/index.tsx @@ -22,7 +22,7 @@ export const MetadataFilterSchema = { z.object({ key: z.string(), op: z.string(), - value: z.string(), + value: z.union([z.string(), z.array(z.string())]), }), ) .optional(), diff --git a/web/src/components/metadata-filter/metadata-filter-conditions.tsx b/web/src/components/metadata-filter/metadata-filter-conditions.tsx index 777d8eb9e..1fb6b4cdf 100644 --- a/web/src/components/metadata-filter/metadata-filter-conditions.tsx +++ b/web/src/components/metadata-filter/metadata-filter-conditions.tsx @@ -73,6 +73,7 @@ export function MetadataFilterConditions({ }) { const { t } = useTranslation(); const form = useFormContext(); + const op = useWatch({ name: `${name}.${index}.op` }); const key = useWatch({ name: fieldName }); const valueOptions = useMemo(() => { if (!key || !metadata?.data || !metadata?.data[key]) return []; @@ -85,6 +86,23 @@ export function MetadataFilterConditions({ return []; }, [key]); + const handleChangeOp = useCallback( + (value: string) => { + form.setValue(`${name}.${index}.op`, value); + if ( + !['in', 'not in'].includes(value) && + !['in', 'not in'].includes(op) + ) { + return; + } + if (value === 'in' || value === 'not in') { + form.setValue(`${name}.${index}.value`, []); + } else { + form.setValue(`${name}.${index}.value`, ''); + } + }, + [form, index, op], + ); return (
{ + handleChangeOp(value); + }} options={switchOperatorOptions} onlyShowSelectedIcon triggerClassName="w-30 bg-transparent border-none" @@ -133,27 +154,30 @@ export function MetadataFilterConditions({ ( - - - {canReference ? ( - - ) : ( - - )} - - - - )} + render={({ field: valueField }) => { + return ( + + + {canReference ? ( + + ) : ( + + )} + + + + ); + }} /> diff --git a/web/src/components/originui/calendar/index.tsx b/web/src/components/originui/calendar/index.tsx index cae720f13..dfcee2655 100644 --- a/web/src/components/originui/calendar/index.tsx +++ b/web/src/components/originui/calendar/index.tsx @@ -92,4 +92,4 @@ function Calendar({ ); } -export { Calendar, DateRange }; +export { Calendar, type DateRange }; diff --git a/web/src/components/ui/input-date.tsx b/web/src/components/ui/input-date.tsx index 4e8daa495..101e582b8 100644 --- a/web/src/components/ui/input-date.tsx +++ b/web/src/components/ui/input-date.tsx @@ -10,7 +10,8 @@ import { Locale } from 'date-fns'; import dayjs from 'dayjs'; import { Calendar as CalendarIcon } from 'lucide-react'; import * as React from 'react'; - +import { TimePicker } from './time-picker'; +// import TimePicker from 'react-time-picker'; interface DateInputProps extends Omit< React.InputHTMLAttributes, 'value' | 'onChange' @@ -45,8 +46,32 @@ const DateInput = React.forwardRef( const [open, setOpen] = React.useState(false); const handleDateSelect = (date: Date | undefined) => { + if (value) { + const valueDate = dayjs(value); + date?.setHours(valueDate.hour()); + date?.setMinutes(valueDate.minute()); + date?.setSeconds(valueDate.second()); + } onChange?.(date); - setOpen(false); + // setOpen(false); + }; + + const handleTimeSelect = (date: Date | undefined) => { + const valueDate = dayjs(value); + if (value) { + date?.setFullYear(valueDate.year()); + date?.setMonth(valueDate.month()); + date?.setDate(valueDate.date()); + } + if (date) { + onChange?.(date); + } else { + valueDate?.hour(0); + valueDate?.minute(0); + valueDate?.second(0); + onChange?.(valueDate.toDate()); + } + // setOpen(false); }; // Determine display format based on the type of date picker @@ -90,12 +115,17 @@ const DateInput = React.forwardRef( mode="single" selected={value} onSelect={handleDateSelect} - initialFocus - {...(showTimeSelect && { - showTimeInput, - timeInputLabel, - })} /> + {showTimeSelect && ( + { + handleTimeSelect(value); + }} + showNow + /> + // + )}
diff --git a/web/src/components/ui/input-select.tsx b/web/src/components/ui/input-select.tsx index 513411865..6a8d66a61 100644 --- a/web/src/components/ui/input-select.tsx +++ b/web/src/components/ui/input-select.tsx @@ -308,12 +308,6 @@ const InputSelect = React.forwardRef( } else { isAlreadySelected = (normalizedValue as string[]).includes(inputValue); } - console.log( - 'showInputAsOption', - hasLabelMatch, - isAlreadySelected, - inputValue.toString().trim(), - ); return ( !hasLabelMatch && !isAlreadySelected && @@ -350,9 +344,9 @@ const InputSelect = React.forwardRef( return (
- {option.label} +
{option.label}
+ )} + +
+ {suffixIcon || ( + + )} +
+
+
+
+ +
+ {/* */} +
+ {/* Hour selection */} +
+ +
+ + {format.toLowerCase().includes('mm') && ( + <> + {/* Minute selection */} +
+ +
+ + )} + + {showSeconds && ( + <> + {/* Second selection */} +
+ +
+ + )} + + {use12Hours && ( +
+ handleAmPmChange(val as 'AM' | 'PM')} + className="w-12" + /> +
+ )} +
+
+ + {/* Extra footer content */} + {renderExtraFooter && ( +
+ {renderExtraFooter()} +
+ )} + + {/* Now button */} + {showNow && ( +
+ +
+ )} + + {/* Addon content */} + {addon && ( +
+ {addon()} +
+ )} +
+ +
+ ); + }, +); + +TimePicker.displayName = 'TimePicker'; + +export { TimePicker, type TimePickerProps }; diff --git a/web/src/pages/dataset/components/metedata/hooks/use-manage-modal.ts b/web/src/pages/dataset/components/metedata/hooks/use-manage-modal.ts index cfd7ce81a..95ec0318c 100644 --- a/web/src/pages/dataset/components/metedata/hooks/use-manage-modal.ts +++ b/web/src/pages/dataset/components/metedata/hooks/use-manage-modal.ts @@ -11,7 +11,11 @@ import { RowSelectionState } from '@tanstack/react-table'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useParams } from 'react-router'; -import { MetadataType, metadataValueTypeEnum } from '../constant'; +import { + DEFAULT_VALUE_TYPE, + MetadataType, + metadataValueTypeEnum, +} from '../constant'; import { IBuiltInMetadataItem, IMetaDataReturnJSONSettings, @@ -182,7 +186,7 @@ export const useMetadataOperations = () => { key, match: originalValue, value: newValuesRes, - type, + valueType: type || DEFAULT_VALUE_TYPE, }; return { ...prev, @@ -193,7 +197,7 @@ export const useMetadataOperations = () => { ...prev, updates: [ ...prev.updates, - { key, match: originalValue, value: newValuesRes, type }, + { key, match: originalValue, value: newValuesRes, valueType: type }, ], }; }); diff --git a/web/src/pages/dataset/components/metedata/interface.ts b/web/src/pages/dataset/components/metedata/interface.ts index f725eaee2..ac1cf6520 100644 --- a/web/src/pages/dataset/components/metedata/interface.ts +++ b/web/src/pages/dataset/components/metedata/interface.ts @@ -107,7 +107,7 @@ interface UpdateOperation { key: string; match: string; value: string | string[]; - type?: MetadataValueType; + valueType?: MetadataValueType; } export interface MetadataOperations { diff --git a/web/src/pages/dataset/components/metedata/manage-modal-column.tsx b/web/src/pages/dataset/components/metedata/manage-modal-column.tsx index 4e8db74f9..fc037e1b8 100644 --- a/web/src/pages/dataset/components/metedata/manage-modal-column.tsx +++ b/web/src/pages/dataset/components/metedata/manage-modal-column.tsx @@ -1,6 +1,8 @@ import { Button } from '@/components/ui/button'; import { Checkbox } from '@/components/ui/checkbox'; import { Input } from '@/components/ui/input'; +import { DateInput } from '@/components/ui/input-date'; +import { formatDate } from '@/utils/date'; import { ColumnDef, Row, Table } from '@tanstack/react-table'; import { ListChevronsDownUp, @@ -14,6 +16,7 @@ import { getMetadataValueTypeLabel, MetadataDeleteMap, MetadataType, + metadataValueTypeEnum, } from './constant'; import { IMetaDataTableData } from './interface'; @@ -80,7 +83,7 @@ export const useMetadataColumns = ({ setEditingValue(null); setShouldSave(true); } - }, [editingValue, setTableData]); + }, [editingValue, setTableData, setShouldSave]); const cancelEditValue = () => { setEditingValue(null); @@ -192,14 +195,6 @@ export const useMetadataColumns = ({ ), cell: ({ row }) => { const values = row.getValue('values') as Array; - // const supportsEnum = isMetadataValueTypeWithEnum( - // row.original.valueType, - // ); - - // if (!supportsEnum || !Array.isArray(values) || values.length === 0) { - // return
; - // } - const displayedValues = expanded ? values : values.slice(0, 2); const hasMore = Array.isArray(values) && values.length > 2; @@ -214,26 +209,46 @@ export const useMetadataColumns = ({ return isEditing ? (
- - setEditingValue({ - ...editingValue, - newValue: e.target.value, - }) - } - onBlur={saveEditedValue} - onKeyDown={(e) => { - if (e.key === 'Enter') { - saveEditedValue(); - } else if (e.key === 'Escape') { - cancelEditValue(); + {row.original.valueType === + metadataValueTypeEnum.time && ( + { + setEditingValue({ + ...editingValue, + newValue: formatDate( + value, + 'YYYY-MM-DDTHH:mm:ss', + ), + }); + // onValueChange(index, formatDate(value), true); + }} + showTimeSelect={true} + /> + )} + {row.original.valueType !== + metadataValueTypeEnum.time && ( + + setEditingValue({ + ...editingValue, + newValue: e.target.value, + }) } - }} - autoFocus - // className="text-sm min-w-20 max-w-32 outline-none bg-transparent px-1 py-0.5" - /> + onBlur={saveEditedValue} + onKeyDown={(e) => { + if (e.key === 'Enter') { + saveEditedValue(); + } else if (e.key === 'Escape') { + cancelEditValue(); + } + }} + autoFocus + // className="text-sm min-w-20 max-w-32 outline-none bg-transparent px-1 py-0.5" + /> + )}
) : (