revert document types

This commit is contained in:
2025-10-31 12:56:09 +01:00
parent 0a87f33d89
commit faf8fec9c7
20 changed files with 12 additions and 1984 deletions
+6 -402
View File
@@ -267,7 +267,6 @@ const AppLayout = () => {
const previewInflightRef = useRef(new Map());
const [tags, setTags] = useState([]);
const [correspondents, setCorrespondents] = useState([]);
const [documentTypes, setDocumentTypes] = useState([]);
const [webdavTokens, setWebdavTokens] = useState([]);
const [webdavTokensLoading, setWebdavTokensLoading] = useState(false);
const [creatingWebdavToken, setCreatingWebdavToken] = useState(false);
@@ -276,7 +275,6 @@ const AppLayout = () => {
const [searchQuery, setSearchQuery] = useState('');
const [activeTagFilters, setActiveTagFilters] = useState([]);
const [activeCorrespondentFilters, setActiveCorrespondentFilters] = useState([]);
const [activeDocumentTypeFilters, setActiveDocumentTypeFilters] = useState([]);
const [searchLoading, setSearchLoading] = useState(false);
const documentsRouteMatch = useMatch('/documents');
const documentsFolderRouteMatch = useMatch('/documents/folder/:folderId');
@@ -302,15 +300,6 @@ const AppLayout = () => {
});
}, []);
const toggleDocumentTypeFilter = useCallback((documentTypeId) => {
setActiveDocumentTypeFilters((previous) => {
if (!documentTypeId) {
return [];
}
return previous.includes(documentTypeId) ? [] : [documentTypeId];
});
}, []);
const initialRefreshAttemptedRef = useRef(Boolean(token));
useEffect(() => {
@@ -346,7 +335,6 @@ const AppLayout = () => {
setSearchQuery('');
setActiveTagFilters([]);
setActiveCorrespondentFilters([]);
setActiveDocumentTypeFilters([]);
setSearchLoading(false);
}, []);
@@ -451,7 +439,6 @@ const AppLayout = () => {
setSearchResults(null);
setTags([]);
setCorrespondents([]);
setDocumentTypes([]);
setWebdavTokens([]);
setWebdavTokensLoading(false);
setCreatingWebdavToken(false);
@@ -460,7 +447,6 @@ const AppLayout = () => {
setSearchQuery('');
setActiveTagFilters([]);
setActiveCorrespondentFilters([]);
setActiveDocumentTypeFilters([]);
setDropOverlayState({ active: false, folderName: DEFAULT_FOLDER_NAME });
setActivePreviewId(null);
setDetailPanelOpen(false);
@@ -496,27 +482,6 @@ const AppLayout = () => {
});
return map;
}, [correspondents]);
const documentTypeLookupByName = useMemo(() => {
const map = new Map();
documentTypes.forEach((entry) => {
if (entry?.name) {
map.set(entry.name.toLowerCase(), entry);
}
});
return map;
}, [documentTypes]);
const documentTypeLookupById = useMemo(() => {
const map = new Map();
documentTypes.forEach((entry) => {
if (entry?.id) {
map.set(entry.id, entry);
}
});
return map;
}, [documentTypes]);
const updateSelectionOrder = useCallback((nextSelection, interactedKeys = []) => {
const nextSet = new Set(nextSelection);
const previousOrder = selectionOrderRef.current.filter((id) => nextSet.has(id));
@@ -652,9 +617,8 @@ const AppLayout = () => {
() =>
searchQuery.trim().length > 0 ||
activeTagFilters.length > 0 ||
activeCorrespondentFilters.length > 0 ||
activeDocumentTypeFilters.length > 0,
[searchQuery, activeTagFilters, activeCorrespondentFilters, activeDocumentTypeFilters],
activeCorrespondentFilters.length > 0,
[searchQuery, activeTagFilters, activeCorrespondentFilters],
);
const applySelectedFolder = useCallback(
@@ -1479,22 +1443,6 @@ const AppLayout = () => {
}
}, [notifyApiError]);
const refreshDocumentTypes = useCallback(async () => {
const requestTenantId = tenantIdRef.current;
try {
const { data } = await api.get('/document-types');
if (tenantIdRef.current !== requestTenantId) {
return;
}
setDocumentTypes(data || []);
} catch (error) {
if (tenantIdRef.current !== requestTenantId) {
return;
}
notifyApiError(error, 'Unable to load document types.');
}
}, [notifyApiError]);
const refreshWebdavTokens = useCallback(async () => {
if (!token) {
return;
@@ -1834,211 +1782,6 @@ const AppLayout = () => {
],
);
const handleDocumentTypeUpdate = useCallback(
async (documentTypeId, changes) => {
if (!documentTypeId) {
throw new Error('Missing document type identifier.');
}
const payload = {};
if (typeof changes.name === 'string') {
const trimmed = changes.name.trim();
if (!trimmed) {
throw new Error('Document type name cannot be empty.');
}
payload.name = trimmed;
}
if (Object.keys(payload).length === 0) {
return false;
}
try {
await api.patch(`/document-types/${documentTypeId}`, payload);
await refreshDocumentTypes();
setStatusMessage('Document type updated.', 'success');
return true;
} catch (error) {
const message = error.response?.data?.error || 'Failed to update document type.';
notifyApiError(error, message);
throw new Error(message);
}
},
[refreshDocumentTypes, notifyApiError, setStatusMessage],
);
const handleDocumentTypeCreate = useCallback(
async ({ name }) => {
const trimmed = typeof name === 'string' ? name.trim() : '';
if (!trimmed) {
throw new Error('Document type name is required.');
}
try {
const { data } = await api.post('/document-types', { name: trimmed });
await refreshDocumentTypes();
setStatusMessage('Document type created.', 'success');
return data;
} catch (error) {
const message = error.response?.data?.error || 'Failed to create document type.';
notifyApiError(error, message);
throw new Error(message);
}
},
[refreshDocumentTypes, notifyApiError, setStatusMessage],
);
const handleDocumentTypeDelete = useCallback(
async (documentTypeId) => {
if (!documentTypeId) {
throw new Error('Missing document type identifier.');
}
const deletedEntry = documentTypeLookupById.get(documentTypeId);
try {
await api.delete(`/document-types/${documentTypeId}`);
mapDocumentCaches((doc) => {
if (!doc) {
return doc;
}
const existingType = doc.document_type;
const existingTypeId = doc.document_type_id ?? existingType?.id ?? null;
const existingTypeName = typeof existingType?.name === 'string' ? existingType.name : undefined;
const shouldClear =
existingTypeId === documentTypeId ||
(!!deletedEntry?.name &&
existingTypeName &&
existingTypeName.toLowerCase() === deletedEntry.name.toLowerCase());
if (!shouldClear) {
return doc;
}
return { ...doc, document_type: null, document_type_id: null };
});
await refreshDocumentTypes();
setStatusMessage('Document type deleted.', 'success');
return true;
} catch (error) {
const message = error.response?.data?.error || 'Failed to delete document type.';
notifyApiError(error, message);
throw new Error(message);
}
},
[
refreshDocumentTypes,
notifyApiError,
setStatusMessage,
mapDocumentCaches,
documentTypeLookupById,
],
);
const handleDocumentTypeAssign = useCallback(
async ({ documentId, documentTypeId, documentType }, { notify = true } = {}) => {
if (!documentId) {
throw new Error('Missing document identifier.');
}
try {
const payload = { document_type_id: documentTypeId ?? null };
const { data } = await api.patch(`/documents/${documentId}`, payload);
const updatedDocument = extractDocumentFromResponse(data);
updateDocumentCaches(documentId, (doc) => {
if (updatedDocument) {
return { ...doc, ...updatedDocument };
}
const next = { ...doc };
if (documentTypeId) {
const entry =
documentTypeLookupById.get(documentTypeId) ||
(documentType?.name ? documentType : null);
next.document_type = entry
? { id: entry.id ?? documentTypeId, name: entry.name }
: { id: documentTypeId, name: '' };
} else {
next.document_type = null;
}
next.document_type_id = documentTypeId ?? null;
return next;
});
if (notify) {
setStatusMessage(
documentTypeId ? 'Document type updated.' : 'Document type cleared.',
'success',
);
}
return true;
} catch (error) {
const message = error.response?.data?.error || 'Failed to update document type.';
notifyApiError(error, message);
return false;
}
},
[
extractDocumentFromResponse,
updateDocumentCaches,
documentTypeLookupById,
notifyApiError,
setStatusMessage,
],
);
const handleDocumentTypeClear = useCallback(
async ({ documentId }, options = {}) =>
handleDocumentTypeAssign({ documentId, documentTypeId: null }, options),
[handleDocumentTypeAssign],
);
const handleDocumentTypeSet = useCallback(
async ({ document, name, input }) => {
if (!document?.id) {
throw new Error('Missing document for document type assignment.');
}
const trimmed = typeof name === 'string' ? name.trim() : '';
if (!trimmed) {
setStatusMessage('Document type name is required.', 'error');
return;
}
let target = documentTypeLookupByName.get(trimmed.toLowerCase()) || null;
if (!target) {
try {
target = await handleDocumentTypeCreate({ name: trimmed });
} catch (error) {
return;
}
}
if (!target?.id) {
setStatusMessage('Unable to resolve document type.', 'error');
return;
}
const success = await handleDocumentTypeAssign(
{ documentId: document.id, documentTypeId: target.id, documentType: target },
{ notify: true },
);
if (success && input) {
input.value = '';
}
},
[
handleDocumentTypeCreate,
handleDocumentTypeAssign,
documentTypeLookupByName,
setStatusMessage,
],
);
const resolveTargetDocumentIds = useCallback(
(candidateIds) => {
const normalized = Array.isArray(candidateIds)
@@ -2052,90 +1795,6 @@ const AppLayout = () => {
[selectedDocumentIds],
);
const handleBulkDocumentTypeSet = useCallback(
async ({ name, input, documentIds }) => {
const trimmed = typeof name === 'string' ? name.trim() : '';
if (!trimmed) {
setStatusMessage('Document type name is required.', 'error');
return;
}
const targets = resolveTargetDocumentIds(documentIds);
if (!targets.length) {
setStatusMessage('Select documents before assigning a document type.', 'error');
return;
}
let target = documentTypeLookupByName.get(trimmed.toLowerCase()) || null;
if (!target) {
try {
target = await handleDocumentTypeCreate({ name: trimmed });
} catch (error) {
return;
}
}
if (!target?.id) {
setStatusMessage('Unable to resolve document type.', 'error');
return;
}
try {
await Promise.all(
targets.map((documentId) =>
api.patch(`/documents/${documentId}`, { document_type_id: target.id }),
),
);
await refreshCurrentFolder();
const suffix = targets.length === 1 ? '' : 's';
setStatusMessage(
`Document type assigned to ${targets.length} document${suffix}.`,
'success',
);
if (input) {
input.value = '';
}
} catch (error) {
const message = error.response?.data?.error || 'Failed to assign document type.';
notifyApiError(error, message);
}
},
[
documentTypeLookupByName,
handleDocumentTypeCreate,
refreshCurrentFolder,
resolveTargetDocumentIds,
setStatusMessage,
notifyApiError,
],
);
const handleBulkDocumentTypeClear = useCallback(
async ({ documentIds }) => {
const targets = resolveTargetDocumentIds(documentIds);
if (!targets.length) {
setStatusMessage('Select documents before clearing the document type.', 'error');
return;
}
try {
await Promise.all(
targets.map((documentId) => api.patch(`/documents/${documentId}`, { document_type_id: null })),
);
await refreshCurrentFolder();
const suffix = targets.length === 1 ? '' : 's';
setStatusMessage(
`Document type cleared from ${targets.length} document${suffix}.`,
'success',
);
} catch (error) {
const message = error.response?.data?.error || 'Failed to clear document type.';
notifyApiError(error, message);
}
},
[resolveTargetDocumentIds, refreshCurrentFolder, setStatusMessage, notifyApiError],
);
const handleTagDelete = useCallback(
async (tagId) => {
if (!tagId) {
@@ -2336,7 +1995,7 @@ const AppLayout = () => {
const initializeAfterLogin = useCallback(async () => {
setLoading(true);
try {
await Promise.all([refreshTags(), refreshCorrespondents(), refreshDocumentTypes()]);
await Promise.all([refreshTags(), refreshCorrespondents()]);
const initialFolder = routeFolderId && routeFolderId !== 'root' ? routeFolderId : 'root';
await loadFolder(initialFolder, { showLoading: false });
} catch (error) {
@@ -2345,7 +2004,7 @@ const AppLayout = () => {
} finally {
setLoading(false);
}
}, [refreshTags, refreshCorrespondents, refreshDocumentTypes, routeFolderId, loadFolder, notifyApiError]);
}, [refreshTags, refreshCorrespondents, routeFolderId, loadFolder, notifyApiError]);
useEffect(() => {
if (!token) {
@@ -4038,12 +3697,7 @@ const AppLayout = () => {
}
}, [creatingFolder, handleFolderCreate, setStatusMessage]);
const {
managementModals,
openTagsModal,
openCorrespondentsModal,
openDocumentTypesModal,
} = useManagementModals({
const { managementModals, openTagsModal, openCorrespondentsModal } = useManagementModals({
locationPathname: location.pathname,
tags,
refreshTags,
@@ -4055,11 +3709,6 @@ const AppLayout = () => {
onCorrespondentCreate: handleCorrespondentCreate,
onCorrespondentUpdate: handleCorrespondentUpdate,
onCorrespondentDelete: handleCorrespondentDelete,
documentTypes,
refreshDocumentTypes,
onDocumentTypeCreate: handleDocumentTypeCreate,
onDocumentTypeUpdate: handleDocumentTypeUpdate,
onDocumentTypeDelete: handleDocumentTypeDelete,
setStatusMessage,
});
@@ -4095,9 +3744,6 @@ const AppLayout = () => {
if (activeCorrespondentFilters.length) {
params.correspondents = activeCorrespondentFilters.join(',');
}
if (activeDocumentTypeFilters.length) {
params.document_types = activeDocumentTypeFilters.join(',');
}
const folderIdentifier = selectedFolder === 'root' ? null : selectedFolder;
if (folderIdentifier) {
params.folder_id = folderIdentifier;
@@ -4179,7 +3825,6 @@ const AppLayout = () => {
searchQuery,
activeTagFilters,
activeCorrespondentFilters,
activeDocumentTypeFilters,
selectedFolder,
notifyApiError,
assetManager,
@@ -4981,7 +4626,7 @@ const AppLayout = () => {
setWorkspaceMode('table');
navigate('/documents', { replace: true });
await Promise.all([refreshTags(), refreshCorrespondents(), refreshDocumentTypes()]);
await Promise.all([refreshTags(), refreshCorrespondents()]);
await loadFolder('root', { showLoading: false, preserveSearch: false });
const tenantLabel = data?.tenant?.name || data?.tenant?.id || 'tenant';
@@ -5002,7 +4647,6 @@ const AppLayout = () => {
navigate,
refreshTags,
refreshCorrespondents,
refreshDocumentTypes,
loadFolder,
],
);
@@ -5128,10 +4772,6 @@ const AppLayout = () => {
activeCorrespondentIds: activeCorrespondentFilters,
onToggleCorrespondentFilter: toggleCorrespondentFilter,
onCreateCorrespondent: (name) => handleCorrespondentCreate({ name }),
documentTypes,
activeDocumentTypeIds: activeDocumentTypeFilters,
onToggleDocumentTypeFilter: toggleDocumentTypeFilter,
onCreateDocumentType: (name) => handleDocumentTypeCreate({ name }),
appStatus,
loading,
previewActive,
@@ -5155,7 +4795,6 @@ const AppLayout = () => {
clearFilters,
correspondents,
currentTenantId,
documentTypes,
folderClickHandlers,
folderNodes,
handleFolderDelete,
@@ -5181,13 +4820,10 @@ const AppLayout = () => {
toggleTagFilter,
handleTagCreate,
handleCorrespondentCreate,
toggleDocumentTypeFilter,
handleDocumentTypeCreate,
handlePromptCreateFolder,
creatingFolder,
handleNeutralHueChange,
neutralHue,
activeDocumentTypeFilters,
],
);
@@ -5222,11 +4858,6 @@ const AppLayout = () => {
correspondents,
onCorrespondentAdd: handleCorrespondentAdd,
onCorrespondentRemove: handleCorrespondentRemove,
documentTypes,
onDocumentTypeSet: handleDocumentTypeSet,
onDocumentTypeClear: handleDocumentTypeClear,
onBulkDocumentTypeSet: handleBulkDocumentTypeSet,
onBulkDocumentTypeClear: handleBulkDocumentTypeClear,
resolveApiPath,
onFolderNavigate: selectFolder,
onClose: handleDetailPanelClose,
@@ -5241,15 +4872,11 @@ const AppLayout = () => {
getDocumentAsset,
handleBulkCorrespondentAdd,
handleBulkCorrespondentRemove,
handleBulkDocumentTypeSet,
handleBulkDocumentTypeClear,
handleBulkSelectionReanalyze,
handleBulkTagAddFromDetail,
handleBulkTagRemoveFromDetail,
handleCorrespondentAdd,
handleCorrespondentRemove,
handleDocumentTypeSet,
handleDocumentTypeClear,
handleDetailPanelClose,
handleDocumentTitleUpdate,
handleTagAdd,
@@ -5262,7 +4889,6 @@ const AppLayout = () => {
selectedPreviewEntry,
tags,
tagLookupById,
documentTypes,
],
);
@@ -5321,16 +4947,6 @@ const AppLayout = () => {
handleDocumentCorrespondentAttach,
handleCorrespondentRemove,
handleCorrespondentAdd,
documentTypes,
refreshDocumentTypes,
handleDocumentTypeUpdate,
handleDocumentTypeCreate,
handleDocumentTypeDelete,
handleDocumentTypeAssign,
handleDocumentTypeClear,
handleDocumentTypeSet,
handleBulkDocumentTypeSet,
handleBulkDocumentTypeClear,
webdavTokens,
webdavTokensLoading,
creatingWebdavToken,
@@ -5366,7 +4982,6 @@ const AppLayout = () => {
notifyApiError,
openTagsModal,
openCorrespondentsModal,
openDocumentTypesModal,
openSettings,
detailPanelOpen,
setDetailPanelOpen,
@@ -5392,16 +5007,6 @@ const AppLayout = () => {
handleDocumentCorrespondentAttach,
handleCorrespondentRemove,
handleCorrespondentAdd,
documentTypes,
refreshDocumentTypes,
handleDocumentTypeUpdate,
handleDocumentTypeCreate,
handleDocumentTypeDelete,
handleDocumentTypeAssign,
handleDocumentTypeClear,
handleDocumentTypeSet,
handleBulkDocumentTypeSet,
handleBulkDocumentTypeClear,
webdavTokens,
webdavTokensLoading,
creatingWebdavToken,
@@ -5436,7 +5041,6 @@ const AppLayout = () => {
notifyApiError,
openTagsModal,
openCorrespondentsModal,
openDocumentTypesModal,
openSettings,
detailPanelOpen,
setDetailPanelOpen,