Files
admin/docs/core/file-manager/frontend.md
T

124 lines
7.9 KiB
Markdown

# 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
```txt
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](./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):
```ts
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.