# 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 in `localStorage` - Logout clears token and redirects to `/login` - `GET /api/users/me` verifies token on protected routes ### Apps page (`/apps`) Cards for each installed app: - **Documents** — link to `/apps/documents`; admin gear icon → `/apps/documents/settings/admin` - **AI Service** — infrastructure card; admin gear icon → `/apps/ai/settings/admin`; no Open button (no user-facing UI) ### 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):** - **Tag editor** — read mode shows chips + Edit button; edit mode has removable chips + input (Enter/comma to add) + Save/Cancel - **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` | | `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 `useState` for UI-only state (editing mode, filter params, etc.) - **Token** — `localStorage`, read by `useAuth` hook, 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 to `httpOnly` cookie 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 - [x] UI component library: shadcn/ui + Tailwind CSS — installed and wired up - [x] AppShell + Sidebar replacing inline Nav component - [x] Light/dark theme context with OS preference detection - [ ] Toast notification system (upload success, save feedback, errors) - [ ] Loading skeletons - [ ] `POST /queue/jobs` integration — 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 - [ ] `httpOnly` cookie auth (requires backend change) - [ ] Bulk document operations (select multiple, bulk delete / bulk categorise)