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

485 lines
11 KiB
Markdown

# 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](../../apps/admin/architecture/Readme.md) inside its own
module boundary.
Source root:
```txt
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
```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
Frontend route config:
```txt
config/routing.tsx
```
Mounted module path:
```txt
/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:
```txt
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:
- `Drive` for file browsing, preview, and viewer-related flows.
- `Tools` for standalone file tools such as image editing and image map editing.
- `Settings` for persisted file-manager UI settings.
The `Tools` group currently contains:
- `ImageEditorPage`
- `ImageMapPage`
## API Boundary
Raw backend calls are centralized in:
```txt
data/api/api.ts
data/api/dto.ts
```
The frontend calls the backend 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 should be mapped before they reach UI code. Entity types live in:
```txt
entities/entry.ts
data/mappers/entryMapper.ts
```
## Data Flow
### Reading Entries
Directory entries are fetched through:
```ts
useGetEntries();
```
The higher-level hook:
```ts
useEntries();
```
does three things:
1. Builds the query payload from `ExplorerStore`.
2. Fetches entries with React Query.
3. 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:
```txt
/folder/subfolder/current
```
### Mutations
Mutations use React Query hooks:
- `useCreateDirectory`
- `useRenameEntry`
- `useDeleteEntry`
- `useDuplicateFiles`
- `useUploadFile`
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:
```txt
data/queries/cache.ts
```
Current helpers:
```ts
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:
```txt
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:
```txt
data/stores/explorerStore.ts
```
Purpose:
- Active path.
- Selected paths.
- Expanded tree paths.
- Breadcrumbs.
- Entries filter.
- Sort field and direction.
### `DriveDialogStore`
File:
```txt
data/stores/driveDialogStore.ts
```
Purpose:
- Create-directory dialog state.
- Rename-entry dialog state.
- Current entry selected for rename.
### `UploadOperationStore`
File:
```txt
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:
```txt
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:
```txt
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:
```ts
entriesStore;
explorerStore;
driveDialogStore;
uploadOperationStore;
deleteOperationStore;
```
Use:
```ts
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:
```ts
useGetFile();
```
### `DriveSettingsPage`
Settings page for persisted file-manager UI preferences.
### `ImageEditorPage`
Image editor page. Uses:
```ts
useGetFile();
useGetRawFile();
useUploadFile();
```
Image editor config and helpers live in the widget layer:
```txt
view/widgets/ImageEditor/config.ts
view/widgets/ImageEditor/helpers.ts
```
This prevents `view` code from importing from `pages`.
### `ImageMapPage`
Image map page. Uses:
```ts
useGetFile();
useGetRawFile();
```
Image map config and helpers live in the widget layer:
```txt
view/widgets/ImageEditor/config.ts
view/widgets/ImageEditor/helpers.ts
```
This prevents `view` code from importing from `pages`.
### `FilePickerDialog`
Reusable picker widgets live in:
```txt
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:
- `ExplorerFileEntryGrid`
- `ExplorerFolderEntryList`
- `ExplorerToolbarFilterDropdown`
- `FileManagerFileIcon`
- `FilePreview`
Do not import backend DTOs into view components. View components should consume frontend entities
and props.
## File Types And Icons
File helpers live in:
```txt
shared/file/
```
File icons live in:
```txt
apps/admin/public/media/files/
```
To add or change an icon:
1. Add the asset under `apps/admin/public/media/files/`.
2. Update `shared/file/fileTypeImportMap.ts`.
3. Confirm `FileManagerFileIcon` renders the expected fallback for unknown extensions.
## Adding A New Query
1. Add or reuse a method in:
```txt
data/api/api.ts
```
2. Add a key in:
```txt
data/queries/keys.ts
```
3. Create a query file:
```txt
data/queries/example.ts
```
4. Map DTOs to entities before returning app-facing data.
Do not import MobX stores or providers inside `data/queries`.
## Adding A New Mutation
1. Add a raw API method in `data/api/api.ts`.
2. Add a mutation hook in `data/queries`.
3. In the consuming widget or operation store flow, call `mutateAsync`.
4. Update `EntriesStore` if lookup state must change.
5. Update `data/queries/cache.ts` helpers 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:
1. Identify whether the data is server state or local UI state.
2. Keep backend calls in `data/api`.
3. Keep React Query hooks free of MobX provider imports.
4. Map DTOs before passing data to view components.
5. Update cache helpers when mutation results should appear immediately.
6. Update backend docs if the frontend change requires a backend contract change.