Close out the read-only surface before cutover with three families of small fixes that the previous phases left unfinished: - Error boundaries: add src/app/error.tsx (route-segment), global-error.tsx (root crash fallback with its own minimal html/body — no Providers dependency since those may be the thing that crashed), and not-found.tsx for a Hebrew 404 instead of the default Next page. - Accessibility: wire usePathname() into AppShell so the current nav item gets aria-current="page" and a gold underline. Add aria-label + aria-hidden on the icon-only buttons that Phase 5 left text-less (corpus trash, parties-field Plus). Nav gets an aria-label of its own. - Metadata template: title on each route now reads "X · עוזר משפטי" via the layout.tsx title.template. Description localized to Jerusalem. - README: full E2E smoke test checklist covering all 9 screens, plus a backend contract table so future phases know which hook wraps which endpoint. Documents the known Gitea→Coolify webhook issue. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
176 lines
8.7 KiB
Markdown
176 lines
8.7 KiB
Markdown
# עוזר משפטי — 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.
|