refactor: centralize frontend status message handling with useStatusToast
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||||
import type { ReactNode } from 'react';
|
import type { ReactNode } from 'react';
|
||||||
|
import { useStatusToast } from '../lib/context/StatusToastContext';
|
||||||
import TagsPanel from '../tags/TagsPanel';
|
import TagsPanel from '../tags/TagsPanel';
|
||||||
import CorrespondentsPanel, { CorrespondentsPanelProps } from '../correspondents/CorrespondentsPanel';
|
import CorrespondentsPanel, { CorrespondentsPanelProps } from '../correspondents/CorrespondentsPanel';
|
||||||
import PanelHeader from '../components/PanelHeader';
|
import PanelHeader from '../components/PanelHeader';
|
||||||
@@ -30,7 +31,6 @@ interface UseManagementModalsArgs {
|
|||||||
onCorrespondentCreate?: (...args: any[]) => void | Promise<void>;
|
onCorrespondentCreate?: (...args: any[]) => void | Promise<void>;
|
||||||
onCorrespondentUpdate?: (...args: any[]) => void | Promise<void>;
|
onCorrespondentUpdate?: (...args: any[]) => void | Promise<void>;
|
||||||
onCorrespondentDelete?: (...args: any[]) => void | Promise<void>;
|
onCorrespondentDelete?: (...args: any[]) => void | Promise<void>;
|
||||||
setStatusMessage?: (message: string, variant?: string) => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface UseManagementModalsResult {
|
interface UseManagementModalsResult {
|
||||||
@@ -53,8 +53,8 @@ export const useManagementModals = ({
|
|||||||
onCorrespondentCreate,
|
onCorrespondentCreate,
|
||||||
onCorrespondentUpdate,
|
onCorrespondentUpdate,
|
||||||
onCorrespondentDelete,
|
onCorrespondentDelete,
|
||||||
setStatusMessage,
|
|
||||||
}: UseManagementModalsArgs): UseManagementModalsResult => {
|
}: UseManagementModalsArgs): UseManagementModalsResult => {
|
||||||
|
const { showToast } = useStatusToast();
|
||||||
const [activeModal, setActiveModal] = useState<string | null>(null);
|
const [activeModal, setActiveModal] = useState<string | null>(null);
|
||||||
|
|
||||||
const openTagsModal = useCallback(() => setActiveModal(TAGS_MODAL), []);
|
const openTagsModal = useCallback(() => setActiveModal(TAGS_MODAL), []);
|
||||||
@@ -117,7 +117,7 @@ export const useManagementModals = ({
|
|||||||
onCreateTag={onTagCreate}
|
onCreateTag={onTagCreate}
|
||||||
onUpdateTag={onTagUpdate}
|
onUpdateTag={onTagUpdate}
|
||||||
onDeleteTag={onTagDelete}
|
onDeleteTag={onTagDelete}
|
||||||
onNotify={setStatusMessage}
|
onNotify={showToast}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -130,7 +130,7 @@ export const useManagementModals = ({
|
|||||||
onTagDelete,
|
onTagDelete,
|
||||||
onTagUpdate,
|
onTagUpdate,
|
||||||
refreshTags,
|
refreshTags,
|
||||||
setStatusMessage,
|
showToast,
|
||||||
tags,
|
tags,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@@ -199,7 +199,7 @@ export const useManagementModals = ({
|
|||||||
onCreate={handleCorrespondentCreateSafe}
|
onCreate={handleCorrespondentCreateSafe}
|
||||||
onUpdate={handleCorrespondentUpdateSafe}
|
onUpdate={handleCorrespondentUpdateSafe}
|
||||||
onDelete={handleCorrespondentDeleteSafe}
|
onDelete={handleCorrespondentDeleteSafe}
|
||||||
onNotify={setStatusMessage}
|
onNotify={showToast}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -213,7 +213,7 @@ export const useManagementModals = ({
|
|||||||
handleCorrespondentDeleteSafe,
|
handleCorrespondentDeleteSafe,
|
||||||
handleCorrespondentUpdateSafe,
|
handleCorrespondentUpdateSafe,
|
||||||
refreshCorrespondents,
|
refreshCorrespondents,
|
||||||
setStatusMessage,
|
showToast,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const managementModals = (
|
const managementModals = (
|
||||||
|
|||||||
@@ -1,18 +1,16 @@
|
|||||||
import { useCallback, useEffect, useRef } from 'react';
|
import { useCallback, useEffect, useRef } from 'react';
|
||||||
import type { MutableRefObject } from 'react';
|
import type { MutableRefObject } from 'react';
|
||||||
import { clearAuthToken, logoutSession, refreshSession, setAuthToken } from '../../lib/api/apiClient';
|
import { clearAuthToken, logoutSession, refreshSession, setAuthToken } from '../../lib/api/apiClient';
|
||||||
|
import { useStatusToast } from '../../lib/context/StatusToastContext';
|
||||||
|
|
||||||
type AppStatus = string;
|
type AppStatus = string;
|
||||||
|
|
||||||
type AppDispatch = (action: { type: string; [key: string]: unknown }) => void;
|
type AppDispatch = (action: { type: string;[key: string]: unknown }) => void;
|
||||||
|
|
||||||
type SetStatusMessage = (message: string, variant?: string) => void;
|
|
||||||
|
|
||||||
interface UseAuthManagerArgs {
|
interface UseAuthManagerArgs {
|
||||||
token?: string | null;
|
token?: string | null;
|
||||||
appStatus: AppStatus;
|
appStatus: AppStatus;
|
||||||
appDispatch: AppDispatch;
|
appDispatch: AppDispatch;
|
||||||
setStatusMessage: SetStatusMessage;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface UseAuthManagerResult {
|
interface UseAuthManagerResult {
|
||||||
@@ -25,10 +23,10 @@ const useAuthManager = ({
|
|||||||
token,
|
token,
|
||||||
appStatus,
|
appStatus,
|
||||||
appDispatch,
|
appDispatch,
|
||||||
setStatusMessage,
|
|
||||||
}: UseAuthManagerArgs): UseAuthManagerResult => {
|
}: UseAuthManagerArgs): UseAuthManagerResult => {
|
||||||
const tokenRef = useRef<string | null>(token);
|
const tokenRef = useRef<string | null>(token);
|
||||||
const initialRefreshAttemptedRef = useRef(Boolean(token));
|
const initialRefreshAttemptedRef = useRef(Boolean(token));
|
||||||
|
const { showToast } = useStatusToast();
|
||||||
|
|
||||||
const refreshAccessToken = useCallback(async (): Promise<string> => {
|
const refreshAccessToken = useCallback(async (): Promise<string> => {
|
||||||
console.log('[Auth] Attempting to refresh access token…');
|
console.log('[Auth] Attempting to refresh access token…');
|
||||||
@@ -61,7 +59,7 @@ const useAuthManager = ({
|
|||||||
if (!token && !initialRefreshAttemptedRef.current && appStatus === 'logged-out') {
|
if (!token && !initialRefreshAttemptedRef.current && appStatus === 'logged-out') {
|
||||||
initialRefreshAttemptedRef.current = true;
|
initialRefreshAttemptedRef.current = true;
|
||||||
console.log('[Auth] Attempting refresh at startup');
|
console.log('[Auth] Attempting refresh at startup');
|
||||||
refreshAccessToken().catch(() => {});
|
refreshAccessToken().catch(() => { });
|
||||||
}
|
}
|
||||||
}, [token, appStatus, refreshAccessToken]);
|
}, [token, appStatus, refreshAccessToken]);
|
||||||
|
|
||||||
@@ -73,9 +71,9 @@ const useAuthManager = ({
|
|||||||
} finally {
|
} finally {
|
||||||
clearAuthToken();
|
clearAuthToken();
|
||||||
appDispatch({ type: 'LOGOUT' });
|
appDispatch({ type: 'LOGOUT' });
|
||||||
setStatusMessage('Logged out.', 'info');
|
showToast('Logged out.', 'info');
|
||||||
}
|
}
|
||||||
}, [appDispatch, setStatusMessage]);
|
}, [appDispatch, showToast]);
|
||||||
|
|
||||||
return { tokenRef, refreshAccessToken, handleLogout };
|
return { tokenRef, refreshAccessToken, handleLogout };
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
|
import { useStatusToast } from '../../lib/context/StatusToastContext';
|
||||||
import { assignCorrespondentsBulk } from '../../lib/api/apiClient';
|
import { assignCorrespondentsBulk } from '../../lib/api/apiClient';
|
||||||
import type { Identifier } from '../../types/identifiers';
|
import type { Identifier } from '../../types/identifiers';
|
||||||
import type { MessageOptions } from '../../types/documents';
|
import type { MessageOptions } from '../../types/documents';
|
||||||
@@ -16,7 +17,6 @@ interface UseBulkDocumentActionsArgs {
|
|||||||
resolveTargetDocumentIds: (ids?: Identifier[] | null) => Identifier[];
|
resolveTargetDocumentIds: (ids?: Identifier[] | null) => Identifier[];
|
||||||
correspondentLookupByName: Map<string, { id?: Identifier }>;
|
correspondentLookupByName: Map<string, { id?: Identifier }>;
|
||||||
handleCorrespondentCreate: (payload: { name: string }) => Promise<{ id?: Identifier } | null>;
|
handleCorrespondentCreate: (payload: { name: string }) => Promise<{ id?: Identifier } | null>;
|
||||||
setStatusMessage: (message: string, variant?: string) => void;
|
|
||||||
selectedDocumentIds?: Identifier[];
|
selectedDocumentIds?: Identifier[];
|
||||||
selectedFolderIds?: Identifier[];
|
selectedFolderIds?: Identifier[];
|
||||||
handleDocumentsDelete: (ids: Identifier[], options?: MessageOptions) => Promise<boolean>;
|
handleDocumentsDelete: (ids: Identifier[], options?: MessageOptions) => Promise<boolean>;
|
||||||
@@ -29,7 +29,6 @@ const useBulkDocumentActions = ({
|
|||||||
resolveTargetDocumentIds,
|
resolveTargetDocumentIds,
|
||||||
correspondentLookupByName,
|
correspondentLookupByName,
|
||||||
handleCorrespondentCreate,
|
handleCorrespondentCreate,
|
||||||
setStatusMessage,
|
|
||||||
selectedDocumentIds,
|
selectedDocumentIds,
|
||||||
selectedFolderIds,
|
selectedFolderIds,
|
||||||
handleDocumentsDelete,
|
handleDocumentsDelete,
|
||||||
@@ -37,16 +36,18 @@ const useBulkDocumentActions = ({
|
|||||||
clearDocumentSelection,
|
clearDocumentSelection,
|
||||||
updateDocumentCaches,
|
updateDocumentCaches,
|
||||||
}: UseBulkDocumentActionsArgs) => {
|
}: UseBulkDocumentActionsArgs) => {
|
||||||
|
const { showToast } = useStatusToast();
|
||||||
|
|
||||||
const handleBulkCorrespondentAdd = useCallback(
|
const handleBulkCorrespondentAdd = useCallback(
|
||||||
async ({ name, input, documentIds }: { name?: string; input?: HTMLInputElement | null; documentIds?: Identifier[] }) => {
|
async ({ name, input, documentIds }: { name?: string; input?: HTMLInputElement | null; documentIds?: Identifier[] }) => {
|
||||||
const trimmed = name?.trim?.() || '';
|
const trimmed = name?.trim?.() || '';
|
||||||
if (!trimmed) {
|
if (!trimmed) {
|
||||||
setStatusMessage('Correspondent name is required.', 'error');
|
showToast('Correspondent name is required.', 'error');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const targets = resolveTargetDocumentIds(documentIds);
|
const targets = resolveTargetDocumentIds(documentIds);
|
||||||
if (!targets.length) {
|
if (!targets.length) {
|
||||||
setStatusMessage('Select documents before assigning correspondents.', 'error');
|
showToast('Select documents before assigning correspondents.', 'error');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -60,7 +61,7 @@ const useBulkDocumentActions = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!target?.id) {
|
if (!target?.id) {
|
||||||
setStatusMessage('Unable to resolve correspondent.', 'error');
|
showToast('Unable to resolve correspondent.', 'error');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,12 +95,12 @@ const useBulkDocumentActions = ({
|
|||||||
const assignedSuffix = assigned === 1 ? '' : 's';
|
const assignedSuffix = assigned === 1 ? '' : 's';
|
||||||
if (removed > 0) {
|
if (removed > 0) {
|
||||||
const removedSuffix = removed === 1 ? '' : 's';
|
const removedSuffix = removed === 1 ? '' : 's';
|
||||||
setStatusMessage(
|
showToast(
|
||||||
`Correspondent assigned (${assigned}) and replaced ${removed} link${removedSuffix}.`,
|
`Correspondent assigned (${assigned}) and replaced ${removed} link${removedSuffix}.`,
|
||||||
'success',
|
'success',
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
setStatusMessage(
|
showToast(
|
||||||
`Correspondent assigned to ${assigned} document${assignedSuffix}.`,
|
`Correspondent assigned to ${assigned} document${assignedSuffix}.`,
|
||||||
'success',
|
'success',
|
||||||
);
|
);
|
||||||
@@ -113,7 +114,7 @@ const useBulkDocumentActions = ({
|
|||||||
correspondentLookupByName,
|
correspondentLookupByName,
|
||||||
handleCorrespondentCreate,
|
handleCorrespondentCreate,
|
||||||
resolveTargetDocumentIds,
|
resolveTargetDocumentIds,
|
||||||
setStatusMessage,
|
showToast,
|
||||||
updateDocumentCaches,
|
updateDocumentCaches,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@@ -121,14 +122,14 @@ const useBulkDocumentActions = ({
|
|||||||
const handleBulkCorrespondentRemove = useCallback(
|
const handleBulkCorrespondentRemove = useCallback(
|
||||||
async ({ assignments = [], documentIds }: { assignments?: CorrespondentAssignment[]; documentIds?: Identifier[] }) => {
|
async ({ assignments = [], documentIds }: { assignments?: CorrespondentAssignment[]; documentIds?: Identifier[] }) => {
|
||||||
if (!assignments.length) {
|
if (!assignments.length) {
|
||||||
setStatusMessage('Select a correspondent to remove.', 'error');
|
showToast('Select a correspondent to remove.', 'error');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const targets = resolveTargetDocumentIds(documentIds);
|
const targets = resolveTargetDocumentIds(documentIds);
|
||||||
|
|
||||||
if (!targets.length) {
|
if (!targets.length) {
|
||||||
setStatusMessage('Select documents before removing correspondents.', 'error');
|
showToast('Select documents before removing correspondents.', 'error');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,18 +163,18 @@ const useBulkDocumentActions = ({
|
|||||||
|
|
||||||
if (removed > 0) {
|
if (removed > 0) {
|
||||||
const removedSuffix = removed === 1 ? '' : 's';
|
const removedSuffix = removed === 1 ? '' : 's';
|
||||||
setStatusMessage(
|
showToast(
|
||||||
`Correspondent removed from ${removed} link${removedSuffix}.`,
|
`Correspondent removed from ${removed} link${removedSuffix}.`,
|
||||||
'success',
|
'success',
|
||||||
);
|
);
|
||||||
} else if (assigned > 0) {
|
} else if (assigned > 0) {
|
||||||
const assignedSuffix = assigned === 1 ? '' : 's';
|
const assignedSuffix = assigned === 1 ? '' : 's';
|
||||||
setStatusMessage(`Correspondent updated ${assigned} link${assignedSuffix}.`, 'info');
|
showToast(`Correspondent updated ${assigned} link${assignedSuffix}.`, 'info');
|
||||||
} else {
|
} else {
|
||||||
setStatusMessage('No correspondents changed.', 'info');
|
showToast('No correspondents changed.', 'info');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[resolveTargetDocumentIds, setStatusMessage, updateDocumentCaches],
|
[resolveTargetDocumentIds, showToast, updateDocumentCaches],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleDeleteSelection = useCallback(async () => {
|
const handleDeleteSelection = useCallback(async () => {
|
||||||
@@ -217,7 +218,7 @@ const useBulkDocumentActions = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!docsOk || !foldersOk) {
|
if (!docsOk || !foldersOk) {
|
||||||
setStatusMessage('Some items could not be deleted. Ensure folders are empty before deletion.', 'error');
|
showToast('Some items could not be deleted. Ensure folders are empty before deletion.', 'error');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -231,14 +232,14 @@ const useBulkDocumentActions = ({
|
|||||||
successParts.push(folderIds.length === 1 ? 'Folder deleted.' : 'Folders deleted.');
|
successParts.push(folderIds.length === 1 ? 'Folder deleted.' : 'Folders deleted.');
|
||||||
}
|
}
|
||||||
|
|
||||||
setStatusMessage(successParts.join(' '), 'success');
|
showToast(successParts.join(' '), 'success');
|
||||||
}, [
|
}, [
|
||||||
clearDocumentSelection,
|
clearDocumentSelection,
|
||||||
handleDocumentsDelete,
|
handleDocumentsDelete,
|
||||||
handleFolderDelete,
|
handleFolderDelete,
|
||||||
selectedDocumentIds,
|
selectedDocumentIds,
|
||||||
selectedFolderIds,
|
selectedFolderIds,
|
||||||
setStatusMessage,
|
showToast,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -1,22 +1,22 @@
|
|||||||
import { MutableRefObject, useCallback, useState } from 'react';
|
import { MutableRefObject, useCallback, useState } from 'react';
|
||||||
|
import { useStatusToast } from '../../lib/context/StatusToastContext';
|
||||||
import type { Correspondent } from '../../types/documents';
|
import type { Correspondent } from '../../types/documents';
|
||||||
|
|
||||||
import { listCorrespondents, createCorrespondent, updateCorrespondent, deleteCorrespondent } from '../../lib/api/apiClient';
|
import { listCorrespondents, createCorrespondent, updateCorrespondent, deleteCorrespondent } from '../../lib/api/apiClient';
|
||||||
|
|
||||||
interface UseCorrespondentsOptions {
|
interface UseCorrespondentsOptions {
|
||||||
notifyApiError: (error: unknown, fallback: string) => void;
|
notifyApiError: (error: unknown, fallback: string) => void;
|
||||||
setStatusMessage: (message: string, variant?: string) => void;
|
|
||||||
tenantIdRef: MutableRefObject<string | null>;
|
tenantIdRef: MutableRefObject<string | null>;
|
||||||
mapDocumentCaches?: (mapper: (doc: any) => any) => void;
|
mapDocumentCaches?: (mapper: (doc: any) => any) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const useCorrespondents = ({
|
const useCorrespondents = ({
|
||||||
notifyApiError,
|
notifyApiError,
|
||||||
setStatusMessage,
|
|
||||||
tenantIdRef,
|
tenantIdRef,
|
||||||
mapDocumentCaches,
|
mapDocumentCaches,
|
||||||
}: UseCorrespondentsOptions) => {
|
}: UseCorrespondentsOptions) => {
|
||||||
const [correspondents, setCorrespondents] = useState<Correspondent[]>([]);
|
const [correspondents, setCorrespondents] = useState<Correspondent[]>([]);
|
||||||
|
const { showToast } = useStatusToast();
|
||||||
|
|
||||||
const refreshCorrespondents = useCallback(async () => {
|
const refreshCorrespondents = useCallback(async () => {
|
||||||
const requestTenantId = tenantIdRef.current;
|
const requestTenantId = tenantIdRef.current;
|
||||||
@@ -56,7 +56,7 @@ const useCorrespondents = ({
|
|||||||
try {
|
try {
|
||||||
await updateCorrespondent(correspondentId, payload);
|
await updateCorrespondent(correspondentId, payload);
|
||||||
await refreshCorrespondents();
|
await refreshCorrespondents();
|
||||||
setStatusMessage('Correspondent updated.', 'success');
|
showToast('Correspondent updated.', 'success');
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const message = error.response?.data?.error || 'Failed to update correspondent.';
|
const message = error.response?.data?.error || 'Failed to update correspondent.';
|
||||||
@@ -64,7 +64,7 @@ const useCorrespondents = ({
|
|||||||
throw new Error(message);
|
throw new Error(message);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[notifyApiError, refreshCorrespondents, setStatusMessage],
|
[notifyApiError, refreshCorrespondents, showToast],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleCorrespondentCreate = useCallback(
|
const handleCorrespondentCreate = useCallback(
|
||||||
@@ -76,7 +76,7 @@ const useCorrespondents = ({
|
|||||||
try {
|
try {
|
||||||
const data = await createCorrespondent({ name: trimmed });
|
const data = await createCorrespondent({ name: trimmed });
|
||||||
await refreshCorrespondents();
|
await refreshCorrespondents();
|
||||||
setStatusMessage('Correspondent created.', 'success');
|
showToast('Correspondent created.', 'success');
|
||||||
return data;
|
return data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const message = error.response?.data?.error || 'Failed to create correspondent.';
|
const message = error.response?.data?.error || 'Failed to create correspondent.';
|
||||||
@@ -84,7 +84,7 @@ const useCorrespondents = ({
|
|||||||
throw new Error(message);
|
throw new Error(message);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[notifyApiError, refreshCorrespondents, setStatusMessage],
|
[notifyApiError, refreshCorrespondents, showToast],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleCorrespondentDelete = useCallback(
|
const handleCorrespondentDelete = useCallback(
|
||||||
@@ -110,7 +110,7 @@ const useCorrespondents = ({
|
|||||||
|
|
||||||
mapDocumentCaches?.(stripFromDoc);
|
mapDocumentCaches?.(stripFromDoc);
|
||||||
|
|
||||||
setStatusMessage('Correspondent deleted.', 'success');
|
showToast('Correspondent deleted.', 'success');
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const message = error.response?.data?.error || 'Failed to delete correspondent.';
|
const message = error.response?.data?.error || 'Failed to delete correspondent.';
|
||||||
@@ -118,7 +118,7 @@ const useCorrespondents = ({
|
|||||||
throw new Error(message);
|
throw new Error(message);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[mapDocumentCaches, notifyApiError, refreshCorrespondents, setStatusMessage],
|
[mapDocumentCaches, notifyApiError, refreshCorrespondents, showToast],
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import type { Dispatch, MutableRefObject, SetStateAction } from 'react';
|
import type { Dispatch, MutableRefObject, SetStateAction } from 'react';
|
||||||
|
import { useStatusToast } from '../../lib/context/StatusToastContext';
|
||||||
import { DEFAULT_FOLDER_NAME } from '../../app/workspaceUtils';
|
import { DEFAULT_FOLDER_NAME } from '../../app/workspaceUtils';
|
||||||
import { getEntryId, isDocumentEntry } from '../../app/entryKey';
|
import { getEntryId, isDocumentEntry } from '../../app/entryKey';
|
||||||
import {
|
import {
|
||||||
@@ -19,8 +20,6 @@ import type { Document, MessageOptions } from '../../types/documents';
|
|||||||
type FolderId = FolderIdentifier | 'root';
|
type FolderId = FolderIdentifier | 'root';
|
||||||
type NullableFolderId = FolderId | null;
|
type NullableFolderId = FolderId | null;
|
||||||
|
|
||||||
type StatusLevel = 'success' | 'error' | 'info' | string;
|
|
||||||
|
|
||||||
type DocumentCacheMapper = (
|
type DocumentCacheMapper = (
|
||||||
doc: Document | null,
|
doc: Document | null,
|
||||||
) => Document | null;
|
) => Document | null;
|
||||||
@@ -43,8 +42,6 @@ type CloseDocumentPreview = () => void;
|
|||||||
|
|
||||||
type NotifyApiError = (error: unknown, fallbackMessage?: string) => void;
|
type NotifyApiError = (error: unknown, fallbackMessage?: string) => void;
|
||||||
|
|
||||||
type SetStatusMessage = (message: string, level?: StatusLevel) => void;
|
|
||||||
|
|
||||||
interface Tag {
|
interface Tag {
|
||||||
id: DocumentId;
|
id: DocumentId;
|
||||||
label: string;
|
label: string;
|
||||||
@@ -94,7 +91,6 @@ interface UseDocumentMutationsArgs {
|
|||||||
setFocusedEntryKey: Dispatch<SetStateAction<string | null>>;
|
setFocusedEntryKey: Dispatch<SetStateAction<string | null>>;
|
||||||
focusedEntryKey: string | null;
|
focusedEntryKey: string | null;
|
||||||
notifyApiError: NotifyApiError;
|
notifyApiError: NotifyApiError;
|
||||||
setStatusMessage: SetStatusMessage;
|
|
||||||
mapDocumentCaches: MapDocumentCaches;
|
mapDocumentCaches: MapDocumentCaches;
|
||||||
folderNodes: Map<FolderId, FolderNode>;
|
folderNodes: Map<FolderId, FolderNode>;
|
||||||
setFolderNodes: Dispatch<SetStateAction<Map<FolderId, FolderNode>>>;
|
setFolderNodes: Dispatch<SetStateAction<Map<FolderId, FolderNode>>>;
|
||||||
@@ -165,7 +161,6 @@ const useDocumentMutations = ({
|
|||||||
setFocusedEntryKey,
|
setFocusedEntryKey,
|
||||||
focusedEntryKey,
|
focusedEntryKey,
|
||||||
notifyApiError,
|
notifyApiError,
|
||||||
setStatusMessage,
|
|
||||||
mapDocumentCaches,
|
mapDocumentCaches,
|
||||||
folderNodes,
|
folderNodes,
|
||||||
setFolderNodes,
|
setFolderNodes,
|
||||||
@@ -181,6 +176,8 @@ const useDocumentMutations = ({
|
|||||||
extractDocumentFromResponse,
|
extractDocumentFromResponse,
|
||||||
ingestDocuments,
|
ingestDocuments,
|
||||||
}: UseDocumentMutationsArgs): UseDocumentMutationsResult => {
|
}: UseDocumentMutationsArgs): UseDocumentMutationsResult => {
|
||||||
|
const { showToast } = useStatusToast();
|
||||||
|
|
||||||
const moveDocumentsToFolder = useCallback(
|
const moveDocumentsToFolder = useCallback(
|
||||||
async (documentIds: Array<DocumentId | Document>, targetFolderId?: NullableFolderId) => {
|
async (documentIds: Array<DocumentId | Document>, targetFolderId?: NullableFolderId) => {
|
||||||
const uniqueIds = Array.from(
|
const uniqueIds = Array.from(
|
||||||
@@ -254,7 +251,7 @@ const useDocumentMutations = ({
|
|||||||
|
|
||||||
const count = uniqueIds.length;
|
const count = uniqueIds.length;
|
||||||
const suffix = count === 1 ? '' : 's';
|
const suffix = count === 1 ? '' : 's';
|
||||||
setStatusMessage(`Moved ${count} document${suffix} to ${targetLabel}.`, 'success');
|
showToast(`Moved ${count} document${suffix} to ${targetLabel}.`, 'success');
|
||||||
|
|
||||||
if (updatedDocsMap.size) {
|
if (updatedDocsMap.size) {
|
||||||
mapDocumentCaches((doc) => {
|
mapDocumentCaches((doc) => {
|
||||||
@@ -335,7 +332,7 @@ const useDocumentMutations = ({
|
|||||||
setFocusedEntryKey,
|
setFocusedEntryKey,
|
||||||
focusedEntryKey,
|
focusedEntryKey,
|
||||||
notifyApiError,
|
notifyApiError,
|
||||||
setStatusMessage,
|
showToast,
|
||||||
mapDocumentCaches,
|
mapDocumentCaches,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@@ -343,19 +340,19 @@ const useDocumentMutations = ({
|
|||||||
const handleThumbnailRegeneration = useCallback(
|
const handleThumbnailRegeneration = useCallback(
|
||||||
async (documentId: DocumentId) => {
|
async (documentId: DocumentId) => {
|
||||||
if (!token) {
|
if (!token) {
|
||||||
setStatusMessage('Log in to manage assets.', 'error');
|
showToast('Log in to manage assets.', 'error');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
await queueDocumentReanalysis(documentId, { force: true });
|
await queueDocumentReanalysis(documentId, { force: true });
|
||||||
setStatusMessage('Document re-analysis queued.', 'info');
|
showToast('Document re-analysis queued.', 'info');
|
||||||
await refreshCurrentFolder();
|
await refreshCurrentFolder();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const message = (error as Record<string, any>)?.response?.data?.error || 'Failed to request thumbnail generation.';
|
const message = (error as Record<string, any>)?.response?.data?.error || 'Failed to request thumbnail generation.';
|
||||||
notifyApiError(error, message);
|
notifyApiError(error, message);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[token, refreshCurrentFolder, notifyApiError, setStatusMessage],
|
[token, refreshCurrentFolder, notifyApiError, showToast],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleDocumentsDelete = useCallback(
|
const handleDocumentsDelete = useCallback(
|
||||||
@@ -365,7 +362,7 @@ const useDocumentMutations = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!token) {
|
if (!token) {
|
||||||
setStatusMessage('Log in to manage documents.', 'error');
|
showToast('Log in to manage documents.', 'error');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -380,7 +377,7 @@ const useDocumentMutations = ({
|
|||||||
|
|
||||||
if (showMessage) {
|
if (showMessage) {
|
||||||
const message = documentIds.length === 1 ? 'Document deleted.' : 'Documents deleted.';
|
const message = documentIds.length === 1 ? 'Document deleted.' : 'Documents deleted.';
|
||||||
setStatusMessage(message, 'success');
|
showToast(message, 'success');
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -396,7 +393,7 @@ const useDocumentMutations = ({
|
|||||||
previewDocumentId,
|
previewDocumentId,
|
||||||
closeDocumentPreview,
|
closeDocumentPreview,
|
||||||
notifyApiError,
|
notifyApiError,
|
||||||
setStatusMessage,
|
showToast,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -404,7 +401,7 @@ const useDocumentMutations = ({
|
|||||||
async (documentId: DocumentId, nextTitle: string) => {
|
async (documentId: DocumentId, nextTitle: string) => {
|
||||||
const trimmed = nextTitle?.trim?.() || '';
|
const trimmed = nextTitle?.trim?.() || '';
|
||||||
if (!trimmed) {
|
if (!trimmed) {
|
||||||
setStatusMessage('Document title cannot be empty.', 'error');
|
showToast('Document title cannot be empty.', 'error');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
@@ -422,7 +419,7 @@ const useDocumentMutations = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
setStatusMessage('Document title updated.', 'success');
|
showToast('Document title updated.', 'success');
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const message = (error as Record<string, any>)?.response?.data?.error || 'Failed to update document title.';
|
const message = (error as Record<string, any>)?.response?.data?.error || 'Failed to update document title.';
|
||||||
@@ -434,7 +431,7 @@ const useDocumentMutations = ({
|
|||||||
extractDocumentFromResponse,
|
extractDocumentFromResponse,
|
||||||
ingestDocuments,
|
ingestDocuments,
|
||||||
notifyApiError,
|
notifyApiError,
|
||||||
setStatusMessage,
|
showToast,
|
||||||
updateDocumentCaches,
|
updateDocumentCaches,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@@ -458,7 +455,7 @@ const useDocumentMutations = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const message = payload.issued_at ? 'Issued date updated.' : 'Issued date cleared.';
|
const message = payload.issued_at ? 'Issued date updated.' : 'Issued date cleared.';
|
||||||
setStatusMessage(message, 'success');
|
showToast(message, 'success');
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const message = (error as Record<string, any>)?.response?.data?.error || 'Failed to update issued date.';
|
const message = (error as Record<string, any>)?.response?.data?.error || 'Failed to update issued date.';
|
||||||
@@ -470,7 +467,7 @@ const useDocumentMutations = ({
|
|||||||
extractDocumentFromResponse,
|
extractDocumentFromResponse,
|
||||||
ingestDocuments,
|
ingestDocuments,
|
||||||
notifyApiError,
|
notifyApiError,
|
||||||
setStatusMessage,
|
showToast,
|
||||||
updateDocumentCaches,
|
updateDocumentCaches,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@@ -505,7 +502,7 @@ const useDocumentMutations = ({
|
|||||||
}
|
}
|
||||||
return { ...doc, tags: [...currentTags, cachedTag] };
|
return { ...doc, tags: [...currentTags, cachedTag] };
|
||||||
});
|
});
|
||||||
setStatusMessage('Tag assigned.', 'success');
|
showToast('Tag assigned.', 'success');
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const message = (error as Record<string, any>)?.response?.data?.error || 'Failed to assign tag.';
|
const message = (error as Record<string, any>)?.response?.data?.error || 'Failed to assign tag.';
|
||||||
@@ -513,7 +510,7 @@ const useDocumentMutations = ({
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[notifyApiError, setStatusMessage, updateDocumentCaches],
|
[notifyApiError, showToast, updateDocumentCaches],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleDocumentTagAdd = useCallback(
|
const handleDocumentTagAdd = useCallback(
|
||||||
@@ -616,7 +613,7 @@ const useDocumentMutations = ({
|
|||||||
try {
|
try {
|
||||||
await deleteDocumentTag(documentId, tagId);
|
await deleteDocumentTag(documentId, tagId);
|
||||||
applyTagRemovalToCaches(documentId, tagId);
|
applyTagRemovalToCaches(documentId, tagId);
|
||||||
setStatusMessage('Tag removed.', 'success');
|
showToast('Tag removed.', 'success');
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const message = (error as Record<string, any>)?.response?.data?.error || 'Failed to remove tag.';
|
const message = (error as Record<string, any>)?.response?.data?.error || 'Failed to remove tag.';
|
||||||
@@ -624,20 +621,20 @@ const useDocumentMutations = ({
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[applyTagRemovalToCaches, notifyApiError, setStatusMessage],
|
[applyTagRemovalToCaches, notifyApiError, showToast],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleFolderDelete = useCallback(
|
const handleFolderDelete = useCallback(
|
||||||
async (folderId?: FolderId, { showMessage = true }: MessageOptions = {}) => {
|
async (folderId?: FolderId, { showMessage = true }: MessageOptions = {}) => {
|
||||||
if (!token) {
|
if (!token) {
|
||||||
if (showMessage) {
|
if (showMessage) {
|
||||||
setStatusMessage('Log in to manage folders.', 'error');
|
showToast('Log in to manage folders.', 'error');
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!folderId || folderId === 'root') {
|
if (!folderId || folderId === 'root') {
|
||||||
if (showMessage) {
|
if (showMessage) {
|
||||||
setStatusMessage('The root folder cannot be removed.', 'error');
|
showToast('The root folder cannot be removed.', 'error');
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -648,7 +645,7 @@ const useDocumentMutations = ({
|
|||||||
const hasDocs = (contents.documents || []).length > 0;
|
const hasDocs = (contents.documents || []).length > 0;
|
||||||
if (hasChildren || hasDocs) {
|
if (hasChildren || hasDocs) {
|
||||||
if (showMessage) {
|
if (showMessage) {
|
||||||
setStatusMessage('Folder must be empty before it can be deleted.', 'error');
|
showToast('Folder must be empty before it can be deleted.', 'error');
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -686,14 +683,14 @@ const useDocumentMutations = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (showMessage) {
|
if (showMessage) {
|
||||||
setStatusMessage('Folder deleted.', 'success');
|
showToast('Folder deleted.', 'success');
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const message = (error as Record<string, any>)?.response?.data?.error || 'Failed to delete folder.';
|
const message = (error as Record<string, any>)?.response?.data?.error || 'Failed to delete folder.';
|
||||||
notifyApiError(error, message);
|
notifyApiError(error, message);
|
||||||
if (showMessage) {
|
if (showMessage) {
|
||||||
setStatusMessage(message, 'error');
|
showToast(message, 'error');
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -706,7 +703,7 @@ const useDocumentMutations = ({
|
|||||||
setSelectedFolder,
|
setSelectedFolder,
|
||||||
setFolderNodes,
|
setFolderNodes,
|
||||||
notifyApiError,
|
notifyApiError,
|
||||||
setStatusMessage,
|
showToast,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ import {
|
|||||||
isDocumentEntry
|
isDocumentEntry
|
||||||
} from '../../app/entryKey';
|
} from '../../app/entryKey';
|
||||||
import useDocumentsSearch from '../../app/useDocumentsSearch';
|
import useDocumentsSearch from '../../app/useDocumentsSearch';
|
||||||
import { useStatusToast, type ToastVariant } from '../../lib/context/StatusToastContext';
|
import { useStatusToast } from '../../lib/context/StatusToastContext';
|
||||||
import useAuthManager from './useAuthManager';
|
import useAuthManager from './useAuthManager';
|
||||||
import useTenantManager from './useTenantManager';
|
import useTenantManager from './useTenantManager';
|
||||||
import useDocuments from './useDocuments';
|
import useDocuments from './useDocuments';
|
||||||
@@ -144,33 +144,26 @@ const useDocumentsWorkspace = ({
|
|||||||
? (tenantOptionsRaw as TenantOption[])
|
? (tenantOptionsRaw as TenantOption[])
|
||||||
: [];
|
: [];
|
||||||
const { showToast } = useStatusToast();
|
const { showToast } = useStatusToast();
|
||||||
const setStatusMessage = useCallback(
|
|
||||||
(message?: string | null, variant: ToastVariant = 'info') => {
|
|
||||||
if (message) {
|
|
||||||
showToast(message, variant);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[showToast],
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleApiReport = useCallback(
|
|
||||||
({ message, variant }) => setStatusMessage(message, variant),
|
|
||||||
[setStatusMessage],
|
|
||||||
);
|
|
||||||
const reportApiError = useApiError({
|
const reportApiError = useApiError({
|
||||||
onReport: handleApiReport,
|
onReport: useCallback(
|
||||||
|
({ message, variant }) => showToast(message, variant),
|
||||||
|
[showToast],
|
||||||
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
const notifyApiError = useCallback(
|
const notifyApiError = useCallback(
|
||||||
(error, fallbackMessage, variant = 'error') =>
|
(error, fallbackMessage, variant = 'error') =>
|
||||||
reportApiError(error, { message: fallbackMessage, variant }),
|
reportApiError(error, { message: fallbackMessage, variant }),
|
||||||
[reportApiError],
|
[reportApiError],
|
||||||
);
|
);
|
||||||
|
|
||||||
const [creatingFolder, setCreatingFolder] = useState(false);
|
const [creatingFolder, setCreatingFolder] = useState(false);
|
||||||
|
|
||||||
const { tokenRef, handleLogout } = useAuthManager({
|
const { tokenRef, handleLogout } = useAuthManager({
|
||||||
token,
|
token,
|
||||||
appStatus,
|
appStatus,
|
||||||
appDispatch,
|
appDispatch,
|
||||||
setStatusMessage,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@@ -514,9 +507,8 @@ const useDocumentsWorkspace = ({
|
|||||||
setTags,
|
setTags,
|
||||||
} = useTags({
|
} = useTags({
|
||||||
notifyApiError,
|
notifyApiError,
|
||||||
setStatusMessage,
|
|
||||||
tagManager,
|
|
||||||
tenantIdRef,
|
tenantIdRef,
|
||||||
|
tagManager,
|
||||||
setActiveTagFilters,
|
setActiveTagFilters,
|
||||||
mapDocumentCaches,
|
mapDocumentCaches,
|
||||||
});
|
});
|
||||||
@@ -544,7 +536,6 @@ const useDocumentsWorkspace = ({
|
|||||||
setCorrespondents,
|
setCorrespondents,
|
||||||
} = useCorrespondents({
|
} = useCorrespondents({
|
||||||
notifyApiError,
|
notifyApiError,
|
||||||
setStatusMessage,
|
|
||||||
tenantIdRef,
|
tenantIdRef,
|
||||||
mapDocumentCaches,
|
mapDocumentCaches,
|
||||||
});
|
});
|
||||||
@@ -558,7 +549,6 @@ const useDocumentsWorkspace = ({
|
|||||||
correspondents,
|
correspondents,
|
||||||
handleCorrespondentCreate,
|
handleCorrespondentCreate,
|
||||||
notifyApiError,
|
notifyApiError,
|
||||||
setStatusMessage,
|
|
||||||
updateDocumentCaches,
|
updateDocumentCaches,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -573,7 +563,6 @@ const useDocumentsWorkspace = ({
|
|||||||
revokePasskey,
|
revokePasskey,
|
||||||
} = usePasskeys({
|
} = usePasskeys({
|
||||||
notifyApiError,
|
notifyApiError,
|
||||||
setStatusMessage,
|
|
||||||
token,
|
token,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -606,7 +595,6 @@ const useDocumentsWorkspace = ({
|
|||||||
refreshTags,
|
refreshTags,
|
||||||
resolveTargetDocumentIds,
|
resolveTargetDocumentIds,
|
||||||
notifyApiError,
|
notifyApiError,
|
||||||
setStatusMessage,
|
|
||||||
updateDocumentCaches,
|
updateDocumentCaches,
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -625,7 +613,6 @@ const useDocumentsWorkspace = ({
|
|||||||
refreshCurrentFolder,
|
refreshCurrentFolder,
|
||||||
shellRef,
|
shellRef,
|
||||||
notifyApiError,
|
notifyApiError,
|
||||||
setStatusMessage,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -761,7 +748,6 @@ const useDocumentsWorkspace = ({
|
|||||||
setFocusedEntryKey,
|
setFocusedEntryKey,
|
||||||
focusedEntryKey,
|
focusedEntryKey,
|
||||||
notifyApiError,
|
notifyApiError,
|
||||||
setStatusMessage,
|
|
||||||
mapDocumentCaches,
|
mapDocumentCaches,
|
||||||
folderNodes,
|
folderNodes,
|
||||||
setFolderNodes,
|
setFolderNodes,
|
||||||
@@ -792,7 +778,6 @@ const useDocumentsWorkspace = ({
|
|||||||
selectedFolder,
|
selectedFolder,
|
||||||
setSelectedFolder,
|
setSelectedFolder,
|
||||||
notifyApiError,
|
notifyApiError,
|
||||||
setStatusMessage,
|
|
||||||
navigate,
|
navigate,
|
||||||
handleFileDrop,
|
handleFileDrop,
|
||||||
moveDocumentsToFolder,
|
moveDocumentsToFolder,
|
||||||
@@ -905,7 +890,6 @@ const useDocumentsWorkspace = ({
|
|||||||
resolveTargetDocumentIds,
|
resolveTargetDocumentIds,
|
||||||
correspondentLookupByName,
|
correspondentLookupByName,
|
||||||
handleCorrespondentCreate,
|
handleCorrespondentCreate,
|
||||||
setStatusMessage,
|
|
||||||
selectedDocumentIds,
|
selectedDocumentIds,
|
||||||
selectedFolderIds,
|
selectedFolderIds,
|
||||||
handleDocumentsDelete,
|
handleDocumentsDelete,
|
||||||
@@ -914,8 +898,6 @@ const useDocumentsWorkspace = ({
|
|||||||
updateDocumentCaches,
|
updateDocumentCaches,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const ensureAssetUrl = useCallback(
|
const ensureAssetUrl = useCallback(
|
||||||
async (documentId, asset, { force = false } = {}) => {
|
async (documentId, asset, { force = false } = {}) => {
|
||||||
if (!documentId || !asset?.id) {
|
if (!documentId || !asset?.id) {
|
||||||
@@ -942,8 +924,6 @@ const useDocumentsWorkspace = ({
|
|||||||
[assetManager, updateDocumentCaches, notifyApiError],
|
[assetManager, updateDocumentCaches, notifyApiError],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const handlePromptCreateFolder = useCallback(async (parentId?: Identifier | null) => {
|
const handlePromptCreateFolder = useCallback(async (parentId?: Identifier | null) => {
|
||||||
if (creatingFolder) {
|
if (creatingFolder) {
|
||||||
return;
|
return;
|
||||||
@@ -954,19 +934,19 @@ const useDocumentsWorkspace = ({
|
|||||||
}
|
}
|
||||||
const trimmed = input.trim();
|
const trimmed = input.trim();
|
||||||
if (!trimmed) {
|
if (!trimmed) {
|
||||||
setStatusMessage('Folder name cannot be empty.', 'error');
|
showToast('Folder name cannot be empty.', 'error');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setCreatingFolder(true);
|
setCreatingFolder(true);
|
||||||
try {
|
try {
|
||||||
const success = await handleFolderCreate(trimmed, parentId);
|
const success = await handleFolderCreate(trimmed, parentId);
|
||||||
if (!success) {
|
if (!success) {
|
||||||
setStatusMessage('Unable to create folder. Check the status message for details.', 'error');
|
showToast('Unable to create folder. Check the status message for details.', 'error');
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
setCreatingFolder(false);
|
setCreatingFolder(false);
|
||||||
}
|
}
|
||||||
}, [creatingFolder, handleFolderCreate, setStatusMessage]);
|
}, [creatingFolder, handleFolderCreate, showToast]);
|
||||||
|
|
||||||
const { managementModals, openTagsModal, openCorrespondentsModal } = useManagementModals({
|
const { managementModals, openTagsModal, openCorrespondentsModal } = useManagementModals({
|
||||||
locationPathname: location.pathname,
|
locationPathname: location.pathname,
|
||||||
@@ -980,7 +960,6 @@ const useDocumentsWorkspace = ({
|
|||||||
onCorrespondentCreate: handleCorrespondentCreate,
|
onCorrespondentCreate: handleCorrespondentCreate,
|
||||||
onCorrespondentUpdate: handleCorrespondentUpdate,
|
onCorrespondentUpdate: handleCorrespondentUpdate,
|
||||||
onCorrespondentDelete: handleCorrespondentDelete,
|
onCorrespondentDelete: handleCorrespondentDelete,
|
||||||
setStatusMessage,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const [settingsOpen, setSettingsOpen] = useState(false);
|
const [settingsOpen, setSettingsOpen] = useState(false);
|
||||||
@@ -1126,7 +1105,6 @@ const useDocumentsWorkspace = ({
|
|||||||
appDispatch,
|
appDispatch,
|
||||||
currentTenantId,
|
currentTenantId,
|
||||||
resetWorkspaceState,
|
resetWorkspaceState,
|
||||||
setStatusMessage,
|
|
||||||
notifyApiError,
|
notifyApiError,
|
||||||
refreshTags,
|
refreshTags,
|
||||||
refreshCorrespondents,
|
refreshCorrespondents,
|
||||||
@@ -1148,7 +1126,6 @@ const useDocumentsWorkspace = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const uiContext = {
|
const uiContext = {
|
||||||
setStatusMessage,
|
|
||||||
notifyApiError,
|
notifyApiError,
|
||||||
settingsOpen,
|
settingsOpen,
|
||||||
openSettings,
|
openSettings,
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { MutableRefObject, useCallback, useState } from 'react';
|
import { MutableRefObject, useCallback, useState } from 'react';
|
||||||
|
import { useStatusToast } from '../../lib/context/StatusToastContext';
|
||||||
import type { TagId, TenantId } from '../../types/identifiers';
|
import type { TagId, TenantId } from '../../types/identifiers';
|
||||||
import type { Tag } from '../../types/documents';
|
import type { Tag } from '../../types/documents';
|
||||||
|
|
||||||
@@ -11,7 +12,6 @@ interface TagManagerInterface {
|
|||||||
interface UseTagsOptions {
|
interface UseTagsOptions {
|
||||||
// apiClient removed
|
// apiClient removed
|
||||||
notifyApiError: (error: unknown, fallback: string) => void;
|
notifyApiError: (error: unknown, fallback: string) => void;
|
||||||
setStatusMessage: (message: string, variant?: string) => void;
|
|
||||||
tagManager: TagManagerInterface;
|
tagManager: TagManagerInterface;
|
||||||
tenantIdRef: MutableRefObject<TenantId | null>;
|
tenantIdRef: MutableRefObject<TenantId | null>;
|
||||||
setActiveTagFilters: (updater: (prev: Array<TagId>) => Array<TagId>) => void;
|
setActiveTagFilters: (updater: (prev: Array<TagId>) => Array<TagId>) => void;
|
||||||
@@ -21,13 +21,13 @@ interface UseTagsOptions {
|
|||||||
const useTags = ({
|
const useTags = ({
|
||||||
// apiClient removed
|
// apiClient removed
|
||||||
notifyApiError,
|
notifyApiError,
|
||||||
setStatusMessage,
|
|
||||||
tagManager,
|
tagManager,
|
||||||
tenantIdRef,
|
tenantIdRef,
|
||||||
setActiveTagFilters,
|
setActiveTagFilters,
|
||||||
mapDocumentCaches,
|
mapDocumentCaches,
|
||||||
}: UseTagsOptions) => {
|
}: UseTagsOptions) => {
|
||||||
const [tags, setTags] = useState<Tag[]>([]);
|
const [tags, setTags] = useState<Tag[]>([]);
|
||||||
|
const { showToast } = useStatusToast();
|
||||||
|
|
||||||
const refreshTags = useCallback(async () => {
|
const refreshTags = useCallback(async () => {
|
||||||
const requestTenantId = tenantIdRef.current;
|
const requestTenantId = tenantIdRef.current;
|
||||||
@@ -66,7 +66,7 @@ const useTags = ({
|
|||||||
try {
|
try {
|
||||||
await updateTag(tagId, payload);
|
await updateTag(tagId, payload);
|
||||||
await refreshTags();
|
await refreshTags();
|
||||||
setStatusMessage('Tag updated.', 'success');
|
showToast('Tag updated.', 'success');
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const message = error.response?.data?.error || 'Failed to update tag.';
|
const message = error.response?.data?.error || 'Failed to update tag.';
|
||||||
@@ -74,7 +74,7 @@ const useTags = ({
|
|||||||
throw new Error(message);
|
throw new Error(message);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[notifyApiError, refreshTags, setStatusMessage],
|
[notifyApiError, refreshTags, showToast],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleTagCreate = useCallback(
|
const handleTagCreate = useCallback(
|
||||||
@@ -83,14 +83,14 @@ const useTags = ({
|
|||||||
try {
|
try {
|
||||||
await createTag(payload);
|
await createTag(payload);
|
||||||
await refreshTags();
|
await refreshTags();
|
||||||
setStatusMessage('Tag created.', 'success');
|
showToast('Tag created.', 'success');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const message = error.response?.data?.error || 'Failed to create tag.';
|
const message = error.response?.data?.error || 'Failed to create tag.';
|
||||||
notifyApiError(error, message);
|
notifyApiError(error, message);
|
||||||
throw new Error(message);
|
throw new Error(message);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[notifyApiError, refreshTags, setStatusMessage, tagManager],
|
[notifyApiError, refreshTags, showToast, tagManager],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleTagDelete = useCallback(
|
const handleTagDelete = useCallback(
|
||||||
@@ -117,7 +117,7 @@ const useTags = ({
|
|||||||
mapDocumentCaches?.(stripTagFromDoc);
|
mapDocumentCaches?.(stripTagFromDoc);
|
||||||
|
|
||||||
await refreshTags();
|
await refreshTags();
|
||||||
setStatusMessage('Tag deleted.', 'success');
|
showToast('Tag deleted.', 'success');
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const message = error.response?.data?.error || 'Failed to delete tag.';
|
const message = error.response?.data?.error || 'Failed to delete tag.';
|
||||||
@@ -125,7 +125,7 @@ const useTags = ({
|
|||||||
throw new Error(message);
|
throw new Error(message);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[mapDocumentCaches, notifyApiError, refreshTags, setActiveTagFilters, setStatusMessage],
|
[mapDocumentCaches, notifyApiError, refreshTags, setActiveTagFilters, showToast],
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { MutableRefObject, useCallback } from 'react';
|
import { MutableRefObject, useCallback } from 'react';
|
||||||
import type { NavigateFunction } from 'react-router-dom';
|
import type { NavigateFunction } from 'react-router-dom';
|
||||||
import type { FolderId, TenantId } from '../../types/identifiers';
|
import type { FolderId, TenantId } from '../../types/identifiers';
|
||||||
|
import { useStatusToast } from '../../lib/context/StatusToastContext';
|
||||||
|
|
||||||
import { api, listTenants, switchTenant } from '../../lib/api/apiClient';
|
import { api, listTenants, switchTenant } from '../../lib/api/apiClient';
|
||||||
|
|
||||||
@@ -13,7 +14,6 @@ interface UseTenantManagerOptions {
|
|||||||
appDispatch: (action: any) => void;
|
appDispatch: (action: any) => void;
|
||||||
currentTenantId: TenantId | null;
|
currentTenantId: TenantId | null;
|
||||||
resetWorkspaceState: () => void;
|
resetWorkspaceState: () => void;
|
||||||
setStatusMessage: (message: string, variant?: string) => void;
|
|
||||||
notifyApiError: (error: unknown, message: string) => void;
|
notifyApiError: (error: unknown, message: string) => void;
|
||||||
refreshTags: () => Promise<void>;
|
refreshTags: () => Promise<void>;
|
||||||
refreshCorrespondents: () => Promise<void>;
|
refreshCorrespondents: () => Promise<void>;
|
||||||
@@ -28,7 +28,6 @@ const useTenantManager = ({
|
|||||||
appDispatch,
|
appDispatch,
|
||||||
currentTenantId,
|
currentTenantId,
|
||||||
resetWorkspaceState,
|
resetWorkspaceState,
|
||||||
setStatusMessage,
|
|
||||||
notifyApiError,
|
notifyApiError,
|
||||||
refreshTags,
|
refreshTags,
|
||||||
refreshCorrespondents,
|
refreshCorrespondents,
|
||||||
@@ -38,6 +37,7 @@ const useTenantManager = ({
|
|||||||
tokenRef,
|
tokenRef,
|
||||||
tenantIdRef,
|
tenantIdRef,
|
||||||
}: UseTenantManagerOptions) => {
|
}: UseTenantManagerOptions) => {
|
||||||
|
const { showToast } = useStatusToast();
|
||||||
const handleTenantSelect = useCallback(
|
const handleTenantSelect = useCallback(
|
||||||
async (tenantOption: TenantOption | null, { refreshOnly = false }: { refreshOnly?: boolean } = {}) => {
|
async (tenantOption: TenantOption | null, { refreshOnly = false }: { refreshOnly?: boolean } = {}) => {
|
||||||
const requestedTenantId = tenantOption?.id ?? null;
|
const requestedTenantId = tenantOption?.id ?? null;
|
||||||
@@ -88,7 +88,7 @@ const useTenantManager = ({
|
|||||||
await loadFolder('root', { preserveSearch: false });
|
await loadFolder('root', { preserveSearch: false });
|
||||||
|
|
||||||
const tenantLabel = data?.tenant?.name || data?.tenant?.id || 'tenant';
|
const tenantLabel = data?.tenant?.name || data?.tenant?.id || 'tenant';
|
||||||
setStatusMessage(`Switched to ${tenantLabel}.`, 'info');
|
showToast(`Switched to ${tenantLabel}.`, 'info');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
notifyApiError(error, 'Failed to switch tenant.');
|
notifyApiError(error, 'Failed to switch tenant.');
|
||||||
}
|
}
|
||||||
@@ -103,7 +103,7 @@ const useTenantManager = ({
|
|||||||
refreshCorrespondents,
|
refreshCorrespondents,
|
||||||
refreshTags,
|
refreshTags,
|
||||||
resetWorkspaceState,
|
resetWorkspaceState,
|
||||||
setStatusMessage,
|
showToast,
|
||||||
tenantIdRef,
|
tenantIdRef,
|
||||||
tokenRef,
|
tokenRef,
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { useCallback, useMemo } from 'react';
|
import { useCallback, useMemo } from 'react';
|
||||||
|
import { useStatusToast } from '../../../lib/context/StatusToastContext';
|
||||||
import type { Identifier } from '../../../types/identifiers';
|
import type { Identifier } from '../../../types/identifiers';
|
||||||
|
|
||||||
|
|
||||||
@@ -14,7 +15,6 @@ interface UseDocumentCorrespondentActionsArgs {
|
|||||||
correspondents: CorrespondentOption[];
|
correspondents: CorrespondentOption[];
|
||||||
handleCorrespondentCreate: (payload: { name: string }) => Promise<CorrespondentOption | null>;
|
handleCorrespondentCreate: (payload: { name: string }) => Promise<CorrespondentOption | null>;
|
||||||
notifyApiError: (error: unknown, fallback: string) => void;
|
notifyApiError: (error: unknown, fallback: string) => void;
|
||||||
setStatusMessage: (message: string, variant?: string) => void;
|
|
||||||
updateDocumentCaches?: (
|
updateDocumentCaches?: (
|
||||||
id: Identifier,
|
id: Identifier,
|
||||||
updater: (doc: { correspondents?: CorrespondentOption[] } | null) => { correspondents?: CorrespondentOption[] } | null,
|
updater: (doc: { correspondents?: CorrespondentOption[] } | null) => { correspondents?: CorrespondentOption[] } | null,
|
||||||
@@ -25,9 +25,10 @@ const useDocumentCorrespondentActions = ({
|
|||||||
correspondents,
|
correspondents,
|
||||||
handleCorrespondentCreate,
|
handleCorrespondentCreate,
|
||||||
notifyApiError,
|
notifyApiError,
|
||||||
setStatusMessage,
|
|
||||||
updateDocumentCaches,
|
updateDocumentCaches,
|
||||||
}: UseDocumentCorrespondentActionsArgs) => {
|
}: UseDocumentCorrespondentActionsArgs) => {
|
||||||
|
const { showToast } = useStatusToast();
|
||||||
|
|
||||||
const correspondentLookupByName = useMemo(() => {
|
const correspondentLookupByName = useMemo(() => {
|
||||||
const map = new Map<string, CorrespondentOption>();
|
const map = new Map<string, CorrespondentOption>();
|
||||||
correspondents.forEach((correspondent) => {
|
correspondents.forEach((correspondent) => {
|
||||||
@@ -71,7 +72,7 @@ const useDocumentCorrespondentActions = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (notify) {
|
if (notify) {
|
||||||
setStatusMessage('Correspondent assigned.', 'success');
|
showToast('Correspondent assigned.', 'success');
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -80,7 +81,7 @@ const useDocumentCorrespondentActions = ({
|
|||||||
throw new Error(message);
|
throw new Error(message);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[correspondents, notifyApiError, setStatusMessage, updateDocumentCaches],
|
[correspondents, notifyApiError, showToast, updateDocumentCaches],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleCorrespondentRemove = useCallback(
|
const handleCorrespondentRemove = useCallback(
|
||||||
@@ -103,7 +104,7 @@ const useDocumentCorrespondentActions = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (notify) {
|
if (notify) {
|
||||||
setStatusMessage('Correspondent removed.', 'success');
|
showToast('Correspondent removed.', 'success');
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -112,7 +113,7 @@ const useDocumentCorrespondentActions = ({
|
|||||||
throw new Error(message);
|
throw new Error(message);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[notifyApiError, setStatusMessage, updateDocumentCaches],
|
[notifyApiError, showToast, updateDocumentCaches],
|
||||||
);
|
);
|
||||||
|
|
||||||
const normalizeOption = (
|
const normalizeOption = (
|
||||||
@@ -138,7 +139,7 @@ const useDocumentCorrespondentActions = ({
|
|||||||
}
|
}
|
||||||
const trimmed = name?.trim?.() || '';
|
const trimmed = name?.trim?.() || '';
|
||||||
if (!trimmed) {
|
if (!trimmed) {
|
||||||
setStatusMessage('Correspondent name is required.', 'error');
|
showToast('Correspondent name is required.', 'error');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,7 +153,7 @@ const useDocumentCorrespondentActions = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!target?.id) {
|
if (!target?.id) {
|
||||||
setStatusMessage('Unable to resolve correspondent.', 'error');
|
showToast('Unable to resolve correspondent.', 'error');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,7 +167,7 @@ const useDocumentCorrespondentActions = ({
|
|||||||
input.value = '';
|
input.value = '';
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
setStatusMessage('Failed to assign correspondent.', 'error');
|
showToast('Failed to assign correspondent.', 'error');
|
||||||
console.error('[documents] assign correspondent failed', error);
|
console.error('[documents] assign correspondent failed', error);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -174,7 +175,7 @@ const useDocumentCorrespondentActions = ({
|
|||||||
correspondentLookupByName,
|
correspondentLookupByName,
|
||||||
handleCorrespondentCreate,
|
handleCorrespondentCreate,
|
||||||
handleDocumentCorrespondentAttach,
|
handleDocumentCorrespondentAttach,
|
||||||
setStatusMessage,
|
showToast,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { useCallback, useMemo } from 'react';
|
import { useCallback, useMemo } from 'react';
|
||||||
import type { DragEvent } from 'react';
|
import type { DragEvent } from 'react';
|
||||||
|
import { useStatusToast } from '../../../lib/context/StatusToastContext';
|
||||||
import { DEFAULT_FOLDER_NAME, hasFiles } from '../../../app/workspaceUtils';
|
import { DEFAULT_FOLDER_NAME, hasFiles } from '../../../app/workspaceUtils';
|
||||||
import {
|
import {
|
||||||
createFolder,
|
createFolder,
|
||||||
@@ -35,7 +36,6 @@ interface UseFolderTreeActionsOptions {
|
|||||||
selectedFolder: FolderKey;
|
selectedFolder: FolderKey;
|
||||||
setSelectedFolder: (folderId: FolderKey) => void;
|
setSelectedFolder: (folderId: FolderKey) => void;
|
||||||
notifyApiError: (error: unknown, message?: string) => void;
|
notifyApiError: (error: unknown, message?: string) => void;
|
||||||
setStatusMessage: (message: string, level?: string) => void;
|
|
||||||
navigate?: (path: string, options?: { replace?: boolean }) => void;
|
navigate?: (path: string, options?: { replace?: boolean }) => void;
|
||||||
handleFileDrop: (dataTransfer: DataTransfer, folderId: FolderKey) => Promise<void> | void;
|
handleFileDrop: (dataTransfer: DataTransfer, folderId: FolderKey) => Promise<void> | void;
|
||||||
moveDocumentsToFolder: (docIds: FolderId[], folderId: FolderKey) => Promise<void>;
|
moveDocumentsToFolder: (docIds: FolderId[], folderId: FolderKey) => Promise<void>;
|
||||||
@@ -54,7 +54,6 @@ const useFolderTreeActions = ({
|
|||||||
selectedFolder,
|
selectedFolder,
|
||||||
setSelectedFolder,
|
setSelectedFolder,
|
||||||
notifyApiError,
|
notifyApiError,
|
||||||
setStatusMessage,
|
|
||||||
navigate,
|
navigate,
|
||||||
handleFileDrop,
|
handleFileDrop,
|
||||||
moveDocumentsToFolder,
|
moveDocumentsToFolder,
|
||||||
@@ -65,11 +64,13 @@ const useFolderTreeActions = ({
|
|||||||
isInvalidFolderDrop,
|
isInvalidFolderDrop,
|
||||||
setCreatingFolder,
|
setCreatingFolder,
|
||||||
}: UseFolderTreeActionsOptions) => {
|
}: UseFolderTreeActionsOptions) => {
|
||||||
|
const { showToast } = useStatusToast();
|
||||||
|
|
||||||
const moveFolder = useCallback(
|
const moveFolder = useCallback(
|
||||||
async (folderId: FolderKey, targetFolderId: FolderKey | null) => {
|
async (folderId: FolderKey, targetFolderId: FolderKey | null) => {
|
||||||
const node = folderNodes.get(folderId);
|
const node = folderNodes.get(folderId);
|
||||||
if (!node) {
|
if (!node) {
|
||||||
setStatusMessage('Folder metadata unavailable. Try refreshing.', 'error');
|
showToast('Folder metadata unavailable. Try refreshing.', 'error');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,7 +135,7 @@ const useFolderTreeActions = ({
|
|||||||
setSelectedFolder(folderId);
|
setSelectedFolder(folderId);
|
||||||
}
|
}
|
||||||
|
|
||||||
setStatusMessage('Folder moved.', 'success');
|
showToast('Folder moved.', 'success');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const message = error.response?.data?.error || 'Failed to move folder.';
|
const message = error.response?.data?.error || 'Failed to move folder.';
|
||||||
notifyApiError(error, message);
|
notifyApiError(error, message);
|
||||||
@@ -146,7 +147,7 @@ const useFolderTreeActions = ({
|
|||||||
selectedFolder,
|
selectedFolder,
|
||||||
setFolderNodes,
|
setFolderNodes,
|
||||||
setSelectedFolder,
|
setSelectedFolder,
|
||||||
setStatusMessage,
|
showToast,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -180,12 +181,12 @@ const useFolderTreeActions = ({
|
|||||||
const handleFolderRename = useCallback(
|
const handleFolderRename = useCallback(
|
||||||
async (folderId: FolderKey, nextName: string) => {
|
async (folderId: FolderKey, nextName: string) => {
|
||||||
if (!token) {
|
if (!token) {
|
||||||
setStatusMessage('Log in to rename folders.', 'error');
|
showToast('Log in to rename folders.', 'error');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const trimmed = nextName?.trim?.() || '';
|
const trimmed = nextName?.trim?.() || '';
|
||||||
if (!trimmed) {
|
if (!trimmed) {
|
||||||
setStatusMessage('Folder name cannot be empty.', 'error');
|
showToast('Folder name cannot be empty.', 'error');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
@@ -200,7 +201,7 @@ const useFolderTreeActions = ({
|
|||||||
return next;
|
return next;
|
||||||
});
|
});
|
||||||
|
|
||||||
setStatusMessage('Folder renamed.', 'success');
|
showToast('Folder renamed.', 'success');
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const message = error.response?.data?.error || 'Failed to rename folder.';
|
const message = error.response?.data?.error || 'Failed to rename folder.';
|
||||||
@@ -211,7 +212,7 @@ const useFolderTreeActions = ({
|
|||||||
[
|
[
|
||||||
notifyApiError,
|
notifyApiError,
|
||||||
setFolderNodes,
|
setFolderNodes,
|
||||||
setStatusMessage,
|
showToast,
|
||||||
token,
|
token,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@@ -219,11 +220,11 @@ const useFolderTreeActions = ({
|
|||||||
const handleFolderCreate = useCallback(
|
const handleFolderCreate = useCallback(
|
||||||
async (name: string, parentId?: FolderKey | null) => {
|
async (name: string, parentId?: FolderKey | null) => {
|
||||||
if (!token) {
|
if (!token) {
|
||||||
setStatusMessage('Log in to create folders.', 'error');
|
showToast('Log in to create folders.', 'error');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!name.trim()) {
|
if (!name.trim()) {
|
||||||
setStatusMessage('Folder name cannot be empty.', 'error');
|
showToast('Folder name cannot be empty.', 'error');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -243,7 +244,7 @@ const useFolderTreeActions = ({
|
|||||||
if (!folderData?.id) {
|
if (!folderData?.id) {
|
||||||
throw new Error('Folder creation failed.');
|
throw new Error('Folder creation failed.');
|
||||||
}
|
}
|
||||||
setStatusMessage('Folder created.', 'success');
|
showToast('Folder created.', 'success');
|
||||||
setFolderNodes((prev) => {
|
setFolderNodes((prev) => {
|
||||||
const next = new Map(prev);
|
const next = new Map(prev);
|
||||||
const parentId = folderData.parent_id ?? payload.parent_id ?? 'root';
|
const parentId = folderData.parent_id ?? payload.parent_id ?? 'root';
|
||||||
@@ -278,7 +279,7 @@ const useFolderTreeActions = ({
|
|||||||
} finally {
|
} finally {
|
||||||
setCreatingFolder(false);
|
setCreatingFolder(false);
|
||||||
if (!succeeded) {
|
if (!succeeded) {
|
||||||
setStatusMessage('Folder creation failed.', 'error');
|
showToast('Folder creation failed.', 'error');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -288,7 +289,7 @@ const useFolderTreeActions = ({
|
|||||||
selectedFolder,
|
selectedFolder,
|
||||||
setCreatingFolder,
|
setCreatingFolder,
|
||||||
setFolderNodes,
|
setFolderNodes,
|
||||||
setStatusMessage,
|
showToast,
|
||||||
token,
|
token,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@@ -297,13 +298,13 @@ const useFolderTreeActions = ({
|
|||||||
async (folderId: FolderKey, { showMessage = true }: MessageOptions = {}) => {
|
async (folderId: FolderKey, { showMessage = true }: MessageOptions = {}) => {
|
||||||
if (!token) {
|
if (!token) {
|
||||||
if (showMessage) {
|
if (showMessage) {
|
||||||
setStatusMessage('Log in to manage folders.', 'error');
|
showToast('Log in to manage folders.', 'error');
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!folderId || folderId === 'root') {
|
if (!folderId || folderId === 'root') {
|
||||||
if (showMessage) {
|
if (showMessage) {
|
||||||
setStatusMessage('The root folder cannot be removed.', 'error');
|
showToast('The root folder cannot be removed.', 'error');
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -337,14 +338,14 @@ const useFolderTreeActions = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (showMessage) {
|
if (showMessage) {
|
||||||
setStatusMessage('Folder deleted.', 'success');
|
showToast('Folder deleted.', 'success');
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const message = error.response?.data?.error || 'Failed to delete folder.';
|
const message = error.response?.data?.error || 'Failed to delete folder.';
|
||||||
notifyApiError(error, message);
|
notifyApiError(error, message);
|
||||||
if (showMessage) {
|
if (showMessage) {
|
||||||
setStatusMessage(message, 'error');
|
showToast(message, 'error');
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -356,7 +357,7 @@ const useFolderTreeActions = ({
|
|||||||
selectedFolder,
|
selectedFolder,
|
||||||
setFolderNodes,
|
setFolderNodes,
|
||||||
setSelectedFolder,
|
setSelectedFolder,
|
||||||
setStatusMessage,
|
showToast,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -404,7 +405,7 @@ const useFolderTreeActions = ({
|
|||||||
setDraggedFolderId(null);
|
setDraggedFolderId(null);
|
||||||
const invalidMove = folderIds.some((sourceId) => isInvalidFolderDrop(sourceId, folderId));
|
const invalidMove = folderIds.some((sourceId) => isInvalidFolderDrop(sourceId, folderId));
|
||||||
if (invalidMove) {
|
if (invalidMove) {
|
||||||
setStatusMessage(
|
showToast(
|
||||||
'Cannot move a folder into itself or one of its descendants.',
|
'Cannot move a folder into itself or one of its descendants.',
|
||||||
'error',
|
'error',
|
||||||
);
|
);
|
||||||
@@ -492,7 +493,7 @@ const useFolderTreeActions = ({
|
|||||||
selectedFolder,
|
selectedFolder,
|
||||||
setDraggedDocumentIds,
|
setDraggedDocumentIds,
|
||||||
setDraggedFolderId,
|
setDraggedFolderId,
|
||||||
setStatusMessage,
|
showToast,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
|
import { useStatusToast } from '../../../lib/context/StatusToastContext';
|
||||||
|
|
||||||
import type { Identifier } from '../../../types/identifiers';
|
import type { Identifier } from '../../../types/identifiers';
|
||||||
|
|
||||||
@@ -20,7 +21,6 @@ interface UseDocumentTaggingArgs {
|
|||||||
refreshTags: () => Promise<void> | void;
|
refreshTags: () => Promise<void> | void;
|
||||||
resolveTargetDocumentIds: (ids?: Identifier[] | null) => Identifier[];
|
resolveTargetDocumentIds: (ids?: Identifier[] | null) => Identifier[];
|
||||||
notifyApiError: (error: unknown, message: string) => void;
|
notifyApiError: (error: unknown, message: string) => void;
|
||||||
setStatusMessage: (message: string, variant?: string) => void;
|
|
||||||
updateDocumentCaches?: (id: Identifier, updater: (doc: TagRecord | null) => TagRecord | null) => void;
|
updateDocumentCaches?: (id: Identifier, updater: (doc: TagRecord | null) => TagRecord | null) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,9 +44,10 @@ const useDocumentTagActions = ({
|
|||||||
refreshTags,
|
refreshTags,
|
||||||
resolveTargetDocumentIds,
|
resolveTargetDocumentIds,
|
||||||
notifyApiError,
|
notifyApiError,
|
||||||
setStatusMessage,
|
|
||||||
updateDocumentCaches,
|
updateDocumentCaches,
|
||||||
}: UseDocumentTaggingArgs) => {
|
}: UseDocumentTaggingArgs) => {
|
||||||
|
const { showToast } = useStatusToast();
|
||||||
|
|
||||||
const bulkTagOperation = useCallback(
|
const bulkTagOperation = useCallback(
|
||||||
async ({ labels, action, documentIds }: BulkTagOperationArgs): Promise<BulkTagOperationResult> => {
|
async ({ labels, action, documentIds }: BulkTagOperationArgs): Promise<BulkTagOperationResult> => {
|
||||||
const normalized = labels.map((label) => label.trim()).filter((label) => label.length > 0);
|
const normalized = labels.map((label) => label.trim()).filter((label) => label.length > 0);
|
||||||
@@ -184,12 +185,12 @@ const useDocumentTagActions = ({
|
|||||||
async ({ label, input, documentIds }: { label?: string; input?: HTMLInputElement | null; documentIds?: Identifier[] }) => {
|
async ({ label, input, documentIds }: { label?: string; input?: HTMLInputElement | null; documentIds?: Identifier[] }) => {
|
||||||
const trimmed = label?.trim?.() || '';
|
const trimmed = label?.trim?.() || '';
|
||||||
if (!trimmed) {
|
if (!trimmed) {
|
||||||
setStatusMessage('Enter a tag label.', 'error');
|
showToast('Enter a tag label.', 'error');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const targetIds = resolveTargetDocumentIds(documentIds);
|
const targetIds = resolveTargetDocumentIds(documentIds);
|
||||||
if (!targetIds.length) {
|
if (!targetIds.length) {
|
||||||
setStatusMessage('Select documents before assigning tags.', 'error');
|
showToast('Select documents before assigning tags.', 'error');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const result = await bulkTagOperation({
|
const result = await bulkTagOperation({
|
||||||
@@ -199,7 +200,7 @@ const useDocumentTagActions = ({
|
|||||||
});
|
});
|
||||||
if (result?.ok) {
|
if (result?.ok) {
|
||||||
const { tagCount, docsCount } = result;
|
const { tagCount, docsCount } = result;
|
||||||
setStatusMessage(
|
showToast(
|
||||||
`Assigned ${tagCount} tag${tagCount === 1 ? '' : 's'} to ${docsCount} document${docsCount === 1 ? '' : 's'
|
`Assigned ${tagCount} tag${tagCount === 1 ? '' : 's'} to ${docsCount} document${docsCount === 1 ? '' : 's'
|
||||||
}.`,
|
}.`,
|
||||||
'success',
|
'success',
|
||||||
@@ -209,19 +210,19 @@ const useDocumentTagActions = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[bulkTagOperation, resolveTargetDocumentIds, setStatusMessage],
|
[bulkTagOperation, resolveTargetDocumentIds, showToast],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleBulkTagRemoveFromDetail = useCallback(
|
const handleBulkTagRemoveFromDetail = useCallback(
|
||||||
async ({ label, input, documentIds }: { label?: string; input?: HTMLInputElement | null; documentIds?: Identifier[] }) => {
|
async ({ label, input, documentIds }: { label?: string; input?: HTMLInputElement | null; documentIds?: Identifier[] }) => {
|
||||||
const trimmed = label?.trim?.() || '';
|
const trimmed = label?.trim?.() || '';
|
||||||
if (!trimmed) {
|
if (!trimmed) {
|
||||||
setStatusMessage('Enter a tag label to remove.', 'error');
|
showToast('Enter a tag label to remove.', 'error');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const targetIds = resolveTargetDocumentIds(documentIds);
|
const targetIds = resolveTargetDocumentIds(documentIds);
|
||||||
if (!targetIds.length) {
|
if (!targetIds.length) {
|
||||||
setStatusMessage('Select documents before removing tags.', 'error');
|
showToast('Select documents before removing tags.', 'error');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const result = await bulkTagOperation({
|
const result = await bulkTagOperation({
|
||||||
@@ -231,7 +232,7 @@ const useDocumentTagActions = ({
|
|||||||
});
|
});
|
||||||
if (result?.ok) {
|
if (result?.ok) {
|
||||||
const { docsCount } = result;
|
const { docsCount } = result;
|
||||||
setStatusMessage(
|
showToast(
|
||||||
`Removed tags from ${docsCount} document${docsCount === 1 ? '' : 's'}.`,
|
`Removed tags from ${docsCount} document${docsCount === 1 ? '' : 's'}.`,
|
||||||
'success',
|
'success',
|
||||||
);
|
);
|
||||||
@@ -239,17 +240,17 @@ const useDocumentTagActions = ({
|
|||||||
input.value = '';
|
input.value = '';
|
||||||
}
|
}
|
||||||
} else if (result?.reason === 'tag-missing') {
|
} else if (result?.reason === 'tag-missing') {
|
||||||
setStatusMessage(`Tag “${result.label}” not found.`, 'error');
|
showToast(`Tag “${result.label}” not found.`, 'error');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[bulkTagOperation, resolveTargetDocumentIds, setStatusMessage],
|
[bulkTagOperation, resolveTargetDocumentIds, showToast],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleBulkSelectionReanalyze = useCallback(
|
const handleBulkSelectionReanalyze = useCallback(
|
||||||
async (documentIdsOverride: Identifier[] | null = null) => {
|
async (documentIdsOverride: Identifier[] | null = null) => {
|
||||||
const targetIds = resolveTargetDocumentIds(documentIdsOverride);
|
const targetIds = resolveTargetDocumentIds(documentIdsOverride);
|
||||||
if (!targetIds.length) {
|
if (!targetIds.length) {
|
||||||
setStatusMessage('Select documents before requesting re-analysis.', 'error');
|
showToast('Select documents before requesting re-analysis.', 'error');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -262,7 +263,7 @@ const useDocumentTagActions = ({
|
|||||||
const queued = payload?.queued != null
|
const queued = payload?.queued != null
|
||||||
? Number(payload.queued)
|
? Number(payload.queued)
|
||||||
: targetIds.length;
|
: targetIds.length;
|
||||||
setStatusMessage(
|
showToast(
|
||||||
`Queued re-analysis for ${queued} document${queued === 1 ? '' : 's'}.`,
|
`Queued re-analysis for ${queued} document${queued === 1 ? '' : 's'}.`,
|
||||||
'success',
|
'success',
|
||||||
);
|
);
|
||||||
@@ -272,7 +273,7 @@ const useDocumentTagActions = ({
|
|||||||
notifyApiError(error, message);
|
notifyApiError(error, message);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[resolveTargetDocumentIds, notifyApiError, setStatusMessage],
|
[resolveTargetDocumentIds, notifyApiError, showToast],
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { useCallback, useRef, useState } from 'react';
|
import { useCallback, useRef, useState } from 'react';
|
||||||
import type { Dispatch, MutableRefObject, SetStateAction } from 'react';
|
import type { Dispatch, MutableRefObject, SetStateAction } from 'react';
|
||||||
import useFileDrop from './useFileDrop';
|
import useFileDrop from './useFileDrop';
|
||||||
|
import { useStatusToast } from '../../../lib/context/StatusToastContext';
|
||||||
import { DEFAULT_FOLDER_NAME, hasFiles } from '../../../app/workspaceUtils';
|
import { DEFAULT_FOLDER_NAME, hasFiles } from '../../../app/workspaceUtils';
|
||||||
import { fetchDocument, uploadDocument, resolveFolderPath } from '../../../lib/api/apiClient';
|
import { fetchDocument, uploadDocument, resolveFolderPath } from '../../../lib/api/apiClient';
|
||||||
import type { Identifier } from '../../../types/identifiers';
|
import type { Identifier } from '../../../types/identifiers';
|
||||||
@@ -26,9 +27,7 @@ type UploadQueueItem = {
|
|||||||
conflictDocumentId: Identifier | null;
|
conflictDocumentId: Identifier | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
type StatusLevel = 'success' | 'info' | 'warning' | 'error' | string;
|
|
||||||
type NotifyApiError = (error: unknown, fallbackMessage?: string) => void;
|
type NotifyApiError = (error: unknown, fallbackMessage?: string) => void;
|
||||||
type SetStatusMessage = (message: string, level?: StatusLevel) => void;
|
|
||||||
|
|
||||||
type DropOverlayState = {
|
type DropOverlayState = {
|
||||||
active: boolean;
|
active: boolean;
|
||||||
@@ -99,7 +98,6 @@ interface UseDocumentUploadsArgs {
|
|||||||
refreshCurrentFolder: () => Promise<void>;
|
refreshCurrentFolder: () => Promise<void>;
|
||||||
shellRef: MutableRefObject<HTMLElement | null>;
|
shellRef: MutableRefObject<HTMLElement | null>;
|
||||||
notifyApiError?: NotifyApiError;
|
notifyApiError?: NotifyApiError;
|
||||||
setStatusMessage?: SetStatusMessage;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface UseDocumentUploadsResult {
|
interface UseDocumentUploadsResult {
|
||||||
@@ -128,7 +126,6 @@ const useDocumentUploads = ({
|
|||||||
refreshCurrentFolder,
|
refreshCurrentFolder,
|
||||||
shellRef,
|
shellRef,
|
||||||
notifyApiError,
|
notifyApiError,
|
||||||
setStatusMessage,
|
|
||||||
}: UseDocumentUploadsArgs): UseDocumentUploadsResult => {
|
}: UseDocumentUploadsArgs): UseDocumentUploadsResult => {
|
||||||
const [dropOverlayState, setDropOverlayState] = useState<DropOverlayState>({
|
const [dropOverlayState, setDropOverlayState] = useState<DropOverlayState>({
|
||||||
active: false,
|
active: false,
|
||||||
@@ -138,6 +135,7 @@ const useDocumentUploads = ({
|
|||||||
const folderPathCacheRef = useRef<Map<string, FolderId>>(new Map());
|
const folderPathCacheRef = useRef<Map<string, FolderId>>(new Map());
|
||||||
const queueIdRef = useRef(0);
|
const queueIdRef = useRef(0);
|
||||||
const [uploadQueue, setUploadQueue] = useState<UploadQueueItem[]>([]);
|
const [uploadQueue, setUploadQueue] = useState<UploadQueueItem[]>([]);
|
||||||
|
const { showToast } = useStatusToast();
|
||||||
|
|
||||||
const uploadFile = useCallback(
|
const uploadFile = useCallback(
|
||||||
async (file: File, targetFolderId: FolderId) => {
|
async (file: File, targetFolderId: FolderId) => {
|
||||||
@@ -180,12 +178,12 @@ const useDocumentUploads = ({
|
|||||||
}
|
}
|
||||||
const message = error.response?.data?.error || `Failed to upload ${file.name}.`;
|
const message = error.response?.data?.error || `Failed to upload ${file.name}.`;
|
||||||
notifyApiError?.(error, message);
|
notifyApiError?.(error, message);
|
||||||
setStatusMessage?.(message, 'error');
|
showToast(message, 'error');
|
||||||
const wrapped = Object.assign(new Error(message), { response: error.response });
|
const wrapped = Object.assign(new Error(message), { response: error.response });
|
||||||
throw wrapped;
|
throw wrapped;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[notifyApiError, setStatusMessage],
|
[notifyApiError, showToast],
|
||||||
);
|
);
|
||||||
|
|
||||||
const appendQueueItems = useCallback((entries: FileEntry[], targetFolderId?: FolderId) => {
|
const appendQueueItems = useCallback((entries: FileEntry[], targetFolderId?: FolderId) => {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { useState, useCallback } from 'react';
|
import { useState, useCallback } from 'react';
|
||||||
|
import { useStatusToast } from '../lib/context/StatusToastContext';
|
||||||
/* global PublicKeyCredentialCreationOptions, CredentialCreationOptions */
|
/* global PublicKeyCredentialCreationOptions, CredentialCreationOptions */
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@@ -14,7 +15,6 @@ import {
|
|||||||
} from '../lib/api/apiClient';
|
} from '../lib/api/apiClient';
|
||||||
import type { PasskeyId } from '../types/identifiers';
|
import type { PasskeyId } from '../types/identifiers';
|
||||||
|
|
||||||
type StatusMessageFn = (message: string, variant?: string) => void;
|
|
||||||
type NotifyApiErrorFn = (error: unknown, message: string) => void;
|
type NotifyApiErrorFn = (error: unknown, message: string) => void;
|
||||||
|
|
||||||
type ApiError = {
|
type ApiError = {
|
||||||
@@ -71,7 +71,6 @@ type RevokePasskeyResult =
|
|||||||
|
|
||||||
interface UsePasskeysArgs {
|
interface UsePasskeysArgs {
|
||||||
notifyApiError: NotifyApiErrorFn;
|
notifyApiError: NotifyApiErrorFn;
|
||||||
setStatusMessage: StatusMessageFn;
|
|
||||||
token?: string | null;
|
token?: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,12 +88,13 @@ interface UsePasskeysResult {
|
|||||||
) => Promise<RevokePasskeyResult>;
|
) => Promise<RevokePasskeyResult>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const usePasskeys = ({ notifyApiError, setStatusMessage, token }: UsePasskeysArgs): UsePasskeysResult => {
|
const usePasskeys = ({ notifyApiError, token }: UsePasskeysArgs): UsePasskeysResult => {
|
||||||
const [passkeys, setPasskeys] = useState<PasskeyRecord[]>([]);
|
const [passkeys, setPasskeys] = useState<PasskeyRecord[]>([]);
|
||||||
const [passkeysSupported, setPasskeysSupported] = useState<boolean | null>(null);
|
const [passkeysSupported, setPasskeysSupported] = useState<boolean | null>(null);
|
||||||
const [passkeysLoading, setPasskeysLoading] = useState(false);
|
const [passkeysLoading, setPasskeysLoading] = useState(false);
|
||||||
const [registeringPasskey, setRegisteringPasskey] = useState(false);
|
const [registeringPasskey, setRegisteringPasskey] = useState(false);
|
||||||
const [revokingPasskeyId, setRevokingPasskeyId] = useState<PasskeyId | null>(null);
|
const [revokingPasskeyId, setRevokingPasskeyId] = useState<PasskeyId | null>(null);
|
||||||
|
const { showToast } = useStatusToast();
|
||||||
|
|
||||||
const refreshPasskeys = useCallback(async (): Promise<void> => {
|
const refreshPasskeys = useCallback(async (): Promise<void> => {
|
||||||
if (!token) {
|
if (!token) {
|
||||||
@@ -122,7 +122,7 @@ const usePasskeys = ({ notifyApiError, setStatusMessage, token }: UsePasskeysArg
|
|||||||
async ({ nickname }: { nickname?: string } = {}): Promise<RegisterPasskeyResult> => {
|
async ({ nickname }: { nickname?: string } = {}): Promise<RegisterPasskeyResult> => {
|
||||||
if (!isWebAuthnAvailable()) {
|
if (!isWebAuthnAvailable()) {
|
||||||
setPasskeysSupported(false);
|
setPasskeysSupported(false);
|
||||||
setStatusMessage('Passkeys are not supported in this browser.', 'error');
|
showToast('Passkeys are not supported in this browser.', 'error');
|
||||||
return { ok: false, reason: 'unsupported' };
|
return { ok: false, reason: 'unsupported' };
|
||||||
}
|
}
|
||||||
if (registeringPasskey) {
|
if (registeringPasskey) {
|
||||||
@@ -166,12 +166,12 @@ const usePasskeys = ({ notifyApiError, setStatusMessage, token }: UsePasskeysArg
|
|||||||
await finishPasskeyRegistration(payload);
|
await finishPasskeyRegistration(payload);
|
||||||
await refreshPasskeys();
|
await refreshPasskeys();
|
||||||
setPasskeysSupported(true);
|
setPasskeysSupported(true);
|
||||||
setStatusMessage('Passkey registered.', 'success');
|
showToast('Passkey registered.', 'success');
|
||||||
return { ok: true };
|
return { ok: true };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const typedError = error as ApiError;
|
const typedError = error as ApiError;
|
||||||
if (typedError?.name === 'NotAllowedError') {
|
if (typedError?.name === 'NotAllowedError') {
|
||||||
setStatusMessage('Passkey registration cancelled.', 'info');
|
showToast('Passkey registration cancelled.', 'info');
|
||||||
return { ok: false, reason: 'cancelled' };
|
return { ok: false, reason: 'cancelled' };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,7 +187,7 @@ const usePasskeys = ({ notifyApiError, setStatusMessage, token }: UsePasskeysArg
|
|||||||
setRegisteringPasskey(false);
|
setRegisteringPasskey(false);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[notifyApiError, refreshPasskeys, registeringPasskey, setStatusMessage],
|
[notifyApiError, refreshPasskeys, registeringPasskey, showToast],
|
||||||
);
|
);
|
||||||
|
|
||||||
const revokePasskey = useCallback(
|
const revokePasskey = useCallback(
|
||||||
@@ -202,7 +202,7 @@ const usePasskeys = ({ notifyApiError, setStatusMessage, token }: UsePasskeysArg
|
|||||||
try {
|
try {
|
||||||
await deletePasskey(passkeyId, { reason });
|
await deletePasskey(passkeyId, { reason });
|
||||||
await refreshPasskeys();
|
await refreshPasskeys();
|
||||||
setStatusMessage('Passkey revoked.', 'success');
|
showToast('Passkey revoked.', 'success');
|
||||||
return { ok: true };
|
return { ok: true };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const message = (error as ApiError)?.response?.data?.error || 'Failed to revoke passkey.';
|
const message = (error as ApiError)?.response?.data?.error || 'Failed to revoke passkey.';
|
||||||
@@ -212,7 +212,7 @@ const usePasskeys = ({ notifyApiError, setStatusMessage, token }: UsePasskeysArg
|
|||||||
setRevokingPasskeyId(null);
|
setRevokingPasskeyId(null);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[notifyApiError, refreshPasskeys, setStatusMessage],
|
[notifyApiError, refreshPasskeys, showToast],
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
Reference in New Issue
Block a user