# 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 | | `/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 in `localStorage` - Logout clears token and redirects to `/login` - `GET /api/users/me` verifies 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 via `PATCH /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_count` indicator (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 — 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 to `httpOnly` cookie 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 - [x] SourcePanel with views + searchable category navigation - [x] DocumentSlideOver replacing expand-in-row - [x] Filter chip system - [x] Multi-file upload with queue panel + drag-and-drop - [x] Bulk actions bar (share, delete) - [x] Document sharing UI (Sharing section + Shared with me view) - [x] AI suggestion confirm/reject UI (folder + filename) - [x] Groups admin UI - [x] Replace Axios with native fetch; add global 401 → `/login` redirect for expired sessions - [ ] Toast notification system - [ ] Loading skeletons - [ ] Cmd+K global search (`CommandDialog`) - [ ] Advanced filter: extracted data fields (needs backend support) - [ ] `httpOnly` cookie auth (requires backend change) - [ ] TanStack Virtual for category list > 200 items