feat: Centralize document preview logic into useDocumentPreview and add correspondentLookupByName to useCorrespondents
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import { useCallback, useEffect, useRef } from 'react';
|
import { useCallback, useEffect, useRef, useMemo } from 'react';
|
||||||
import type {
|
import type {
|
||||||
Dispatch,
|
Dispatch,
|
||||||
MutableRefObject,
|
MutableRefObject,
|
||||||
@@ -36,6 +36,8 @@ interface UseDocumentPreviewResult {
|
|||||||
openDocumentPreview: (documentId: DocumentId, options?: { replace?: boolean }) => void;
|
openDocumentPreview: (documentId: DocumentId, options?: { replace?: boolean }) => void;
|
||||||
closeDocumentPreview: (folderId?: FolderId) => void;
|
closeDocumentPreview: (folderId?: FolderId) => void;
|
||||||
resetPreviewState: () => void;
|
resetPreviewState: () => void;
|
||||||
|
previewWorkspaceDocument: Document | null;
|
||||||
|
previewActive: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const useDocumentPreview = ({
|
const useDocumentPreview = ({
|
||||||
@@ -127,11 +129,22 @@ const useDocumentPreview = ({
|
|||||||
};
|
};
|
||||||
}, [routeDocumentId, ensurePreviewData, notifyApiError, closeDocumentPreview]);
|
}, [routeDocumentId, ensurePreviewData, notifyApiError, closeDocumentPreview]);
|
||||||
|
|
||||||
|
const previewWorkspaceDocument = useMemo(() => {
|
||||||
|
if (!routeDocumentId) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return documentsManager.getById(routeDocumentId);
|
||||||
|
}, [routeDocumentId, documentsManager]);
|
||||||
|
|
||||||
|
const previewActive = Boolean(routeDocumentId && previewWorkspaceDocument);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
ensurePreviewData,
|
ensurePreviewData,
|
||||||
openDocumentPreview,
|
openDocumentPreview,
|
||||||
closeDocumentPreview,
|
closeDocumentPreview,
|
||||||
resetPreviewState,
|
resetPreviewState,
|
||||||
|
previewWorkspaceDocument,
|
||||||
|
previewActive,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ interface UseManagementModalsArgs {
|
|||||||
onTagDelete?: (...args: any[]) => void | Promise<void>;
|
onTagDelete?: (...args: any[]) => void | Promise<void>;
|
||||||
correspondents?: Correspondent[];
|
correspondents?: Correspondent[];
|
||||||
correspondentLookupById?: Map<Identifier, Correspondent> | null;
|
correspondentLookupById?: Map<Identifier, Correspondent> | null;
|
||||||
|
correspondentLookupByName?: Map<string, Correspondent> | null;
|
||||||
refreshCorrespondents?: () => void | Promise<void>;
|
refreshCorrespondents?: () => void | Promise<void>;
|
||||||
onCorrespondentCreate?: (...args: any[]) => void | Promise<void>;
|
onCorrespondentCreate?: (...args: any[]) => void | Promise<void>;
|
||||||
onCorrespondentUpdate?: (...args: any[]) => void | Promise<void>;
|
onCorrespondentUpdate?: (...args: any[]) => void | Promise<void>;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useCallback, useSyncExternalStore } from 'react';
|
import { useCallback, useSyncExternalStore, useMemo } from 'react';
|
||||||
import { useStatusToast } from '../../lib/context/StatusToastContext';
|
import { useStatusToast } from '../../lib/context/StatusToastContext';
|
||||||
import type { Correspondent } from '../../types/documents';
|
import type { Correspondent } from '../../types/documents';
|
||||||
import type { Identifier } from '../../types/identifiers';
|
import type { Identifier } from '../../types/identifiers';
|
||||||
@@ -114,9 +114,20 @@ const useCorrespondents = ({
|
|||||||
[documentsManager, notifyApiError, correspondentManager, showToast],
|
[documentsManager, notifyApiError, correspondentManager, showToast],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const correspondentLookupByName = useMemo(() => {
|
||||||
|
const map = new Map<string, Correspondent>();
|
||||||
|
for (const correspondent of correspondents) {
|
||||||
|
if (correspondent.name) {
|
||||||
|
map.set(correspondent.name.toLowerCase(), correspondent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}, [correspondents]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
correspondents,
|
correspondents,
|
||||||
correspondentLookupById: correspondentsSnapshot,
|
correspondentLookupById: correspondentsSnapshot,
|
||||||
|
correspondentLookupByName,
|
||||||
refreshCorrespondents,
|
refreshCorrespondents,
|
||||||
handleCorrespondentCreate,
|
handleCorrespondentCreate,
|
||||||
handleCorrespondentUpdate,
|
handleCorrespondentUpdate,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useCallback, useMemo } from 'react';
|
import { useCallback } from 'react';
|
||||||
import { useStatusToast } from '../../lib/context/StatusToastContext';
|
import { useStatusToast } from '../../lib/context/StatusToastContext';
|
||||||
import type { Identifier } from '../../types/identifiers';
|
import type { Identifier } from '../../types/identifiers';
|
||||||
import type { Correspondent } from '../../types/documents';
|
import type { Correspondent } from '../../types/documents';
|
||||||
@@ -21,22 +21,12 @@ const useDocumentCorrespondentMutations = ({
|
|||||||
const notifyApiError = useNotifyApiError();
|
const notifyApiError = useNotifyApiError();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
correspondents,
|
|
||||||
correspondentManager,
|
correspondentManager,
|
||||||
|
correspondentLookupByName,
|
||||||
} = correspondentsState;
|
} = correspondentsState;
|
||||||
|
|
||||||
const { documentsManager } = documentsState;
|
const { documentsManager } = documentsState;
|
||||||
|
|
||||||
const correspondentLookupByName = useMemo(() => {
|
|
||||||
const map = new Map<string, Correspondent>();
|
|
||||||
correspondents.forEach((correspondent) => {
|
|
||||||
if (correspondent?.name) {
|
|
||||||
map.set(correspondent.name.toLowerCase(), correspondent);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return map;
|
|
||||||
}, [correspondents]);
|
|
||||||
|
|
||||||
const handleDocumentCorrespondentAttach = useCallback(
|
const handleDocumentCorrespondentAttach = useCallback(
|
||||||
async (
|
async (
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ import type {
|
|||||||
SelectionState,
|
SelectionState,
|
||||||
TagsState,
|
TagsState,
|
||||||
CorrespondentsState,
|
CorrespondentsState,
|
||||||
ActionsState,
|
|
||||||
} from '../types/workspaceTypes';
|
} from '../types/workspaceTypes';
|
||||||
import type { Tag, Correspondent } from '../../types/documents';
|
import type { Tag, Correspondent } from '../../types/documents';
|
||||||
import useDocumentCorrespondentMutations from './useDocumentCorrespondentMutations';
|
import useDocumentCorrespondentMutations from './useDocumentCorrespondentMutations';
|
||||||
@@ -36,7 +35,7 @@ interface UseDocumentMutationsArgs {
|
|||||||
selectionState: SelectionState;
|
selectionState: SelectionState;
|
||||||
tagsState: TagsState;
|
tagsState: TagsState;
|
||||||
correspondentsState: CorrespondentsState;
|
correspondentsState: CorrespondentsState;
|
||||||
actions: ActionsState;
|
closeDocumentPreview: () => void;
|
||||||
previewDocumentId?: DocumentId | null;
|
previewDocumentId?: DocumentId | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,7 +67,6 @@ interface UseDocumentMutationsResult {
|
|||||||
handleDocumentCorrespondentAttach: (args: { documentId: DocumentId; correspondentId: DocumentId; correspondent?: Correspondent | Partial<Correspondent> | null }) => Promise<boolean>;
|
handleDocumentCorrespondentAttach: (args: { documentId: DocumentId; correspondentId: DocumentId; correspondent?: Correspondent | Partial<Correspondent> | null }) => Promise<boolean>;
|
||||||
handleDocumentCorrespondentDetach: (args: { documentId: DocumentId; correspondentId: DocumentId }) => Promise<boolean>;
|
handleDocumentCorrespondentDetach: (args: { documentId: DocumentId; correspondentId: DocumentId }) => Promise<boolean>;
|
||||||
handleDocumentCorrespondentAdd: (args: { document: { id?: string }; name?: string; input?: HTMLInputElement | null; option?: Correspondent | Partial<Correspondent> | string | null }) => Promise<void>;
|
handleDocumentCorrespondentAdd: (args: { document: { id?: string }; name?: string; input?: HTMLInputElement | null; option?: Correspondent | Partial<Correspondent> | string | null }) => Promise<void>;
|
||||||
correspondentLookupByName: Map<string, Correspondent>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const useDocumentMutations = ({
|
const useDocumentMutations = ({
|
||||||
@@ -77,7 +75,7 @@ const useDocumentMutations = ({
|
|||||||
selectionState,
|
selectionState,
|
||||||
tagsState,
|
tagsState,
|
||||||
correspondentsState,
|
correspondentsState,
|
||||||
actions,
|
closeDocumentPreview,
|
||||||
previewDocumentId,
|
previewDocumentId,
|
||||||
}: UseDocumentMutationsArgs): UseDocumentMutationsResult => {
|
}: UseDocumentMutationsArgs): UseDocumentMutationsResult => {
|
||||||
const { showToast } = useStatusToast();
|
const { showToast } = useStatusToast();
|
||||||
@@ -102,7 +100,6 @@ const useDocumentMutations = ({
|
|||||||
handleDocumentCorrespondentAttach,
|
handleDocumentCorrespondentAttach,
|
||||||
handleDocumentCorrespondentDetach,
|
handleDocumentCorrespondentDetach,
|
||||||
handleDocumentCorrespondentAdd,
|
handleDocumentCorrespondentAdd,
|
||||||
correspondentLookupByName,
|
|
||||||
} = useDocumentCorrespondentMutations({
|
} = useDocumentCorrespondentMutations({
|
||||||
correspondentsState,
|
correspondentsState,
|
||||||
documentsState: { documentsManager: documentsState.documentsManager },
|
documentsState: { documentsManager: documentsState.documentsManager },
|
||||||
@@ -115,13 +112,13 @@ const useDocumentMutations = ({
|
|||||||
showToast('Analysis queued.', 'info');
|
showToast('Analysis queued.', 'info');
|
||||||
// Close preview if it's the current one to allow refresh?
|
// Close preview if it's the current one to allow refresh?
|
||||||
if (previewDocumentId === documentId) {
|
if (previewDocumentId === documentId) {
|
||||||
actions.closeDocumentPreview();
|
closeDocumentPreview();
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
notifyApiError(error, 'Failed to queue analysis.');
|
notifyApiError(error, 'Failed to queue analysis.');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[actions, notifyApiError, previewDocumentId, showToast],
|
[closeDocumentPreview, notifyApiError, previewDocumentId, showToast],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleDocumentsDelete = useCallback(
|
const handleDocumentsDelete = useCallback(
|
||||||
@@ -240,7 +237,6 @@ const useDocumentMutations = ({
|
|||||||
handleDocumentCorrespondentAttach,
|
handleDocumentCorrespondentAttach,
|
||||||
handleDocumentCorrespondentDetach,
|
handleDocumentCorrespondentDetach,
|
||||||
handleDocumentCorrespondentAdd,
|
handleDocumentCorrespondentAdd,
|
||||||
correspondentLookupByName,
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -273,6 +273,13 @@ const useDocumentsWorkspace = ({
|
|||||||
foldersManager,
|
foldersManager,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const resolveFolderPath = useCallback(
|
||||||
|
(folderId) => {
|
||||||
|
return resolveBreadcrumbs(folderId || 'root', folderNodes as any);
|
||||||
|
},
|
||||||
|
[folderNodes],
|
||||||
|
);
|
||||||
|
|
||||||
const [currentSubfolders, setCurrentSubfolders] = useState<Array<{ id?: FolderNodeId; name?: string | null;[key: string]: unknown }>>([]);
|
const [currentSubfolders, setCurrentSubfolders] = useState<Array<{ id?: FolderNodeId; name?: string | null;[key: string]: unknown }>>([]);
|
||||||
|
|
||||||
const foldersSnapshot = useSyncExternalStore(
|
const foldersSnapshot = useSyncExternalStore(
|
||||||
@@ -384,18 +391,7 @@ const useDocumentsWorkspace = ({
|
|||||||
}
|
}
|
||||||
}, [selectedFolder, documentsSortField, documentsSortDirection, fetchFolderData, updateViewState, notifyApiError]);
|
}, [selectedFolder, documentsSortField, documentsSortDirection, fetchFolderData, updateViewState, notifyApiError]);
|
||||||
|
|
||||||
const {
|
const documentsSearch = useDocumentsSearch({
|
||||||
searchQuery,
|
|
||||||
setSearchQuery,
|
|
||||||
searchResultIds,
|
|
||||||
setSearchResultIds,
|
|
||||||
searchLoading,
|
|
||||||
activeTagFilters,
|
|
||||||
setActiveTagFilters,
|
|
||||||
activeCorrespondentFilters,
|
|
||||||
setActiveCorrespondentFilters,
|
|
||||||
documentsFilterValue,
|
|
||||||
} = useDocumentsSearch({
|
|
||||||
api: apiClient,
|
api: apiClient,
|
||||||
selectedFolder,
|
selectedFolder,
|
||||||
locationPathname: location.pathname,
|
locationPathname: location.pathname,
|
||||||
@@ -407,8 +403,8 @@ const useDocumentsWorkspace = ({
|
|||||||
documentsManager,
|
documentsManager,
|
||||||
});
|
});
|
||||||
|
|
||||||
const documentsFilter = documentsFilterValue;
|
const documentsFilter = documentsSearch.documentsFilterValue;
|
||||||
const showingSearchResults = searchResultIds !== null;
|
const showingSearchResults = documentsSearch.searchResultIds !== null;
|
||||||
|
|
||||||
// Live Filter: Ensure we only show documents that actually belong to the current folder.
|
// Live Filter: Ensure we only show documents that actually belong to the current folder.
|
||||||
// Since 'documents' is reactive, if a document is moved, its folder_id updates immediately.
|
// Since 'documents' is reactive, if a document is moved, its folder_id updates immediately.
|
||||||
@@ -431,7 +427,7 @@ const useDocumentsWorkspace = ({
|
|||||||
} = useWorkspaceViewData({
|
} = useWorkspaceViewData({
|
||||||
documents: liveFilteredDocuments,
|
documents: liveFilteredDocuments,
|
||||||
documentLookup,
|
documentLookup,
|
||||||
searchResultIds,
|
searchResultIds: documentsSearch.searchResultIds,
|
||||||
showingSearchResults,
|
showingSearchResults,
|
||||||
currentSubfolders: visibleSubfolders,
|
currentSubfolders: visibleSubfolders,
|
||||||
});
|
});
|
||||||
@@ -440,6 +436,8 @@ const useDocumentsWorkspace = ({
|
|||||||
openDocumentPreview,
|
openDocumentPreview,
|
||||||
closeDocumentPreview,
|
closeDocumentPreview,
|
||||||
resetPreviewState,
|
resetPreviewState,
|
||||||
|
previewWorkspaceDocument,
|
||||||
|
previewActive,
|
||||||
} = useDocumentPreview({
|
} = useDocumentPreview({
|
||||||
routeDocumentId: previewDocumentId,
|
routeDocumentId: previewDocumentId,
|
||||||
documentsManager,
|
documentsManager,
|
||||||
@@ -471,7 +469,7 @@ const useDocumentsWorkspace = ({
|
|||||||
|
|
||||||
useWorkspaceSelectionSync({
|
useWorkspaceSelectionSync({
|
||||||
showingSearchResults,
|
showingSearchResults,
|
||||||
searchQuery,
|
searchQuery: documentsSearch.searchQuery,
|
||||||
setSelectedEntries,
|
setSelectedEntries,
|
||||||
setSelectionOrder,
|
setSelectionOrder,
|
||||||
selectionOrderRef,
|
selectionOrderRef,
|
||||||
@@ -486,7 +484,7 @@ const useDocumentsWorkspace = ({
|
|||||||
const tagsState = useTags({
|
const tagsState = useTags({
|
||||||
tenantIdRef,
|
tenantIdRef,
|
||||||
tagManager,
|
tagManager,
|
||||||
setActiveTagFilters,
|
setActiveTagFilters: documentsSearch.setActiveTagFilters,
|
||||||
documentsManager,
|
documentsManager,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -584,10 +582,10 @@ const useDocumentsWorkspace = ({
|
|||||||
selectionAnchorRef.current = null;
|
selectionAnchorRef.current = null;
|
||||||
setDraggedDocumentIds([]);
|
setDraggedDocumentIds([]);
|
||||||
setDraggedFolderId(null);
|
setDraggedFolderId(null);
|
||||||
setSearchResultIds(null);
|
documentsSearch.setSearchResultIds(null);
|
||||||
setSearchQuery('');
|
documentsSearch.setSearchQuery('');
|
||||||
setActiveTagFilters([]);
|
documentsSearch.setActiveTagFilters([]);
|
||||||
setActiveCorrespondentFilters([]);
|
documentsSearch.setActiveCorrespondentFilters([]);
|
||||||
setActivePreviewId(null);
|
setActivePreviewId(null);
|
||||||
detailPanelControlRef.current.close();
|
detailPanelControlRef.current.close();
|
||||||
assetManager.reset();
|
assetManager.reset();
|
||||||
@@ -612,10 +610,7 @@ const useDocumentsWorkspace = ({
|
|||||||
setDocuments,
|
setDocuments,
|
||||||
setDraggedDocumentIds,
|
setDraggedDocumentIds,
|
||||||
setDraggedFolderId,
|
setDraggedFolderId,
|
||||||
setSearchResultIds,
|
documentsSearch,
|
||||||
setSearchQuery,
|
|
||||||
setActiveTagFilters,
|
|
||||||
setActiveCorrespondentFilters,
|
|
||||||
setActivePreviewId,
|
setActivePreviewId,
|
||||||
resetPreviewState,
|
resetPreviewState,
|
||||||
upload,
|
upload,
|
||||||
@@ -631,16 +626,12 @@ const useDocumentsWorkspace = ({
|
|||||||
const documentsState = {
|
const documentsState = {
|
||||||
documentLookup,
|
documentLookup,
|
||||||
setDocuments,
|
setDocuments,
|
||||||
setSearchResultIds,
|
setSearchResultIds: documentsSearch.setSearchResultIds,
|
||||||
documentsManager,
|
documentsManager,
|
||||||
extractDocumentFromResponse,
|
extractDocumentFromResponse,
|
||||||
ingestDocuments: (docs: unknown[]) => documentsManager.ingest(docs),
|
ingestDocuments: (docs: unknown[]) => documentsManager.ingest(docs),
|
||||||
};
|
};
|
||||||
|
|
||||||
const actionsState = {
|
|
||||||
closeDocumentPreview,
|
|
||||||
};
|
|
||||||
|
|
||||||
const documentMutationsResult = useDocumentMutations({
|
const documentMutationsResult = useDocumentMutations({
|
||||||
documentsState,
|
documentsState,
|
||||||
folderState,
|
folderState,
|
||||||
@@ -653,13 +644,12 @@ const useDocumentsWorkspace = ({
|
|||||||
...correspondentsState,
|
...correspondentsState,
|
||||||
correspondentManager,
|
correspondentManager,
|
||||||
},
|
},
|
||||||
actions: actionsState,
|
closeDocumentPreview,
|
||||||
previewDocumentId,
|
previewDocumentId,
|
||||||
});
|
});
|
||||||
|
|
||||||
const {
|
const {
|
||||||
moveDocumentsToFolder,
|
moveDocumentsToFolder,
|
||||||
handleThumbnailRegeneration,
|
|
||||||
handleDocumentsDelete,
|
handleDocumentsDelete,
|
||||||
handleDocumentTagAdd,
|
handleDocumentTagAdd,
|
||||||
handleDocumentTagAttach,
|
handleDocumentTagAttach,
|
||||||
@@ -669,7 +659,6 @@ const useDocumentsWorkspace = ({
|
|||||||
handleDocumentCorrespondentAttach,
|
handleDocumentCorrespondentAttach,
|
||||||
handleDocumentCorrespondentDetach,
|
handleDocumentCorrespondentDetach,
|
||||||
handleDocumentCorrespondentAdd,
|
handleDocumentCorrespondentAdd,
|
||||||
correspondentLookupByName,
|
|
||||||
} = documentMutationsResult;
|
} = documentMutationsResult;
|
||||||
|
|
||||||
const mutations = {
|
const mutations = {
|
||||||
@@ -802,7 +791,7 @@ const useDocumentsWorkspace = ({
|
|||||||
handleDeleteSelection,
|
handleDeleteSelection,
|
||||||
} = useBulkDocumentActions({
|
} = useBulkDocumentActions({
|
||||||
resolveTargetDocumentIds,
|
resolveTargetDocumentIds,
|
||||||
correspondentLookupByName,
|
correspondentLookupByName: correspondentsState.correspondentLookupByName,
|
||||||
handleCorrespondentCreate: correspondentsState.handleCorrespondentCreate,
|
handleCorrespondentCreate: correspondentsState.handleCorrespondentCreate,
|
||||||
selectedDocumentIds,
|
selectedDocumentIds,
|
||||||
selectedFolderIds,
|
selectedFolderIds,
|
||||||
@@ -874,7 +863,8 @@ const useDocumentsWorkspace = ({
|
|||||||
onTagDelete: async (tagId: string) => { await tagsState.handleTagDelete(tagId); },
|
onTagDelete: async (tagId: string) => { await tagsState.handleTagDelete(tagId); },
|
||||||
correspondents: correspondentsState.correspondents,
|
correspondents: correspondentsState.correspondents,
|
||||||
correspondentLookupById: correspondentsState.correspondentLookupById,
|
correspondentLookupById: correspondentsState.correspondentLookupById,
|
||||||
refreshCorrespondents: correspondentsState.refreshCorrespondents,
|
correspondentLookupByName: correspondentsState.correspondentLookupByName,
|
||||||
|
refreshCorrespondents,
|
||||||
onCorrespondentCreate: correspondentsState.handleCorrespondentCreate,
|
onCorrespondentCreate: correspondentsState.handleCorrespondentCreate,
|
||||||
onCorrespondentUpdate: correspondentsState.handleCorrespondentUpdate,
|
onCorrespondentUpdate: correspondentsState.handleCorrespondentUpdate,
|
||||||
onCorrespondentDelete: correspondentsState.handleCorrespondentDelete,
|
onCorrespondentDelete: correspondentsState.handleCorrespondentDelete,
|
||||||
@@ -903,21 +893,11 @@ const useDocumentsWorkspace = ({
|
|||||||
return () => window.removeEventListener('keydown', handleKeyDown);
|
return () => window.removeEventListener('keydown', handleKeyDown);
|
||||||
}, [settingsOpen]);
|
}, [settingsOpen]);
|
||||||
|
|
||||||
const {
|
const detailPanel = useDetailWorkspace({
|
||||||
detailPanelProps,
|
|
||||||
detailPanelOpen,
|
|
||||||
openDetailPanel,
|
|
||||||
previewActive,
|
|
||||||
previewWorkspaceDocument,
|
|
||||||
resolveFolderPath,
|
|
||||||
} = useDetailWorkspace({
|
|
||||||
documents: viewDocuments,
|
|
||||||
documentLookup,
|
documentLookup,
|
||||||
folderNodes,
|
folderNodes,
|
||||||
detailPanelControlRef,
|
detailPanelControlRef,
|
||||||
detailFolderFetchRef,
|
detailFolderFetchRef,
|
||||||
previewDocumentId,
|
|
||||||
activePreviewId,
|
|
||||||
openDocumentPreview: openDocumentPreviewForDetail,
|
openDocumentPreview: openDocumentPreviewForDetail,
|
||||||
handleDocumentTitleUpdate,
|
handleDocumentTitleUpdate,
|
||||||
handleDocumentIssuedUpdate,
|
handleDocumentIssuedUpdate,
|
||||||
@@ -932,6 +912,7 @@ const useDocumentsWorkspace = ({
|
|||||||
tags: tagsState.tags,
|
tags: tagsState.tags,
|
||||||
tagLookupById: tagsState.tagLookupById,
|
tagLookupById: tagsState.tagLookupById,
|
||||||
correspondentLookupById: correspondentsState.correspondentLookupById,
|
correspondentLookupById: correspondentsState.correspondentLookupById,
|
||||||
|
resolveFolderPath,
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleEntryPointerCore = useEntryPointerCore({
|
const handleEntryPointerCore = useEntryPointerCore({
|
||||||
@@ -985,7 +966,7 @@ const useDocumentsWorkspace = ({
|
|||||||
const tags = {
|
const tags = {
|
||||||
...tagsState,
|
...tagsState,
|
||||||
tagManager,
|
tagManager,
|
||||||
activeTagFilters,
|
activeTagFilters: documentsSearch.activeTagFilters,
|
||||||
// Add derived/action handlers that were previously in tagsContext
|
// Add derived/action handlers that were previously in tagsContext
|
||||||
handleDocumentTagAttach,
|
handleDocumentTagAttach,
|
||||||
handleDocumentTagDetach,
|
handleDocumentTagDetach,
|
||||||
@@ -996,7 +977,7 @@ const useDocumentsWorkspace = ({
|
|||||||
|
|
||||||
const correspondents = {
|
const correspondents = {
|
||||||
...correspondentsState,
|
...correspondentsState,
|
||||||
activeCorrespondentFilters,
|
activeCorrespondentFilters: documentsSearch.activeCorrespondentFilters,
|
||||||
// Add derived/action handlers
|
// Add derived/action handlers
|
||||||
handleDocumentCorrespondentAttach,
|
handleDocumentCorrespondentAttach,
|
||||||
handleDocumentCorrespondentDetach,
|
handleDocumentCorrespondentDetach,
|
||||||
@@ -1013,26 +994,19 @@ const useDocumentsWorkspace = ({
|
|||||||
closeDocumentPreview,
|
closeDocumentPreview,
|
||||||
ensureAssetUrl,
|
ensureAssetUrl,
|
||||||
getDocumentAsset,
|
getDocumentAsset,
|
||||||
handleThumbnailRegeneration,
|
|
||||||
};
|
|
||||||
|
|
||||||
const detailPanel = {
|
|
||||||
detailPanelProps,
|
|
||||||
detailPanelOpen,
|
|
||||||
openDetailPanel,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const search = {
|
const search = {
|
||||||
searchQuery,
|
searchQuery: documentsSearch.searchQuery,
|
||||||
documentsFilter,
|
documentsFilter,
|
||||||
searchLoading,
|
searchLoading: documentsSearch.searchLoading,
|
||||||
documentsViewMode,
|
documentsViewMode,
|
||||||
handleDocumentsViewModeChange,
|
handleDocumentsViewModeChange,
|
||||||
documentsSortField,
|
documentsSortField,
|
||||||
documentsSortDirection,
|
documentsSortDirection,
|
||||||
handleDocumentsSortFieldChange,
|
handleDocumentsSortFieldChange,
|
||||||
handleDocumentsSortDirectionToggle,
|
handleDocumentsSortDirectionToggle,
|
||||||
searchResultIds,
|
searchResultIds: documentsSearch.searchResultIds,
|
||||||
documents: viewDocuments,
|
documents: viewDocuments,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -22,8 +22,6 @@ export interface DocumentsManagerInterface {
|
|||||||
remove(ids: Array<DocumentId>): boolean;
|
remove(ids: Array<DocumentId>): boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
type CloseDocumentPreview = () => void;
|
|
||||||
|
|
||||||
export interface DocumentsState {
|
export interface DocumentsState {
|
||||||
documentLookup: Map<DocumentId, Document>;
|
documentLookup: Map<DocumentId, Document>;
|
||||||
setDocuments: Dispatch<SetStateAction<Document[]>>;
|
setDocuments: Dispatch<SetStateAction<Document[]>>;
|
||||||
@@ -62,16 +60,11 @@ export interface TagsState {
|
|||||||
export interface CorrespondentsState {
|
export interface CorrespondentsState {
|
||||||
correspondents: Correspondent[];
|
correspondents: Correspondent[];
|
||||||
correspondentLookupById: Map<Identifier, Correspondent>;
|
correspondentLookupById: Map<Identifier, Correspondent>;
|
||||||
|
correspondentLookupByName?: Map<string, Correspondent>;
|
||||||
refreshCorrespondents: () => Promise<void>;
|
refreshCorrespondents: () => Promise<void>;
|
||||||
correspondentManager: CorrespondentManager;
|
correspondentManager: CorrespondentManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ActionsState {
|
|
||||||
closeDocumentPreview: CloseDocumentPreview;
|
|
||||||
handleFileDrop?: (dataTransfer: DataTransfer, folderId: FolderId) => Promise<void> | void;
|
|
||||||
moveDocumentsToFolder?: (docIds: FolderId[], folderId: FolderId) => Promise<void>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface DragState {
|
export interface DragState {
|
||||||
draggedDocumentIds: DocumentId[];
|
draggedDocumentIds: DocumentId[];
|
||||||
draggedFolderId: FolderId | null;
|
draggedFolderId: FolderId | null;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useCallback, useEffect, useMemo } from 'react';
|
import { useCallback, useEffect } from 'react';
|
||||||
import type { MutableRefObject } from 'react';
|
import type { MutableRefObject } from 'react';
|
||||||
import { resolveDocumentAssetUrl } from '../../lib/assets/AssetManager';
|
import { resolveDocumentAssetUrl } from '../../lib/assets/AssetManager';
|
||||||
import { useDetailPanel } from '../../app/useDetailPanel';
|
import { useDetailPanel } from '../../app/useDetailPanel';
|
||||||
@@ -6,7 +6,6 @@ import type { DocumentInfoPanelProps } from '../components/DocumentInfoPanel';
|
|||||||
import type { EnsureAssetUrl, GetAsset } from '../../lib/assets/AssetManager';
|
import type { EnsureAssetUrl, GetAsset } from '../../lib/assets/AssetManager';
|
||||||
import type { Identifier } from '../../types/identifiers';
|
import type { Identifier } from '../../types/identifiers';
|
||||||
import type { Document } from '../../types/documents';
|
import type { Document } from '../../types/documents';
|
||||||
import { resolveBreadcrumbs } from '../../documents/logic/breadcrumbs';
|
|
||||||
import type { Tag, Correspondent } from '../../types/documents';
|
import type { Tag, Correspondent } from '../../types/documents';
|
||||||
import { listFolderContents } from '../../lib/api/apiClient';
|
import { listFolderContents } from '../../lib/api/apiClient';
|
||||||
import { useStatusToast } from '../../lib/context/StatusToastContext';
|
import { useStatusToast } from '../../lib/context/StatusToastContext';
|
||||||
@@ -19,13 +18,10 @@ interface FolderNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface UseDetailWorkspaceArgs {
|
interface UseDetailWorkspaceArgs {
|
||||||
documents: Document[];
|
|
||||||
documentLookup: Map<Identifier, Document>;
|
documentLookup: Map<Identifier, Document>;
|
||||||
folderNodes: Map<Identifier | 'root', FolderNode>;
|
folderNodes: Map<Identifier | 'root', FolderNode>;
|
||||||
detailPanelControlRef: MutableRefObject<{ open?: (documentId: Identifier) => void; close?: () => void } | null>;
|
detailPanelControlRef: MutableRefObject<{ open?: (documentId: Identifier) => void; close?: () => void } | null>;
|
||||||
detailFolderFetchRef: MutableRefObject<Set<Identifier | 'root'>>;
|
detailFolderFetchRef: MutableRefObject<Set<Identifier | 'root'>>;
|
||||||
previewDocumentId?: Identifier | null;
|
|
||||||
activePreviewId?: Identifier | null;
|
|
||||||
openDocumentPreview?: (args: { documentIds: Identifier[] }) => void;
|
openDocumentPreview?: (args: { documentIds: Identifier[] }) => void;
|
||||||
handleDocumentTitleUpdate?: (docId: Identifier, title: string) => Promise<boolean> | boolean;
|
handleDocumentTitleUpdate?: (docId: Identifier, title: string) => Promise<boolean> | boolean;
|
||||||
handleDocumentIssuedUpdate?: (docId: Identifier, issued: number | null) => Promise<boolean> | boolean;
|
handleDocumentIssuedUpdate?: (docId: Identifier, issued: number | null) => Promise<boolean> | boolean;
|
||||||
@@ -40,6 +36,7 @@ interface UseDetailWorkspaceArgs {
|
|||||||
tags?: unknown[];
|
tags?: unknown[];
|
||||||
tagLookupById?: Map<Identifier, Tag> | null;
|
tagLookupById?: Map<Identifier, Tag> | null;
|
||||||
correspondentLookupById?: Map<Identifier, Correspondent> | null;
|
correspondentLookupById?: Map<Identifier, Correspondent> | null;
|
||||||
|
resolveFolderPath?: (folderId?: Identifier | 'root') => Array<{ id: Identifier | 'root'; name: string }>;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface UseDetailWorkspaceResult {
|
interface UseDetailWorkspaceResult {
|
||||||
@@ -49,20 +46,15 @@ interface UseDetailWorkspaceResult {
|
|||||||
closeDetailPanel: ReturnType<typeof useDetailPanel>['closeDetailPanel'];
|
closeDetailPanel: ReturnType<typeof useDetailPanel>['closeDetailPanel'];
|
||||||
handleDetailPanelClose: () => void;
|
handleDetailPanelClose: () => void;
|
||||||
inspectDocument: (docId: Identifier | null) => void;
|
inspectDocument: (docId: Identifier | null) => void;
|
||||||
previewActive: boolean;
|
|
||||||
previewWorkspaceDocument: Document | null;
|
|
||||||
resolveThumbnailUrlForDoc: (doc: Document | null) => string | null;
|
resolveThumbnailUrlForDoc: (doc: Document | null) => string | null;
|
||||||
resolveFolderPath: (folderId?: Identifier | 'root') => Array<{ id: Identifier | 'root'; name: string }>;
|
resolveFolderPath: (folderId?: Identifier | 'root') => Array<{ id: Identifier | 'root'; name: string }>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const useDetailWorkspace = ({
|
const useDetailWorkspace = ({
|
||||||
documents,
|
|
||||||
documentLookup,
|
documentLookup,
|
||||||
folderNodes,
|
folderNodes,
|
||||||
detailPanelControlRef,
|
detailPanelControlRef,
|
||||||
detailFolderFetchRef,
|
detailFolderFetchRef,
|
||||||
previewDocumentId,
|
|
||||||
activePreviewId,
|
|
||||||
openDocumentPreview,
|
openDocumentPreview,
|
||||||
handleDocumentTitleUpdate,
|
handleDocumentTitleUpdate,
|
||||||
handleDocumentIssuedUpdate,
|
handleDocumentIssuedUpdate,
|
||||||
@@ -77,6 +69,7 @@ const useDetailWorkspace = ({
|
|||||||
tags,
|
tags,
|
||||||
tagLookupById,
|
tagLookupById,
|
||||||
correspondentLookupById,
|
correspondentLookupById,
|
||||||
|
resolveFolderPath,
|
||||||
}: UseDetailWorkspaceArgs): UseDetailWorkspaceResult => {
|
}: UseDetailWorkspaceArgs): UseDetailWorkspaceResult => {
|
||||||
const { showToast } = useStatusToast();
|
const { showToast } = useStatusToast();
|
||||||
const notifyApiError = useNotifyApiError();
|
const notifyApiError = useNotifyApiError();
|
||||||
@@ -97,12 +90,6 @@ const useDetailWorkspace = ({
|
|||||||
};
|
};
|
||||||
}, [detailPanelControlRef, openDetailPanel, closeDetailPanel]);
|
}, [detailPanelControlRef, openDetailPanel, closeDetailPanel]);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (detailPanelOpen && activePreviewId) {
|
|
||||||
openDetailPanel(activePreviewId);
|
|
||||||
}
|
|
||||||
}, [detailPanelOpen, activePreviewId, openDetailPanel]);
|
|
||||||
|
|
||||||
// Prefetch folder ancestors for breadcrumb display
|
// Prefetch folder ancestors for breadcrumb display
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const folderId = detailPanelDocument?.folder_id;
|
const folderId = detailPanelDocument?.folder_id;
|
||||||
@@ -144,24 +131,6 @@ const useDetailWorkspace = ({
|
|||||||
}
|
}
|
||||||
}, [detailPanelDocument, folderNodes, detailFolderFetchRef]);
|
}, [detailPanelDocument, folderNodes, detailFolderFetchRef]);
|
||||||
|
|
||||||
const resolveFolderPath = useCallback(
|
|
||||||
(folderId) => {
|
|
||||||
return resolveBreadcrumbs(folderId || 'root', folderNodes as any);
|
|
||||||
},
|
|
||||||
[folderNodes],
|
|
||||||
);
|
|
||||||
|
|
||||||
const previewWorkspaceDocument = useMemo(() => {
|
|
||||||
if (!previewDocumentId) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return documentLookup.get(previewDocumentId)
|
|
||||||
|| documents.find((doc) => doc.id === previewDocumentId)
|
|
||||||
|| null;
|
|
||||||
}, [previewDocumentId, documentLookup, documents]);
|
|
||||||
|
|
||||||
const previewActive = Boolean(previewDocumentId && previewWorkspaceDocument);
|
|
||||||
|
|
||||||
const resolveThumbnailUrlForDoc = useCallback(
|
const resolveThumbnailUrlForDoc = useCallback(
|
||||||
(doc) =>
|
(doc) =>
|
||||||
resolveDocumentAssetUrl(doc, 'thumbnail', {
|
resolveDocumentAssetUrl(doc, 'thumbnail', {
|
||||||
@@ -219,7 +188,6 @@ const useDetailWorkspace = ({
|
|||||||
onTagAdd: handleDetailTagAdd,
|
onTagAdd: handleDetailTagAdd,
|
||||||
onTagRemove: handleDetailTagRemove,
|
onTagRemove: handleDetailTagRemove,
|
||||||
onOpenPreview: openDocumentPreview,
|
onOpenPreview: openDocumentPreview,
|
||||||
activePreviewId,
|
|
||||||
onUpdateTitle: handleDocumentTitleUpdate,
|
onUpdateTitle: handleDocumentTitleUpdate,
|
||||||
onUpdateIssued: handleDocumentIssuedUpdate,
|
onUpdateIssued: handleDocumentIssuedUpdate,
|
||||||
ensureAssetUrl,
|
ensureAssetUrl,
|
||||||
@@ -240,8 +208,6 @@ const useDetailWorkspace = ({
|
|||||||
closeDetailPanel,
|
closeDetailPanel,
|
||||||
handleDetailPanelClose,
|
handleDetailPanelClose,
|
||||||
inspectDocument,
|
inspectDocument,
|
||||||
previewActive,
|
|
||||||
previewWorkspaceDocument,
|
|
||||||
resolveThumbnailUrlForDoc,
|
resolveThumbnailUrlForDoc,
|
||||||
resolveFolderPath,
|
resolveFolderPath,
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user