Backend polls each registered service's /health endpoint every 30 s via a background asyncio task. GET /api/services exposes the live status snapshot. The Apps page now renders from this endpoint — showing "Unavailable" (dimmed, non-clickable) when a service is registered but its container is unreachable. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
7.1 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/admin |
DocumentAdminSettingsPage |
Admin only |
/apps/ai/settings/admin |
AIAdminSettingsPage |
Admin only |
/admin |
AdminPage |
Admin only |
/profile |
ProfilePage |
Required |
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
Apps page (/apps)
Cards are rendered dynamically from GET /api/services (polled every 30 s via TanStack Query):
- healthy=true + app_path set — clickable card with "Available" badge
- healthy=true + no app_path — non-clickable card (e.g. AI Service — no user UI)
- healthy=false — non-clickable, dimmed card with "Unavailable" badge and explanation text
- Admin settings link shown for admins regardless of health status
Sidebar navigation
Apps is an expandable accordion in the sidebar:
- Documents sub-item (expandable) — lists all user categories beneath it; clicking a category navigates to
/apps/documents?category_id=<id> - AI Service is not listed (no openable UI)
- Sections auto-open when navigating to their route
- In collapsed (icons-only) mode, clicking the Apps icon navigates to
/apps
Documents page (/apps/documents)
Upload: PDF file input, 202 response, error display.
Filter bar:
- Search input (400ms debounce) — matches title, filename, tags, document_type
- Status dropdown (all / pending / processing / done / failed)
- Type dropdown (all / invoice / bill / receipt / order / expense / revenue / unknown)
- Sort selector (upload date / processed date / title / filename / file size / type / status)
- Asc/Desc toggle
- "Clear filters" button (appears when any filter is active)
Pagination: Prev/Next with "X–Y of Z" count. Only shown when total > per_page.
Document row (collapsed):
- Inline title editor (pencil icon, Enter to save, Esc to cancel; shows filename in italic when no title)
- Status badge (colour-coded)
- Document type label
- File size
- View button (opens PDF in new tab via blob URL — auth-gated)
- Download button
- Delete button (confirm dialog)
Document row (expanded):
- Extracted data table — all AI-extracted JSON fields (excludes
tags,suggested_categories) - Error message — shown if status=failed
- Categories — assigned chips with remove; dropdown to assign existing; AI-suggested chips with Accept / Create & Assign / Dismiss
- Status polling — auto-refetches every 3s while status is pending/processing; invalidates document list on done/failed
AI Admin Settings (/apps/ai/settings/admin)
- Provider selector (lmstudio / ollama / anthropic)
- Per-provider fields (base URL, model, API key)
- Test Connection button (
POST /api/settings/ai/test) - Save button
Document Admin Settings (/apps/documents/settings/admin)
- Upload Limits section only (max PDF size in MB)
- Save button
Admin page (/admin)
- User list with role and active status
- Inline role/status editing
Profile page (/profile)
- Display and edit personal information
API client (src/api/client.ts)
Key functions:
| Function | Description |
|---|---|
listDocuments(params) |
GET /documents — returns DocumentPage; supports category_id filter |
uploadDocument(file) |
POST /documents/upload |
deleteDocument(id) |
DELETE /documents/{id} |
downloadDocument(id, filename) |
Blob URL download |
viewDocument(id) |
Blob URL → window.open, auto-revoke after 60s |
getDocumentStatus(id) |
Poll endpoint |
listCategories() |
All categories for user |
createCategory(name) |
Create category |
assignCategory(docId, catId) |
Assign |
removeCategory(docId, catId) |
Remove |
updateDocumentTags(id, tags) |
PATCH /documents/{id}/tags |
updateDocumentTitle(id, title) |
PATCH /documents/{id}/title |
getAISettings() |
GET /settings/ai (masked) |
updateAISettings(data) |
PATCH /settings/ai |
testAIConnection() |
POST /settings/ai/test |
getDocumentLimits() |
GET /settings/documents/limits |
updateDocumentLimits(data) |
PATCH /settings/documents/limits |
State management
- TanStack Query — all server state;
queryKey: ["documents", params]for cache isolation per filter/page combination - No global store — local
useStatefor UI-only state (editing mode, filter params, etc.) - Token —
localStorage, read byuseAuthhook, injected by Axios interceptor
Component inventory
| Component | Path | Description |
|---|---|---|
AppShell |
src/components/AppShell.tsx |
Layout wrapper: Sidebar + scrollable main content |
Sidebar |
src/components/Sidebar.tsx |
Collapsible left nav (icons-only ↔ icons+labels) |
ThemeToggle |
src/components/ThemeToggle.tsx |
Sun/moon ghost icon button; persists to localStorage |
Button |
src/components/ui/button.tsx |
shadcn/ui Button (default, ghost, outline, destructive) |
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 — "Loading…" text only
- No group/sharing UI — blocked on backend groups system
- No app permission UI — all apps visible to all authenticated users
Future work
- UI component library: shadcn/ui + Tailwind CSS — installed and wired up
- AppShell + Sidebar replacing inline Nav component
- Light/dark theme context with OS preference detection
- Toast notification system (upload success, save feedback, errors)
- Loading skeletons
POST /queue/jobsintegration — show AI processing queue status / progress per document- Re-process document button (
POST /documents/{id}/reprocess— needs backend endpoint first) - Advanced filter: extracted data fields (vendor, due date, amount) — needs backend support
- Groups + document sharing UI — blocked on backend
- App permissions UI in Admin page
httpOnlycookie auth (requires backend change)- Bulk document operations (select multiple, bulk delete / bulk categorise)