import { ConfirmDeleteDialog, ConfirmDeleteDialogNode, } from '@/components/confirm-delete-dialog'; 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 { MetadataType } from './hook'; 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 (
onValueChange(index, e.target.value)} onBlur={() => onBlur(index)} />
); }, ); export const ManageValuesModal = (props: IManageValuesProps) => { const { title, data, isEditField, visible, isAddValue, isShowDescription, isShowValueSwitch, isVerticalShowValue, hideModal, onSave, addUpdateValue, addDeleteValue, existsKeys, type, } = props; const [metaData, setMetaData] = useState(data); const { t } = useTranslation(); const [valueError, setValueError] = useState>({ field: '', values: '', }); const [deleteDialogContent, setDeleteDialogContent] = useState({ visible: false, title: '', name: '', warnText: '', onOk: () => {}, onCancel: () => {}, }); const hideDeleteModal = () => { setDeleteDialogContent({ visible: false, title: '', name: '', warnText: '', onOk: () => {}, onCancel: () => {}, }); }; // Use functional update to avoid closure issues const handleChange = useCallback( (field: string, value: any) => { if (field === 'field' && existsKeys.includes(value)) { setValueError((prev) => { return { ...prev, field: type === MetadataType.Setting ? t('knowledgeDetails.metadata.fieldExists') : t('knowledgeDetails.metadata.fieldNameExists'), }; }); } else if (field === 'field' && !existsKeys.includes(value)) { setValueError((prev) => { return { ...prev, field: '', }; }); } setMetaData((prev) => ({ ...prev, [field]: value, })); }, [existsKeys, type, t], ); // Maintain separate state for each input box const [tempValues, setTempValues] = useState([...data.values]); useEffect(() => { setTempValues([...data.values]); setMetaData(data); }, [data]); const handleHideModal = useCallback(() => { hideModal(); setMetaData({} as IMetaDataTableData); }, [hideModal]); const handleSave = useCallback(() => { if (type === MetadataType.Setting && valueError.field) { return; } if (!metaData.restrictDefinedValues && isShowValueSwitch) { const newMetaData = { ...metaData, values: [] }; onSave(newMetaData); } else { onSave(metaData); } handleHideModal(); }, [metaData, onSave, handleHideModal, isShowValueSwitch, type, valueError]); // Handle value changes, only update temporary state const handleValueChange = useCallback( (index: number, value: string) => { setTempValues((prev) => { if (prev.includes(value)) { setValueError((prev) => { return { ...prev, values: t('knowledgeDetails.metadata.valueExists'), }; }); } else { setValueError((prev) => { return { ...prev, values: '', }; }); } const newValues = [...prev]; newValues[index] = value; return newValues; }); }, [t], ); // Handle blur event, synchronize to main state const handleValueBlur = useCallback(() => { addUpdateValue(metaData.field, [...new Set([...tempValues])]); handleChange('values', [...new Set([...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], ); const showDeleteModal = (item: string, callback: () => void) => { setDeleteDialogContent({ visible: true, title: t('common.delete') + ' ' + t('knowledgeDetails.metadata.metadata'), name: metaData.field + '/' + item, warnText: t('knowledgeDetails.metadata.deleteWarn', { field: t('knowledgeDetails.metadata.field') + '/' + t('knowledgeDetails.metadata.values'), }), onOk: () => { hideDeleteModal(); callback(); }, onCancel: () => { hideDeleteModal(); }, }); }; // Handle adding new value const handleAddValue = useCallback(() => { setTempValues((prev) => [...new Set([...prev, ''])]); // Synchronize to main state setMetaData((prev) => ({ ...prev, values: [...new Set([...prev.values, ''])], })); }, []); return (
{!isEditField && (
{metaData.field}
)} {isEditField && (
{t('knowledgeDetails.metadata.fieldName')}
{ const value = e.target?.value || ''; if (/^[a-zA-Z_]*$/.test(value)) { handleChange('field', value); } }} />
{valueError.field}
)} {isShowDescription && (
{t('knowledgeDetails.metadata.description')}