feat: Rework sidebar with dedicated components for menu, folders, tags, correspondents, and search, including tenant switching and theme controls.

This commit is contained in:
2025-11-25 15:09:20 +01:00
parent 2c42e63bab
commit d6206786f7
14 changed files with 1192 additions and 896 deletions
@@ -350,6 +350,7 @@ const useDocumentsWorkspace = ({
searchResultIds,
setSearchResultIds,
searchLoading,
activeTagFilters,
setActiveTagFilters,
activeCorrespondentFilters,
setActiveCorrespondentFilters,
@@ -966,7 +967,7 @@ const useDocumentsWorkspace = ({
const handlePromptCreateFolder = useCallback(async () => {
const handlePromptCreateFolder = useCallback(async (parentId?: Identifier | null) => {
if (creatingFolder) {
return;
}
@@ -981,7 +982,7 @@ const useDocumentsWorkspace = ({
}
setCreatingFolder(true);
try {
const success = await handleFolderCreate(trimmed);
const success = await handleFolderCreate(trimmed, parentId);
if (!success) {
setStatusMessage('Unable to create folder. Check the status message for details.', 'error');
}
@@ -1198,8 +1199,10 @@ const useDocumentsWorkspace = ({
refreshCurrentFolder,
currentSubfolders,
documents: viewDocuments,
searchQuery,
searchResultIds,
folderClickHandlers,
selectedFolder,
handleFolderDragStart,
handleFolderDragEnd,
draggedFolderId,
@@ -1211,6 +1214,7 @@ const useDocumentsWorkspace = ({
handleDocumentDragEnd,
searchLoading,
tagLookupById,
activeTagFilters,
activeCorrespondentFilters,
ensureAssetUrl,
getDocumentAsset,
@@ -170,7 +170,8 @@ const useFolderTree = ({
],
);
const expandFolderAncestors = useCallback((targetId: FolderId | null) => {
const expandFolderAncestors = useCallback((targetId: FolderId | null, options: { includeSelf?: boolean } = {}) => {
const { includeSelf = true } = options;
if (!targetId || targetId === 'root') {
setFolderNodes((prev: Map<FolderId, FolderTreeNode>) => {
const root = prev.get('root');
@@ -186,6 +187,13 @@ const useFolderTree = ({
const next = new Map<FolderId, FolderTreeNode>(prev);
let currentId = targetId;
let guard = 0;
// If not including self, start from parent
if (!includeSelf) {
const node = next.get(currentId);
currentId = node?.parentId ?? 'root';
}
while (currentId && guard < 32) {
guard += 1;
const node = next.get(currentId);
@@ -59,7 +59,7 @@ interface UseFolderTreeActionsOptions {
setSelectedFolder: (folderId: FolderKey) => void;
ensureFolderData: (folderId: FolderKey, options?: EnsureFolderOptions) => Promise<any>;
ensureFolderAncestorsLoaded: (folderId: FolderKey) => Promise<void>;
expandFolderAncestors: (folderId: FolderKey) => void;
expandFolderAncestors: (folderId: FolderKey, options?: { includeSelf?: boolean }) => void;
applySelectedFolder: (folderId: FolderKey, contents: any) => void;
notifyApiError: (error: unknown, message?: string) => void;
setStatusMessage: (message: string, level?: string) => void;
@@ -250,7 +250,7 @@ const useFolderTreeActions = ({
const targetId = folderId && folderId !== 'root' ? folderId : 'root';
await ensureFolderAncestorsLoaded(targetId);
expandFolderAncestors(targetId);
expandFolderAncestors(targetId, { includeSelf: false });
if (!navigate || immediate) {
await loadFolder(targetId, { preserveSearch: isFilterActive });
@@ -327,7 +327,7 @@ const useFolderTreeActions = ({
);
const handleFolderCreate = useCallback(
async (name: string) => {
async (name: string, parentId?: FolderKey | null) => {
if (!token) {
setStatusMessage('Log in to create folders.', 'error');
return false;
@@ -336,9 +336,14 @@ const useFolderTreeActions = ({
setStatusMessage('Folder name cannot be empty.', 'error');
return false;
}
const targetParentId = parentId !== undefined
? (parentId === 'root' ? null : parentId)
: (selectedFolder === 'root' ? null : selectedFolder);
const payload = {
name: name.trim(),
parent_id: selectedFolder === 'root' ? null : selectedFolder,
parent_id: targetParentId,
};
setCreatingFolder(true);
let succeeded = false;
@@ -372,7 +377,11 @@ const useFolderTreeActions = ({
});
return next;
});
await ensureFolderData(selectedFolder, { force: true, prefetchDepth: 1 });
// Refresh the parent folder to ensure consistency
const refreshTarget = targetParentId || 'root';
await ensureFolderData(refreshTarget, { force: true, prefetchDepth: 1 });
await selectFolder(folderData.id, { immediate: true });
succeeded = true;
return true;