Files
legal-ai/web-ui/AGENTS.md
Chaim b0efa700da
All checks were successful
G12 Leak-Guard / leak-guard (pull_request) Successful in 7s
feat(ui): IA redesign → production · /home pending-approvals card + Claude Design gate (2/17)
- /home: כרטיס "מה ממתין להכרעתך" ב-aside (מצביע INV-IA1 ל-/approvals; usePendingApprovals,
  ללא מונה-מתחרה) — תואם מוקאפ 04-home המאושר.
- web-ui/AGENTS.md: §"שער-עיצוב חובה — Claude Design קודם" — כל יצירת/שינוי עמוד-UI
  עוברת קודם דרך פרויקט Claude Design "עוזר משפטי — IA Redesign (X17)"
  (7a85b323-d880-4b6d-bac5-d4aa396fe93c) לאישור, ורק אז מוטמע (הנחיית חיים).

בדיקה: npx tsc --noEmit ✓.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 22:10:56 +00:00

11 KiB
Raw Blame History

This is NOT the Next.js you know

This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in node_modules/next/dist/docs/ before writing any code. Heed deprecation notices.


⚠️ שער-עיצוב חובה — Claude Design קודם (הנחיית חיים, 2026-06-11)

כל יצירת עמוד/דף חדש, וכל שינוי-UI בעמוד/דף קיים — מכל סיבה — חייב לעבור קודם דרך Claude Design לאישור, ורק לאחר אישור מטמיעים בקוד. אין לשנות/ליצור עמוד-UI ישירות בלי מעבר דרך השער.

הפרויקט הקנוני (כל הדפים נמצאים שם):

  • שם: עוזר משפטי — IA Redesign (X17) ב-claude.ai/design.
  • מזהה-פרויקט: 7a85b323-d880-4b6d-bac5-d4aa396fe93c.
  • גישה: כלי DesignSync (claude.ai/design). מכיל את כל 17 הדפים כ-preview-cards תחת קבוצה IA Redesign (X17), בנויים מטוקני-העיצוב האמיתיים (globals.css: navy #0f172a, gold #a97d3a, cream #f5f1e8, parchment, rule…), Heebo, RTL.

הפרוטוקול לכל בקשת-עמוד/שינוי-UI:

  1. בקשת-עיצוב ל-Claude Design — לבנות/לעדכן את ה-preview של העמוד בפרויקט הזה (HTML self-contained, <!-- @dsCard group="IA Redesign (X17)" --> כשורה ראשונה, טוקנים+Heebo+RTL זהים), write_files + register_assets דרך DesignSync.
  2. להציג לחיים לאישור — ולהמתין לאישור מפורש.
  3. רק אחרי אישור — להטמיע בקוד (Next 16 + Tailwind v4 + shadcn), לשמר hooks/לוגיקה, tsc/lint, worktree + PR.

חריג יחיד: תיקון-באג שאינו משנה את ה-UI הוויזואלי (לוגיקה/נתון/hook בלבד) — לא דורש מעבר דרך השער. בכל ספק — דרך השער.

רקע: יוזמת ה-IA (X17, #127/#130132) → מוקאפי-Claude-Design ל-17 הדפים (מאושרים) → תרגום לפרודקשן. ראה memory feedback_claude_design_gate ו-project_ia_redesign_waves.


Stack

Layer Technology Version
Framework Next.js 16.2.3
UI React 19.2.4
Styles Tailwind CSS v4
Components shadcn/ui latest via shadcn CLI
Data fetching TanStack Query v5
Forms react-hook-form + zod v7 / v4
Language TypeScript 5
Direction Hebrew RTL dir="rtl" throughout

Commands

# Regenerate API types from the live FastAPI schema — RUN AFTER EVERY BACKEND CHANGE
npm run api:types

# Validate before every push
npm run lint
npm run build

# Local dev (rare — prod runs inside Docker; no local Python env exists)
npm run dev          # requires NEXT_PUBLIC_API_ORIGIN=http://127.0.0.1:8000 or similar

npm run api:types is mandatory any time a FastAPI endpoint is added, removed, or its request/response shape changes. It fetches https://legal-ai.nautilus.marcusgroup.org/openapi.json and writes src/lib/api/types.ts.


Backend Proxy — /api/*

next.config.ts transparently rewrites all /api/* requests to the FastAPI backend:

  • In Docker (production): http://127.0.0.1:8000
  • Override via env var: NEXT_PUBLIC_API_ORIGIN

Never hardcode the backend origin in component code. Always use relative paths like /api/cases.

The typed fetch wrapper lives in src/lib/api/client.ts — use apiRequest<T>(path, options). It throws ApiError on non-2xx responses with the parsed body and status code.


API Types — Never Edit by Hand

src/lib/api/types.ts is auto-generated by openapi-typescript from the live FastAPI OpenAPI schema.

  • Do NOT edit src/lib/api/types.ts manually — changes will be overwritten on the next npm run api:types run.
  • The typed helper modules in src/lib/api/ (e.g. cases.ts, documents.ts, precedents.ts) ARE hand-written and import from types.ts. These are safe to edit.
  • When adding a new API domain, create a new typed module in src/lib/api/<domain>.ts following the existing pattern.

Tailwind CSS v4 — Breaking Changes from v3

Tailwind v4 has a completely different configuration model.

What does NOT exist in v4:

  • tailwind.config.ts / tailwind.config.js — there is no config file
  • @tailwind base; / @tailwind components; / @tailwind utilities; directives
  • tailwind.config.theme.extend object

What v4 uses instead:

/* globals.css — already set up, do not change */
@import "tailwindcss";
@import "tw-animate-css";
@import "shadcn/tailwind.css";

@theme {
  /* Design tokens defined here as CSS custom properties */
  --color-navy: #0f172a;
  /* ... */
}
  • Custom tokens go inside @theme {} in globals.css.
  • Custom variants use @custom-variant.
  • Class names are the same (e.g. bg-navy, text-gold), but the config source is CSS, not JS.
  • PostCSS is configured via @tailwindcss/postcss (devDependency).

shadcn/ui Components

Adding a new component:

npx shadcn add <component-name>
# e.g. npx shadcn add table

Installed components live in src/components/ui/. They are editable (shadcn copies the source, not a package import). The radix-ui package (v1.4) is the underlying primitive.

  • Do NOT npm install @radix-ui/react-* directly — use npx shadcn add which installs the correct Radix version and generates the shadcn wrapper.
  • Design tokens in globals.css (--color-navy, --color-gold, etc.) are already mapped to the shadcn semantic tokens (background, foreground, primary, etc.), so shadcn components inherit the editorial/judicial aesthetic automatically.

TanStack Query v5

v5 has breaking API changes from v4. Key patterns used in this codebase:

import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";

// Reading data
const { data, isLoading, isError } = useQuery({
  queryKey: ["cases"],
  queryFn: () => apiRequest<CaseListResponse>("/api/cases"),
});

// Writing data
const queryClient = useQueryClient();
const mutation = useMutation({
  mutationFn: (body: CreateCaseRequest) =>
    apiRequest<Case>("/api/cases", { method: "POST", body }),
  onSuccess: () => {
    queryClient.invalidateQueries({ queryKey: ["cases"] });
  },
});

v5 changes from v4:

  • useQuery no longer accepts positional arguments — always use the options object.
  • isLoading is replaced by isPending for mutations (but isLoading still works for queries).
  • onSuccess/onError/onSettled callbacks on useQuery are removed — use mutation callbacks or useEffect instead.
  • getQueryData / setQueryData are unchanged.

The shared QueryClient is created in src/lib/api/client.ts via makeQueryClient() and provided by src/lib/providers.tsx.


RTL — Hebrew UI Rules

All UI is Hebrew, right-to-left. The <html> element has dir="rtl" and lang="he".

Use logical CSS properties instead of directional ones:

Avoid (directional) Use (logical)
ml-* / mr-* ms-* (start) / me-* (end)
pl-* / pr-* ps-* (start) / pe-* (end)
text-left text-start
text-right text-end
float-left float-start
border-l-* border-s-*

In RTL, "start" = right side, "end" = left side. Using logical properties means the layout works automatically without RTL overrides.

Flexbox direction: flex-row in RTL naturally flows right-to-left. Use flex-row-reverse only when you need LTR inside an RTL context.


Project Structure

src/
├── app/                    # Next.js App Router pages
│   ├── layout.tsx          # Root layout — sets dir="rtl", applies fonts
│   ├── globals.css         # Tailwind v4 imports + design tokens + :root vars
│   ├── cases/              # Case management pages
│   ├── precedents/         # Precedent library pages
│   ├── methodology/        # Methodology browser
│   ├── training/           # Training document management
│   ├── settings/           # Application settings
│   └── skills/             # Skills management
├── components/
│   ├── ui/                 # shadcn primitives (editable copies)
│   ├── app-shell.tsx       # Top-level shell with nav
│   ├── cases/              # Case-domain components
│   ├── documents/          # Document viewer components
│   ├── precedents/         # Precedent components
│   └── compose/            # Decision drafting / block editor
├── lib/
│   ├── api/
│   │   ├── types.ts        # AUTO-GENERATED — never edit
│   │   ├── client.ts       # apiRequest<T> + QueryClient factory
│   │   ├── cases.ts        # Typed case API helpers
│   │   ├── documents.ts    # Typed document API helpers
│   │   └── ...             # One file per API domain
│   ├── providers.tsx       # TanStack Query + theme providers
│   ├── utils.ts            # cn() and other shared utilities
│   ├── doc-types.ts        # Document type constants
│   └── sse.ts              # Server-Sent Events helper for streaming

Forms

Forms use react-hook-form (v7) with zod (v4) validation via @hookform/resolvers:

import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";

const schema = z.object({ title: z.string().min(1) });
type FormValues = z.infer<typeof schema>;

const form = useForm<FormValues>({ resolver: zodResolver(schema) });

Notifications / Toasts

Use sonner (import { toast } from "sonner"). The <Toaster> is mounted in src/lib/providers.tsx.

toast.success("התיק נשמר בהצלחה");
toast.error("שגיאה בשמירה");

Streaming (SSE)

Server-sent events are used for long-running AI operations (drafting, analysis). The helper is in src/lib/sse.ts. Use it instead of raw EventSource.


Deploy

This frontend runs inside Docker via Coolify — not as a standalone Node process.

  • No npm run dev on the server — there is no local Python environment for the backend.
  • To see changes in production: git commit + git push origin main → Gitea Actions builds image → Coolify redeploys (~2-4 min).
  • Prod URL: https://legal-ai.nautilus.marcusgroup.org
  • The Next.js output is standalone (see next.config.ts: output: "standalone").