# 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 ```bash # 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(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/.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:** ```css /* 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: ```bash npx shadcn add # 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: ```typescript import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; // Reading data const { data, isLoading, isError } = useQuery({ queryKey: ["cases"], queryFn: () => apiRequest("/api/cases"), }); // Writing data const queryClient = useQueryClient(); const mutation = useMutation({ mutationFn: (body: CreateCaseRequest) => apiRequest("/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 `` 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 + 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`: ```typescript 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; const form = useForm({ resolver: zodResolver(schema) }); ``` --- ## Notifications / Toasts Use **sonner** (`import { toast } from "sonner"`). The `` is mounted in `src/lib/providers.tsx`. ```typescript 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"`).