Files
Business-Management/frontend/STATUS.md
T
curo1305 003fbee20f Move plugin settings access from sidebar to app card
Remove the "Extensions" section from the sidebar nav. Instead, each app
card on the Apps page shows an "Extension" button when the current user
has access to that app's plugin (matched by service ID). The button links
to /settings/plugins/:id alongside the existing admin Settings button.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 02:31:12 +02:00

10 KiB
Raw Blame History

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 (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 (full_name or email). React JSX text rendering HTML-escapes all values — no dangerouslySetInnerHTML is used anywhere on this page.
  • Grid of pinned app cards drawn from GET /api/services, filtered to the user's saved list.
  • Customize mode (pencil button): shows all services; + / toggle buttons on each card; changes committed with Save via PATCH /api/users/me/preferences.
  • Empty-state prompt when no apps are pinned.

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

App cards — Extension button:

  • GET /api/plugins is queried on the Apps page (already user-filtered by backend)
  • If an app's id matches a plugin id, an "Extension" button is shown on that card
  • Button links to /settings/plugins/:id alongside the existing admin "Settings" button
  • Only users with plugin access see the button (backend filters GET /api/plugins)

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 "XY 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 — Users page (/admin/users)

  • User list with role and active status
  • Inline active status toggle
  • Create user form (email, name, password, admin flag)
  • Delete user

Admin — Groups page (/admin/groups)

  • Group list with name, description, member count
  • Create group (name, optional description)
  • Edit group name / description inline panel
  • Delete group (with confirmation)
  • Expand group row to manage members: view members, remove members, add non-members from dropdown

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
confirmFolderSuggestion(docId) POST /documents/{id}/suggestions/folder/confirm
rejectFolderSuggestion(docId) POST /documents/{id}/suggestions/folder/reject
confirmFilenameSuggestion(docId) POST /documents/{id}/suggestions/filename/confirm
rejectFilenameSuggestion(docId) POST /documents/{id}/suggestions/filename/reject
getAISettings() GET /settings/ai (masked)
updateAISettings(data) PATCH /settings/ai
testAIConnection() POST /settings/ai/test
getDocumentLimits() GET /settings/documents/limits
adminListGroups() GET /admin/groups
adminCreateGroup(data) POST /admin/groups
adminGetGroup(id) GET /admin/groups/{id} with members
adminUpdateGroup(id, data) PATCH /admin/groups/{id}
adminDeleteGroup(id) DELETE /admin/groups/{id}
adminAddGroupMember(gId, uId) POST /admin/groups/{gId}/members/{uId}
adminRemoveGroupMember(gId, uId) DELETE /admin/groups/{gId}/members/{uId}
updateDocumentLimits(data) PATCH /settings/documents/limits
getPlugins() GET /plugins — list accessible plugins
getPluginManifest(id) GET /plugins/{id}/manifest
getPluginSettings(id) GET /plugins/{id}/settings
updatePluginSettings(id, data) PATCH /plugins/{id}/settings

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.)
  • TokenlocalStorage, 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; includes dynamic "Extensions" section
ThemeToggle src/components/ThemeToggle.tsx Sun/moon ghost icon button; persists to localStorage
PluginSchemaForm src/components/PluginSchemaForm.tsx JSON Schema → React form (boolean/string/number/readOnly fields)
PluginSettingsPage src/pages/PluginSettingsPage.tsx Generic plugin settings page (manifest-driven)
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 app permission UI per group — groups exist but permission grants are not yet implemented
  • 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
  • Generic plugin infrastructure: Extensions sidebar section, PluginSchemaForm, PluginSettingsPage
  • Suggestion badges in DocumentsPage for suggested_folder / suggested_filename (confirm/reject buttons)
  • Toast notification system (upload success, save feedback, errors)
  • Loading skeletons
  • POST /queue/jobs integration — show AI processing queue status / progress per document
  • Advanced filter: extracted data fields (vendor, due date, amount) — needs backend support
  • Groups admin UI — list, create, edit, delete, add/remove members
  • App permissions UI per group (blocked on backend group_app_permissions)
  • Document sharing UI (blocked on backend)
  • httpOnly cookie auth (requires backend change)
  • Bulk document operations (select multiple, bulk delete / bulk categorise)