Files
legal-ai/web-ui/AGENTS.md
Chaim ea0532b7ba
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m39s
fix: weekly-feedback-job handler writes to file only (no Paperclip issue)
CEO wakes for weekly-feedback-job via agents.invoke without issueId,
so $PAPERCLIP_TASK_ID is empty. Removed steps 4-5 (comment + close
issue) from handler — now file-write only with stdout logging.

Also commits pending docs and agent instructions from prior session.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 11:08:14 +00:00

8.7 KiB

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.


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").