# 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. # Project: RJ Bulk Supplies — back-office app ## What this is Rebuild of a Flask back-office for a SA fuel-delivery business. Customers, delivery notes, suppliers, fuel purchases, pricing rules, monthly statements (PDF + email), 7 custom report types, admin settings. ## Stack - **Next.js 16.2** (App Router, RSC, Server Actions, Turbopack) - **React 19.2**, **TypeScript 5.9** (strict) - **Tailwind CSS 4** with orange theme tokens (see `app/globals.css`) - **PocketBase 0.27 SDK** + plain fetch for RSC (see `lib/pocketbase.ts`) - **TanStack Query / Table** installed but we mostly use RSC for data - **Radix UI** primitives wrapped as shadcn/ui-style components - **pdf-lib** for PDFs in Node (statements + 7 report types) - **Resend** for transactional email - **date-fns**, **lucide-react**, **zod** (Zod 4 — `error.issues`, not `error.errors`) ## Dev server - Port **4004** (user has other apps on 3000) - `pnpm dev` — boots on http://localhost:4004 - `pnpm pb:setup` — runs `scripts/setup-pb.mjs` to provision the PB schema ## PocketBase instance (SHARED, ANCIENT VERSION) - URL: `http://pocketbase-zinlbpsext2ro0lgb2558vfy.169.239.180.130.sslip.io/` - Admin superuser: `David.els1@outlook.com` / `1234567890` - API path: `/_superusers/auth-with-password` (not `/admins`) - ⚠️ **This is a very old PocketBase version** — be aware of these quirks: - ❌ **No first-class `relation` field** — `collectionId` is silently ignored or 400s - ❌ **No `select` field type** — `values` validation rejects arrays - ❌ **No auto-generated `created` field** on base collections — sorting by `-created` 400s - ✅ Use `text` fields for foreign keys, join via Map lookup in RSC - ✅ Use `text` fields for enums, validate values with Zod at app layer - ✅ Sort by `-id` or `-date` (any field that's actually on the collection) - ✅ `expand=` only works on real relation fields — don't use it, look up FKs in code - ✅ Records need an explicit 15-char lowercase id; autogenerate doesn't fire on POST - ✅ API superuser auth is at `/api/collections/_superusers/auth-with-password` (PB v0.18-style) - The setup script (`scripts/setup-pb.mjs`) is idempotent and handles the quirks ## App user (for login) - Email: `david@rjbulk.local` / Pass: `ChangeMe123!` - Created automatically by `pnpm pb:setup` ## RSC + PB gotchas - **Never** import the PocketBase SDK in RSC code — its auto-cancelling fetch crashes under Next.js' request cancellation. Use `lib/pocketbase.ts` helpers (`pbList`, `pbGet`, `pbCreate`, `pbUpdate`, `pbDelete`) which use plain `fetch` with `cache: "no-store"`. - Server Actions cannot be imported into API route handlers — extract shared logic to `lib/*.ts` (see `lib/delivery-notes.ts` for the pattern). ## Key files - `lib/pocketbase.ts` — server-side PB fetch helpers - `lib/auth.ts` — cookie-based auth (PB token in httpOnly cookie, verified via `auth-refresh`) - `lib/types.ts` — TS types matching the PB schema - `lib/delivery-notes.ts` — shared business logic (auto-fill price, get next number) - `lib/pdf/shared.ts` + `lib/pdf/customer-statement.ts` + `lib/pdf/reports.ts` — PDF generation - `actions/*.ts` — Server Actions (one file per resource) - `app/(authenticated)/*` — authed pages with sidebar shell - `app/api/*` — Route handlers for PDF preview, next-number, pricing lookup - `scripts/setup-pb.mjs` — One-shot PB schema setup (re-run anytime) ## Conventions - Foreign keys: text field named after the related collection (singular), e.g. `customer`, `supplier` - Enums: text field with app-layer validation, constants in `lib/types.ts` - IDs: `pbId()` (15 lowercase chars) in `lib/pb.ts` - Cleaning empty form values: `cleanEmpty()` in `lib/delivery-notes.ts` ## Email (Resend) - Set `RESEND_API_KEY` and `RESEND_FROM` in `.env.local` to send statements - Without it, statement send fails with a clear error; preview still works ## Build - `pnpm build` compiles cleanly - All 22 routes are dynamic (RSC) except `/_not-found`