RJBULK-APP/AGENTS.md
SJCoetz 135a6e9fe6 docs: document Gmail SMTP configuration
Co-Authored-By: Claude <noreply@anthropic.com>

Co-authored-by: Orca <help@stably.ai>
2026-08-03 01:21:54 +02:00

81 lines
4.6 KiB
Markdown

<!-- BEGIN:nextjs-agent-rules -->
# 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.
<!-- END:nextjs-agent-rules -->
# 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)
- **Gmail SMTP via Nodemailer** 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 (Gmail SMTP)
- Set `GMAIL_SMTP_USER` to `rjbulk01@gmail.com` and `GMAIL_SMTP_APP_PASSWORD` to a Google app password in `.env.local` or the production environment.
- Gmail SMTP uses `smtp.gmail.com` on port 587 with STARTTLS.
- The app sends statements and test emails from `rjbulk01@gmail.com` and does not use a default CC.
- Without the Gmail variables, statement sending and test-email sending fail with a clear error; statement preview still works.
## Build
- `pnpm build` compiles cleanly
- All 22 routes are dynamic (RSC) except `/_not-found`