440 lines
9.8 KiB
Markdown
440 lines
9.8 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, settings, and reusable file picker widgets.
|
|
|
|
This module follows the general [React frontend architecture](../../apps-architecture/react-frontend/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/editor/image/-/*` | `ImageEditorPage` | Edit image files. |
|
|
|
|
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.
|
|
|
|
## 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`.
|
|
|
|
### `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.
|