- backend/app/routers/storage_config.py: 5 admin-only endpoints proxying storage-service config + migration API (GET/PATCH/POST/DELETE) - backend/app/main.py: register storage_config router - frontend/src/api/client.ts: StorageStatus, MigrationStatus, StorageBackendConfig interfaces + 5 API functions - frontend/src/pages/StorageAdminPage.tsx: full admin UI — backend health dot, driver selector (local/S3/WebDAV), conditional credential fields, Test & Migrate button, live 2s-poll migration progress bar, Cancel - frontend/src/App.tsx: /admin/storage route (AdminRoute guard) - CLAUDE.md: storage enforcement rule, updated Docker tables (6 services, 3 volumes), §20 in merge checklist - backend/CLAUDE.md, frontend/CLAUDE.md, doc-service/CLAUDE.md, ai-service/CLAUDE.md: updated to reflect storage-service integration - tests/ALL_TESTS.md + tests/storage-service_tests.md: §20 (20 tests) - backend/STATUS.md, frontend/STATUS.md: updated with new endpoints/routes - changelog/2026-04-20_storage-service.md: full change log Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
9.8 KiB
Frontend — Status
What it is
React 18 + TypeScript + Vite SPA styled with shadcn/ui (Radix primitives) and Tailwind CSS v3. Design tokens are CSS custom properties supporting light/dark themes. In dev it runs on port 5173 and proxies /api/* to backend:8000. In prod it is served by nginx on port 80.
All API calls go through src/api/client.ts (single Axios instance, JWT injected via request interceptor from localStorage).
Routes
| Path | Component | Auth |
|---|---|---|
/login |
LoginPage |
Public |
/ |
DashboardPage |
Required |
/apps |
AppsPage |
Required |
/apps/documents |
DocumentsPage |
Required |
/apps/documents/settings |
DocServiceSettingsPage |
ServiceAdminRoute (is_admin OR doc-service-admin) |
/apps/ai/settings |
AIAdminSettingsPage |
ServiceAdminRoute (is_admin OR ai-service-admin) |
/admin |
AdminPage (redirects to /admin/users) |
Admin only |
/admin/users |
AdminUsersPage |
Admin only |
/admin/groups |
AdminGroupsPage |
Admin only |
/admin/storage |
StorageAdminPage |
Admin only |
/profile |
ProfilePage |
Required |
/settings |
SettingsPage (placeholder) |
Required |
/settings/plugins/:id |
PluginSettingsPage |
Required (per-plugin access control) |
PrivateRoute redirects to /login when no token. AdminRoute redirects to / when not admin.
Current functionality
Auth
- Login form (
POST /api/auth/login) stores JWT inlocalStorage - Logout clears token and redirects to
/login GET /api/users/meverifies token on protected routes
Home dashboard (/)
Personalised landing page per user:
- Time-aware greeting with the user's display name
- Grid of pinned app cards from
GET /api/services, filtered to user's saved list - Customize mode (pencil button): shows all services;
+/−toggle; commits viaPATCH /api/users/me/preferences
Apps page (/apps)
Cards from GET /api/services (polled every 30 s):
- healthy + app_path → clickable card with "Available" badge
- healthy + no app_path → non-clickable card
- unhealthy → dimmed, non-clickable, "Unavailable"
- Settings button visible to admins and service-admin group members
Sidebar navigation
Apps expandable accordion: Documents single NavLink to /apps/documents. Category navigation moved to SourcePanel (only visible on /apps/documents route). Admin section (Users, Groups, Appearance) for admins. Collapsible to icon-only mode.
Documents page (/apps/documents) — three-column layout
SourcePanel (240px, left): Appears only on /apps/documents.
- Views: All Documents / Mine / Shared with me (URL param
?view=) - Category tree with client-side search (searchable when > 4 categories)
- Inline new category form
- "Manage categories" button opens
ManageCategoriesDialog
Toolbar: Debounced search input (400ms) + filter chips system.
- Filter chips: Status, Document type, Category (each adds a removable chip)
- "Add filter" button opens a two-step picker (dimension → value)
- Sort via clickable column headers (↑/↓ chevron)
Compact table rows:
- Columns: checkbox | title/filename | type | status dot | categories (2 + overflow) | sharing icon | date | size | 3-dot actions
- Row click opens
DocumentSlideOver - Shared-with-me rows show a primary border accent
DocumentSlideOver (480px, right slide-over):
- Metadata (status dot, size, dates, source)
- Inline title edit (pencil icon)
- Type picker (chips for each doc type)
- AI Suggestions — folder and filename confirm/reject buttons (was missing before, now implemented)
- Extracted data key-value table
- Categories multi-select combobox (search-to-filter)
- AI-suggested categories with Assign / Create & Assign actions
- Tags chip editor (add/remove inline)
- Sharing section (owner only): lists groups the doc is shared with; group picker combobox (filtered to user's own groups); remove share button
- Raw text section (collapsed by default)
- Re-analyse / Delete actions (owner only)
Bulk actions bar (floating, bottom center, owner view only):
- Appears when any rows are checked
- Share with group (opens group picker → shares all selected)
- Delete (confirm dialog)
- Clear selection
Upload experience:
- Full-page drag-and-drop overlay (activates on
dragenter) - Multi-file upload (iterates all selected/dropped files)
- Bottom-right upload queue panel (collapsible toast) with per-file status + "Review →" link after upload
Document sharing:
- Owner shares doc with any of their own groups from the slide-over
- Recipient sees shared docs in "Shared with me" view
- Recipient can View + Download only (no edit/delete/share)
share_countindicator (Users icon) in table rows
Polling: List query refetches every 3s automatically when any visible doc is pending/processing (single query, not per-document). Uses TanStack Query refetchInterval function.
AI Service Settings (/apps/ai/settings)
Provider selector, per-provider fields, Test Connection, Save.
Document Service Settings (/apps/documents/settings)
Upload limits + watch directory config.
Admin — Storage page (/admin/storage)
Current backend status (green/red health dot). Driver selector (local/S3/WebDAV) with conditional credential fields. "Test & Migrate" button triggers an async migration that copies all objects to the new backend, verifies, then switches atomically. Live progress bar with 2s polling (states: validating → migrating → switching → cleaning → done). Cancel button during in-progress migrations.
Admin — Users page (/admin/users)
User list, toggle active, create user, delete user.
Admin — Groups page (/admin/groups)
Group list, create, edit name/description, delete, add/remove members.
Profile page (/profile)
Display and edit personal information.
API client (src/api/client.ts)
No Axios — replaced with a native fetch wrapper (request()). Global 401 handler clears the token and redirects to /login, fixing the expired-session blank-page bug. All exported function signatures are unchanged.
Key document-related functions:
| Function | Description |
|---|---|
listDocuments(params) |
GET /documents — paginated with filters |
listSharedWithMe(params) |
GET /documents/shared-with-me |
uploadDocument(file) |
POST /documents/upload |
deleteDocument(id) |
DELETE /documents/{id} |
downloadDocument(id, filename) |
Blob URL download |
viewDocument(id) |
Blob URL → new tab, 60s revoke |
getDocumentShares(docId) |
GET /documents/{id}/shares |
addDocumentShare(docId, groupId) |
POST /documents/{id}/shares |
removeDocumentShare(docId, groupId) |
DELETE /documents/{id}/shares/{group_id} |
getMyGroups() |
GET /users/me/groups (for share picker) |
confirmFolderSuggestion(docId) |
Apply AI folder suggestion |
rejectFolderSuggestion(docId) |
Dismiss AI folder suggestion |
confirmFilenameSuggestion(docId) |
Apply AI filename suggestion |
rejectFilenameSuggestion(docId) |
Dismiss AI filename suggestion |
State management
- TanStack Query — all server state
["documents", params]— owned doc list (refetchInterval when pending/processing)["documents-shared", params]— shared-with-me list["categories"]— all user categories (shared across SourcePanel + DocumentSlideOver)["document-shares", docId]— shares for a specific document["my-groups"]— current user's group memberships
- URL search params —
view,page,sort,order,search,status,document_type,category_id - Local
useState— UI-only state (drag, upload queue, active doc ID, selected IDs, slide-over open)
Component inventory
| Component | Path | Description |
|---|---|---|
AppShell |
src/components/AppShell.tsx |
Layout: Sidebar + SourcePanel (on /apps/documents) + main |
Sidebar |
src/components/Sidebar.tsx |
Collapsible left nav (categories removed, replaced by SourcePanel) |
SourcePanel |
src/components/SourcePanel.tsx |
Views + searchable category tree (docs route only) |
ManageCategoriesDialog |
src/components/ManageCategoriesDialog.tsx |
Category CRUD modal |
DocumentSlideOver |
src/components/DocumentSlideOver.tsx |
Right slide-over: detail, edit, share, AI suggestions |
ThemeToggle |
src/components/ThemeToggle.tsx |
Sun/moon toggle |
PluginSchemaForm |
src/components/PluginSchemaForm.tsx |
JSON Schema → React form |
Button |
src/components/ui/button.tsx |
shadcn/ui Button |
Input |
src/components/ui/input.tsx |
shadcn/ui Input |
Known limitations / not implemented
- JWT in
localStorage— XSS risk; migrate tohttpOnlycookie when backend supports it - No toast / notification system — errors shown inline; success is silent
- No loading skeletons — spinner only
- Raw text not in DocumentOut — slide-over shows a placeholder; full text requires direct backend API call
Future work
- SourcePanel with views + searchable category navigation
- DocumentSlideOver replacing expand-in-row
- Filter chip system
- Multi-file upload with queue panel + drag-and-drop
- Bulk actions bar (share, delete)
- Document sharing UI (Sharing section + Shared with me view)
- AI suggestion confirm/reject UI (folder + filename)
- Groups admin UI
- Replace Axios with native fetch; add global 401 →
/loginredirect for expired sessions - Admin storage page with live migration progress bar
- Toast notification system
- Loading skeletons
- Cmd+K global search (
CommandDialog) - Advanced filter: extracted data fields (needs backend support)
httpOnlycookie auth (requires backend change)- TanStack Virtual for category list > 200 items