116 lines
5.1 KiB
Markdown
116 lines
5.1 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 -->
|
|
|
|
<!-- BEGIN:pb-select-field-gotcha -->
|
|
# PocketBase select fields are not self-service
|
|
|
|
The `order_items.item_status` field is a `select` with a fixed list of
|
|
values. If you want to set a new value (e.g. `delivery_note` for the
|
|
new delivery-note flow), you must add it to the schema in the PB admin
|
|
**first** — the API will 500 with "Failed to update record." otherwise.
|
|
The local `pb-exports/pb_schema-2.json` is a snapshot for reference
|
|
only; the source of truth is the running PB instance.
|
|
|
|
If a new code path sets a select value that doesn't exist yet, ask the
|
|
user to add it via the PB admin before debugging the API. They do this
|
|
manually (it's a UI-only change in the PB admin).
|
|
<!-- END:pb-select-field-gotcha -->
|
|
|
|
<!-- BEGIN:pb-attachments-pattern -->
|
|
# Reuse existing multi-file fields for new attachment types
|
|
|
|
The `order_items` collection has a multi-file field `order_item_attachements`
|
|
(`maxSelect: 10`, accepts any MIME). When adding a new document type
|
|
to attach to an order item (e.g. delivery note PDF), upload the file
|
|
to this field rather than creating a new attachment collection. The
|
|
operator queries by some key field on the item (e.g.
|
|
`delivery_note_number`) to find the items, then reads the attachments
|
|
off each item.
|
|
|
|
Pattern in code:
|
|
1. POST endpoint: generate the PDF, upload via multipart/form-data
|
|
using the `order_item_attachements` field on each item in the group
|
|
2. GET endpoint: query `order_items` by the key field, find a file
|
|
whose name matches the expected pattern (e.g. `DN26071401.pdf`),
|
|
stream the bytes back via PB's `/api/files/...` URL
|
|
3. Stable filename per document type so the retrieval side can
|
|
disambiguate from other attachments on the same item
|
|
<!-- END:pb-attachments-pattern -->
|
|
|
|
<!-- BEGIN:file-url-paths -->
|
|
# File URLs go through `/api/v1/files/`, never `/api/files/`
|
|
|
|
The WFDASH file-serving proxy lives at
|
|
`/api/v1/files/{collection}/{recordId}/{filename}` (route file:
|
|
`src/app/api/v1/files/[collection]/[recordId]/[filename]/route.ts`).
|
|
Every `<a href={...}>` and every fetch from a client component that
|
|
opens a repair/quote/order attachment must use the `/api/v1/` prefix.
|
|
|
|
`/api/files/{collection}/{recordId}/{filename}` (no `v1/`) 404s —
|
|
that's the *PocketBase* convention, not a route in this app. The
|
|
PB instance serves files at that path, and the WFDASH proxy
|
|
forwards to it server-side, but a client that points directly at
|
|
`/api/files/...` gets the Next.js 404 page.
|
|
|
|
**Gotcha:** PocketBase record responses (e.g. `record.delivery_note`
|
|
as a `string[]` of filenames) sometimes come back as fully-qualified
|
|
PB URLs like `http://pb-host/api/files/...` — those should be
|
|
re-written to `/api/v1/files/...` on the client before rendering.
|
|
The existing detail page builds the path from scratch from the
|
|
record id + filename, which sidesteps the issue. Don't blindly
|
|
trust the URL PB hands back.
|
|
|
|
The `src/app/api/invoicing/delivery-note-pdf/route.test.ts` test
|
|
file intentionally matches `/api/files/...` in an expect — that's
|
|
asserting the *raw PB response* shape, not the URL the client
|
|
should use. Different concern.
|
|
<!-- END:file-url-paths -->
|
|
|
|
<!-- BEGIN:data-layer-discipline -->
|
|
# Data access layer discipline
|
|
|
|
The app has two coexisting data layers:
|
|
|
|
- **Legacy**: `src/lib/pb-helpers.ts` — used directly by Server Components
|
|
under `src/app/(app)/**/page.tsx` and form components.
|
|
- **Current**: `src/lib/server/*-service.ts` — exposed via `/api/v1/*` route
|
|
handlers and `/api/mcp`, used by the agent API and external integrations.
|
|
|
|
Both layers read the same PocketBase collections. Pick the right one
|
|
depending on what you're doing.
|
|
|
|
## Rules
|
|
|
|
**For new endpoints / features**: put business logic in
|
|
`src/lib/server/<feature>-service.ts` and expose it via a thin
|
|
`src/app/api/v1/<feature>/<op>/route.ts`. Do NOT extend `pb-helpers.ts`.
|
|
|
|
**For new pages / form components**: call `/api/v1/*` (or have the
|
|
Server Component call the corresponding `lib/server/*-service.ts`
|
|
function directly). Do NOT import from `pb-helpers.ts`.
|
|
|
|
**When touching a page that already uses `pb-helpers.ts`**: migrate its
|
|
data calls to the service layer in the same commit. One resource at a
|
|
time, no big-bang rewrites. Lowest-risk order: customers → orders →
|
|
quotes → suppliers → companies → everything else.
|
|
|
|
## How to spot you're reaching for the legacy layer
|
|
|
|
`pb-helpers.ts` is the file in question. Grep for it:
|
|
|
|
```bash
|
|
grep -rn 'from "@/lib/pb-helpers"' src/ --include="*.ts" --include="*.tsx"
|
|
```
|
|
|
|
Each hit is one caller to migrate when you're in the area.
|
|
|
|
## Migration cost indicator
|
|
|
|
A single page rewrite is typically: replace `getX(...)` import + call
|
|
with `await fetch('/api/v1/x?...')` (or a direct service-layer call),
|
|
then update the page to await. Half-day budget for 5 pages if no
|
|
weird cross-resource joins are involved.
|
|
<!-- END:data-layer-discipline -->
|