refactor: introduce dedicated identifier types for improved clarity and type safety

This commit is contained in:
2025-11-24 23:47:36 +01:00
parent 22f0c040a2
commit 70e7bda87e
79 changed files with 1567 additions and 1572 deletions
+1 -2
View File
@@ -11,8 +11,7 @@ import { DocumentsHeaderBreadcrumb } from '../documents/panel/DocumentsPanelHead
import { SidebarProvider, useSidebarContext } from '../sidebar/SidebarContext';
import { PanelManagerProvider, usePanelManager } from './PanelManagerContext';
import Sidebar from '../sidebar/Sidebar';
type Identifier = string | number;
import type { Identifier } from '../types/identifiers';
type EnsureAssetUrl = (
docId: Identifier,
+10 -10
View File
@@ -28,7 +28,7 @@ interface StatusMessage {
}
interface TenantOption {
id?: string | number | null;
id?: string | null;
name?: string | null;
}
@@ -321,7 +321,7 @@ const LoginRoute: React.FC = () => {
const payload: {
magic_token: string;
username?: string;
preferred_tenant_id?: string | number;
preferred_tenant_id?: string;
} = {
magic_token: magicToken,
};
@@ -520,14 +520,14 @@ const LoginRoute: React.FC = () => {
onCancelSelection={handleCancelSelection}
selectingTenantId={selectingTenantId}
onPasskeyLogin={handlePasskeyLogin}
passkeySupported={passkeySupported}
passkeyLoading={passkeyLoading}
onSignup={handleSignup}
signupSupported={signupSupported}
signupLoading={signupLoading}
magicLoginPending={magicLoginPending}
initialUsername={magicLoginParams.username || ''}
/>
passkeySupported={passkeySupported}
passkeyLoading={passkeyLoading}
onSignup={handleSignup}
signupSupported={signupSupported}
signupLoading={signupLoading}
magicLoginPending={magicLoginPending}
initialUsername={magicLoginParams.username || ''}
/>
</div>
);
};
+4 -4
View File
@@ -15,12 +15,12 @@ import PanelHeader from '../ui/PanelHeader';
type UploadStatus = 'pending' | 'uploading' | 'success' | 'duplicate' | 'error' | (string & {});
interface UploadQueueItem {
id: string | number;
id: string;
name: string;
status: UploadStatus;
error?: string | null;
document?: { id?: string | number; title?: string };
conflictDocumentId?: string | number;
document?: { id?: string; title?: string };
conflictDocumentId?: string;
}
interface UploadQueueOverlayProps {
@@ -191,7 +191,7 @@ const UploadQueueOverlay = ({ queue = [], onClearQueue }: UploadQueueOverlayProp
{item.error}
</span>
) : (
<span>{meta.label}</span>
<span>{meta.label}</span>
)}
</div>
</div>
+4 -2
View File
@@ -1,13 +1,15 @@
import type { DocumentId, FolderId } from '../types/identifiers';
// Entry key utilities for workspace selection
// Entry keys are strings in the format "document:id" or "folder:id"
const ENTRY_KEY_SEPARATOR = ':';
// Create entry key strings
export const createDocumentEntryKey = (documentId: string | number): string =>
export const createDocumentEntryKey = (documentId: DocumentId): string =>
`document${ENTRY_KEY_SEPARATOR}${documentId}`;
export const createFolderEntryKey = (folderId: string | number): string =>
export const createFolderEntryKey = (folderId: FolderId): string =>
`folder${ENTRY_KEY_SEPARATOR}${folderId}`;
// Type guards for entry key strings
+5 -5
View File
@@ -1,19 +1,19 @@
import { useCallback, useEffect, useRef, useState } from 'react';
export interface DetailDocument {
id?: string | number;
id?: string;
[key: string]: unknown;
}
interface UseDetailPanelOptions {
documentLookup: Map<string | number, DetailDocument>;
documentLookup: Map<string, DetailDocument>;
orderedSelectedDocuments: DetailDocument[];
}
interface OpenDetailPanelArgs {
documentId?: string | number;
documentId?: string;
document?: DetailDocument | null;
documentIds?: Array<string | number>;
documentIds?: Array<string>;
documents?: DetailDocument[];
}
@@ -22,7 +22,7 @@ export const useDetailPanel = ({
orderedSelectedDocuments,
}: UseDetailPanelOptions) => {
const [detailPanelOpen, setDetailPanelOpen] = useState(false);
const [detailPanelDocId, setDetailPanelDocId] = useState<string | number | null>(null);
const [detailPanelDocId, setDetailPanelDocId] = useState<string | null>(null);
const [detailPanelDocument, setDetailPanelDocument] = useState<DetailDocument | null>(null);
const latestOrderedDocsRef = useRef<DetailDocument[]>([]);
+2 -2
View File
@@ -5,8 +5,8 @@ import type {
SetStateAction,
} from 'react';
import { fetchDocument } from '../lib/apiClient';
import type { DocumentId } from '../types/identifiers';
type DocumentId = string | number;
type FolderId = DocumentId | 'root';
type DocumentLike = {
@@ -102,7 +102,7 @@ const useDocumentPreview = ({
async (documentId: DocumentId, { force = false }: { force?: boolean } = {}): Promise<DocumentLink | null> => {
if (!documentId) return null;
const existing = documentLinks.get(documentId);
const existing = documentLinks.get(documentId);
const now = Date.now();
const expiresAt = existing?.expiresAt ?? null;
if (!force && existing && (!expiresAt || expiresAt > now)) {
+6 -7
View File
@@ -6,8 +6,7 @@ import {
isFolderEntry,
getEntryId,
} from './entryKey';
type DocumentId = string | number;
import type { DocumentId } from '../types/identifiers';
interface SelectionEventLike {
shiftKey?: boolean;
@@ -21,7 +20,7 @@ interface UseDocumentSelectionOptions {
}
interface ApplySelectionOptions {
anchor?: string;
anchor: string | null;
interactedKeys?: string[];
}
@@ -35,8 +34,8 @@ export const useDocumentSelection = ({
const selectionOrderRef = useRef<string[]>(initialEntries);
const selectionAnchorRef = useRef<string | null>(null);
const selectionInitializedRef = useRef(false);
const [focusedDocumentId, setFocusedDocumentId] = useState<DocumentId | undefined>(undefined);
const [focusedRowKey, setFocusedRowKey] = useState<string | undefined>(undefined);
const [focusedDocumentId, setFocusedDocumentId] = useState<DocumentId | null>(null);
const [focusedRowKey, setFocusedRowKey] = useState<string | null>(null);
const visibleRowKeySetRef = useRef<Set<string>>(new Set());
const navigableRowKeysRef = useRef<string[]>([]);
@@ -90,7 +89,7 @@ export const useDocumentSelection = ({
const applySelection = useCallback(
(
rowKeys: Array<string | null>,
{ anchor, interactedKeys = [] }: ApplySelectionOptions = {},
{ anchor = null, interactedKeys = [] }: ApplySelectionOptions = { anchor: null, interactedKeys: [] },
) => {
const visibleRowKeySet = visibleRowKeySetRef.current;
const unique: string[] = [];
@@ -117,7 +116,7 @@ export const useDocumentSelection = ({
}
});
let resolvedAnchor = anchor;
let resolvedAnchor: string | null = anchor ?? null;
if (resolvedAnchor && !unique.includes(resolvedAnchor)) {
resolvedAnchor = null;
}
+1 -2
View File
@@ -2,8 +2,7 @@ import { useCallback, useEffect, useMemo, useState } from 'react';
import type { Dispatch, SetStateAction } from 'react';
import { TAG_FILTER_UNTAGGED } from './workspaceUtils';
import { listDocuments } from '../lib/apiClient';
type Identifier = string | number;
import type { Identifier } from '../types/identifiers';
type DocumentLike = { id?: Identifier } & Record<string, unknown>;
+2 -2
View File
@@ -9,13 +9,13 @@ const TAGS_MODAL = 'tags';
const CORRESPONDENTS_MODAL = 'correspondents';
interface TagRecord {
id?: string | number;
id?: string;
label?: string;
[key: string]: unknown;
}
interface CorrespondentRecord {
id?: string | number;
id?: string;
name?: string;
[key: string]: unknown;
}
+4 -4
View File
@@ -10,8 +10,8 @@ interface SelectionEntry {
}
interface WorkspaceSelectionOptions {
onDocumentActivate?: (id: string | number) => void;
onInspectFolder?: (id: string | number) => void;
onDocumentActivate?: (id: string) => void;
onInspectFolder?: (id: string) => void;
}
const identity = <T,>(value: T) => value;
@@ -71,7 +71,7 @@ export const useWorkspaceSelection = ({
);
const inspectDocument = useCallback(
(documentId?: string | number) => {
(documentId?: string) => {
if (!documentId) return;
onDocumentActivate(documentId);
},
@@ -79,7 +79,7 @@ export const useWorkspaceSelection = ({
);
const inspectFolder = useCallback(
(folderId?: string | number) => {
(folderId?: string) => {
if (!folderId) return;
onInspectFolder(folderId);
},
+1 -2
View File
@@ -5,8 +5,7 @@ import DocumentsPanel from '../documents/panel/DocumentsPanel';
import DocumentViewerPanel from '../preview/DocumentViewerPanel';
import { usePanelManager } from './PanelManagerContext';
import { FolderManagerProvider } from '../folders/FolderManagerContext';
type Identifier = string | number;
import type { Identifier } from '../types/identifiers';
type EnsureAssetUrl = (docId: Identifier, asset: unknown, options?: Record<string, unknown>) => Promise<unknown> | void;
type EnsurePreviewData = (docId: Identifier, options?: Record<string, unknown>) => Promise<unknown>;