document types
This commit is contained in:
+489
-36
@@ -27,6 +27,7 @@ import AssetManager, { getAssetFromVersion, resolveDocumentAssetUrl, createAsset
|
||||
import useApiError from './hooks/useApiError';
|
||||
import TagsPanel from './tags/TagsPanel';
|
||||
import CorrespondentsPanel from './correspondents/CorrespondentsPanel';
|
||||
import DocumentTypesPanel from './documentTypes/DocumentTypesPanel';
|
||||
import TagManager from './tag_manager';
|
||||
import Sidebar from './sidebar/Sidebar';
|
||||
import SettingsModal from './settings/SettingsModal';
|
||||
@@ -442,6 +443,7 @@ const AppLayout = () => {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [isTagsModalOpen, setTagsModalOpen] = useState(false);
|
||||
const [isCorrespondentsModalOpen, setCorrespondentsModalOpen] = useState(false);
|
||||
const [isDocumentTypesModalOpen, setDocumentTypesModalOpen] = useState(false);
|
||||
const [isSettingsModalOpen, setSettingsModalOpen] = useState(false);
|
||||
const [creatingFolder, setCreatingFolder] = useState(false);
|
||||
const [folderNodes, setFolderNodes] = useState(() => {
|
||||
@@ -531,6 +533,7 @@ 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);
|
||||
@@ -539,6 +542,7 @@ 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');
|
||||
@@ -564,6 +568,15 @@ const AppLayout = () => {
|
||||
});
|
||||
}, []);
|
||||
|
||||
const toggleDocumentTypeFilter = useCallback((documentTypeId) => {
|
||||
setActiveDocumentTypeFilters((previous) => {
|
||||
if (!documentTypeId) {
|
||||
return [];
|
||||
}
|
||||
return previous.includes(documentTypeId) ? [] : [documentTypeId];
|
||||
});
|
||||
}, []);
|
||||
|
||||
const initialRefreshAttemptedRef = useRef(Boolean(token));
|
||||
|
||||
useEffect(() => {
|
||||
@@ -599,6 +612,7 @@ const AppLayout = () => {
|
||||
setSearchQuery('');
|
||||
setActiveTagFilters([]);
|
||||
setActiveCorrespondentFilters([]);
|
||||
setActiveDocumentTypeFilters([]);
|
||||
setSearchLoading(false);
|
||||
}, []);
|
||||
|
||||
@@ -628,6 +642,17 @@ const AppLayout = () => {
|
||||
}
|
||||
const assetManager = assetManagerRef.current;
|
||||
|
||||
const extractDocumentFromResponse = useCallback(
|
||||
(payload) => {
|
||||
if (!payload) {
|
||||
return null;
|
||||
}
|
||||
const hydratedDetail = assetManager.hydrateDetail(payload);
|
||||
return hydratedDetail?.document || payload.document || payload;
|
||||
},
|
||||
[assetManager],
|
||||
);
|
||||
|
||||
const tagManagerRef = useRef(null);
|
||||
if (!tagManagerRef.current) {
|
||||
tagManagerRef.current = new TagManager();
|
||||
@@ -692,6 +717,7 @@ const AppLayout = () => {
|
||||
setSearchResults(null);
|
||||
setTags([]);
|
||||
setCorrespondents([]);
|
||||
setDocumentTypes([]);
|
||||
setWebdavTokens([]);
|
||||
setWebdavTokensLoading(false);
|
||||
setCreatingWebdavToken(false);
|
||||
@@ -699,6 +725,8 @@ const AppLayout = () => {
|
||||
setWebdavTokenSecret(null);
|
||||
setSearchQuery('');
|
||||
setActiveTagFilters([]);
|
||||
setActiveCorrespondentFilters([]);
|
||||
setActiveDocumentTypeFilters([]);
|
||||
setDropOverlayState({ active: false, folderName: DEFAULT_FOLDER_NAME });
|
||||
setActivePreviewId(null);
|
||||
setDetailPanelOpen(false);
|
||||
@@ -735,6 +763,26 @@ 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));
|
||||
@@ -870,8 +918,9 @@ const AppLayout = () => {
|
||||
() =>
|
||||
searchQuery.trim().length > 0 ||
|
||||
activeTagFilters.length > 0 ||
|
||||
activeCorrespondentFilters.length > 0,
|
||||
[searchQuery, activeTagFilters, activeCorrespondentFilters],
|
||||
activeCorrespondentFilters.length > 0 ||
|
||||
activeDocumentTypeFilters.length > 0,
|
||||
[searchQuery, activeTagFilters, activeCorrespondentFilters, activeDocumentTypeFilters],
|
||||
);
|
||||
|
||||
const applySelectedFolder = useCallback(
|
||||
@@ -1696,6 +1745,22 @@ 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;
|
||||
@@ -2049,6 +2114,308 @@ 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 = (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 ?? (typeof existingType === 'object' ? existingType?.id : null);
|
||||
const existingTypeName =
|
||||
typeof existingType === 'string'
|
||||
? existingType
|
||||
: existingType?.name || existingType?.label || '';
|
||||
|
||||
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 }, { 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);
|
||||
next.document_type = entry || { id: documentTypeId, name: entry?.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 = (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 },
|
||||
{ notify: true },
|
||||
);
|
||||
|
||||
if (success && input) {
|
||||
input.value = '';
|
||||
}
|
||||
},
|
||||
[
|
||||
handleDocumentTypeCreate,
|
||||
handleDocumentTypeAssign,
|
||||
documentTypeLookupByName,
|
||||
setStatusMessage,
|
||||
],
|
||||
);
|
||||
|
||||
const resolveTargetDocumentIds = useCallback(
|
||||
(candidateIds) => {
|
||||
const normalized = Array.isArray(candidateIds)
|
||||
? candidateIds.filter(Boolean)
|
||||
: [];
|
||||
if (normalized.length) {
|
||||
return Array.from(new Set(normalized));
|
||||
}
|
||||
return selectedDocumentIds;
|
||||
},
|
||||
[selectedDocumentIds],
|
||||
);
|
||||
|
||||
const handleBulkDocumentTypeSet = useCallback(
|
||||
async ({ name, input, documentIds }) => {
|
||||
const trimmed = (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) {
|
||||
@@ -2249,7 +2616,7 @@ const AppLayout = () => {
|
||||
const initializeAfterLogin = useCallback(async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
await Promise.all([refreshTags(), refreshCorrespondents()]);
|
||||
await Promise.all([refreshTags(), refreshCorrespondents(), refreshDocumentTypes()]);
|
||||
const initialFolder = routeFolderId && routeFolderId !== 'root' ? routeFolderId : 'root';
|
||||
await loadFolder(initialFolder, { showLoading: false });
|
||||
} catch (error) {
|
||||
@@ -2258,7 +2625,7 @@ const AppLayout = () => {
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [refreshTags, refreshCorrespondents, routeFolderId, loadFolder, notifyApiError]);
|
||||
}, [refreshTags, refreshCorrespondents, refreshDocumentTypes, routeFolderId, loadFolder, notifyApiError]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!token) {
|
||||
@@ -2289,19 +2656,6 @@ const AppLayout = () => {
|
||||
selectFolder,
|
||||
]);
|
||||
|
||||
const resolveTargetDocumentIds = useCallback(
|
||||
(candidateIds) => {
|
||||
const normalized = Array.isArray(candidateIds)
|
||||
? candidateIds.filter(Boolean)
|
||||
: [];
|
||||
if (normalized.length) {
|
||||
return Array.from(new Set(normalized));
|
||||
}
|
||||
return selectedDocumentIds;
|
||||
},
|
||||
[selectedDocumentIds],
|
||||
);
|
||||
|
||||
const handleBulkCorrespondentAdd = useCallback(
|
||||
async ({ name, input, documentIds }) => {
|
||||
const trimmed = (name || '').trim();
|
||||
@@ -3582,16 +3936,15 @@ const AppLayout = () => {
|
||||
|
||||
setLoading(true);
|
||||
try {
|
||||
const { data } = await api.patch(`/documents/${documentId}`, { title: trimmed });
|
||||
const hydratedDetail = assetManager.hydrateDetail(data);
|
||||
const hydratedDocument = hydratedDetail?.document || data.document || data;
|
||||
const { data } = await api.patch(`/documents/${documentId}`, { title: trimmed });
|
||||
const updatedDocument = extractDocumentFromResponse(data);
|
||||
|
||||
updateDocumentCaches(documentId, (doc) => {
|
||||
if (hydratedDocument) {
|
||||
return { ...doc, ...hydratedDocument };
|
||||
}
|
||||
return { ...doc, title: trimmed };
|
||||
});
|
||||
updateDocumentCaches(documentId, (doc) => {
|
||||
if (updatedDocument) {
|
||||
return { ...doc, ...updatedDocument };
|
||||
}
|
||||
return { ...doc, title: trimmed };
|
||||
});
|
||||
|
||||
setStatusMessage('Document title updated.', 'success');
|
||||
return true;
|
||||
@@ -3603,7 +3956,7 @@ const AppLayout = () => {
|
||||
setLoading(false);
|
||||
}
|
||||
},
|
||||
[assetManager, notifyApiError, setStatusMessage, updateDocumentCaches],
|
||||
[notifyApiError, setStatusMessage, updateDocumentCaches, extractDocumentFromResponse],
|
||||
);
|
||||
|
||||
const applyTagRemovalToCaches = useCallback(
|
||||
@@ -3967,6 +4320,7 @@ const AppLayout = () => {
|
||||
|
||||
const openTagsModal = useCallback(() => {
|
||||
setCorrespondentsModalOpen(false);
|
||||
setDocumentTypesModalOpen(false);
|
||||
setTagsModalOpen(true);
|
||||
}, []);
|
||||
|
||||
@@ -3977,12 +4331,23 @@ const AppLayout = () => {
|
||||
const openCorrespondentsModal = useCallback(() => {
|
||||
setTagsModalOpen(false);
|
||||
setCorrespondentsModalOpen(true);
|
||||
setDocumentTypesModalOpen(false);
|
||||
}, []);
|
||||
|
||||
const closeCorrespondentsModal = useCallback(() => {
|
||||
setCorrespondentsModalOpen(false);
|
||||
}, []);
|
||||
|
||||
const openDocumentTypesModal = useCallback(() => {
|
||||
setTagsModalOpen(false);
|
||||
setCorrespondentsModalOpen(false);
|
||||
setDocumentTypesModalOpen(true);
|
||||
}, []);
|
||||
|
||||
const closeDocumentTypesModal = useCallback(() => {
|
||||
setDocumentTypesModalOpen(false);
|
||||
}, []);
|
||||
|
||||
const openSettingsModal = useCallback(() => {
|
||||
setSettingsModalOpen(true);
|
||||
}, []);
|
||||
@@ -3994,11 +4359,12 @@ const AppLayout = () => {
|
||||
useEffect(() => {
|
||||
setTagsModalOpen(false);
|
||||
setCorrespondentsModalOpen(false);
|
||||
setDocumentTypesModalOpen(false);
|
||||
setSettingsModalOpen(false);
|
||||
}, [location.pathname]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isTagsModalOpen && !isCorrespondentsModalOpen && !isSettingsModalOpen) {
|
||||
if (!isTagsModalOpen && !isCorrespondentsModalOpen && !isDocumentTypesModalOpen && !isSettingsModalOpen) {
|
||||
return;
|
||||
}
|
||||
const handleKeyDown = (event) => {
|
||||
@@ -4008,6 +4374,8 @@ const AppLayout = () => {
|
||||
closeTagsModal();
|
||||
} else if (isCorrespondentsModalOpen) {
|
||||
closeCorrespondentsModal();
|
||||
} else if (isDocumentTypesModalOpen) {
|
||||
closeDocumentTypesModal();
|
||||
} else if (isSettingsModalOpen) {
|
||||
closeSettingsModal();
|
||||
}
|
||||
@@ -4020,9 +4388,11 @@ const AppLayout = () => {
|
||||
}, [
|
||||
isTagsModalOpen,
|
||||
isCorrespondentsModalOpen,
|
||||
isDocumentTypesModalOpen,
|
||||
isSettingsModalOpen,
|
||||
closeTagsModal,
|
||||
closeCorrespondentsModal,
|
||||
closeDocumentTypesModal,
|
||||
closeSettingsModal,
|
||||
]);
|
||||
|
||||
@@ -4054,6 +4424,9 @@ 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;
|
||||
@@ -4135,6 +4508,7 @@ const AppLayout = () => {
|
||||
searchQuery,
|
||||
activeTagFilters,
|
||||
activeCorrespondentFilters,
|
||||
activeDocumentTypeFilters,
|
||||
selectedFolder,
|
||||
notifyApiError,
|
||||
assetManager,
|
||||
@@ -4936,7 +5310,7 @@ const AppLayout = () => {
|
||||
setWorkspaceMode('table');
|
||||
navigate('/documents', { replace: true });
|
||||
|
||||
await Promise.all([refreshTags(), refreshCorrespondents()]);
|
||||
await Promise.all([refreshTags(), refreshCorrespondents(), refreshDocumentTypes()]);
|
||||
await loadFolder('root', { showLoading: false, preserveSearch: false });
|
||||
|
||||
const tenantLabel = data?.tenant?.name || data?.tenant?.id || 'tenant';
|
||||
@@ -4957,6 +5331,7 @@ const AppLayout = () => {
|
||||
navigate,
|
||||
refreshTags,
|
||||
refreshCorrespondents,
|
||||
refreshDocumentTypes,
|
||||
loadFolder,
|
||||
],
|
||||
);
|
||||
@@ -5082,6 +5457,10 @@ const AppLayout = () => {
|
||||
activeCorrespondentIds: activeCorrespondentFilters,
|
||||
onToggleCorrespondentFilter: toggleCorrespondentFilter,
|
||||
onCreateCorrespondent: (name) => handleCorrespondentCreate({ name }),
|
||||
documentTypes,
|
||||
activeDocumentTypeIds: activeDocumentTypeFilters,
|
||||
onToggleDocumentTypeFilter: toggleDocumentTypeFilter,
|
||||
onCreateDocumentType: (name) => handleDocumentTypeCreate({ name }),
|
||||
appStatus,
|
||||
loading,
|
||||
previewActive,
|
||||
@@ -5105,6 +5484,7 @@ const AppLayout = () => {
|
||||
clearFilters,
|
||||
correspondents,
|
||||
currentTenantId,
|
||||
documentTypes,
|
||||
folderClickHandlers,
|
||||
folderNodes,
|
||||
handleFolderDelete,
|
||||
@@ -5130,10 +5510,13 @@ const AppLayout = () => {
|
||||
toggleTagFilter,
|
||||
handleTagCreate,
|
||||
handleCorrespondentCreate,
|
||||
toggleDocumentTypeFilter,
|
||||
handleDocumentTypeCreate,
|
||||
handlePromptCreateFolder,
|
||||
creatingFolder,
|
||||
handleNeutralHueChange,
|
||||
neutralHue,
|
||||
activeDocumentTypeFilters,
|
||||
],
|
||||
);
|
||||
|
||||
@@ -5168,6 +5551,11 @@ const AppLayout = () => {
|
||||
correspondents,
|
||||
onCorrespondentAdd: handleCorrespondentAdd,
|
||||
onCorrespondentRemove: handleCorrespondentRemove,
|
||||
documentTypes,
|
||||
onDocumentTypeSet: handleDocumentTypeSet,
|
||||
onDocumentTypeClear: handleDocumentTypeClear,
|
||||
onBulkDocumentTypeSet: handleBulkDocumentTypeSet,
|
||||
onBulkDocumentTypeClear: handleBulkDocumentTypeClear,
|
||||
resolveApiPath,
|
||||
onFolderNavigate: selectFolder,
|
||||
onClose: handleDetailPanelClose,
|
||||
@@ -5182,11 +5570,15 @@ const AppLayout = () => {
|
||||
getDocumentAsset,
|
||||
handleBulkCorrespondentAdd,
|
||||
handleBulkCorrespondentRemove,
|
||||
handleBulkDocumentTypeSet,
|
||||
handleBulkDocumentTypeClear,
|
||||
handleBulkSelectionReanalyze,
|
||||
handleBulkTagAddFromDetail,
|
||||
handleBulkTagRemoveFromDetail,
|
||||
handleCorrespondentAdd,
|
||||
handleCorrespondentRemove,
|
||||
handleDocumentTypeSet,
|
||||
handleDocumentTypeClear,
|
||||
handleDetailPanelClose,
|
||||
handleDocumentTitleUpdate,
|
||||
handleTagAdd,
|
||||
@@ -5199,6 +5591,7 @@ const AppLayout = () => {
|
||||
selectedPreviewEntry,
|
||||
tags,
|
||||
tagLookupById,
|
||||
documentTypes,
|
||||
],
|
||||
);
|
||||
|
||||
@@ -5257,6 +5650,16 @@ const AppLayout = () => {
|
||||
handleDocumentCorrespondentAttach,
|
||||
handleCorrespondentRemove,
|
||||
handleCorrespondentAdd,
|
||||
documentTypes,
|
||||
refreshDocumentTypes,
|
||||
handleDocumentTypeUpdate,
|
||||
handleDocumentTypeCreate,
|
||||
handleDocumentTypeDelete,
|
||||
handleDocumentTypeAssign,
|
||||
handleDocumentTypeClear,
|
||||
handleDocumentTypeSet,
|
||||
handleBulkDocumentTypeSet,
|
||||
handleBulkDocumentTypeClear,
|
||||
previewActive,
|
||||
previewWorkspaceDocument,
|
||||
previewWorkspaceEntry,
|
||||
@@ -5275,6 +5678,7 @@ const AppLayout = () => {
|
||||
notifyApiError,
|
||||
openTagsModal,
|
||||
openCorrespondentsModal,
|
||||
openDocumentTypesModal,
|
||||
openSettingsModal,
|
||||
detailPanelOpen,
|
||||
setDetailPanelOpen,
|
||||
@@ -5300,6 +5704,16 @@ const AppLayout = () => {
|
||||
handleDocumentCorrespondentAttach,
|
||||
handleCorrespondentRemove,
|
||||
handleCorrespondentAdd,
|
||||
documentTypes,
|
||||
refreshDocumentTypes,
|
||||
handleDocumentTypeUpdate,
|
||||
handleDocumentTypeCreate,
|
||||
handleDocumentTypeDelete,
|
||||
handleDocumentTypeAssign,
|
||||
handleDocumentTypeClear,
|
||||
handleDocumentTypeSet,
|
||||
handleBulkDocumentTypeSet,
|
||||
handleBulkDocumentTypeClear,
|
||||
previewActive,
|
||||
previewWorkspaceDocument,
|
||||
previewWorkspaceEntry,
|
||||
@@ -5317,6 +5731,7 @@ const AppLayout = () => {
|
||||
notifyApiError,
|
||||
openTagsModal,
|
||||
openCorrespondentsModal,
|
||||
openDocumentTypesModal,
|
||||
openSettingsModal,
|
||||
detailPanelOpen,
|
||||
setDetailPanelOpen,
|
||||
@@ -5402,12 +5817,48 @@ const AppLayout = () => {
|
||||
</button>
|
||||
</div>
|
||||
<div className="panel-modal__body">
|
||||
<CorrespondentsPanel
|
||||
correspondents={correspondents}
|
||||
onRefresh={refreshCorrespondents}
|
||||
onCreate={handleCorrespondentCreate}
|
||||
onUpdate={handleCorrespondentUpdate}
|
||||
onDelete={handleCorrespondentDelete}
|
||||
<CorrespondentsPanel
|
||||
correspondents={correspondents}
|
||||
onRefresh={refreshCorrespondents}
|
||||
onCreate={handleCorrespondentCreate}
|
||||
onUpdate={handleCorrespondentUpdate}
|
||||
onDelete={handleCorrespondentDelete}
|
||||
onNotify={setStatusMessage}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{isDocumentTypesModalOpen && (
|
||||
<div
|
||||
className="modal-backdrop"
|
||||
role="presentation"
|
||||
onClick={closeDocumentTypesModal}
|
||||
>
|
||||
<div
|
||||
className="modal modal--panel"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
aria-labelledby="document-types-modal-title"
|
||||
onClick={(event) => event.stopPropagation()}
|
||||
>
|
||||
<div className="panel-modal__header">
|
||||
<h3 id="document-types-modal-title">Manage Document Types</h3>
|
||||
<button
|
||||
type="button"
|
||||
className="secondary"
|
||||
onClick={closeDocumentTypesModal}
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
<div className="panel-modal__body">
|
||||
<DocumentTypesPanel
|
||||
documentTypes={documentTypes}
|
||||
onRefresh={refreshDocumentTypes}
|
||||
onCreate={handleDocumentTypeCreate}
|
||||
onUpdate={handleDocumentTypeUpdate}
|
||||
onDelete={handleDocumentTypeDelete}
|
||||
onNotify={setStatusMessage}
|
||||
/>
|
||||
</div>
|
||||
@@ -5450,6 +5901,7 @@ const DocumentsRoute = () => {
|
||||
skeuoWorkspaceProps,
|
||||
openTagsModal,
|
||||
openCorrespondentsModal,
|
||||
openDocumentTypesModal,
|
||||
previewWorkspaceDocument,
|
||||
previewWorkspaceEntry,
|
||||
closeDocumentPreview,
|
||||
@@ -5471,9 +5923,10 @@ const DocumentsRoute = () => {
|
||||
...sidebarProps,
|
||||
onManageTags: openTagsModal,
|
||||
onManageCorrespondents: openCorrespondentsModal,
|
||||
onManageDocumentTypes: openDocumentTypesModal,
|
||||
onCollapse: collapseSidebar,
|
||||
}),
|
||||
[sidebarProps, openTagsModal, openCorrespondentsModal, collapseSidebar],
|
||||
[sidebarProps, openTagsModal, openCorrespondentsModal, openDocumentTypesModal, collapseSidebar],
|
||||
);
|
||||
|
||||
const breadcrumbs = documentsTableProps?.breadcrumbs || null;
|
||||
|
||||
Reference in New Issue
Block a user