# Workflow PocketBase Frontend A read-only Next.js frontend that browses two PocketBase collections: - `ysterbaard_directors` - `ysterbaard_companies` The PocketBase instance is private. The superuser JWT is held **server-side only** — a Next.js route handler proxies every collection request, attaches the bearer token, and forwards to PB. The browser bundle never sees `PB_TOKEN`. ## Routes | Path | Surface | |------|---------| | `/` | Dashboard — total counts, status rollup, legend | | `/directors` | Sortable, searchable list (table on desktop, cards on mobile) | | `/directors/[id]` | Director profile + linked companies | | `/companies` | Sortable, searchable, status-filtered list | | `/companies/[id]` | Company detail + linked director card | ## Stack - **Next.js 14** (App Router) + React 18 + TypeScript - **Tailwind CSS** for styling (Airbnb light theme — white canvas, Rausch coral accent) - **pocketbase JS SDK** on the server only - **TanStack Query v5** for client-side pagination + search - **Inter** as the webfont, via `next/font/google` ## Setup ### 1. Install ```bash npm install ``` ### 2. Pull the superuser JWT from the admin UI PocketBase v0.39 admin auth (`/api/admins/auth-with-password`) is unreachable from the public REST API — the only working admin auth lives behind `/_/`. Pull the JWT from the UI's localStorage: 1. Open `https://pb.agent.apphub.co.za/_/` and sign in with: - Username: `ysterbaard@apphub.co.za` - Password: `123456789` 2. Open DevTools, go to the **Console**, and run: ```js localStorage .getItem('__pb_superusers__/_') .match(/"token":"([^"]+)"/)[1] ``` 3. Copy the JWT string (no quotes). ### 3. Fill in `.env.local` ```bash cp .env.local.example .env.local ``` Edit `.env.local` so that: ``` PB_URL=https://pb.agent.apphub.co.za PB_TOKEN= ``` ### 4. Run the dev server ```bash npm run dev ``` Open `http://localhost:3000`. ## Acceptance criteria - [x] All 5 routes render with real data from PocketBase - [x] No superuser token leaks to the browser bundle (proxy holds it in `process.env`) - [x] `/directors` and `/companies` paginate, search, and link to detail pages - [x] Detail pages show the relation (`director` ↔ companies) correctly via `expand=director` - [ ] Lighthouse mobile score ≥ 90 (verify after deploy) - [ ] Local dev runs without errors ## Out of scope - Authentication UI for end users - Write/edit forms (this is read-only) - Charts / analytics dashboards (just stat cards) - Admin panel re-skin ## Architecture notes ### Server / client split - **Server Components** fetch the initial page directly through `lib/pb.ts` so the first paint always shows real data. No client spinner on cold load. - **Client Components** (the two Explorers) take that first page as `initialData` and re-fetch through `/api/pb/records/list` for pagination, search, and filter changes. - **API route handlers** sit in `app/api/pb/records/[list|...id]/route.ts`. They are the only place `PB_TOKEN` is referenced. ### Filter syntax PocketBase filter expressions used by the explorers: - Case-insensitive search: `field :~ 'value'` - Equality: `field = 'value'` - Boolean: `is_active = true` - Range match: `enterprise_status :~ 'deregist'` The `build*Filter` helpers in `app/directors/DirectorExplorer.tsx` and `app/companies/CompanyExplorer.tsx` are the canonical place to read / change the filter behaviour. ### Browser safety - `lib/pb.ts` imports `"server-only"` so any accidental import from a `"use client"` module trips a build-time error. - Tailwind utility classes reference CSS variables (`bg-surface`, `text-fg`, `border-border` — wired in `tailwind.config.ts`). No raw hex outside `globals.css`. ## Project structure ``` . ├── app/ │ ├── api/pb/records/ │ │ ├── list/route.ts # GET paginated list │ │ └── [id]/route.ts # GET single record │ ├── companies/ │ │ ├── [id]/page.tsx │ │ ├── CompanyExplorer.tsx │ │ └── page.tsx │ ├── directors/ │ │ ├── [id]/page.tsx │ │ ├── DirectorExplorer.tsx │ │ └── page.tsx │ ├── error.tsx │ ├── layout.tsx │ ├── not-found.tsx │ ├── globals.css │ └── page.tsx ├── components/ │ ├── NavBar.tsx │ ├── PageHeader.tsx │ ├── QueryProvider.tsx │ ├── Skeleton.tsx │ ├── StatCard.tsx │ └── StatusBadge.tsx ├── lib/ │ ├── api.ts # Browser-safe client (calls /api/pb/...) │ ├── format.ts # Defensive formatters │ ├── pb.ts # Server-only PB client │ └── types.ts # Director + Company + PBList types ├── .env.local.example ├── .gitignore ├── next.config.mjs ├── package.json ├── postcss.config.mjs ├── tailwind.config.ts └── tsconfig.json ``` ## Deploy Local dev only for this iteration. To wire up Vercel later: 1. Add the project as a Vercel app pointing at this directory. 2. Set the two env vars (`PB_URL`, `PB_TOKEN`) in the Vercel project settings. 3. The 14.x Next.js App Router setup auto-builds with no extra config.