11 KiB
File Manager Frontend
The frontend module implements the admin file manager UI. It provides file browsing, previews, uploads, deletion, duplication, renaming, image editing tools, image map tools, settings, and reusable file picker widgets.
This module follows the general React frontend architecture inside its own module boundary.
Source root:
apps/admin/src/core/FileManager/
Main Principle
React Query owns server state. MobX owns local UI and operation state.
Server state examples:
- Directory entries.
- File details.
- Raw file blobs.
- Search results.
- Create, rename, delete, duplicate, and upload mutations.
Local state examples:
- Active path.
- Selected paths.
- Expanded tree paths.
- Filter and sort controls.
- Dialog open state.
- Upload and delete operation progress.
- Cached lookup of already loaded entries for UI behavior.
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
Frontend route config:
config/routing.tsx
Mounted module path:
/file-manager/*
Current child routes:
| 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. |
Path helpers live in:
shared/routing/driveLinks.ts
shared/routing/openEntryNewTab.ts
shared/path/leadingSlash.ts
Use these helpers instead of manually building file-manager URLs.
Menu Structure
File Manager navigation is grouped into three top-level menu entries:
Drivefor file browsing, preview, and viewer-related flows.Toolsfor standalone file tools such as image editing and image map editing.Settingsfor persisted file-manager UI settings.
The Tools group currently contains:
ImageEditorPageImageMapPage
API Boundary
Raw backend calls are centralized in:
data/api/api.ts
data/api/dto.ts
The frontend calls the backend 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 should be mapped before they reach UI code. Entity types live in:
entities/entry.ts
data/mappers/entryMapper.ts
Data Flow
Reading Entries
Directory entries are fetched through:
useGetEntries();
The higher-level hook:
useEntries();
does three things:
- Builds the query payload from
ExplorerStore. - Fetches entries with React Query.
- Syncs loaded entries into
EntriesStore.
EntriesStore is not the fetch owner. It is an index of loaded entries used by the sidebar tree,
selection lookups, active entry lookup, and operation state.
Deep Links
When opening a nested folder directly, useEntries() preloads ancestor folders through React Query.
This keeps the sidebar tree and entry index usable for paths like:
/folder/subfolder/current
Mutations
Mutations use React Query hooks:
useCreateDirectoryuseRenameEntryuseDeleteEntryuseDuplicateFilesuseUploadFile
After a successful mutation, the UI updates local cache and stores directly. It does not invalidate entries queries after every operation.
This avoids unnecessary refetches when many items are created, deleted, or uploaded in sequence.
Query Cache Updates
Entries cache helpers live in:
data/queries/cache.ts
Current helpers:
addEntriesToCache(parentPath, entries);
removeEntriesFromCache(paths);
renameEntryInCache(prevPath, renamedEntry);
These helpers update existing React Query cache data in memory. They do not trigger a network request.
Important behavior:
- Added entries are inserted into cached lists for the matching parent folder.
- Added entries are filter-aware.
- Deleted entries are removed from all cached entry pages.
- Renamed entries are updated across cached entry pages.
Tradeoff: because the module avoids refetching, exact backend sort order may not be perfect after local cache insertion until the folder is naturally refetched or reloaded.
Stores
EntriesStore
File:
data/stores/entriesStore.ts
Purpose:
- Keep an index of loaded entries by path.
- Provide child lookup by parent path.
- Support selected-entry and active-entry lookup.
- Support local remove and rename updates for already loaded subtrees.
It should not fetch data.
ExplorerStore
File:
data/stores/explorerStore.ts
Purpose:
- Active path.
- Selected paths.
- Expanded tree paths.
- Breadcrumbs.
- Entries filter.
- Sort field and direction.
DriveDialogStore
File:
data/stores/driveDialogStore.ts
Purpose:
- Create-directory dialog state.
- Rename-entry dialog state.
- Current entry selected for rename.
UploadOperationStore
File:
data/stores/uploadOperationStore.ts
Purpose:
- Upload queue.
- Upload progress.
- Upload retry, remove, and abort.
- Upload operation card visibility.
It receives the React Query mutation function from the view layer and manages only operation state.
DeleteOperationStore
File:
data/stores/deleteOperationStore.ts
Purpose:
- Delete queue.
- Delete progress.
- Delete retry and remove.
- Delete operation card visibility.
It receives the React Query mutation function from the view layer and manages only operation state.
SettingsStore
File:
data/stores/settingsStore.ts
Purpose:
- Persisted view mode.
- Compact mode.
- Sidebar visibility.
- Grid size and related display preferences.
Providers
DriveProvider creates and exposes feature stores:
entriesStore;
explorerStore;
driveDialogStore;
uploadOperationStore;
deleteOperationStore;
Use:
const { entriesStore, explorerStore } = useDrive();
SettingsProvider owns persisted settings such as view mode, compact mode, sidebar visibility, and
grid size.
Pages And Widgets
DriveExplorerPage
Main file browser page. Composes:
DriveProvider- Explorer toolbar.
- Explorer content.
- Explorer sidebar.
- Footer.
- Operation watcher.
DriveViewerPage
File details and preview page. Uses:
useGetFile();
DriveSettingsPage
Settings page for persisted file-manager UI preferences.
ImageEditorPage
Image editor page. Uses:
useGetFile();
useGetRawFile();
useUploadFile();
Image editor config and helpers live in the widget layer:
view/widgets/ImageEditor/config.ts
view/widgets/ImageEditor/helpers.ts
This prevents view code from importing from pages.
ImageMapPage
Image map page. Uses:
useGetFile();
useGetRawFile();
Image map config and helpers live in the widget layer:
view/widgets/ImageEditor/config.ts
view/widgets/ImageEditor/helpers.ts
This prevents view code from importing from pages.
FilePickerDialog
Reusable picker widgets live in:
view/widgets/FilePickerDialog/
Keep picker behavior feature-scoped, but pass reusable UI state through props and context instead of coupling low-level UI components to page-level stores.
View Layer Rules
Feature widgets may use File Manager providers such as useDrive() and useSettings().
Lower-level reusable UI components should stay prop-driven. Examples:
ExplorerFileEntryGridExplorerFolderEntryListExplorerToolbarFilterDropdownFileManagerFileIconFilePreview
Do not import backend DTOs into view components. View components should consume frontend entities and props.
File Types And Icons
File helpers live in:
shared/file/
File icons live in:
apps/admin/public/media/files/
To add or change an icon:
- Add the asset under
apps/admin/public/media/files/. - Update
shared/file/fileTypeImportMap.ts. - Confirm
FileManagerFileIconrenders the expected fallback for unknown extensions.
Adding A New Query
- Add or reuse a method in:
data/api/api.ts
- Add a key in:
data/queries/keys.ts
- Create a query file:
data/queries/example.ts
- Map DTOs to entities before returning app-facing data.
Do not import MobX stores or providers inside data/queries.
Adding A New Mutation
- Add a raw API method in
data/api/api.ts. - Add a mutation hook in
data/queries. - In the consuming widget or operation store flow, call
mutateAsync. - Update
EntriesStoreif lookup state must change. - Update
data/queries/cache.tshelpers if visible query cache must change.
Do not invalidate entries queries after each operation unless a full refetch is intentionally required.
Frontend Change Checklist
Before changing frontend behavior:
- Identify whether the data is server state or local UI state.
- Keep backend calls in
data/api. - Keep React Query hooks free of MobX provider imports.
- Map DTOs before passing data to view components.
- Update cache helpers when mutation results should appear immediately.
- Update backend docs if the frontend change requires a backend contract change.