Fix: Metadata bugs. (#12111)

### What problem does this PR solve?

Fix: Metadata bugs.

### Type of change

- [x] Bug Fix (non-breaking change which fixes an issue)

---------

Co-authored-by: Kevin Hu <kevinhu.sh@gmail.com>
This commit is contained in:
chanx
2025-12-23 14:16:57 +08:00
committed by GitHub
parent d1bc7ad2ee
commit 8e6ddd7c1b
20 changed files with 184 additions and 63 deletions

View File

@ -118,7 +118,7 @@ export const FilterField = memo(
setShowAll(!showAll); setShowAll(!showAll);
}} }}
> >
<FormLabel className="text-text-primary"> <FormLabel className="text-text-secondary text-sm">
{item.label} {item.label}
</FormLabel> </FormLabel>
{showAll ? ( {showAll ? (

View File

@ -33,6 +33,7 @@ export type CheckboxFormMultipleProps = {
onChange?: FilterChange; onChange?: FilterChange;
onOpenChange?: (open: boolean) => void; onOpenChange?: (open: boolean) => void;
setOpen(open: boolean): void; setOpen(open: boolean): void;
filterGroup?: Record<string, string[]>;
}; };
function CheckboxFormMultiple({ function CheckboxFormMultiple({
@ -40,6 +41,7 @@ function CheckboxFormMultiple({
value, value,
onChange, onChange,
setOpen, setOpen,
filterGroup,
}: CheckboxFormMultipleProps) { }: CheckboxFormMultipleProps) {
const [resolvedFilters, setResolvedFilters] = const [resolvedFilters, setResolvedFilters] =
useState<FilterCollection[]>(filters); useState<FilterCollection[]>(filters);
@ -120,6 +122,20 @@ function CheckboxFormMultiple({
} }
}, [form, value, resolvedFilters, fieldsDict]); }, [form, value, resolvedFilters, fieldsDict]);
const filterList = useMemo(() => {
const filterSet = filterGroup
? Object.values(filterGroup).reduce<Set<string>>((pre, cur) => {
cur.forEach((item) => pre.add(item));
return pre;
}, new Set())
: new Set();
return [...filterSet];
}, [filterGroup]);
const notInfilterGroup = useMemo(() => {
return filters.filter((x) => !filterList.includes(x.field));
}, [filterList, filters]);
return ( return (
<Form {...form}> <Form {...form}>
<form <form
@ -127,34 +143,70 @@ function CheckboxFormMultiple({
className="space-y-8 px-5 py-2.5" className="space-y-8 px-5 py-2.5"
onReset={() => form.reset()} onReset={() => form.reset()}
> >
{filters.map((x) => ( <div className="space-y-4">
<FormField {filterGroup &&
key={x.field} Object.keys(filterGroup).map((key) => {
control={form.control} const filterKeys = filterGroup[key];
name={x.field} const thisFilters = filters.filter((x) =>
render={() => ( filterKeys.includes(x.field),
<FormItem className="space-y-4"> );
<div> return (
<FormLabel className="text-text-primary">{x.label}</FormLabel> <div
key={key}
className="flex flex-col space-y-4 border-b border-border-button pb-4"
>
<div className="text-text-primary text-sm">{key}</div>
<div className="flex flex-col space-y-4">
{thisFilters.map((x) => (
<FilterField
key={x.field}
item={{ ...x, id: x.field }}
parent={{
...x,
id: x.field,
field: ``,
}}
/>
))}
</div>
</div> </div>
{x.list.map((item) => { );
return ( })}
<FilterField {notInfilterGroup &&
key={item.id} notInfilterGroup.map((x) => {
item={{ ...item }} return (
parent={{ <FormField
...x, key={x.field}
id: x.field, control={form.control}
// field: `${x.field}${item.field ? '.' + item.field : ''}`, name={x.field}
}} render={() => (
/> <FormItem className="space-y-4">
); <div>
})} <FormLabel className="text-text-primary text-sm">
<FormMessage /> {x.label}
</FormItem> </FormLabel>
)} </div>
/> {x.list.map((item) => {
))} return (
<FilterField
key={item.id}
item={{ ...item }}
parent={{
...x,
id: x.field,
// field: `${x.field}${item.field ? '.' + item.field : ''}`,
}}
/>
);
})}
<FormMessage />
</FormItem>
)}
/>
);
})}
</div>
<div className="flex justify-end gap-5"> <div className="flex justify-end gap-5">
<Button <Button
type="button" type="button"
@ -185,6 +237,7 @@ export function FilterPopover({
onChange, onChange,
onOpenChange, onOpenChange,
filters, filters,
filterGroup,
}: PropsWithChildren & Omit<CheckboxFormMultipleProps, 'setOpen'>) { }: PropsWithChildren & Omit<CheckboxFormMultipleProps, 'setOpen'>) {
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const onOpenChangeFun = useCallback( const onOpenChangeFun = useCallback(
@ -203,6 +256,7 @@ export function FilterPopover({
value={value} value={value}
filters={filters} filters={filters}
setOpen={setOpen} setOpen={setOpen}
filterGroup={filterGroup}
></CheckboxFormMultiple> ></CheckboxFormMultiple>
</PopoverContent> </PopoverContent>
</Popover> </Popover>

View File

@ -58,9 +58,11 @@ export default function ListFilterBar({
filters, filters,
className, className,
icon, icon,
filterGroup,
}: PropsWithChildren<IProps & Omit<CheckboxFormMultipleProps, 'setOpen'>> & { }: PropsWithChildren<IProps & Omit<CheckboxFormMultipleProps, 'setOpen'>> & {
className?: string; className?: string;
icon?: ReactNode; icon?: ReactNode;
filterGroup?: Record<string, string[]>;
}) { }) {
const filterCount = useMemo(() => { const filterCount = useMemo(() => {
return typeof value === 'object' && value !== null return typeof value === 'object' && value !== null
@ -99,6 +101,7 @@ export default function ListFilterBar({
value={value} value={value}
onChange={onChange} onChange={onChange}
filters={filters} filters={filters}
filterGroup={filterGroup}
onOpenChange={onOpenChange} onOpenChange={onOpenChange}
> >
<FilterButton count={filterCount}></FilterButton> <FilterButton count={filterCount}></FilterButton>

View File

@ -122,9 +122,9 @@ export const useFetchDocumentList = () => {
page: pagination.current, page: pagination.current,
}, },
{ {
suffix: filterValue.type, suffix: filterValue.type as string[],
run_status: filterValue.run, run_status: filterValue.run as string[],
metadata: filterValue.metadata, metadata: filterValue.metadata as Record<string, string[]>,
}, },
); );
if (ret.data.code === 0) { if (ret.data.code === 0) {

View File

@ -33,4 +33,5 @@ export interface IFetchKnowledgeListRequestParams {
export interface IFetchDocumentListRequestBody { export interface IFetchDocumentListRequestBody {
suffix?: string[]; suffix?: string[];
run_status?: string[]; run_status?: string[];
metadata?: Record<string, string[]>;
} }

View File

@ -191,6 +191,8 @@ Procedural Memory: Learned skills, habits, and automated procedures.`,
fieldName: 'Field name', fieldName: 'Field name',
editMetadata: 'Edit metadata', editMetadata: 'Edit metadata',
}, },
metadataField: 'Metadata field',
systemAttribute: 'System attribute',
localUpload: 'Local upload', localUpload: 'Local upload',
fileSize: 'File size', fileSize: 'File size',
fileType: 'File type', fileType: 'File type',

View File

@ -198,20 +198,24 @@ export const useManageMetaDataModal = (
const { setDocumentMeta } = useSetDocumentMeta(); const { setDocumentMeta } = useSetDocumentMeta();
useEffect(() => { useEffect(() => {
if (data) { if (type === MetadataType.Manage) {
setTableData(data); if (data) {
} else { setTableData(data);
setTableData([]); } else {
setTableData([]);
}
} }
}, [data]); }, [data, type]);
useEffect(() => { useEffect(() => {
if (metaData) { if (type !== MetadataType.Manage) {
setTableData(metaData); if (metaData) {
} else { setTableData(metaData);
setTableData([]); } else {
setTableData([]);
}
} }
}, [metaData]); }, [metaData, type]);
const handleDeleteSingleValue = useCallback( const handleDeleteSingleValue = useCallback(
(field: string, value: string) => { (field: string, value: string) => {

View File

@ -54,7 +54,6 @@ export const ManageMetadataModal = (props: IManageModalProps) => {
values: [], values: [],
}); });
const [currentValueIndex, setCurrentValueIndex] = useState<number>(0);
const [deleteDialogContent, setDeleteDialogContent] = useState({ const [deleteDialogContent, setDeleteDialogContent] = useState({
visible: false, visible: false,
title: '', title: '',
@ -95,12 +94,12 @@ export const ManageMetadataModal = (props: IManageModalProps) => {
description: '', description: '',
values: [], values: [],
}); });
setCurrentValueIndex(tableData.length || 0); // setCurrentValueIndex(tableData.length || 0);
showManageValuesModal(); showManageValuesModal();
}; };
const handleEditValueRow = useCallback( const handleEditValueRow = useCallback(
(data: IMetaDataTableData, index: number) => { (data: IMetaDataTableData) => {
setCurrentValueIndex(index); // setCurrentValueIndex(index);
setValueData(data); setValueData(data);
showManageValuesModal(); showManageValuesModal();
}, },
@ -186,7 +185,7 @@ export const ManageMetadataModal = (props: IManageModalProps) => {
variant={'ghost'} variant={'ghost'}
className="bg-transparent px-1 py-0" className="bg-transparent px-1 py-0"
onClick={() => { onClick={() => {
handleEditValueRow(row.original, row.index); handleEditValueRow(row.original);
}} }}
> >
<Settings /> <Settings />
@ -244,16 +243,32 @@ export const ManageMetadataModal = (props: IManageModalProps) => {
const handleSaveValues = (data: IMetaDataTableData) => { const handleSaveValues = (data: IMetaDataTableData) => {
setTableData((prev) => { setTableData((prev) => {
if (currentValueIndex >= prev.length) { //If the keys are the same, they need to be merged.
return [...prev, data]; const fieldMap = new Map<string, any>();
prev.forEach((item) => {
if (fieldMap.has(item.field)) {
const existingItem = fieldMap.get(item.field);
const mergedValues = [
...new Set([...existingItem.values, ...item.values]),
];
fieldMap.set(item.field, { ...existingItem, values: mergedValues });
} else {
fieldMap.set(item.field, item);
}
});
if (fieldMap.has(data.field)) {
const existingItem = fieldMap.get(data.field);
const mergedValues = [
...new Set([...existingItem.values, ...data.values]),
];
fieldMap.set(data.field, { ...existingItem, values: mergedValues });
} else { } else {
return prev.map((item, index) => { fieldMap.set(data.field, data);
if (index === currentValueIndex) {
return data;
}
return item;
});
} }
return Array.from(fieldMap.values());
}); });
}; };

View File

@ -110,8 +110,8 @@ export const ManageValuesModal = (props: IManageValuesProps) => {
// Handle blur event, synchronize to main state // Handle blur event, synchronize to main state
const handleValueBlur = useCallback(() => { const handleValueBlur = useCallback(() => {
addUpdateValue(metaData.field, [...tempValues]); addUpdateValue(metaData.field, [...new Set([...tempValues])]);
handleChange('values', [...tempValues]); handleChange('values', [...new Set([...tempValues])]);
}, [handleChange, tempValues, metaData, addUpdateValue]); }, [handleChange, tempValues, metaData, addUpdateValue]);
// Handle delete operation // Handle delete operation
@ -139,12 +139,12 @@ export const ManageValuesModal = (props: IManageValuesProps) => {
// Handle adding new value // Handle adding new value
const handleAddValue = useCallback(() => { const handleAddValue = useCallback(() => {
setTempValues((prev) => [...prev, '']); setTempValues((prev) => [...new Set([...prev, ''])]);
// Synchronize to main state // Synchronize to main state
setMetaData((prev) => ({ setMetaData((prev) => ({
...prev, ...prev,
values: [...prev.values, ''], values: [...new Set([...prev.values, ''])],
})); }));
}, []); }, []);

View File

@ -3,11 +3,13 @@ import {
AutoQuestionsFormField, AutoQuestionsFormField,
} from '@/components/auto-keywords-form-field'; } from '@/components/auto-keywords-form-field';
import { ConfigurationFormContainer } from '../configuration-form-container'; import { ConfigurationFormContainer } from '../configuration-form-container';
import { AutoMetadata } from './common-item';
export function AudioConfiguration() { export function AudioConfiguration() {
return ( return (
<ConfigurationFormContainer> <ConfigurationFormContainer>
<> <>
<AutoMetadata />
<AutoKeywordsFormField></AutoKeywordsFormField> <AutoKeywordsFormField></AutoKeywordsFormField>
<AutoQuestionsFormField></AutoQuestionsFormField> <AutoQuestionsFormField></AutoQuestionsFormField>
</> </>

View File

@ -7,6 +7,7 @@ import {
ConfigurationFormContainer, ConfigurationFormContainer,
MainContainer, MainContainer,
} from '../configuration-form-container'; } from '../configuration-form-container';
import { AutoMetadata } from './common-item';
export function BookConfiguration() { export function BookConfiguration() {
return ( return (
@ -16,6 +17,7 @@ export function BookConfiguration() {
</ConfigurationFormContainer> </ConfigurationFormContainer>
<ConfigurationFormContainer> <ConfigurationFormContainer>
<AutoMetadata />
<AutoKeywordsFormField></AutoKeywordsFormField> <AutoKeywordsFormField></AutoKeywordsFormField>
<AutoQuestionsFormField></AutoQuestionsFormField> <AutoQuestionsFormField></AutoQuestionsFormField>
</ConfigurationFormContainer> </ConfigurationFormContainer>

View File

@ -3,11 +3,13 @@ import {
AutoQuestionsFormField, AutoQuestionsFormField,
} from '@/components/auto-keywords-form-field'; } from '@/components/auto-keywords-form-field';
import { ConfigurationFormContainer } from '../configuration-form-container'; import { ConfigurationFormContainer } from '../configuration-form-container';
import { AutoMetadata } from './common-item';
export function EmailConfiguration() { export function EmailConfiguration() {
return ( return (
<ConfigurationFormContainer> <ConfigurationFormContainer>
<> <>
<AutoMetadata />
<AutoKeywordsFormField></AutoKeywordsFormField> <AutoKeywordsFormField></AutoKeywordsFormField>
<AutoQuestionsFormField></AutoQuestionsFormField> <AutoQuestionsFormField></AutoQuestionsFormField>
</> </>

View File

@ -7,6 +7,7 @@ import {
ConfigurationFormContainer, ConfigurationFormContainer,
MainContainer, MainContainer,
} from '../configuration-form-container'; } from '../configuration-form-container';
import { AutoMetadata } from './common-item';
export function LawsConfiguration() { export function LawsConfiguration() {
return ( return (
@ -16,6 +17,7 @@ export function LawsConfiguration() {
</ConfigurationFormContainer> </ConfigurationFormContainer>
<ConfigurationFormContainer> <ConfigurationFormContainer>
<AutoMetadata />
<AutoKeywordsFormField></AutoKeywordsFormField> <AutoKeywordsFormField></AutoKeywordsFormField>
<AutoQuestionsFormField></AutoQuestionsFormField> <AutoQuestionsFormField></AutoQuestionsFormField>
</ConfigurationFormContainer> </ConfigurationFormContainer>

View File

@ -7,6 +7,7 @@ import {
ConfigurationFormContainer, ConfigurationFormContainer,
MainContainer, MainContainer,
} from '../configuration-form-container'; } from '../configuration-form-container';
import { AutoMetadata } from './common-item';
export function ManualConfiguration() { export function ManualConfiguration() {
return ( return (
@ -16,6 +17,7 @@ export function ManualConfiguration() {
</ConfigurationFormContainer> </ConfigurationFormContainer>
<ConfigurationFormContainer> <ConfigurationFormContainer>
<AutoMetadata />
<AutoKeywordsFormField></AutoKeywordsFormField> <AutoKeywordsFormField></AutoKeywordsFormField>
<AutoQuestionsFormField></AutoQuestionsFormField> <AutoQuestionsFormField></AutoQuestionsFormField>
</ConfigurationFormContainer> </ConfigurationFormContainer>

View File

@ -4,12 +4,14 @@ import {
} from '@/components/auto-keywords-form-field'; } from '@/components/auto-keywords-form-field';
import { LayoutRecognizeFormField } from '@/components/layout-recognize-form-field'; import { LayoutRecognizeFormField } from '@/components/layout-recognize-form-field';
import { ConfigurationFormContainer } from '../configuration-form-container'; import { ConfigurationFormContainer } from '../configuration-form-container';
import { AutoMetadata } from './common-item';
export function OneConfiguration() { export function OneConfiguration() {
return ( return (
<ConfigurationFormContainer> <ConfigurationFormContainer>
<LayoutRecognizeFormField></LayoutRecognizeFormField> <LayoutRecognizeFormField></LayoutRecognizeFormField>
<> <>
<AutoMetadata />
<AutoKeywordsFormField></AutoKeywordsFormField> <AutoKeywordsFormField></AutoKeywordsFormField>
<AutoQuestionsFormField></AutoQuestionsFormField> <AutoQuestionsFormField></AutoQuestionsFormField>
</> </>

View File

@ -7,6 +7,7 @@ import {
ConfigurationFormContainer, ConfigurationFormContainer,
MainContainer, MainContainer,
} from '../configuration-form-container'; } from '../configuration-form-container';
import { AutoMetadata } from './common-item';
export function PaperConfiguration() { export function PaperConfiguration() {
return ( return (
@ -16,6 +17,7 @@ export function PaperConfiguration() {
</ConfigurationFormContainer> </ConfigurationFormContainer>
<ConfigurationFormContainer> <ConfigurationFormContainer>
<AutoMetadata />
<AutoKeywordsFormField></AutoKeywordsFormField> <AutoKeywordsFormField></AutoKeywordsFormField>
<AutoQuestionsFormField></AutoQuestionsFormField> <AutoQuestionsFormField></AutoQuestionsFormField>
</ConfigurationFormContainer> </ConfigurationFormContainer>

View File

@ -3,11 +3,13 @@ import {
AutoQuestionsFormField, AutoQuestionsFormField,
} from '@/components/auto-keywords-form-field'; } from '@/components/auto-keywords-form-field';
import { ConfigurationFormContainer } from '../configuration-form-container'; import { ConfigurationFormContainer } from '../configuration-form-container';
import { AutoMetadata } from './common-item';
export function PictureConfiguration() { export function PictureConfiguration() {
return ( return (
<ConfigurationFormContainer> <ConfigurationFormContainer>
<> <>
<AutoMetadata />
<AutoKeywordsFormField></AutoKeywordsFormField> <AutoKeywordsFormField></AutoKeywordsFormField>
<AutoQuestionsFormField></AutoQuestionsFormField> <AutoQuestionsFormField></AutoQuestionsFormField>
</> </>

View File

@ -7,6 +7,7 @@ import {
ConfigurationFormContainer, ConfigurationFormContainer,
MainContainer, MainContainer,
} from '../configuration-form-container'; } from '../configuration-form-container';
import { AutoMetadata } from './common-item';
export function PresentationConfiguration() { export function PresentationConfiguration() {
return ( return (
@ -16,6 +17,7 @@ export function PresentationConfiguration() {
</ConfigurationFormContainer> </ConfigurationFormContainer>
<ConfigurationFormContainer> <ConfigurationFormContainer>
<AutoMetadata />
<AutoKeywordsFormField></AutoKeywordsFormField> <AutoKeywordsFormField></AutoKeywordsFormField>
<AutoQuestionsFormField></AutoQuestionsFormField> <AutoQuestionsFormField></AutoQuestionsFormField>
</ConfigurationFormContainer> </ConfigurationFormContainer>

View File

@ -16,7 +16,7 @@ import { useFetchKnowledgeBaseConfiguration } from '@/hooks/use-knowledge-reques
import { Pen, Upload } from 'lucide-react'; import { Pen, Upload } from 'lucide-react';
import { useMemo } from 'react'; import { useMemo } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useManageMetadata } from '../components/metedata/hook'; import { MetadataType, useManageMetadata } from '../components/metedata/hook';
import { ManageMetadataModal } from '../components/metedata/manage-modal'; import { ManageMetadataModal } from '../components/metedata/manage-modal';
import { DatasetTable } from './dataset-table'; import { DatasetTable } from './dataset-table';
import Generate from './generate-button/generate'; import Generate from './generate-button/generate';
@ -53,7 +53,7 @@ export default function Dataset() {
const { data: dataSetData } = useFetchKnowledgeBaseConfiguration({ const { data: dataSetData } = useFetchKnowledgeBaseConfiguration({
refreshCount, refreshCount,
}); });
const { filters, onOpenChange } = useSelectDatasetFilters(); const { filters, onOpenChange, filterGroup } = useSelectDatasetFilters();
const { const {
createLoading, createLoading,
@ -90,6 +90,7 @@ export default function Dataset() {
onSearchChange={handleInputChange} onSearchChange={handleInputChange}
searchString={searchString} searchString={searchString}
value={filterValue} value={filterValue}
filterGroup={filterGroup}
onChange={handleFilterSubmit} onChange={handleFilterSubmit}
onOpenChange={onOpenChange} onOpenChange={onOpenChange}
filters={filters} filters={filters}
@ -105,7 +106,25 @@ export default function Dataset() {
<Button <Button
variant={'ghost'} variant={'ghost'}
className="border border-border-button" className="border border-border-button"
onClick={() => showManageMetadataModal()} onClick={() =>
showManageMetadataModal({
type: MetadataType.Manage,
isCanAdd: false,
isEditField: true,
title: (
<div className="flex flex-col gap-2">
<div className="text-base font-normal">
{t('knowledgeDetails.metadata.manageMetadata')}
</div>
<div className="text-sm text-text-secondary">
{t(
'knowledgeDetails.metadata.manageMetadataForDataset',
)}
</div>
</div>
),
})
}
> >
<div className="flex gap-1 items-center"> <div className="flex gap-1 items-center">
<Pen size={14} /> <Pen size={14} />
@ -179,6 +198,7 @@ export default function Dataset() {
// selectedRowKeys={selectedRowKeys} // selectedRowKeys={selectedRowKeys}
tableData={tableData} tableData={tableData}
isCanAdd={metadataConfig.isCanAdd} isCanAdd={metadataConfig.isCanAdd}
isEditField={metadataConfig.isEditField}
isDeleteSingleValue={metadataConfig.isDeleteSingleValue} isDeleteSingleValue={metadataConfig.isDeleteSingleValue}
type={metadataConfig.type} type={metadataConfig.type}
otherData={metadataConfig.record} otherData={metadataConfig.record}

View File

@ -49,9 +49,13 @@ export function useSelectDatasetFilters() {
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 }, { field: 'metadata', label: 'Metadata field', list: metaDataList },
] as FilterCollection[]; ] as FilterCollection[];
}, [fileStatus, fileTypes, metaDataList]); }, [fileStatus, fileTypes, metaDataList]);
return { filters, onOpenChange }; const filterGroup = {
[t('systemAttribute')]: ['type', 'run'],
// [t('metadataField')]: ['metadata'],
};
return { filters, onOpenChange, filterGroup };
} }