Files

7.9 KiB

File Manager Frontend

Admin file manager UI: browsing, previews, uploads, delete/duplicate/rename, image editing and image map tools, settings, and reusable file pickers. Source: apps/admin/src/core/FileManager/.

Main principle: React Query owns server state (entries, file details, raw blobs, search, mutations); MobX owns local UI and operation state (active/selected/expanded paths, filters and sort, dialog state, upload/delete progress, loaded-entry lookup).

Directory Structure

FileManager/
  config/        Routes, menu entries, messages, translations.
  data/
    api/         Raw HTTP methods and DTOs.
    hooks/       Feature hooks that combine queries with stores.
    mappers/     DTO-to-entity mapping.
    queries/     React Query hooks, keys, mutations, and cache helpers.
    stores/      MobX local state stores.
  entities/      Domain entities and settings types.
  providers/     Feature providers and context hooks.
  pages/         Route-level screens.
  shared/        FileManager utilities that do not depend on view code.
  view/          UI components, widgets, forms, and layouts.

Routes

Module mounted at /file-manager/*, configured in config/routing.tsx. Menu groups: Drive (browsing, viewer), Tools (image editor, image map), Settings.

Route Page Purpose
/file-manager/drive/explorer/* DriveExplorerPage Browse folders and files.
/file-manager/drive/viewer/-/* DriveViewerPage View file details and preview.
/file-manager/drive/settings DriveSettingsPage Configure file-manager UI settings.
/file-manager/tools/image-editor/-/* ImageEditorPage Edit image files.
/file-manager/tools/image-map/-/* ImageMapPage Create and edit image map areas for images.

Build file-manager URLs with the helpers in shared/routing/driveLinks.ts, shared/routing/openEntryNewTab.ts, shared/path/leadingSlash.ts — never manually.

API Boundary

Raw backend calls are centralized in data/api/api.ts + data/api/dto.ts, calling the routes documented in backend.md:

API Method Backend Route
getEntries GET /file-manager/*
getFile GET /file-manager/files/details/*
getRawFile GET /file-manager/files/raw/*
createDirectory POST /file-manager/folders
renameEntry PUT /file-manager/entries/rename
removeEntry DELETE /file-manager/*
uploadFile POST /file-manager/files
duplicateFiles POST /file-manager/files/duplicate
searchEntries GET /file-manager/search

DTOs are mapped before reaching UI code (entities/entry.ts, data/mappers/entryMapper.ts).

Data Flow

  • Reading entries — useEntries() builds the query payload from ExplorerStore, fetches via React Query (useGetEntries), and syncs loaded entries into EntriesStore. EntriesStore is not the fetch owner — it is an index of loaded entries for the sidebar tree, selection, active-entry lookup, and operation state.
  • Deep links — for a nested folder opened directly, useEntries() preloads ancestor folders so the sidebar tree and entry index work for paths like /folder/subfolder/current.
  • Mutations — useCreateDirectory, useRenameEntry, useDeleteEntry, useDuplicateFiles, useUploadFile. After success, the UI updates the local cache and stores directly instead of invalidating entries queries — no refetch storm when many items are created, deleted, or uploaded in sequence.

Query Cache Updates

data/queries/cache.ts updates existing React Query cache data in memory (no network request):

addEntriesToCache(parentPath, entries); // filter-aware insertion into the parent folder's cached lists
removeEntriesFromCache(paths); // removal from all cached entry pages
renameEntryInCache(prevPath, renamedEntry);

Tradeoff: since the module avoids refetching, exact backend sort order may drift after local insertion until the folder is naturally refetched.

Stores

All in data/stores/:

Store Owns
EntriesStore Index of loaded entries by path, child/selected/active lookup, local remove and rename. Never fetches.
ExplorerStore Active path, selected paths, expanded tree paths, breadcrumbs, filter, sort.
DriveDialogStore Create-directory and rename dialogs, entry selected for rename.
UploadOperationStore Upload queue, progress, retry/remove/abort, operation card visibility.
DeleteOperationStore Delete queue, progress, retry/remove, operation card visibility.
SettingsStore Persisted view mode, compact mode, sidebar visibility, grid size.

Operation stores receive the React Query mutation function from the view layer and manage only operation state.

Providers

DriveProvider creates and exposes the feature stores — const { entriesStore, explorerStore } = useDrive();. SettingsProvider owns persisted settings.

Pages and Widgets

  • DriveExplorerPage — main browser: DriveProvider, toolbar, content, sidebar, footer, operation watcher.
  • DriveViewerPage — details and preview, via useGetFile().
  • DriveSettingsPage — persisted UI preferences.
  • ImageEditorPage / ImageMapPage — use useGetFile() + useGetRawFile() (editor also useUploadFile()). Config and helpers live in view/widgets/ImageEditor/ so view code never imports from pages.
  • FilePickerDialog — reusable picker widgets in view/widgets/FilePickerDialog/; keep picker behavior feature-scoped and pass reusable UI state through props/context instead of coupling low-level components to page-level stores.

View Layer Rules

Feature widgets may use useDrive() and useSettings(). Lower-level reusable components (ExplorerFileEntryGrid, FileManagerFileIcon, FilePreview, …) stay prop-driven. Never import backend DTOs into view components — views consume frontend entities and props.

File Types and Icons

Helpers in shared/file/; icons in apps/admin/public/media/files/. To add an icon: add the asset there, update shared/file/fileTypeImportMap.ts, and confirm FileManagerFileIcon falls back correctly for unknown extensions.

Adding a Query or Mutation

  1. Add/reuse a method in data/api/api.ts.
  2. Query: add a key in data/queries/keys.ts and a query file in data/queries/, mapping DTOs to entities before returning. Mutation: add a mutation hook in data/queries, call mutateAsync from the consuming widget or operation store.
  3. Update EntriesStore if lookup state changes; update data/queries/cache.ts if the visible cache must change.
  4. Never import MobX stores or providers inside data/queries. Don't invalidate entries queries per operation unless a full refetch is intentional.

Frontend Change Checklist

  1. Is the data server state or local UI state?
  2. Backend calls stay in data/api; React Query hooks stay free of MobX provider imports.
  3. Map DTOs before view components.
  4. Update cache helpers when mutation results should appear immediately.
  5. Update backend docs if the change requires a backend contract change.