Reconcile CIPC BizPortal against the Workflow database for every
director ID number we hold, and report only the two kinds of
difference that need a decision:
1. new appointment - CIPC lists the person as a director of company
X, the database does not.
2. no longer a director - the database lists the person at company X,
CIPC no longer shows them there.
Everything that is in sync is not reported, so the list stays short
enough to work through by hand.
- New page /cipc-report with an explorer that runs the check, polls
the run and lists the exceptions.
- New API route /api/cipc/report: POST starts a background batch run
of the existing "Bizportal Directopr id no search.py" (invoked
exactly as it already behaves, including its own login rotation),
GET returns run status and the exception lists. ?demo=1 returns a
fixed sample so the screen can be shown without a live run.
- New action route /api/cipc/report/action for the per-exception
follow-up actions.
- scripts/cipc-report/build_report.py renders the run into a PDF with
the exceptions marked, plus the sample report used by ?demo=1.
The BizPortal tooling only exists on the Windows dev machine, so like
the other CIPC routes this tab cannot run the live search from the
Coolify Linux image.
|
||
|---|---|---|
| .. | ||
| app | ||
| assets | ||
| components | ||
| docs/superpowers | ||
| lib | ||
| scripts | ||
| .cipc-service-launcher.cmd | ||
| .dockerignore | ||
| .env.example | ||
| .run-dev.ps1 | ||
| .start-cipc-service.ps1 | ||
| docker-compose.yml | ||
| Dockerfile | ||
| favicon.png | ||
| image-1.png | ||
| image-2.png | ||
| image.png | ||
| logo.png | ||
| middleware.ts | ||
| next-env.d.ts | ||
| next.config.mjs | ||
| package-lock.json | ||
| package.json | ||
| postcss.config.mjs | ||
| README.md | ||
| Sample_ChangeInShareholders_Letter.docx | ||
| Sample_ChangeInShareholders_Letter.pdf | ||
| tailwind.config.ts | ||
| tsconfig.json | ||
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.