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 fromExplorerStore, fetches via React Query (useGetEntries), and syncs loaded entries intoEntriesStore.EntriesStoreis 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, viauseGetFile().DriveSettingsPage— persisted UI preferences.ImageEditorPage/ImageMapPage— useuseGetFile()+useGetRawFile()(editor alsouseUploadFile()). Config and helpers live inview/widgets/ImageEditor/soviewcode never imports frompages.FilePickerDialog— reusable picker widgets inview/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
- Add/reuse a method in
data/api/api.ts. - Query: add a key in
data/queries/keys.tsand a query file indata/queries/, mapping DTOs to entities before returning. Mutation: add a mutation hook indata/queries, callmutateAsyncfrom the consuming widget or operation store. - Update
EntriesStoreif lookup state changes; updatedata/queries/cache.tsif the visible cache must change. - 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
- Is the data server state or local UI state?
- Backend calls stay in
data/api; React Query hooks stay free of MobX provider imports. - Map DTOs before view components.
- Update cache helpers when mutation results should appear immediately.
- Update backend docs if the change requires a backend contract change.