Files
papercrate/frontend/src/sidebar/useSidebarProps.ts
T
2025-11-13 01:07:55 +01:00

289 lines
8.5 KiB
TypeScript

import { useMemo } from 'react';
import type { DragEvent } from 'react';
import { TAG_FILTER_UNTAGGED } from '../app/appLayoutUtils';
import type { StatusMessage } from '../hooks/documents/store/useDocumentsStore';
type Identifier = string | number;
interface FolderTreeNode {
id: Identifier;
name?: string;
hasChildren?: boolean;
loaded?: boolean;
expanded?: boolean;
children: Identifier[];
[key: string]: unknown;
}
type FolderNodeMap = Map<Identifier, FolderTreeNode>;
type FolderToggleHandler = (folderId: Identifier) => void | Promise<void>;
type FolderDropHandler = (
event: DragEvent<HTMLElement>,
folderId: Identifier,
) => void | Promise<void>;
interface FolderClickHandlers {
onToggle: FolderToggleHandler;
onSelect: (folderId: Identifier | null) => void;
onDrop: FolderDropHandler;
onDragOver: (event: DragEvent<HTMLElement>, folderId: Identifier) => void;
onDragLeave: (event: DragEvent<HTMLElement>) => void;
}
interface TagOption {
id?: Identifier;
label?: string;
color?: string | null;
[key: string]: unknown;
}
interface CorrespondentOption {
id?: Identifier;
name?: string;
[key: string]: unknown;
}
interface TenantOption {
id?: Identifier;
name?: string;
[key: string]: unknown;
}
interface UploadQueueItem {
id: string;
name?: string;
size?: number | null;
folderId?: Identifier | 'root';
status?: string;
error?: unknown;
code?: number | null;
document?: unknown;
conflictDocumentId?: Identifier | null;
[key: string]: unknown;
}
type UploadFilesHandler = (
files: FileList | File[] | Iterable<File>,
targetFolderId: Identifier | 'root',
) => void | Promise<void>;
interface UseSidebarPropsArgs {
folderNodes: FolderNodeMap;
folderClickHandlers: FolderClickHandlers;
handleFolderDelete: (folderId: Identifier) => void | Promise<void>;
handleFolderRename: (folderId: Identifier, name: string) => void | Promise<void>;
selectedFolder: Identifier | null;
handleFolderDragStart: (event: DragEvent<HTMLElement>, folderId: Identifier) => void;
handleFolderDragEnd: (event: DragEvent<HTMLElement>) => void;
draggedFolderId: Identifier | null;
handlePromptCreateFolder?: () => void;
creatingFolder: boolean;
tags: TagOption[];
activeTagFilters: Identifier[];
toggleTagFilter: (tagId: Identifier) => void;
handleTagCreate: (payload: { label?: string }) => void | Promise<void>;
correspondents: CorrespondentOption[];
activeCorrespondentFilters: Identifier[];
toggleCorrespondentFilter: (correspondentId: Identifier) => void;
handleCorrespondentCreate: (payload: { name?: string }) =>
| Promise<CorrespondentOption | null | void>
| CorrespondentOption
| null
| void;
appStatus: string;
loading: boolean;
previewActive: boolean;
searchQuery: string;
handleSearchChange: (value: string) => void;
handleSearchSubmit: () => void;
clearFilters: () => void;
isFilterActive: boolean;
handleLogout: () => void | Promise<void>;
status: StatusMessage | null;
tenantName: string | null;
tenantOptions: TenantOption[];
currentTenantId: Identifier | null;
handleTenantSelect: (tenant: TenantOption | null, options?: { refreshOnly?: boolean }) => void | Promise<void>;
openSettings?: () => void;
handleFileSelection?: UploadFilesHandler;
uploadQueue?: UploadQueueItem[];
}
interface SidebarHookResult {
folderNodes: FolderNodeMap;
onToggle: FolderClickHandlers['onToggle'];
onSelect: FolderClickHandlers['onSelect'];
onDrop: FolderClickHandlers['onDrop'];
onDragOver: FolderClickHandlers['onDragOver'];
onDragLeave: FolderClickHandlers['onDragLeave'];
onDeleteFolder: UseSidebarPropsArgs['handleFolderDelete'];
onRenameFolder: UseSidebarPropsArgs['handleFolderRename'];
selectedFolder: Identifier | null;
onFolderDragStart: UseSidebarPropsArgs['handleFolderDragStart'];
onFolderDragEnd: UseSidebarPropsArgs['handleFolderDragEnd'];
draggedFolderId: Identifier | null;
onCreateFolder: UseSidebarPropsArgs['handlePromptCreateFolder'];
creatingFolder: boolean;
tags: TagOption[];
untaggedFilterId: typeof TAG_FILTER_UNTAGGED;
activeTagIds: Identifier[];
onToggleTagFilter: UseSidebarPropsArgs['toggleTagFilter'];
onCreateTag: (label: string) => void;
correspondents: CorrespondentOption[];
activeCorrespondentIds: Identifier[];
onToggleCorrespondentFilter: UseSidebarPropsArgs['toggleCorrespondentFilter'];
onCreateCorrespondent: (name: string) => void;
appStatus: string;
loading: boolean;
previewActive: boolean;
searchQuery: string;
onSearchChange: UseSidebarPropsArgs['handleSearchChange'];
onSearchSubmit: UseSidebarPropsArgs['handleSearchSubmit'];
onSearchClear: UseSidebarPropsArgs['clearFilters'];
isFilterActive: boolean;
onLogout: UseSidebarPropsArgs['handleLogout'];
status: StatusMessage | null;
tenantName: string | null;
tenants: TenantOption[];
activeTenantId: Identifier | null;
onSelectTenant: UseSidebarPropsArgs['handleTenantSelect'];
onOpenSettings: UseSidebarPropsArgs['openSettings'];
onUploadFiles: (files: FileList | File[] | Iterable<File>, targetFolderId?: Identifier | 'root') => void;
uploadQueue?: UploadQueueItem[];
}
const useSidebarProps = ({
folderNodes,
folderClickHandlers,
handleFolderDelete,
handleFolderRename,
selectedFolder,
handleFolderDragStart,
handleFolderDragEnd,
draggedFolderId,
handlePromptCreateFolder,
creatingFolder,
tags,
activeTagFilters,
toggleTagFilter,
handleTagCreate,
correspondents,
activeCorrespondentFilters,
toggleCorrespondentFilter,
handleCorrespondentCreate,
appStatus,
loading,
previewActive,
searchQuery,
handleSearchChange,
handleSearchSubmit,
clearFilters,
isFilterActive,
handleLogout,
status,
tenantName,
tenantOptions,
currentTenantId,
handleTenantSelect,
openSettings,
handleFileSelection,
uploadQueue,
}: UseSidebarPropsArgs): SidebarHookResult =>
useMemo(
() => ({
folderNodes,
onToggle: folderClickHandlers.onToggle,
onSelect: folderClickHandlers.onSelect,
onDrop: folderClickHandlers.onDrop,
onDragOver: folderClickHandlers.onDragOver,
onDragLeave: folderClickHandlers.onDragLeave,
onDeleteFolder: handleFolderDelete,
onRenameFolder: handleFolderRename,
selectedFolder,
onFolderDragStart: handleFolderDragStart,
onFolderDragEnd: handleFolderDragEnd,
draggedFolderId,
onCreateFolder: handlePromptCreateFolder,
creatingFolder,
tags,
untaggedFilterId: TAG_FILTER_UNTAGGED,
activeTagIds: activeTagFilters,
onToggleTagFilter: toggleTagFilter,
onCreateTag: (label) => handleTagCreate({ label }),
correspondents,
activeCorrespondentIds: activeCorrespondentFilters,
onToggleCorrespondentFilter: toggleCorrespondentFilter,
onCreateCorrespondent: (name) => handleCorrespondentCreate({ name }),
appStatus,
loading,
previewActive,
searchQuery,
onSearchChange: handleSearchChange,
onSearchSubmit: handleSearchSubmit,
onSearchClear: clearFilters,
isFilterActive,
onLogout: handleLogout,
status,
tenantName,
tenants: tenantOptions,
activeTenantId: currentTenantId,
onSelectTenant: handleTenantSelect,
onOpenSettings: openSettings,
onUploadFiles: (files, targetFolderId) => {
if (!handleFileSelection) {
return;
}
if (!files) {
return;
}
const normalizedFiles = Array.isArray(files) ? files : Array.from(files);
if (normalizedFiles.length === 0) {
return;
}
const folderId = targetFolderId ?? selectedFolder ?? 'root';
handleFileSelection(normalizedFiles, folderId);
},
uploadQueue,
}),
[
handleFileSelection,
uploadQueue,
activeCorrespondentFilters,
activeTagFilters,
appStatus,
clearFilters,
correspondents,
creatingFolder,
currentTenantId,
folderClickHandlers,
folderNodes,
handleCorrespondentCreate,
handleFolderDragEnd,
handleFolderDragStart,
handleFolderDelete,
handleFolderRename,
handleLogout,
handlePromptCreateFolder,
handleSearchChange,
handleSearchSubmit,
handleTagCreate,
handleTenantSelect,
isFilterActive,
loading,
openSettings,
previewActive,
searchQuery,
draggedFolderId,
selectedFolder,
status,
tags,
tenantName,
tenantOptions,
toggleCorrespondentFilter,
toggleTagFilter,
],
);
export default useSidebarProps;