Commit graph

44 commits

Author SHA1 Message Date
ed3c6de272 Compliance snapshot redesign: BizPortal AR summary + SARS placeholder; CIPC address labels; Math.abs amount fix 2026-08-27 21:18:22 +02:00
Ysterbaard
a79145f0d2 Fix: clearing a field in the edit modal did not persist
The modal's submit handler wrapped every optional field with
"if (value.trim()) fields.x = value.trim()". When the user
deleted the content of a field, the value was empty, the guard
returned false, and the field was OMITTED from the PATCH entirely.
PB then left the existing value in place.

Symptom: edit a field, delete its content, save. The DB still
has the old value.

Fix: always send every field. Empty string ("") clears a value
in PocketBase (verified end-to-end). Required-field validation
(surname, first_names, enterprise_no, enterprise_name) still
guards against empty required values up-front.

Also fixed the modal's React Query invalidation to target the
list pages' actual keys (["companies"], ["directors"]) in
addition to the incorrect ["pb-list"] key. This makes the
list page refetch after save.

Same fix applied to DeleteConfirmModal for consistency.
2026-08-19 12:04:58 +02:00
Ysterbaard
7f0337a39c Fix list page modal not refetching after save
When the edit modal was opened from /companies or /directors (the
list pages) and the user saved, the modal called
onAfterSave={() => {}} — an empty handler. The modal also fired
qc.invalidateQueries(['pb-list', collection]), but the list
page's queryKey is ['companies', ...] (or ['directors', ...]),
not ['pb-list', ...]. So the list page never re-fetched.

Result: stale row data persisted in the list state. Re-opening
the modal showed the original values, making it look like the
save hadn't taken effect (it had — the data was in PB; the list
just didn't know about it).

Fix: wire onAfterSave to the existing refreshAll() helper in
both list pages so the list refetches from PB and the local
editingCompany/editingDirector state stays in sync.
2026-08-19 11:42:02 +02:00
Ysterbaard
42b4b2812b Fix structured-address + onboarding fields not persisting from edit modal
The /api/pb/records/[id] proxy had an allowlist of editable fields that
was written before the 2026-08-18 onboarding field additions. The new
fields were silently dropped before reaching PocketBase, so the modal
appeared to save successfully but the data never landed in PB.

Added to COMPANY_EDIT_FIELDS:
  physical_address_line_1/2, physical_town_city, physical_province,
  physical_postal_code, postal_address_line_1/2, postal_town_city,
  postal_province, postal_postal_code, company_email, company_cell_phone,
  public_officer_name, company_secretary_name, financial_year_end_month,
  authorised_shares

Added to DIRECTOR_EDIT_FIELDS:
  residential_address_line_1/2, residential_town_city, residential_province,
  residential_postal_code, postal_address_line_1/2, postal_town_city,
  postal_province, postal_postal_code, date_of_birth

End-to-end tested via proxy PATCH — fields now land in PB.
2026-08-19 10:20:11 +02:00
Ysterbaard
1fecf02214 Add pre-send validation gate + idempotent sign-back to appointment flow
PB: 3 new fields on ysterbaard_director_appointments
  - proceed_anyway (bool) — true if user proceeded with missing emails
  - missing_emails (text) — comma-separated keys
  - missing_emails_detail (text) — JSON per-recipient validation

App:
  - POST /api/appointments: builds recipient report (initial +
    distro); returns 422 + report if proceed_anyway is missing
    and any recipient is missing/invalid. preview=1 returns the
    report without sending.
  - PATCH /api/appointments/[id]: tightened idempotency guard to
    also short-circuit on signature_received (not just
    mail_sent_to_all). Prevents double-send on double-click.
  - DirectorAppointmentTab: two-step modal — "Validate recipients"
    surfaces the report (status dots + per-recipient email),
    then Send / Proceed anyway / Fix records first. New
    "company_email" gap surfaced before any email leaves.
  - lib/format.ts: isValidEmail() shared helper.

End-to-end tested: 422 enforces server-side, preview returns
the report, double-PATCH signs only once.
2026-08-18 21:29:08 +02:00
Ysterbaard
9bd35898a2 Add onboarding form fields to companies + directors (visual + editable)
PB: 15 fields on ysterbaard_companies (financial_year_end_month,
authorised_shares, structured physical/postal address, company_cell_phone,
public_officer_name, company_secretary_name), 11 fields on
ysterbaard_directors (date_of_birth, structured residential/postal address).

App: types widened in lib/types.ts. EditEntityModal rebuilt with full
address sections (line 1, line 2, town/city, SA province dropdown,
postal code), DOB, company-level fields, financial year end month,
authorised shares, public officer, company secretary.

View pages: structured address blocks now show labeled fields
(line 1, line 2, town/city, province, postal code) on both
/companies/[id] and /directors/[id].
2026-08-18 12:25:06 +02:00
Dave
4066c1c43c Use master template style (Arial 11pt, justified, 1.5 spacing) for appointment letter PDF 2026-08-17 22:58:51 +02:00
Dave
efa4331b99 PDF daemon + attachments for appointment emails
- New /Volumes/KIOXIA/.hermes/profiles/ysterbaard/scripts/pdfd.js
  standalone HTML->PDF daemon on localhost:3070, calls soffice via
  execSync (proven working pattern from resignation form watcher.js)
- app/api/appointments/route.ts generates acceptance-letter.pdf via
  daemon, attaches via MIME multipart/mixed
- app/api/appointments/[id]/route.ts renders signed-letter.pdf with
  embedded signature, attaches to distro mail
- Idempotency guard: skip distro if status is already mail_sent_to_all
2026-08-17 22:34:58 +02:00
Dave
8ae44e42e4 Fix signed distro: send HTML email with inline signature image (no PDF required) 2026-08-17 22:16:41 +02:00
Dave
ca697a3b19 Fix signature canvas: preventDefault on touch events, scale coords to canvas pixels 2026-08-17 22:03:07 +02:00
Dave
620a2373bb Fix signing page: show canvas unless status is signature_received or mail_sent_to_all 2026-08-17 21:57:11 +02:00
Dave
937fa922cc Remove blocking PDF generation from appointment POST (LibreOffice hangs from Next.js) 2026-08-17 21:45:36 +02:00
Dave
e8ef4589c9 Simplify appointment email: HTML body, no PDF attachment (LibreOffice spawn unreliable from Node.js) 2026-08-17 20:57:51 +02:00
Dave
cef29c7f1f Fix LibreOffice PDF output: check /private/tmp and rename to /tmp/ 2026-08-17 20:51:55 +02:00
Dave
1b120d2677 Fix LibreOffice: spawnSync array form + HOME/PATH env for Next.js server 2026-08-17 20:47:58 +02:00
Dave
ff5697507d Use spawn (not shell) for himalaya — avoids /bin/sh ETIMEDOUT 2026-08-17 20:44:45 +02:00
Dave
00634d2e1d Use himalaya message send --save Sent (saves to Sent folder) 2026-08-17 20:39:22 +02:00
Dave
6e0e6d1da7 Increase himalaya SMTP timeout to 60s 2026-08-17 20:37:22 +02:00
Dave
61c5830028 Fix himalaya command: use 'smtp send' with --mail-from/--rcpt-to flags 2026-08-17 20:31:53 +02:00
Dave
660db975e4 Increase LibreOffice timeout to 120s, add graceful fallback to text-only email 2026-08-17 20:23:02 +02:00
Dave
37d94f4241 Add company_email to EditEntityModal company form 2026-08-17 20:08:33 +02:00
Dave
b0b625414a Add company_email to company table and detail page 2026-08-17 19:19:12 +02:00
Dave
19f60eba42 fix: handle unknown/empty task statuses without crashing
A task with status='' caused the Kanban byColumn memo to throw
TypeError: undefined is not an object (out[t.status].push).

Defensive UI: bucket unknown statuses into backlog with a console
warning. Defensive API: reject PATCHes with invalid status values
(including empty string) with a 400. Cleaned up two existing
empty-status rows in PB.
2026-08-17 19:00:42 +02:00
Dave
c349998fa1 feat: Task Kanban UI + Python task worker
Adds the visible front-end and the worker back-end for the agent
task system. The PocketBase schema, API routes, and ToastProvider
were already in the previous commit; this commit completes the
picture.

UI
- app/tasks/{page,KanbanBoard,TaskCard,CardModal,TaskDetail,api,columns}
  Full Kanban with six columns (Backlog, Ready, In Progress,
  Blocked, Done, Failed), drag-and-drop between columns, quick-add
  chips, type/priority filters, task detail panel with retry/cancel/
  delete, 5s polling, transition toasts.
- components/NavBar.tsx: 'Tasks' link in primary nav.
- app/bizportal-2/BizPortal2Explorer.tsx: 'Send to Kanban' button
  on the result view — dispatches the current search to the agent
  queue as a 'ready' task.

Back-end
- scripts/task_worker.py: PocketBase polling worker with two
  lanes. Deterministic handles bizportal_search/reserve, pdf_generate,
  email_send. Custom lane writes to /tmp/agent_inbox/ for the
  LLM agent to pick up. Atomic claim via PB filter, auto-retry up
  to 3x, structured log on each task.
- scripts/bizportal_service.py: bugfix — _is_logged_in was using
  a non-CSS selector so session-recovery was a no-op. Also made
  the login flow re-runnable mid-session (handles BizPortal's
  ~20-minute session timeout transparently).
- scripts/init_pb_schema.py: duration_ms no longer capped at 0.

Verified end-to-end:
- CK search via worker: 1 company, 3 directors, ~10s
- ID search via worker: 4 companies, 7 total directors, ~34s
- Auto-retry + status transitions + toast fires
2026-08-17 18:57:02 +02:00
Dave
2fb979b98a Director appointment acceptance — full feature
- DirectorAppointmentTab: table + 'Acceptance Letter' modal with company/director dropdowns
- GET/POST /api/appointments: create record, generate HTML letter, convert to PDF via LibreOffice, email to director via himalaya SMTP
- PATCH /api/appointments/[id]: save signature PNG, embed in signed letter PDF, mail to all company directors
- GET /api/appointments/sign/[token]: public token lookup
- /sign/[token]/page.tsx: public signature canvas (mouse + touch)
- ysterbaard_director_appointments PB collection (requires manual schema setup)
- Section 3 'Director Appointment' now active in ResolutionExplorer
- NEXT_PUBLIC_APP_URL added to .env.local
2026-08-17 18:39:14 +02:00
Dave
da504c45a0 wip: registration wizard, address splits, document panel, appointments
Uncommitted work in progress at the point of starting the Kanban
project. Captures:
- Registration wizard refactor + multi-step routing
- structured physical/postal address fields on companies and directors
- appointments flow (sign + verify)
- document type manager + upload/panel for companies and directors
- status actions, approve button, action/dropdown buttons
- step2 review page

tsconfig.tsbuildinfo is now ignored (build artefact).
2026-08-17 18:33:56 +02:00
Dave
7c3847fbb0 feat: BizPortal 2.0 — Python Playwright sidecar
Replaces the brittle agent-driven /api/bizportal/search queue with a
deterministic Python FastAPI service that drives BizPortal via a real
headless Chromium. The old flow is untouched.

What this adds:
- scripts/bizportal_service.py — FastAPI + Playwright service with
  /search, /reserve, /health endpoints. Persistent login session
  via storage_state.json. Locator-based selectors (vs eval JS).
- app/bizportal-2/ — new /bizportal-2 page, synchronous fetch
  (no polling), shows round-trip duration, mirrors the existing
  bulk-add + link UX.
- app/api/bizportal-2/{search,reserve,health}/route.ts — thin
  proxies to the Python sidecar.
- scripts/README.md, .env.example, .gitignore, requirements.txt.
- .gitignore: ignore tsconfig.tsbuildinfo (build artefact).

Wiring:
- components/NavBar.tsx: 'BizPortal 2.0' nav entry with TEST badge.
- app/bizportal/page.tsx: 'Try BizPortal 2.0' action link in
  header so the new option is discoverable from the existing page.

Run: cd scripts && source .venv/bin/activate && uvicorn
bizportal_service:app --port 7001
2026-08-17 18:33:36 +02:00
Dave
2d402c0482 fix: strip id/system fields before creating PB records 2026-08-14 21:06:21 +02:00
Dave
c1b98a6eca feat: split company physical and postal addresses into structured fields 2026-08-14 21:00:14 +02:00
Dave
4c0a256d63 feat: split director residential and postal addresses into structured fields 2026-08-14 20:49:44 +02:00
Dave
8f26181514 feat: remove step 2 and step 3 — will rebuild with skill-driven flow 2026-08-14 20:39:18 +02:00
Dave
c8fcddfbf1 feat: print to PDF button on registration detail view 2026-08-14 19:51:59 +02:00
Dave
5cedaf714c feat: add BizPortal tab + name-already-approved flow + registrations improvements 2026-08-14 18:08:51 +02:00
Dave
c5ee550795 feat: Revert button also in name_approved section 2026-08-11 21:07:48 +02:00
Dave
567972bd19 fix: Revert button also in bizportal_in_progress section 2026-08-11 21:07:00 +02:00
Dave
d05a2440db feat: Revert to Step 1 (New Names) button — clears CIPC fields on rejection 2026-08-11 20:59:46 +02:00
Dave
59c460a3d6 feat: Check Mail button on Step 2 + check-cipc-mail API route 2026-08-11 18:33:03 +02:00
SJCoetz
d2a7de5ccc Add /api/register/all endpoint + fix pb.ts timeout + wire registrations page to it 2026-07-30 20:46:42 +02:00
SJCoetz
68ce2bd586 Redesign registrations list: client-side table with copy/share links, sorted newest first 2026-07-30 20:38:24 +02:00
SJCoetz
45e017c7c3 Add 'View all →' shortcut to Registrations page in Resolutions tab 2026-07-30 18:24:23 +02:00
SJCoetz
2d06ca27d6 Use type=tel for all ID number fields to trigger numeric keypad on mobile 2026-07-30 17:53:41 +02:00
SJCoetz
adead9148f Add native Share button to registration link dialog 2026-07-30 16:38:05 +02:00
SJCoetz
e9b5324e1c build: remove empty public dir from docker image
Add a conditional cleanup step to remove the public directory if it does not exist or contains no files. This reduces the final docker image size since Next.js standalone builds automatically inline public assets when they are present, making the copied public directory often redundant.
2026-07-23 19:26:49 +02:00
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