# עוזר משפטי — Web UI (Next.js rewrite) The Next.js 16 rewrite of `legal-ai.nautilus.marcusgroup.org`, currently hosted side-by-side with the legacy vanilla `index.html` at: - **Staging:** https://legal-ai-next.nautilus.marcusgroup.org (auto-deployed from `ui-rewrite` branch via Coolify) - **Production FastAPI:** https://legal-ai.nautilus.marcusgroup.org (same backend, old UI still default) The rewrite talks to the existing FastAPI via proxy rewrites in `next.config.ts` — no CORS setup, no duplicated backend. ## Stack - Next.js 16.2.3 (App Router, Turbopack, `output: "standalone"`) - React 19.2 · TypeScript · Tailwind v4 · shadcn/ui (radix-nova preset) - TanStack Query v5 + TanStack Table v8 - react-hook-form + zod for mutations - react-dropzone for uploads; EventSource for SSE progress - Heebo via `next/font/google`; design tokens in `src/app/globals.css` ## Local development ```bash npm install npm run dev # http://localhost:3000 npm run build # full type check + production build npm run lint npm run api:types # regenerate src/lib/api/types.ts from FastAPI's OpenAPI ``` ### API connection By default the dev server proxies to production FastAPI (`https://legal-ai.nautilus.marcusgroup.org`). To point at a different backend, set: ```bash export NEXT_PUBLIC_API_ORIGIN=http://localhost:8000 npm run dev ``` ## Project layout ``` src/ ├── app/ # Route segments (App Router) │ ├── layout.tsx # Root: Providers + RTL html + fonts │ ├── page.tsx # Home: KPIs + status donut + cases table │ ├── error.tsx # Route-segment error boundary │ ├── global-error.tsx # Root crash fallback │ ├── not-found.tsx # 404 │ ├── cases/ │ │ ├── new/ # 3-step create wizard │ │ └── [caseNumber]/ │ │ ├── page.tsx # Case detail (tabs + workflow timeline) │ │ └── compose/ # Research analysis + chair-position editor │ ├── training/ # Style portrait + corpus + compare (3 tabs) │ ├── skills/ # Paperclip skills inventory │ └── diagnostics/ # DB health + failed/stuck docs ├── components/ │ ├── app-shell.tsx # Header + nav with aria-current │ ├── cases/ # Home + detail screens │ ├── compose/ # Research analysis editor │ ├── documents/ # UploadSheet │ ├── training/ # Style report / corpus / compare panels │ ├── wizard/ # Case create wizard + parties-field │ └── ui/ # shadcn primitives ├── lib/ │ ├── api/ # Typed hooks per domain (cases, documents, research, │ │ # system, skills, training) │ ├── schemas/ # zod schemas (case create / update) │ ├── practice-area.ts # Multi-tenant axis enum + deriveSubtype() │ ├── sse.ts # EventSource wrapper │ ├── providers.tsx # QueryClient + Toaster │ └── utils.ts # cn() ``` ## Smoke test (run after every deploy) Use any browser at the staging URL. Every step should be doable **without console errors** and each mutation should produce a visible toast. ### 1. Home · `/` - [ ] Header nav shows 5 items; the current page is underlined in gold - [ ] 4 KPI cards render real numbers (סה״כ · בהכנה · בכתיבה · מוכנים) - [ ] Cases table lists existing cases; search filters by case number or title - [ ] "פיזור סטטוסים" donut renders with a legend - [ ] "+ תיק חדש" button in the top-left navigates to `/cases/new` ### 2. Create case · `/cases/new` - [ ] 3-step wizard: פרטי יסוד → צדדים → השלמות - [ ] Type `1500-25` → appeal_subtype auto-fills to "רישוי ובנייה" - [ ] Type `8500-25` → subtype auto-fills to "היטל השבחה" - [ ] Manually pick a different subtype → auto-fill stops - [ ] Submitting with invalid case number shows a zod field error (no crash) - [ ] Successful create → toast "תיק חדש נוצר" → router pushes to `/cases/{number}` ### 3. Case detail · `/cases/[caseNumber]` - [ ] Header shows status badge + gold "ועדת ערר · X" practice-area badge - [ ] Tabs switch cleanly: סקירה / מסמכים / פעולות - [ ] Workflow timeline on the right shows the current phase highlighted in gold - [ ] פעולות tab → "עריכת פרטי תיק" dialog opens; submitting updates the header without full reload (optimistic cache patch) - [ ] "העלאת מסמכים" sheet opens from the tab row; drag-drop fires a POST and a live progress bar appears via SSE ### 4. Compose · `/cases/[caseNumber]/compose` - [ ] If analysis-and-research.md exists: threshold claims + issues render as collapsible cards - [ ] Chair-position textarea auto-saves on blur with "✓ נשמר {time}" indicator - [ ] If 404 (no analysis yet): empty state card renders, no error toast ### 5. Training · `/training` - [ ] **Report tab:** headline card, 4 KPIs, subject donut, anatomy bars, top-12 signature phrases - [ ] **Corpus tab:** table of corpus decisions with a trash icon per row (aria-label present) - [ ] Deleting a decision refreshes both the corpus table and the report KPIs - [ ] **Compare tab:** two Selects, pick 2 different decisions, side-by-side panels + shared/only-A/only-B pattern lists ### 6. Skills · `/skills` - [ ] Card grid of Paperclip skills with sync-status badges (מסונכרן / DB בלבד / לא סונכרן) - [ ] Chars + file counts render; "לא ידוע" doesn't appear for installed skills ### 7. Diagnostics · `/diagnostics` - [ ] DB status card shows "מחובר" in green - [ ] Table counts populate for cases / documents / chunks / corpus / patterns - [ ] Failed + stuck document lists render (empty states OK) - [ ] Page self-refreshes every 10s — check the network tab for recurring calls ### 8. Error boundary - [ ] Visit `/cases/NOT-REAL-999-99` → case detail shows an error card with the FastAPI message and "חזרה לרשימת התיקים" button (no white screen) - [ ] Visit `/anything-broken-xyz` → custom 404 page with "חזרה לבית" button ### 9. Keyboard + RTL - [ ] Tab through the home page — focus rings are gold, visible - [ ] Wizard progresses via Enter on the "הבא" button - [ ] Screen reader announces nav items with "עמוד נוכחי" on the active one ## Deploy ``` git push # → Coolify auto-build on branch ui-rewrite (~90 s) ``` > **Known issue:** the Gitea → Coolify webhook is not firing at the time of writing. Trigger a manual deploy via the Coolify MCP (`mcp__coolify__deploy` with app UUID `l146g36mtlp0k03vrwkyrgkk`) or the Coolify UI until the webhook is fixed. ## Phase tracking See `~/.claude/plans/joyful-marinating-sutton.md` for the 7-phase rewrite plan and `~/legal-ai-ui-rewrite/.taskmaster/tasks/tasks.json` for the task board. | Phase | Scope | Status | |---|---|---| | 1 | Scaffold + Coolify staging | ✅ | | 2 | API client + typed hooks + probe | ✅ | | 3 | Read views (home, case detail, compose) | ✅ | | 4 | Mutations (wizard, edit, upload+SSE) | ✅ | | 4.5 | Practice-area integration | ✅ | | 5 | Secondary screens (training, skills, diagnostics) | ✅ | | 6 | Polish, a11y, error boundaries, smoke test | ✅ | | 7 | DNS cutover to production | pending | ## Backend contract The new UI consumes the existing FastAPI at `legal-ai/web/app.py`. Key endpoints currently relied on: | Endpoint | Hook | Used by | |---|---|---| | `GET /api/cases?detail=true` | `useCases` | home table, KPIs | | `GET /api/cases/{n}/details` | `useCase` | case detail | | `POST /api/cases/create` | `useCreateCase` | wizard | | `PUT /api/cases/{n}` | `useUpdateCase` | inline edit | | `DELETE /api/cases?case_number=...` | (MCP only so far) | admin cleanup | | `POST /api/cases/{n}/documents/upload-tagged` | `useUploadDocument` | upload sheet | | `GET /api/progress/{task_id}` (SSE) | `useProgress` | upload progress | | `GET /api/cases/{n}/research/analysis` | `useResearchAnalysis` | compose | | `PATCH .../chair-position` | `useSaveChairPosition` | chair editor | | `GET /api/training/style-report` | `useStyleReport` | training/report tab | | `GET /api/training/corpus` | `useCorpus` | training/corpus tab | | `GET /api/training/compare` | `useCompare` | training/compare tab | | `DELETE /api/training/corpus/{id}` | `useDeleteCorpusEntry` | corpus tab | | `GET /api/system/diagnostics` | `useDiagnostics` | diagnostics page | | `GET /api/admin/skills` | `useSkills` | skills page | Any new endpoint should get a typed hook in `src/lib/api/` — do not reach into `fetch` from component code.