Add complete production-ready Ysterbaard PocketBase registry browser frontend: - Next.js 14 App Router with TypeScript, Tailwind CSS, and TanStack React Query - Secure server-side PocketBase API proxy to avoid exposing superuser tokens - Read-only directors and companies registry browse pages with search, filtering, and pagination - Client onboarding workflow for creating and managing client submissions - Docker container configuration for easy deployment - All supporting UI components, utility functions, and configuration files
5.2 KiB
Workflow PocketBase Frontend
A read-only Next.js frontend that browses two PocketBase collections:
ysterbaard_directorsysterbaard_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
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:
-
Open
https://pb.agent.apphub.co.za/_/and sign in with:- Username:
ysterbaard@apphub.co.za - Password:
123456789
- Username:
-
Open DevTools, go to the Console, and run:
localStorage .getItem('__pb_superusers__/_') .match(/"token":"([^"]+)"/)[1] -
Copy the JWT string (no quotes).
3. Fill in .env.local
cp .env.local.example .env.local
Edit .env.local so that:
PB_URL=https://pb.agent.apphub.co.za
PB_TOKEN=<paste the JWT here>
4. Run the dev server
npm run dev
Open http://localhost:3000.
Acceptance criteria
- All 5 routes render with real data from PocketBase
- No superuser token leaks to the browser bundle (proxy holds it in
process.env) /directorsand/companiespaginate, search, and link to detail pages- Detail pages show the relation (
director↔ companies) correctly viaexpand=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.tsso the first paint always shows real data. No client spinner on cold load. - Client Components (the two Explorers) take that first page as
initialDataand re-fetch through/api/pb/records/listfor pagination, search, and filter changes. - API route handlers sit in
app/api/pb/records/[list|...id]/route.ts. They are the only placePB_TOKENis 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.tsimports"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 intailwind.config.ts). No raw hex outsideglobals.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:
- Add the project as a Vercel app pointing at this directory.
- Set the two env vars (
PB_URL,PB_TOKEN) in the Vercel project settings. - The 14.x Next.js App Router setup auto-builds with no extra config.