mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-01-03 11:05:30 +08:00
Feature/1217 (#12087)
### What problem does this PR solve? feature: Complete metadata functionality ### Type of change - [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
170
web/src/components/list-filter-bar/filter-field.tsx
Normal file
170
web/src/components/list-filter-bar/filter-field.tsx
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
import { Checkbox } from '@/components/ui/checkbox';
|
||||||
|
import { ChevronDown, ChevronUp } from 'lucide-react';
|
||||||
|
import { memo, useState } from 'react';
|
||||||
|
import { ControllerRenderProps, useFormContext } from 'react-hook-form';
|
||||||
|
import { FormControl, FormField, FormItem, FormLabel } from '../ui/form';
|
||||||
|
import { FilterType } from './interface';
|
||||||
|
const handleCheckChange = ({
|
||||||
|
checked,
|
||||||
|
field,
|
||||||
|
item,
|
||||||
|
isNestedField = false,
|
||||||
|
parentId = '',
|
||||||
|
}: {
|
||||||
|
checked: boolean;
|
||||||
|
field: ControllerRenderProps<
|
||||||
|
{ [x: string]: { [x: string]: any } | string[] },
|
||||||
|
string
|
||||||
|
>;
|
||||||
|
item: FilterType;
|
||||||
|
isNestedField?: boolean;
|
||||||
|
parentId?: string;
|
||||||
|
}) => {
|
||||||
|
if (isNestedField && parentId) {
|
||||||
|
const currentValue = field.value || {};
|
||||||
|
const currentParentValues =
|
||||||
|
(currentValue as Record<string, string[]>)[parentId] || [];
|
||||||
|
|
||||||
|
const newParentValues = checked
|
||||||
|
? [...currentParentValues, item.id.toString()]
|
||||||
|
: currentParentValues.filter(
|
||||||
|
(value: string) => value !== item.id.toString(),
|
||||||
|
);
|
||||||
|
|
||||||
|
const newValue = {
|
||||||
|
...currentValue,
|
||||||
|
[parentId]: newParentValues,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (newValue[parentId].length === 0) {
|
||||||
|
delete newValue[parentId];
|
||||||
|
}
|
||||||
|
|
||||||
|
return field.onChange(newValue);
|
||||||
|
} else {
|
||||||
|
const list = checked
|
||||||
|
? [...(Array.isArray(field.value) ? field.value : []), item.id.toString()]
|
||||||
|
: (Array.isArray(field.value) ? field.value : []).filter(
|
||||||
|
(value) => value !== item.id.toString(),
|
||||||
|
);
|
||||||
|
return field.onChange(list);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const FilterItem = memo(
|
||||||
|
({
|
||||||
|
item,
|
||||||
|
field,
|
||||||
|
level = 0,
|
||||||
|
}: {
|
||||||
|
item: FilterType;
|
||||||
|
field: ControllerRenderProps<
|
||||||
|
{ [x: string]: { [x: string]: any } | string[] },
|
||||||
|
string
|
||||||
|
>;
|
||||||
|
level: number;
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`flex items-center justify-between text-text-primary text-xs ${level > 0 ? 'ml-4' : ''}`}
|
||||||
|
>
|
||||||
|
<FormItem className="flex flex-row space-x-3 space-y-0 items-center">
|
||||||
|
<FormControl>
|
||||||
|
<Checkbox
|
||||||
|
checked={field.value?.includes(item.id.toString())}
|
||||||
|
onCheckedChange={(checked: boolean) =>
|
||||||
|
handleCheckChange({ checked, field, item })
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</FormControl>
|
||||||
|
<FormLabel onClick={(e) => e.stopPropagation()}>
|
||||||
|
{item.label}
|
||||||
|
</FormLabel>
|
||||||
|
</FormItem>
|
||||||
|
{item.count !== undefined && (
|
||||||
|
<span className="text-sm">{item.count}</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
export const FilterField = memo(
|
||||||
|
({
|
||||||
|
item,
|
||||||
|
parent,
|
||||||
|
level = 0,
|
||||||
|
}: {
|
||||||
|
item: FilterType;
|
||||||
|
parent: FilterType;
|
||||||
|
level?: number;
|
||||||
|
}) => {
|
||||||
|
const form = useFormContext();
|
||||||
|
const [showAll, setShowAll] = useState(false);
|
||||||
|
const hasNestedList = item.list && item.list.length > 0;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FormField
|
||||||
|
key={item.id}
|
||||||
|
control={form.control}
|
||||||
|
name={parent.field as string}
|
||||||
|
render={({ field }) => {
|
||||||
|
if (hasNestedList) {
|
||||||
|
return (
|
||||||
|
<div className={`flex flex-col gap-2 ${level > 0 ? 'ml-4' : ''}`}>
|
||||||
|
<div
|
||||||
|
className="flex items-center justify-between cursor-pointer"
|
||||||
|
onClick={() => {
|
||||||
|
setShowAll(!showAll);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<FormLabel className="text-text-primary">
|
||||||
|
{item.label}
|
||||||
|
</FormLabel>
|
||||||
|
{showAll ? (
|
||||||
|
<ChevronUp size={12} />
|
||||||
|
) : (
|
||||||
|
<ChevronDown size={12} />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{showAll &&
|
||||||
|
item.list?.map((child) => (
|
||||||
|
<FilterField
|
||||||
|
key={child.id}
|
||||||
|
item={child}
|
||||||
|
parent={{
|
||||||
|
...item,
|
||||||
|
field: `${parent.field}.${item.field}`,
|
||||||
|
}}
|
||||||
|
level={level + 1}
|
||||||
|
/>
|
||||||
|
// <FilterItem key={child.id} item={child} field={child.field} level={level+1} />
|
||||||
|
// <div
|
||||||
|
// className="flex flex-row space-x-3 space-y-0 items-center"
|
||||||
|
// key={child.id}
|
||||||
|
// >
|
||||||
|
// <FormControl>
|
||||||
|
// <Checkbox
|
||||||
|
// checked={field.value?.includes(child.id.toString())}
|
||||||
|
// onCheckedChange={(checked) =>
|
||||||
|
// handleCheckChange({ checked, field, item: child })
|
||||||
|
// }
|
||||||
|
// />
|
||||||
|
// </FormControl>
|
||||||
|
// <FormLabel onClick={(e) => e.stopPropagation()}>
|
||||||
|
// {child.label}
|
||||||
|
// </FormLabel>
|
||||||
|
// </div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return <FilterItem item={item} field={field} level={level} />;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
FilterField.displayName = 'FilterField';
|
||||||
@ -4,21 +4,27 @@ import {
|
|||||||
PopoverTrigger,
|
PopoverTrigger,
|
||||||
} from '@/components/ui/popover';
|
} from '@/components/ui/popover';
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
import { PropsWithChildren, useCallback, useEffect, useState } from 'react';
|
import {
|
||||||
|
PropsWithChildren,
|
||||||
|
useCallback,
|
||||||
|
useEffect,
|
||||||
|
useMemo,
|
||||||
|
useState,
|
||||||
|
} from 'react';
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
import { ZodArray, ZodString, z } from 'zod';
|
import { ZodArray, ZodString, z } from 'zod';
|
||||||
|
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Checkbox } from '@/components/ui/checkbox';
|
|
||||||
import {
|
import {
|
||||||
Form,
|
Form,
|
||||||
FormControl,
|
|
||||||
FormField,
|
FormField,
|
||||||
FormItem,
|
FormItem,
|
||||||
FormLabel,
|
FormLabel,
|
||||||
FormMessage,
|
FormMessage,
|
||||||
} from '@/components/ui/form';
|
} from '@/components/ui/form';
|
||||||
import { t } from 'i18next';
|
import { t } from 'i18next';
|
||||||
|
import { FilterField } from './filter-field';
|
||||||
import { FilterChange, FilterCollection, FilterValue } from './interface';
|
import { FilterChange, FilterCollection, FilterValue } from './interface';
|
||||||
|
|
||||||
export type CheckboxFormMultipleProps = {
|
export type CheckboxFormMultipleProps = {
|
||||||
@ -35,29 +41,71 @@ function CheckboxFormMultiple({
|
|||||||
onChange,
|
onChange,
|
||||||
setOpen,
|
setOpen,
|
||||||
}: CheckboxFormMultipleProps) {
|
}: CheckboxFormMultipleProps) {
|
||||||
const fieldsDict = filters?.reduce<Record<string, Array<any>>>((pre, cur) => {
|
const [resolvedFilters, setResolvedFilters] =
|
||||||
pre[cur.field] = [];
|
useState<FilterCollection[]>(filters);
|
||||||
return pre;
|
|
||||||
}, {});
|
|
||||||
|
|
||||||
const FormSchema = z.object(
|
useEffect(() => {
|
||||||
filters.reduce<Record<string, ZodArray<ZodString, 'many'>>>((pre, cur) => {
|
if (filters && filters.length > 0) {
|
||||||
pre[cur.field] = z.array(z.string());
|
setResolvedFilters(filters);
|
||||||
|
}
|
||||||
|
}, [filters]);
|
||||||
|
|
||||||
// .refine((value) => value.some((item) => item), {
|
const fieldsDict = useMemo(() => {
|
||||||
// message: 'You have to select at least one item.',
|
if (resolvedFilters.length === 0) {
|
||||||
// });
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return resolvedFilters.reduce<Record<string, any>>((pre, cur) => {
|
||||||
|
const hasNested = cur.list?.some(
|
||||||
|
(item) => item.list && item.list.length > 0,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (hasNested) {
|
||||||
|
pre[cur.field] = {};
|
||||||
|
} else {
|
||||||
|
pre[cur.field] = [];
|
||||||
|
}
|
||||||
return pre;
|
return pre;
|
||||||
}, {}),
|
}, {});
|
||||||
);
|
}, [resolvedFilters]);
|
||||||
|
|
||||||
|
const FormSchema = useMemo(() => {
|
||||||
|
if (resolvedFilters.length === 0) {
|
||||||
|
return z.object({});
|
||||||
|
}
|
||||||
|
|
||||||
|
return z.object(
|
||||||
|
resolvedFilters.reduce<
|
||||||
|
Record<
|
||||||
|
string,
|
||||||
|
ZodArray<ZodString, 'many'> | z.ZodObject<any> | z.ZodOptional<any>
|
||||||
|
>
|
||||||
|
>((pre, cur) => {
|
||||||
|
const hasNested = cur.list?.some(
|
||||||
|
(item) => item.list && item.list.length > 0,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (hasNested) {
|
||||||
|
pre[cur.field] = z
|
||||||
|
.record(z.string(), z.array(z.string().optional()).optional())
|
||||||
|
.optional();
|
||||||
|
} else {
|
||||||
|
pre[cur.field] = z.array(z.string().optional()).optional();
|
||||||
|
}
|
||||||
|
|
||||||
|
return pre;
|
||||||
|
}, {}),
|
||||||
|
);
|
||||||
|
}, [resolvedFilters]);
|
||||||
|
|
||||||
const form = useForm<z.infer<typeof FormSchema>>({
|
const form = useForm<z.infer<typeof FormSchema>>({
|
||||||
resolver: zodResolver(FormSchema),
|
resolver: resolvedFilters.length > 0 ? zodResolver(FormSchema) : undefined,
|
||||||
defaultValues: fieldsDict,
|
defaultValues: fieldsDict,
|
||||||
});
|
});
|
||||||
|
|
||||||
function onSubmit(data: z.infer<typeof FormSchema>) {
|
function onSubmit() {
|
||||||
onChange?.(data);
|
const formValues = form.getValues();
|
||||||
|
onChange?.({ ...formValues });
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,8 +115,10 @@ function CheckboxFormMultiple({
|
|||||||
}, [fieldsDict, onChange, setOpen]);
|
}, [fieldsDict, onChange, setOpen]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
form.reset(value);
|
if (resolvedFilters.length > 0) {
|
||||||
}, [form, value]);
|
form.reset(value || fieldsDict);
|
||||||
|
}
|
||||||
|
}, [form, value, resolvedFilters, fieldsDict]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form {...form}>
|
<Form {...form}>
|
||||||
@ -85,44 +135,21 @@ function CheckboxFormMultiple({
|
|||||||
render={() => (
|
render={() => (
|
||||||
<FormItem className="space-y-4">
|
<FormItem className="space-y-4">
|
||||||
<div>
|
<div>
|
||||||
<FormLabel className="text-base text-text-sub-title-invert">
|
<FormLabel className="text-text-primary">{x.label}</FormLabel>
|
||||||
{x.label}
|
|
||||||
</FormLabel>
|
|
||||||
</div>
|
</div>
|
||||||
{x.list.map((item) => (
|
{x.list.map((item) => {
|
||||||
<FormField
|
return (
|
||||||
key={item.id}
|
<FilterField
|
||||||
control={form.control}
|
key={item.id}
|
||||||
name={x.field}
|
item={{ ...item }}
|
||||||
render={({ field }) => {
|
parent={{
|
||||||
return (
|
...x,
|
||||||
<div className="flex items-center justify-between text-text-primary text-xs">
|
id: x.field,
|
||||||
<FormItem
|
// field: `${x.field}${item.field ? '.' + item.field : ''}`,
|
||||||
key={item.id}
|
}}
|
||||||
className="flex flex-row space-x-3 space-y-0 items-center "
|
/>
|
||||||
>
|
);
|
||||||
<FormControl>
|
})}
|
||||||
<Checkbox
|
|
||||||
checked={field.value?.includes(item.id)}
|
|
||||||
onCheckedChange={(checked) => {
|
|
||||||
return checked
|
|
||||||
? field.onChange([...field.value, item.id])
|
|
||||||
: field.onChange(
|
|
||||||
field.value?.filter(
|
|
||||||
(value) => value !== item.id,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</FormControl>
|
|
||||||
<FormLabel>{item.label}</FormLabel>
|
|
||||||
</FormItem>
|
|
||||||
<span className=" text-sm">{item.count}</span>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
</FormItem>
|
</FormItem>
|
||||||
)}
|
)}
|
||||||
@ -137,7 +164,13 @@ function CheckboxFormMultiple({
|
|||||||
>
|
>
|
||||||
{t('common.clear')}
|
{t('common.clear')}
|
||||||
</Button>
|
</Button>
|
||||||
<Button type="submit" size={'sm'}>
|
<Button
|
||||||
|
type="submit"
|
||||||
|
onClick={() => {
|
||||||
|
console.log(form.formState.errors, form.getValues());
|
||||||
|
}}
|
||||||
|
size={'sm'}
|
||||||
|
>
|
||||||
{t('common.submit')}
|
{t('common.submit')}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -17,6 +17,7 @@ interface IProps {
|
|||||||
onSearchChange?: ChangeEventHandler<HTMLInputElement>;
|
onSearchChange?: ChangeEventHandler<HTMLInputElement>;
|
||||||
showFilter?: boolean;
|
showFilter?: boolean;
|
||||||
leftPanel?: ReactNode;
|
leftPanel?: ReactNode;
|
||||||
|
preChildren?: ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const FilterButton = React.forwardRef<
|
export const FilterButton = React.forwardRef<
|
||||||
@ -46,6 +47,7 @@ export const FilterButton = React.forwardRef<
|
|||||||
export default function ListFilterBar({
|
export default function ListFilterBar({
|
||||||
title,
|
title,
|
||||||
children,
|
children,
|
||||||
|
preChildren,
|
||||||
searchString,
|
searchString,
|
||||||
onSearchChange,
|
onSearchChange,
|
||||||
showFilter = true,
|
showFilter = true,
|
||||||
@ -63,7 +65,18 @@ export default function ListFilterBar({
|
|||||||
const filterCount = useMemo(() => {
|
const filterCount = useMemo(() => {
|
||||||
return typeof value === 'object' && value !== null
|
return typeof value === 'object' && value !== null
|
||||||
? Object.values(value).reduce((pre, cur) => {
|
? Object.values(value).reduce((pre, cur) => {
|
||||||
return pre + cur.length;
|
if (Array.isArray(cur)) {
|
||||||
|
return pre + cur.length;
|
||||||
|
}
|
||||||
|
if (typeof cur === 'object') {
|
||||||
|
return (
|
||||||
|
pre +
|
||||||
|
Object.values(cur).reduce((pre, cur) => {
|
||||||
|
return pre + cur.length;
|
||||||
|
}, 0)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return pre;
|
||||||
}, 0)
|
}, 0)
|
||||||
: 0;
|
: 0;
|
||||||
}, [value]);
|
}, [value]);
|
||||||
@ -80,6 +93,7 @@ export default function ListFilterBar({
|
|||||||
{leftPanel || title}
|
{leftPanel || title}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-5 items-center">
|
<div className="flex gap-5 items-center">
|
||||||
|
{preChildren}
|
||||||
{showFilter && (
|
{showFilter && (
|
||||||
<FilterPopover
|
<FilterPopover
|
||||||
value={value}
|
value={value}
|
||||||
|
|||||||
@ -1,6 +1,9 @@
|
|||||||
export type FilterType = {
|
export type FilterType = {
|
||||||
id: string;
|
id: string;
|
||||||
|
field?: string;
|
||||||
label: string | JSX.Element;
|
label: string | JSX.Element;
|
||||||
|
list?: FilterType[];
|
||||||
|
value?: string | string[];
|
||||||
count?: number;
|
count?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -10,6 +13,9 @@ export type FilterCollection = {
|
|||||||
list: FilterType[];
|
list: FilterType[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export type FilterValue = Record<string, Array<string>>;
|
export type FilterValue = Record<
|
||||||
|
string,
|
||||||
|
Array<string> | Record<string, Array<string>>
|
||||||
|
>;
|
||||||
|
|
||||||
export type FilterChange = (value: FilterValue) => void;
|
export type FilterChange = (value: FilterValue) => void;
|
||||||
|
|||||||
@ -124,6 +124,7 @@ export const useFetchDocumentList = () => {
|
|||||||
{
|
{
|
||||||
suffix: filterValue.type,
|
suffix: filterValue.type,
|
||||||
run_status: filterValue.run,
|
run_status: filterValue.run,
|
||||||
|
metadata: filterValue.metadata,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
if (ret.data.code === 0) {
|
if (ret.data.code === 0) {
|
||||||
@ -196,6 +197,7 @@ export const useGetDocumentFilter = (): {
|
|||||||
filter: data?.filter || {
|
filter: data?.filter || {
|
||||||
run_status: {},
|
run_status: {},
|
||||||
suffix: {},
|
suffix: {},
|
||||||
|
metadata: {},
|
||||||
},
|
},
|
||||||
onOpenChange: handleOnpenChange,
|
onOpenChange: handleOnpenChange,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -60,4 +60,5 @@ interface GraphRag {
|
|||||||
export type IDocumentInfoFilter = {
|
export type IDocumentInfoFilter = {
|
||||||
run_status: Record<number, number>;
|
run_status: Record<number, number>;
|
||||||
suffix: Record<string, number>;
|
suffix: Record<string, number>;
|
||||||
|
metadata: Record<string, Record<string, number>>;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -204,7 +204,8 @@ export const ManageValuesModal = (props: IManageValuesProps) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{metaData.restrictDefinedValues && (
|
{((metaData.restrictDefinedValues && isShowValueSwitch) ||
|
||||||
|
!isShowValueSwitch) && (
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
<div className="flex justify-between items-center">
|
<div className="flex justify-between items-center">
|
||||||
<div>{t('knowledgeDetails.metadata.values')}</div>
|
<div>{t('knowledgeDetails.metadata.values')}</div>
|
||||||
|
|||||||
@ -216,12 +216,6 @@ const Generate: React.FC<GenerateProps> = (props) => {
|
|||||||
? graphRunData
|
? graphRunData
|
||||||
: raptorRunData
|
: raptorRunData
|
||||||
) as ITraceInfo;
|
) as ITraceInfo;
|
||||||
console.log(
|
|
||||||
name,
|
|
||||||
'data',
|
|
||||||
data,
|
|
||||||
!data || (!data.progress && data.progress !== 0),
|
|
||||||
);
|
|
||||||
return (
|
return (
|
||||||
<div key={name}>
|
<div key={name}>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
|
|||||||
@ -25,12 +25,33 @@ export function useSelectDatasetFilters() {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}, [filter.run_status, t]);
|
}, [filter.run_status, t]);
|
||||||
|
const metaDataList = useMemo(() => {
|
||||||
|
if (filter.metadata) {
|
||||||
|
return Object.keys(filter.metadata).map((x) => ({
|
||||||
|
id: x.toString(),
|
||||||
|
field: x.toString(),
|
||||||
|
label: x.toString(),
|
||||||
|
list: Object.keys(filter.metadata[x]).map((y) => ({
|
||||||
|
id: y.toString(),
|
||||||
|
field: y.toString(),
|
||||||
|
label: y.toString(),
|
||||||
|
value: [y],
|
||||||
|
count: filter.metadata[x][y],
|
||||||
|
})),
|
||||||
|
count: Object.keys(filter.metadata[x]).reduce(
|
||||||
|
(acc, cur) => acc + filter.metadata[x][cur],
|
||||||
|
0,
|
||||||
|
),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}, [filter.metadata]);
|
||||||
const filters: FilterCollection[] = useMemo(() => {
|
const filters: FilterCollection[] = useMemo(() => {
|
||||||
return [
|
return [
|
||||||
{ field: 'type', label: 'File Type', list: fileTypes },
|
{ field: 'type', label: 'File Type', list: fileTypes },
|
||||||
{ field: 'run', label: 'Status', list: fileStatus },
|
{ field: 'run', label: 'Status', list: fileStatus },
|
||||||
|
{ field: 'metadata', label: 'metadata', list: metaDataList },
|
||||||
] as FilterCollection[];
|
] as FilterCollection[];
|
||||||
}, [fileStatus, fileTypes]);
|
}, [fileStatus, fileTypes, metaDataList]);
|
||||||
|
|
||||||
return { filters, onOpenChange };
|
return { filters, onOpenChange };
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user