Files
Business-Management/frontend/STATUS.md
T
curo1305 00466a9801 Add generic plugin architecture and watch-directory feature
Introduces a manifest contract so feature containers self-describe their
settings (JSON Schema + access rules). Backend and frontend gain generic
plugin proxy and dynamic Extensions UI with zero feature-specific code.

Doc-service is the first plugin consumer: exposes /plugin/manifest and
/plugin/settings, adds a watchdog-based file watcher that auto-ingests
PDFs from a mounted directory, maps subfolders to categories, supports
AI-suggested folder/filename (user-confirmed), and enforces a no-remove
policy. Access is gated by is_superuser or doc-service-admin group.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 02:09:50 +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

Extensions section (dynamic):

  • Populated from GET /api/plugins (polled via TanStack Query, retry: false)
  • Only shown when the user has access to at least one plugin
  • Each entry links to /settings/plugins/:id
  • No code changes needed to add future plugin-enabled feature containers

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)