Adds the full Next.js app (app/, components/, lib/, scripts/, assets/, docs/, Dockerfile, compose, configs) to version control. Security: - CIPC BizPortal customer-code logins (Login 1 + Login 3) now read CIPC_LOGIN1_CODE / CIPC_LOGIN1_PASSWORD / CIPC_LOGIN3_CODE / CIPC_LOGIN3_PASSWORD from .env.local instead of source. - BizPortal personal login in apply-bizportal route reads BIZPORTAL_ID_NO / BIZPORTAL_PASSWORD. - Added .env.example as the committed template. .gitignore excludes .env*, node_modules, .next, logs, sqlite, Playwright storage_state.json (live CIPC cookies), scratch dirs, client PDFs and debug screenshots.
162 lines
5.2 KiB
Markdown
162 lines
5.2 KiB
Markdown
# 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=<paste the JWT here>
|
|
```
|
|
|
|
### 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.
|