Feature: Implement metadata functionality (#12049)

### What problem does this PR solve?

Feature: Implement metadata functionality

### Type of change

- [x] New Feature (non-breaking change which adds functionality)
This commit is contained in:
chanx
2025-12-19 19:13:33 +08:00
committed by GitHub
parent aceca266ff
commit eeb36a5ce7
19 changed files with 1326 additions and 85 deletions

View File

@ -0,0 +1,370 @@
import message from '@/components/ui/message';
import { useSetModalState } from '@/hooks/common-hooks';
import { useSetDocumentMeta } from '@/hooks/use-document-request';
import {
getMetaDataService,
updateMetaData,
} from '@/services/knowledge-service';
import { useQuery } from '@tanstack/react-query';
import { useCallback, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useParams } from 'umi';
import {
IMetaDataReturnJSONSettings,
IMetaDataReturnJSONType,
IMetaDataReturnType,
IMetaDataTableData,
MetadataOperations,
ShowManageMetadataModalProps,
} from './interface';
export enum MetadataType {
Manage = 1,
UpdateSingle = 2,
Setting = 3,
}
export const util = {
changeToMetaDataTableData(data: IMetaDataReturnType): IMetaDataTableData[] {
return Object.entries(data).map(([key, value]) => {
const values = value.map(([v]) => v.toString());
console.log('values', values);
return {
field: key,
description: '',
values: values,
} as IMetaDataTableData;
});
},
JSONToMetaDataTableData(
data: Record<string, string | string[]>,
): IMetaDataTableData[] {
return Object.entries(data).map(([key, value]) => {
return {
field: key,
description: '',
values: value,
} as IMetaDataTableData;
});
},
tableDataToMetaDataJSON(data: IMetaDataTableData[]): IMetaDataReturnJSONType {
return data.reduce<IMetaDataReturnJSONType>((pre, cur) => {
pre[cur.field] = cur.values;
return pre;
}, {});
},
tableDataToMetaDataSettingJSON(
data: IMetaDataTableData[],
): IMetaDataReturnJSONSettings {
return data.map((item) => {
return {
key: item.field,
description: item.description,
enum: item.values,
};
});
},
metaDataSettingJSONToMetaDataTableData(
data: IMetaDataReturnJSONSettings,
): IMetaDataTableData[] {
return data.map((item) => {
return {
field: item.key,
description: item.description,
values: item.enum,
restrictDefinedValues: !!item.enum?.length,
} as IMetaDataTableData;
});
},
};
export const useMetadataOperations = () => {
const [operations, setOperations] = useState<MetadataOperations>({
deletes: [],
updates: [],
});
const addDeleteRow = useCallback((key: string) => {
setOperations((prev) => ({
...prev,
deletes: [...prev.deletes, { key }],
}));
}, []);
const addDeleteValue = useCallback((key: string, value: string) => {
setOperations((prev) => ({
...prev,
deletes: [...prev.deletes, { key, value }],
}));
}, []);
const addUpdateValue = useCallback(
(key: string, value: string | string[]) => {
setOperations((prev) => ({
...prev,
updates: [...prev.updates, { key, value }],
}));
},
[],
);
const resetOperations = useCallback(() => {
setOperations({
deletes: [],
updates: [],
});
}, []);
return {
operations,
addDeleteRow,
addDeleteValue,
addUpdateValue,
resetOperations,
};
};
export const useFetchMetaDataManageData = (
type: MetadataType = MetadataType.Manage,
) => {
const { id } = useParams();
// const [data, setData] = useState<IMetaDataTableData[]>([]);
// const [loading, setLoading] = useState(false);
// const fetchData = useCallback(async (): Promise<IMetaDataTableData[]> => {
// setLoading(true);
// const { data } = await getMetaDataService({
// kb_id: id as string,
// });
// setLoading(false);
// if (data?.data?.summary) {
// return util.changeToMetaDataTableData(data.data.summary);
// }
// return [];
// }, [id]);
// useEffect(() => {
// if (type === MetadataType.Manage) {
// fetchData()
// .then((res) => {
// setData(res);
// })
// .catch((res) => {
// console.error(res);
// });
// }
// }, [type, fetchData]);
const {
data,
isFetching: loading,
refetch,
} = useQuery<IMetaDataTableData[]>({
queryKey: ['fetchMetaData', id],
enabled: !!id && type === MetadataType.Manage,
initialData: [],
gcTime: 1000,
queryFn: async () => {
const { data } = await getMetaDataService({
kb_id: id as string,
});
if (data?.data?.summary) {
return util.changeToMetaDataTableData(data.data.summary);
}
return [];
},
});
return {
data,
loading,
refetch,
};
};
export const useManageMetaDataModal = (
metaData: IMetaDataTableData[] = [],
type: MetadataType = MetadataType.Manage,
otherData?: Record<string, any>,
) => {
const { id } = useParams();
const { t } = useTranslation();
const { data, loading } = useFetchMetaDataManageData(type);
const [tableData, setTableData] = useState<IMetaDataTableData[]>(metaData);
const { operations, addDeleteRow, addDeleteValue, addUpdateValue } =
useMetadataOperations();
const { setDocumentMeta } = useSetDocumentMeta();
useEffect(() => {
if (data) {
setTableData(data);
} else {
setTableData([]);
}
}, [data]);
useEffect(() => {
if (metaData) {
setTableData(metaData);
} else {
setTableData([]);
}
}, [metaData]);
const handleDeleteSingleValue = useCallback(
(field: string, value: string) => {
addDeleteValue(field, value);
setTableData((prevTableData) => {
const newTableData = prevTableData.map((item) => {
if (item.field === field) {
return {
...item,
values: item.values.filter((v) => v !== value),
};
}
return item;
});
// console.log('newTableData', newTableData, prevTableData);
return newTableData;
});
},
[addDeleteValue],
);
const handleDeleteSingleRow = useCallback(
(field: string) => {
addDeleteRow(field);
setTableData((prevTableData) => {
const newTableData = prevTableData.filter(
(item) => item.field !== field,
);
// console.log('newTableData', newTableData, prevTableData);
return newTableData;
});
},
[addDeleteRow],
);
const handleSaveManage = useCallback(
async (callback: () => void) => {
const { data: res } = await updateMetaData({
kb_id: id as string,
data: operations,
});
if (res.code === 0) {
message.success(t('message.success'));
callback();
}
},
[operations, id, t],
);
const handleSaveUpdateSingle = useCallback(
async (callback: () => void) => {
const reqData = util.tableDataToMetaDataJSON(tableData);
if (otherData?.id) {
const ret = await setDocumentMeta({
documentId: otherData?.id,
meta: JSON.stringify(reqData),
});
if (ret === 0) {
// message.success(t('message.success'));
callback();
}
}
},
[tableData, otherData, setDocumentMeta],
);
const handleSaveSettings = useCallback(
async (callback: () => void) => {
const data = util.tableDataToMetaDataSettingJSON(tableData);
callback();
return data;
},
[tableData],
);
const handleSave = useCallback(
async ({ callback }: { callback: () => void }) => {
switch (type) {
case MetadataType.UpdateSingle:
handleSaveUpdateSingle(callback);
break;
case MetadataType.Manage:
handleSaveManage(callback);
break;
case MetadataType.Setting:
return handleSaveSettings(callback);
default:
handleSaveManage(callback);
break;
}
},
[handleSaveManage, type, handleSaveUpdateSingle, handleSaveSettings],
);
return {
tableData,
setTableData,
handleDeleteSingleValue,
handleDeleteSingleRow,
loading,
handleSave,
addUpdateValue,
addDeleteValue,
};
};
export const useManageMetadata = () => {
const [tableData, setTableData] = useState<IMetaDataTableData[]>([]);
const [config, setConfig] = useState<ShowManageMetadataModalProps>(
{} as ShowManageMetadataModalProps,
);
const {
visible: manageMetadataVisible,
showModal,
hideModal: hideManageMetadataModal,
} = useSetModalState();
const showManageMetadataModal = useCallback(
(config?: ShowManageMetadataModalProps) => {
const { metadata } = config || {};
if (metadata) {
// const dataTemp = Object.entries(metadata).map(([key, value]) => {
// return {
// field: key,
// description: '',
// values: Array.isArray(value) ? value : [value],
// } as IMetaDataTableData;
// });
setTableData(metadata);
console.log('metadata-2', metadata);
}
console.log('metadata-3', metadata);
if (config) {
setConfig(config);
}
showModal();
},
[showModal],
);
return {
manageMetadataVisible,
showManageMetadataModal,
hideManageMetadataModal,
tableData,
config,
};
};
export const useManageValues = () => {
const [updateValues, setUpdateValues] = useState<{
field: string;
values: string[];
} | null>(null);
return { updateValues, setUpdateValues };
};

View File

@ -0,0 +1,80 @@
import { ReactNode } from 'react';
import { MetadataType } from './hook';
export type IMetaDataReturnType = Record<string, Array<Array<string | number>>>;
export type IMetaDataReturnJSONType = Record<
string,
Array<string | number> | string
>;
export interface IMetaDataReturnJSONSettingItem {
key: string;
description?: string;
enum?: string[];
}
export type IMetaDataReturnJSONSettings = Array<IMetaDataReturnJSONSettingItem>;
export type IMetaDataTableData = {
field: string;
description: string;
restrictDefinedValues?: boolean;
values: string[];
};
export type IManageModalProps = {
title: ReactNode;
isShowDescription?: boolean;
isDeleteSingleValue?: boolean;
visible: boolean;
hideModal: () => void;
tableData?: IMetaDataTableData[];
isCanAdd: boolean;
type: MetadataType;
otherData?: Record<string, any>;
isEditField?: boolean;
isAddValue?: boolean;
isShowValueSwitch?: boolean;
isVerticalShowValue?: boolean;
success?: (data: any) => void;
};
export interface IManageValuesProps {
title: ReactNode;
visible: boolean;
isEditField?: boolean;
isAddValue?: boolean;
isShowDescription?: boolean;
isShowValueSwitch?: boolean;
isVerticalShowValue?: boolean;
data: IMetaDataTableData;
hideModal: () => void;
onSave: (data: IMetaDataTableData) => void;
addUpdateValue: (key: string, value: string | string[]) => void;
addDeleteValue: (key: string, value: string) => void;
}
interface DeleteOperation {
key: string;
value?: string;
}
interface UpdateOperation {
key: string;
value: string | string[];
}
export interface MetadataOperations {
deletes: DeleteOperation[];
updates: UpdateOperation[];
}
export interface ShowManageMetadataModalOptions {
title?: ReactNode | string;
}
export type ShowManageMetadataModalProps = Partial<IManageModalProps> & {
metadata?: IMetaDataTableData[];
isCanAdd: boolean;
type: MetadataType;
record?: Record<string, any>;
options?: ShowManageMetadataModalOptions;
title?: ReactNode | string;
isDeleteSingleValue?: boolean;
};

View File

@ -0,0 +1,373 @@
import {
ConfirmDeleteDialog,
ConfirmDeleteDialogNode,
} from '@/components/confirm-delete-dialog';
import { EmptyType } from '@/components/empty/constant';
import Empty from '@/components/empty/empty';
import { Button } from '@/components/ui/button';
import { Modal } from '@/components/ui/modal/modal';
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from '@/components/ui/table';
import { useSetModalState } from '@/hooks/common-hooks';
import {
ColumnDef,
flexRender,
getCoreRowModel,
getFilteredRowModel,
getPaginationRowModel,
getSortedRowModel,
useReactTable,
} from '@tanstack/react-table';
import { Plus, Settings, Trash2 } from 'lucide-react';
import { useCallback, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useManageMetaDataModal } from './hook';
import { IManageModalProps, IMetaDataTableData } from './interface';
import { ManageValuesModal } from './manage-values-modal';
export const ManageMetadataModal = (props: IManageModalProps) => {
const {
title,
visible,
hideModal,
isDeleteSingleValue,
tableData: originalTableData,
isCanAdd,
type: metadataType,
otherData,
isEditField,
isAddValue,
isShowDescription = false,
isShowValueSwitch = false,
isVerticalShowValue = true,
success,
} = props;
const { t } = useTranslation();
const [valueData, setValueData] = useState<IMetaDataTableData>({
field: '',
description: '',
values: [],
});
const [currentValueIndex, setCurrentValueIndex] = useState<number>(0);
const [deleteDialogContent, setDeleteDialogContent] = useState({
visible: false,
title: '',
name: '',
warnText: '',
onOk: () => {},
onCancel: () => {},
});
const {
tableData,
setTableData,
handleDeleteSingleValue,
handleDeleteSingleRow,
handleSave,
addUpdateValue,
addDeleteValue,
} = useManageMetaDataModal(originalTableData, metadataType, otherData);
const {
visible: manageValuesVisible,
showModal: showManageValuesModal,
hideModal: hideManageValuesModal,
} = useSetModalState();
const hideDeleteModal = () => {
setDeleteDialogContent({
visible: false,
title: '',
name: '',
warnText: '',
onOk: () => {},
onCancel: () => {},
});
};
const handAddValueRow = () => {
setValueData({
field: '',
description: '',
values: [],
});
setCurrentValueIndex(tableData.length || 0);
showManageValuesModal();
};
const handleEditValueRow = useCallback(
(data: IMetaDataTableData, index: number) => {
setCurrentValueIndex(index);
setValueData(data);
showManageValuesModal();
},
[showManageValuesModal],
);
const columns: ColumnDef<IMetaDataTableData>[] = useMemo(() => {
const cols: ColumnDef<IMetaDataTableData>[] = [
{
accessorKey: 'field',
header: () => <span>{t('knowledgeDetails.metadata.field')}</span>,
cell: ({ row }) => (
<div className="text-sm text-accent-primary">
{row.getValue('field')}
</div>
),
},
{
accessorKey: 'description',
header: () => <span>{t('knowledgeDetails.metadata.description')}</span>,
cell: ({ row }) => (
<div className="text-sm truncate max-w-32">
{row.getValue('description')}
</div>
),
},
{
accessorKey: 'values',
header: () => <span>{t('knowledgeDetails.metadata.values')}</span>,
cell: ({ row }) => {
const values = row.getValue('values') as Array<string>;
return (
<div className="flex items-center gap-1">
{values.length > 0 &&
values
.filter((value: string, index: number) => index < 2)
?.map((value: string) => {
return (
<Button
key={value}
variant={'ghost'}
className="border border-border-button"
aria-label="Edit"
>
<div className="flex gap-1 items-center">
<div className="text-sm truncate max-w-24">
{value}
</div>
{isDeleteSingleValue && (
<Button
variant={'delete'}
className="p-0 bg-transparent"
onClick={() => {
handleDeleteSingleValue(
row.getValue('field'),
value,
);
}}
>
<Trash2 />
</Button>
)}
</div>
</Button>
);
})}
{values.length > 2 && (
<div className="text-text-secondary self-end">...</div>
)}
</div>
);
},
},
{
accessorKey: 'action',
header: () => <span>{t('knowledgeDetails.metadata.action')}</span>,
meta: {
cellClassName: 'w-12',
},
cell: ({ row }) => (
<div className=" flex opacity-0 group-hover:opacity-100 gap-2">
<Button
variant={'ghost'}
className="bg-transparent px-1 py-0"
onClick={() => {
handleEditValueRow(row.original, row.index);
}}
>
<Settings />
</Button>
<Button
variant={'delete'}
className="p-0 bg-transparent"
onClick={() => {
setDeleteDialogContent({
visible: true,
title:
t('common.delete') +
' ' +
t('knowledgeDetails.metadata.metadata'),
name: row.getValue('field'),
warnText: t('knowledgeDetails.metadata.deleteWarn'),
onOk: () => {
hideDeleteModal();
handleDeleteSingleRow(row.getValue('field'));
},
onCancel: () => {
hideDeleteModal();
},
});
}}
>
<Trash2 />
</Button>
</div>
),
},
];
if (!isShowDescription) {
cols.splice(1, 1);
}
return cols;
}, [
handleDeleteSingleRow,
t,
handleDeleteSingleValue,
isShowDescription,
isDeleteSingleValue,
handleEditValueRow,
]);
const table = useReactTable({
data: tableData as IMetaDataTableData[],
columns,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
getSortedRowModel: getSortedRowModel(),
getFilteredRowModel: getFilteredRowModel(),
manualPagination: true,
});
const handleSaveValues = (data: IMetaDataTableData) => {
setTableData((prev) => {
if (currentValueIndex >= prev.length) {
return [...prev, data];
} else {
return prev.map((item, index) => {
if (index === currentValueIndex) {
return data;
}
return item;
});
}
});
};
return (
<>
<Modal
title={title}
open={visible}
onCancel={hideModal}
maskClosable={false}
okText={t('common.save')}
onOk={async () => {
const res = await handleSave({ callback: hideModal });
console.log('data', res);
success?.(res);
}}
>
<div className="flex flex-col gap-2">
<div className="flex items-center justify-between">
<div>{t('knowledgeDetails.metadata.metadata')}</div>
{isCanAdd && (
<Button
variant={'ghost'}
className="border border-border-button"
onClick={handAddValueRow}
>
<Plus />
</Button>
)}
</div>
<Table rootClassName="max-h-[800px]">
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
{headerGroup.headers.map((header) => (
<TableHead key={header.id}>
{header.isPlaceholder
? null
: flexRender(
header.column.columnDef.header,
header.getContext(),
)}
</TableHead>
))}
</TableRow>
))}
</TableHeader>
<TableBody className="relative">
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map((row) => (
<TableRow
key={row.id}
data-state={row.getIsSelected() && 'selected'}
className="group"
>
{row.getVisibleCells().map((cell) => (
<TableCell key={cell.id}>
{flexRender(
cell.column.columnDef.cell,
cell.getContext(),
)}
</TableCell>
))}
</TableRow>
))
) : (
<TableRow>
<TableCell
colSpan={columns.length}
className="h-24 text-center"
>
<Empty type={EmptyType.Data} />
</TableCell>
</TableRow>
)}
</TableBody>
</Table>
</div>
</Modal>
{manageValuesVisible && (
<ManageValuesModal
title={<div>{t('knowledgeDetails.metadata.editMetadata')}</div>}
visible={manageValuesVisible}
hideModal={hideManageValuesModal}
data={valueData}
onSave={handleSaveValues}
addUpdateValue={addUpdateValue}
addDeleteValue={addDeleteValue}
isEditField={isEditField || isCanAdd}
isAddValue={isAddValue || isCanAdd}
isShowDescription={isShowDescription}
isShowValueSwitch={isShowValueSwitch}
isVerticalShowValue={isVerticalShowValue}
// handleDeleteSingleValue={handleDeleteSingleValue}
// handleDeleteSingleRow={handleDeleteSingleRow}
/>
)}
{deleteDialogContent.visible && (
<ConfirmDeleteDialog
open={deleteDialogContent.visible}
onCancel={deleteDialogContent.onCancel}
onOk={deleteDialogContent.onOk}
title={deleteDialogContent.title}
content={{
node: (
<ConfirmDeleteDialogNode
name={deleteDialogContent.name}
warnText={deleteDialogContent.warnText}
/>
),
}}
/>
)}
</>
);
};

View File

@ -0,0 +1,250 @@
import EditTag from '@/components/edit-tag';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Modal } from '@/components/ui/modal/modal';
import { Switch } from '@/components/ui/switch';
import { Textarea } from '@/components/ui/textarea';
import { Plus, Trash2 } from 'lucide-react';
import { memo, useCallback, useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { IManageValuesProps, IMetaDataTableData } from './interface';
// Create a separate input component, wrapped with memo to avoid unnecessary re-renders
const ValueInputItem = memo(
({
item,
index,
onValueChange,
onDelete,
onBlur,
}: {
item: string;
index: number;
onValueChange: (index: number, value: string) => void;
onDelete: (index: number) => void;
onBlur: (index: number) => void;
}) => {
return (
<div
key={`value-item-${index}`}
className="flex items-center gap-2.5 w-full"
>
<div className="flex-1 w-full">
<Input
value={item}
onChange={(e) => onValueChange(index, e.target.value)}
onBlur={() => onBlur(index)}
/>
</div>
<Button
type="button"
variant="delete"
className="border border-border-button px-1 h-6 w-6 rounded-sm"
onClick={() => onDelete(index)}
>
<Trash2 size={14} className="w-4 h-4" />
</Button>
</div>
);
},
);
export const ManageValuesModal = (props: IManageValuesProps) => {
const {
title,
data,
isEditField,
visible,
isAddValue,
isShowDescription,
isShowValueSwitch,
isVerticalShowValue,
hideModal,
onSave,
addUpdateValue,
addDeleteValue,
} = props;
const [metaData, setMetaData] = useState(data);
const { t } = useTranslation();
// Use functional update to avoid closure issues
const handleChange = useCallback((field: string, value: any) => {
setMetaData((prev) => ({
...prev,
[field]: value,
}));
}, []);
// Maintain separate state for each input box
const [tempValues, setTempValues] = useState<string[]>([...data.values]);
useEffect(() => {
setTempValues([...data.values]);
setMetaData(data);
}, [data]);
const handleHideModal = useCallback(() => {
hideModal();
setMetaData({} as IMetaDataTableData);
}, [hideModal]);
const handleSave = useCallback(() => {
if (!metaData.restrictDefinedValues && isShowValueSwitch) {
const newMetaData = { ...metaData, values: [] };
onSave(newMetaData);
} else {
onSave(metaData);
}
handleHideModal();
}, [metaData, onSave, handleHideModal, isShowValueSwitch]);
// Handle value changes, only update temporary state
const handleValueChange = useCallback((index: number, value: string) => {
setTempValues((prev) => {
const newValues = [...prev];
newValues[index] = value;
return newValues;
});
}, []);
// Handle blur event, synchronize to main state
const handleValueBlur = useCallback(() => {
addUpdateValue(metaData.field, [...tempValues]);
handleChange('values', [...tempValues]);
}, [handleChange, tempValues, metaData, addUpdateValue]);
// Handle delete operation
const handleDelete = useCallback(
(index: number) => {
setTempValues((prev) => {
const newTempValues = [...prev];
addDeleteValue(metaData.field, newTempValues[index]);
newTempValues.splice(index, 1);
return newTempValues;
});
// Synchronize to main state
setMetaData((prev) => {
const newMetaDataValues = [...prev.values];
newMetaDataValues.splice(index, 1);
return {
...prev,
values: newMetaDataValues,
};
});
},
[addDeleteValue, metaData],
);
// Handle adding new value
const handleAddValue = useCallback(() => {
setTempValues((prev) => [...prev, '']);
// Synchronize to main state
setMetaData((prev) => ({
...prev,
values: [...prev.values, ''],
}));
}, []);
return (
<Modal
title={title}
open={visible}
onCancel={handleHideModal}
className="!w-[460px]"
okText={t('common.save')}
onOk={handleSave}
maskClosable={false}
footer={null}
>
<div className="flex flex-col gap-4">
{!isEditField && (
<div className="text-base p-5 border border-border-button rounded-lg">
{metaData.field}
</div>
)}
{isEditField && (
<div className="flex flex-col gap-2">
<div>{t('knowledgeDetails.metadata.fieldName')}</div>
<div>
<Input
value={metaData.field}
onChange={(e) => {
handleChange('field', e.target?.value || '');
}}
/>
</div>
</div>
)}
{isShowDescription && (
<div className="flex flex-col gap-2">
<div>{t('knowledgeDetails.metadata.description')}</div>
<div>
<Textarea
value={metaData.description}
onChange={(e) => {
handleChange('description', e.target?.value || '');
}}
/>
</div>
</div>
)}
{isShowValueSwitch && (
<div className="flex flex-col gap-2">
<div>{t('knowledgeDetails.metadata.restrictDefinedValues')}</div>
<div>
<Switch
checked={metaData.restrictDefinedValues || false}
onCheckedChange={(checked) =>
handleChange('restrictDefinedValues', checked)
}
/>
</div>
</div>
)}
{metaData.restrictDefinedValues && (
<div className="flex flex-col gap-2">
<div className="flex justify-between items-center">
<div>{t('knowledgeDetails.metadata.values')}</div>
{isAddValue && isVerticalShowValue && (
<div>
<Button
variant={'ghost'}
className="border border-border-button"
onClick={handleAddValue}
>
<Plus />
</Button>
</div>
)}
</div>
{isVerticalShowValue && (
<div className="flex flex-col gap-2 w-full">
{tempValues?.map((item, index) => {
return (
<ValueInputItem
key={`value-item-${index}`}
item={item}
index={index}
onValueChange={handleValueChange}
onDelete={handleDelete}
onBlur={handleValueBlur}
/>
);
})}
</div>
)}
{!isVerticalShowValue && (
<EditTag
value={metaData.values}
onChange={(value) => handleChange('values', value)}
/>
)}
</div>
)}
</div>
</Modal>
);
};