- app/indexing/qdrant_client.py: remove the identity-only _qid()
helper and pass chunk_id straight to PointStruct (Qdrant accepts
the UUID string directly).
- services/types.ts: SearchHit gets an explicit, optional
ocr_confidence field so consumers can type the value instead of
casting through metadata.
- widgets/SearchResultCard.tsx: replaces the
(hit.metadata as { ocr_confidence? }) cast with the new field. No
behavior change when the backend omits it.
tsc --noEmit: clean.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
LegacyHUB · Frontend
React + TypeScript + Vite frontend for LegacyHUB, the legacy-document indexing and AI search module of the TeamHUB Suite.
This package ships:
- the application shell (collapsible sidebar, top toolbar, breadcrumb nav, global ⌘K command palette, light/dark theme, notification center, user/profile menu);
- nine pages: Dashboard, Documents, Ingestion Jobs, Search, Document Viewer, Tables & Figures, Quality Control, System Health, Settings;
- a hybrid AI search workspace with semantic / lexical / hybrid modes, live suggestions, expandable filters, highlighted matches, reranker score visualization and side-by-side chunk preview;
- typed service layer (
src/services/*) with Axios + TanStack Query and a mock data backend you can toggle off when the backend is reachable.
Stack
| Concern | Library |
|---|---|
| Bundler | Vite 5 |
| Language | TypeScript 5.6 |
| UI | React 18 |
| Styling | TailwindCSS 3 + custom design tokens |
| Components | shadcn/ui primitives (Radix + cva) |
| Animation | Framer Motion |
| Charts | Recharts |
| Server state | TanStack Query |
| Client state | Zustand |
| Routing | React Router v6 |
| HTTP | Axios |
| Icons | lucide-react |
| Toasts | sonner |
| Virtualization | @tanstack/react-virtual |
Quick start
cd frontend
cp .env.example .env # VITE_USE_MOCK=true for offline UI development
npm install
npm run dev # http://localhost:5173
When the FastAPI backend is running, set VITE_USE_MOCK=false (or simply
VITE_API_BASE_URL=/api/v1 and let the Vite dev proxy at port 8000 handle
routing). All API calls are isolated through src/services/*.ts.
Architecture
frontend/src/
app/ RouterProvider, QueryClient, TooltipProvider, theme bootstrap
pages/ One file per route — composed of widgets + primitives
layouts/ AppShell, Sidebar (collapsible), Topbar, Breadcrumbs, ⌘K palette
widgets/ Domain-specific composite components (KpiCard, Charts, Result cards,
PdfPreviewPane, ChunkPreview, ServiceHealthCard, Timeline)
components/
ui/ shadcn-style primitives — Button, Card, Tabs, Dialog, Select,
Tooltip, Popover, ScrollArea, Command, Skeleton, Progress, …
common/ Domain primitives — Logo, StatusChip, ConfidenceMeter,
QualityFlag, BlockTypeIcon, Highlight, EmptyState, PageHeader,
ThemeToggle
services/ Typed API layer (Axios) + TanStack hooks (one file per resource)
mock/ Deterministic mock data + simulated latency
hooks/ Wrappers around services exposing TanStack Query hooks
stores/ Zustand stores: uiStore (theme, sidebar, palette), searchStore
styles/ Tailwind layer + design tokens (HSL CSS variables)
lib/ cn(), formatBytes/Number/Percent/Duration, relativeTime, etc.
Design system
- Palette — white / light-gray surfaces with a single restrained green
accent (
--primary: 158 64% 32%) matching QMS Hub. - Surfaces — three tiers: sunken (page background), default card, raised
(popovers / dialogs). Glass surfaces via
backdrop-blurfor the topbar. - Corners —
--radius: 14pxproduces soft, premium edges across every component. - Shadows —
shadow-softandshadow-elevatedonly. No harsh drop shadows. - Typography — Inter variable, optical sizes, tabular numbers for data cells, JetBrains Mono for IDs / paths / hashes.
- Motion — Framer Motion
layoutIdfor the active sidebar pill,fade-in-upfor KPI cards, animated tabs and result expansion. - States — skeleton shimmer instead of spinners wherever possible.
Key flows
- Hybrid search (
/search) — Debounced query → TanStack hook hits the backend (or mock). Results are virtualized, scored, optionally reranked. Picking a result hydrates a side-by-side ChunkPreview with the highlighted excerpt, a page thumbnail, citation metadata, and quality flags. - Documents (
/documents) — Virtualized table (TanStack Virtual) supports thousands of rows. Filters: status, OCR threshold, "needs review", free-text search. Clicking a row opens the viewer. - Document Viewer (
/viewer/:id) — Split layout. Left pane: PDF page thumbnails + synchronized large page preview with highlighted OCR blocks. Right pane: extracted chunks / tables / figures / metadata, kept in lock-step with the active page. Below: full pipeline timeline. - Ingestion (
/ingestion) — Submit a folder path withrecursive/forcetoggles → optimistic queue, run history table with live progress bars. - Quality control (
/quality) — Three review queues (low confidence, handwriting, failed extraction) with reviewer actions and an audit log.
Mock vs real backend
src/services/apiClient.ts exports a constant USE_MOCK. When true, every
service module short-circuits to src/services/mock/mockData.ts which
generates deterministic, seeded data: 280 documents, dashboards, ingestion
runs, search results, health and queue snapshots, and per-document detail
(pages, chunks, tables, figures, timeline events).
This lets the frontend be developed and demoed without the Python services running.
Accessibility
- All interactive elements use
ring-focus(visible 2px primary ring). - Sidebar nav exposes tooltips when collapsed.
- Keyboard:
Ctrl/Cmd + Kopens the global command palette.
Responsive layout
- ≥ 1280 px (xl, ultrawide) — three-column dashboards, side-by-side search.
- 1024–1280 px (laptop) — two-column dashboards, stacked search.
- < 1024 px — single column; sidebar collapses to icons only.
Scripts
npm run dev # Vite dev server with /api proxy → :8000
npm run build # type-check + production bundle
npm run preview # preview build
npm run lint
npm run format