apiClient

This commit is contained in:
2025-11-22 15:30:08 +01:00
parent 530218a4b8
commit d3c76bc303
11 changed files with 319 additions and 169 deletions
@@ -2,6 +2,17 @@ import { useCallback } from 'react';
import { isPlainObject } from '../../utils/typeGuards';
import type { Dispatch, MutableRefObject, SetStateAction } from 'react';
import { DEFAULT_FOLDER_NAME, getRowId, isDocumentRowKey } from '../../app/appLayoutUtils';
import {
addDocumentTags,
createTag,
deleteDocumentTag,
deleteFolder,
moveDocumentsBulk,
moveDocumentToFolder,
queueDocumentReanalysis,
trashDocument,
updateDocument,
} from '../../lib/apiClient';
type DocumentId = string | number;
type FolderId = DocumentId | 'root';
@@ -35,12 +46,6 @@ type NotifyApiError = (error: unknown, fallbackMessage?: string) => void;
type SetStatusMessage = (message: string, level?: StatusLevel) => void;
interface ApiClient {
post<T = unknown>(url: string, data?: unknown, config?: Record<string, unknown>): Promise<{ data: T }>;
patch<T = unknown>(url: string, data?: unknown, config?: Record<string, unknown>): Promise<{ data: T }>;
delete<T = unknown>(url: string, config?: Record<string, unknown>): Promise<{ data: T }>;
}
interface Tag {
id: DocumentId;
label: string;
@@ -105,7 +110,6 @@ interface FolderDeleteOptions {
}
interface UseDocumentMutationsArgs {
api: ApiClient;
token?: string | null;
documentLookup: Map<DocumentId, DocumentLike>;
folderLabelMap: Map<FolderId, string>;
@@ -181,7 +185,6 @@ const normalizeDocumentId = (value: unknown): DocumentId | null => {
};
const useDocumentMutations = ({
api,
token,
documentLookup,
folderLabelMap,
@@ -286,12 +289,9 @@ const useDocumentMutations = ({
setLoading(true);
try {
if (uniqueIds.length === 1) {
await api.patch(`/documents/${uniqueIds[0]}/folder`, { folder_id: target });
await moveDocumentToFolder(uniqueIds[0], target);
} else {
await api.post('/documents/bulk/move', {
document_ids: uniqueIds,
folder_id: target,
});
await moveDocumentsBulk(uniqueIds, target);
}
const count = uniqueIds.length;
@@ -382,7 +382,6 @@ const useDocumentMutations = ({
}
},
[
api,
documentLookup,
folderLabelMap,
ensureFolderData,
@@ -413,9 +412,7 @@ const useDocumentMutations = ({
}
setLoading(true);
try {
await api.post(`/documents/${documentId}/assets`, null, {
params: { force: true },
});
await queueDocumentReanalysis(documentId, { force: true });
setStatusMessage('Document re-analysis queued.', 'info');
await refreshCurrentFolder();
} catch (error) {
@@ -425,7 +422,7 @@ const useDocumentMutations = ({
setLoading(false);
}
},
[api, token, refreshCurrentFolder, notifyApiError, setStatusMessage, setLoading],
[token, refreshCurrentFolder, notifyApiError, setStatusMessage, setLoading],
);
const handleDocumentsDelete = useCallback(
@@ -444,7 +441,7 @@ const useDocumentMutations = ({
}
try {
await Promise.all(documentIds.map((documentId) => api.post(`/documents/${documentId}/trash`)));
await Promise.all(documentIds.map((documentId) => trashDocument(documentId)));
removeDocumentsFromCaches(documentIds);
@@ -468,7 +465,6 @@ const useDocumentMutations = ({
}
},
[
api,
token,
removeDocumentsFromCaches,
previewDocumentId,
@@ -489,7 +485,7 @@ const useDocumentMutations = ({
setLoading(true);
try {
const { data } = await api.patch(`/documents/${documentId}`, { title: trimmed });
const data = await updateDocument(documentId, { title: trimmed });
const updatedDocument = extractDocumentFromResponse?.(data);
if (updatedDocument && ingestDocuments) {
@@ -514,7 +510,6 @@ const useDocumentMutations = ({
}
},
[
api,
extractDocumentFromResponse,
ingestDocuments,
notifyApiError,
@@ -529,7 +524,7 @@ const useDocumentMutations = ({
setLoading(true);
const payload = { issued_at: nextIssuedDate || null };
try {
const { data } = await api.patch(`/documents/${documentId}`, payload);
const data = await updateDocument(documentId, payload);
const updatedDocument = extractDocumentFromResponse?.(data);
if (updatedDocument && ingestDocuments) {
@@ -555,7 +550,6 @@ const useDocumentMutations = ({
}
},
[
api,
extractDocumentFromResponse,
ingestDocuments,
notifyApiError,
@@ -584,7 +578,7 @@ const useDocumentMutations = ({
};
try {
await api.post(`/documents/${documentId}/tags`, { tag_ids: [cachedTag.id] });
await addDocumentTags(documentId, [cachedTag.id]);
updateDocumentCaches(documentId, (doc) => {
if (!doc) {
return doc;
@@ -603,7 +597,7 @@ const useDocumentMutations = ({
return false;
}
},
[api, notifyApiError, setStatusMessage, updateDocumentCaches],
[notifyApiError, setStatusMessage, updateDocumentCaches],
);
const handleDocumentTagAdd = useCallback(
@@ -621,8 +615,8 @@ const useDocumentMutations = ({
}
try {
if (!tag) {
const payload = tagManager.buildPayload({ label: normalizedLabel });
const { data } = await api.post('/tags', payload);
const payload = tagManager.buildPayload({ label: normalizedLabel }) as { label: string; color?: string | null };
const data = await createTag(payload);
tag = data as Tag;
await refreshTags();
}
@@ -637,7 +631,7 @@ const useDocumentMutations = ({
notifyApiError(error, 'Failed to assign tag.');
}
},
[api, tags, refreshTags, attachTagToDocument, notifyApiError, tagManager],
[tags, refreshTags, attachTagToDocument, notifyApiError, tagManager],
);
const handleDocumentTagAttach = useCallback(
@@ -706,7 +700,7 @@ const useDocumentMutations = ({
}
try {
await api.delete(`/documents/${documentId}/tags/${tagId}`);
await deleteDocumentTag(documentId, tagId);
applyTagRemovalToCaches(documentId, tagId);
if (refreshTagList) {
await refreshTags();
@@ -721,7 +715,7 @@ const useDocumentMutations = ({
return false;
}
},
[api, applyTagRemovalToCaches, notifyApiError, refreshTags, setStatusMessage],
[applyTagRemovalToCaches, notifyApiError, refreshTags, setStatusMessage],
);
const handleFolderDelete = useCallback(
@@ -757,7 +751,7 @@ const useDocumentMutations = ({
return false;
}
await api.delete(`/folders/${folderId}`);
await deleteFolder(folderId);
setFolderNodes((prev: Map<FolderId, FolderNode>) => {
const next = new Map<FolderId, FolderNode>(prev);
@@ -815,7 +809,6 @@ const useDocumentMutations = ({
}
},
[
api,
token,
ensureFolderData,
selectedFolder,
@@ -566,7 +566,6 @@ const useDocumentsWorkspace = ({
registerPasskey,
revokePasskey,
} = usePasskeys({
api,
notifyApiError,
setStatusMessage,
token,
@@ -815,7 +814,6 @@ const useDocumentsWorkspace = ({
handleDocumentIssuedUpdate,
handleTagRemove,
} = useDocumentMutations({
api,
token,
documentLookup,
folderLabelMap,
@@ -861,7 +859,6 @@ const useDocumentsWorkspace = ({
handleFolderDelete,
folderClickHandlers,
} = useFolderTreeActions({
api,
token,
folderNodes,
setFolderNodes,
@@ -996,7 +993,6 @@ const useDocumentsWorkspace = ({
handleBulkCorrespondentRemove,
handleDeleteSelection,
} = useBulkDocumentActions({
api,
resolveTargetDocumentIds,
correspondentLookupByName,
handleCorrespondentCreate,
@@ -1,6 +1,12 @@
import { useCallback, useMemo } from 'react';
import type { DragEvent } from 'react';
import { DEFAULT_FOLDER_NAME, hasFiles } from '../../app/appLayoutUtils';
import {
createFolder,
deleteFolder,
moveFolder as moveFolderRequest,
renameFolder as renameFolderRequest,
} from '../../lib/apiClient';
type FolderId = string | number;
type FolderKey = FolderId | 'root';
@@ -22,12 +28,6 @@ interface FolderContentsState {
[key: string]: unknown;
}
interface ApiClient {
patch: (url: string, data?: unknown) => Promise<any>;
post: (url: string, data?: unknown) => Promise<{ data: any }>;
delete: (url: string) => Promise<any>;
}
interface EnsureFolderOptions {
force?: boolean;
includeDocuments?: boolean;
@@ -53,7 +53,6 @@ interface FolderClickHandlers {
}
interface UseFolderTreeActionsOptions {
api: ApiClient;
token?: string | null;
folderNodes: Map<FolderKey, FolderNode>;
setFolderNodes: (updater: (prev: Map<FolderKey, FolderNode>) => Map<FolderKey, FolderNode>) => void;
@@ -84,7 +83,6 @@ interface UseFolderTreeActionsOptions {
}
const useFolderTreeActions = ({
api,
token,
folderNodes,
setFolderNodes,
@@ -129,7 +127,7 @@ const useFolderTreeActions = ({
const parent_id = targetKey === 'root' ? null : targetKey;
try {
await api.patch(`/folders/${folderId}`, { parent_id });
await moveFolderRequest(folderId, parent_id);
setFolderNodes((prev) => {
const next = new Map(prev);
@@ -202,7 +200,6 @@ const useFolderTreeActions = ({
}
},
[
api,
ensureFolderData,
folderNodes,
notifyApiError,
@@ -295,7 +292,7 @@ const useFolderTreeActions = ({
setLoading(true);
try {
await api.patch(`/folders/${folderId}`, { name: trimmed });
await renameFolderRequest(folderId, trimmed);
setFolderNodes((prev) => {
const next = new Map(prev);
@@ -331,7 +328,6 @@ const useFolderTreeActions = ({
}
},
[
api,
notifyApiError,
setCurrentFolder,
setFolderContents,
@@ -359,33 +355,37 @@ const useFolderTreeActions = ({
setCreatingFolder(true);
let succeeded = false;
try {
const { data } = await api.post('/folders', payload);
const data = await createFolder(payload);
const folderData = (data as { folder?: { id?: FolderKey; name?: string; parent_id?: FolderKey | null; children?: FolderKey[] } }).folder;
if (!folderData?.id) {
throw new Error('Folder creation failed.');
}
setStatusMessage('Folder created.', 'success');
setFolderNodes((prev) => {
const next = new Map(prev);
const parentId = payload.parent_id || 'root';
const parentId = folderData.parent_id ?? payload.parent_id ?? 'root';
const parentNode = next.get(parentId);
if (parentNode) {
next.set(parentId, {
...parentNode,
children: parentNode.children.concat([data.folder.id]),
children: parentNode.children.concat([folderData.id]),
loaded: true,
hasChildren: true,
});
}
next.set(data.folder.id, {
id: data.folder.id,
name: data.folder.name,
next.set(folderData.id, {
id: folderData.id,
name: folderData.name ?? payload.name,
parentId: parentId,
children: [],
children: folderData.children || [],
expanded: false,
loaded: false,
hasChildren: false,
hasChildren: Array.isArray(folderData.children) ? folderData.children.length > 0 : false,
});
return next;
});
await ensureFolderData(selectedFolder, { force: true, prefetchDepth: 1 });
await selectFolder(data.folder.id, { immediate: true });
await selectFolder(folderData.id, { immediate: true });
succeeded = true;
return true;
} catch (error) {
@@ -400,7 +400,6 @@ const useFolderTreeActions = ({
}
},
[
api,
ensureFolderData,
notifyApiError,
selectFolder,
@@ -445,7 +444,7 @@ const useFolderTreeActions = ({
return false;
}
await api.delete(`/folders/${folderId}`);
await deleteFolder(folderId);
setFolderNodes((prev) => {
const next = new Map(prev);
@@ -503,7 +502,6 @@ const useFolderTreeActions = ({
}
},
[
api,
token,
applySelectedFolder,
ensureFolderData,