RJBULK-APP/AGENTS.md
SJCoetz 1efa979443 feat: initial setup of rj bulk supplies backoffice app
Adds complete initial codebase for the RJ Bulk Supplies fuel delivery backoffice:
- Authentication pages and protected route layouts
- Customer, supplier, fuel purchase, and delivery note management
- Custom reporting and customer statement sending tools
- Full UI component library and Tailwind styling
- Server actions, API routes, and PocketBase integration
- Development scripts and project configuration updates
- Updated README and package metadata
2026-07-19 20:38:16 +02:00

4.3 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.

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