TASKS-1823 WebManager module
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
# 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.
|
||||
Reference in New Issue
Block a user