Files
legal-ai/web-ui
Chaim 2cfdf35191
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m28s
refactor(precedents): keep all LLM calls on the local-MCP path
Architectural correction: every claude_session caller in this project
runs through the local MCP server (~/.claude.json points at
/home/chaim/legal-ai/mcp-server/.venv/bin/python). The Coolify container
has no `claude` CLI and no claude.ai session, so any LLM call originating
from web/ FastAPI fails with "Claude CLI not found" — which is exactly
what we hit on 403-17.

The earlier Anthropic SDK fallback would have made it work, but at
direct API cost. The chair's preference is to stay on the claude.ai
session for everything. So:

- claude_session.py: removed the SDK fallback, restored CLI-only.
  The error message now points the next person at the architectural
  rule in the module docstring instead of papering over it.
- precedent_library.py:ingest_precedent (called from FastAPI on upload)
  now does only the non-LLM half: extract → chunk → embed → store.
  Sets halacha_extraction_status='pending' for the chair to act on.
- reextract_halachot / reextract_metadata kept, but lazy-import their
  extractors so the FastAPI path can't accidentally pull them in. They
  are reachable only via the MCP tools precedent_extract_halachot /
  precedent_extract_metadata, which run locally with CLI.
- Removed POST /api/precedent-library/{id}/extract-halachot and
  /extract-metadata — they were dead ends from the container.
- Dropped the `anthropic` Python dep that the SDK fallback required.
- UI: removed the "refresh halachot" and "sparkles metadata" buttons
  that called those endpoints. Edit sheet now points the chair at the
  MCP tool names instead.

Halacha and metadata extraction for an uploaded precedent now happen
when the chair (via Claude Code) runs:
  mcp__legal-ai__precedent_extract_metadata <case_law_id>
  mcp__legal-ai__precedent_extract_halachot <case_law_id>

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 11:06:08 +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.