WORKFLOW-APP/README.md
SJCoetz bb97fef453 feat: initialize full ysterbaard frontend application
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
2026-07-23 19:10:47 +02:00

5.2 KiB

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

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:

    localStorage
      .getItem('__pb_superusers__/_')
      .match(/"token":"([^"]+)"/)[1]
    
  3. 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)
  • /directors and /companies paginate, search, and link to detail pages
  • 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.