Files
legal-ai/web-ui
Chaim deb1a1eaf4
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 37s
chore(api-types): regenerate after /api/search/cases
Captures accumulated backend drift since last regeneration. Triggered
by the new /api/search/cases endpoint added for header global search,
but the diff also picks up many other endpoints that had been added
without re-running api:types.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 18:10:57 +00:00
..

עוזר משפטי — 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:

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

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:

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.