Files
legal-ai/web-ui
Chaim f3cc9ca9d4
Some checks failed
Build & Deploy / build-and-deploy (push) Has been cancelled
feat: Stage A finalizers + #35/#36/#37 — critical-gap closure
Four parallel sub-agents closed the remaining critical gaps from the
26/05 Stage A/B sprint. Each block independently tested; aggregated here.

## #30/#31 finalizers (sub-agent A)
* Auto-derive practice_area in case_create from case_number prefix
  (1xxx→rishuy_uvniya, 8xxx→betterment_levy, 9xxx→compensation_197);
  default for CaseCreateRequest is now "" (the DB constraint catches
  any stray "appeals_committee").
* practice_area.py: derive_subtype now handles axis-B domain values
  (rishuy_uvniya/betterment_levy/compensation_197) without parsing the
  case number; new helper derive_domain_practice_area().
* Halacha re-extraction verified unnecessary — all 6 reclassified
  records already had is_binding=false and approved halachot.
* Regression tests: 6 cases in tests/test_corpus_constraints.py
  covering practice_area enum, internal-committee chair/district,
  external-upload arar prefix, MCP guard.
* UI: district input → Select dropdown (7 districts) in
  precedent-edit-sheet.tsx, preserving legacy free-text values.

## #37 בל"מ subtypes (sub-agent B)
* 3 new appeal_subtypes: extension_request_{building_permit,
  betterment_levy,compensation}. APPEALS_COMMITTEE_SUBTYPES extended,
  SUBTYPES_BY_AREA mappings added.
* New helpers: is_blam_subject(), is_blam_subtype(),
  derive_subtype_with_blam(case_number, subject, practice_area).
  case_create now uses it to auto-detect "בקשה להארכת מועד" subjects.
* 3 methodology templates under docs/methodology/extension-request-*.md.
* paperclip_client.py mapping updated for the 3 new subtypes
  (extension_request_building_permit→CMP, the other two→CMPA).
* Frontend: bilingual "בל"מ" badge + filter dropdown on cases list +
  detail header; appeal-type-bars collapseBlam() merges בל"מ into its
  parent domain for aggregate bars.
* Wizard auto-detects בל"מ from subject during case creation.
* 3 Berlinger cases (1017/1018/1019-03-26) migrated to
  appeal_subtype=extension_request_building_permit via psql.

## #35 missing_precedents feature (sub-agent C)
* Schema V13: missing_precedents table (citation, case_id, party,
  legal_topic, status, linked_case_law_id, claim_quote, ...) +
  FK constraints + 3 indexes. Applied via psql + idempotent migration.
* 6 db.py service functions, 3 MCP tools, 6 FastAPI endpoints
  (POST/GET/PATCH/DELETE/upload — upload routes by citation prefix
  to ingest_internal_decision or ingest_precedent).
* Next.js page /missing-precedents with 5 status tabs + filters +
  sidebar badge counter + detail drawer with metadata edit + smart
  upload form that switches fields per committee/court.
* Bootstrap: 7 rows imported from the JSON file
  (3 citations × cases, all status=closed with linked_case_law_id).
* legal-researcher.md: new §2ב.5 with missing_precedent_create
  usage + dedup semantics + tool grant.

## #36 legal_arguments aggregation (sub-agent D)
* Schema V14: legal_arguments + legal_argument_propositions M:M.
  Applied via psql.
* New service argument_aggregator.py with two functions —
  aggregate_claims_to_arguments() (Claude CLI / claude_session) and
  get_legal_arguments(). Graceful llm_unavailable handling when CLI
  is missing (containers).
* 2 MCP tools + 2 API endpoints (POST .../aggregate-arguments as
  BackgroundTask, GET .../legal-arguments).
* Frontend: shadcn Accordion + new legal-arguments-panel.tsx with
  hierarchical (party → priority badge → arguments) display, "טיעונים"
  tab on the case page, "חשב/חשב מחדש" buttons.
* scripts/backfill_legal_arguments.py + SCRIPTS.md entry — dry-run
  found 8 candidate cases including 1017/1018/1019.

## Open follow-ups (intentionally deferred)
* npm run api:types in web-ui (CLAUDE.md flow) — recommended before
  the next UI commit; not required for backend deployment.
* Run backfill_legal_arguments.py --apply once the container picks up
  the new aggregator service.
* webhook on missing-precedents upload-close to Paperclip (optional).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 08:34:40 +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.