Commit Graph
257 Commits
Author SHA1 Message Date
joshuaandClaude Opus 5.5 2d4bc5c38d Roadmap: AI helpers as the 2.0 headline, and later AI stages
CI / Backend tests (push) Waiting to run
CI / Frontend lint, typecheck, build (push) Waiting to run
CI / End-to-end tests (push) Blocked by required conditions
CI / Deploy tagged release (push) Blocked by required conditions
- §15 AI helpers (2.0): one-button helpers (draft an event, summarise
  reports and feedback, draft messages, improve text) on Claude Haiku 5.5,
  called through the Hub, which holds the key, meters usage in rand and
  enforces a monthly cap per licence; admin usage view; POPIA notes
- §16 AI later stages (unversioned): Ask AI, help/navigation assistant,
  actions with confirmation, more helpers, long-term self-hosted AI
- Hub: AI cap as the first per-licence limit, AiUsage model; decisions

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012mt6TCv85T9VKRq3cV7rXq
2026-10-24 14:36:25 +02:00
joshua f7a19f254a Merge branch 'feat/popia-retention'
CI / Backend tests (push) Canceled after 0s
CI / Frontend lint, typecheck, build (push) Canceled after 0s
CI / End-to-end tests (push) Canceled after 0s
CI / Deploy tagged release (push) Canceled after 0s
v1.40.0
2026-10-23 20:26:01 +02:00
joshuaandClaude Opus 5.5 8c5e6bd23c Bump to 1.40.0
CI / Backend tests (push) Canceled after 0s
CI / Frontend lint, typecheck, build (push) Canceled after 0s
CI / End-to-end tests (push) Canceled after 0s
CI / Deploy tagged release (push) Canceled after 0s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012mt6TCv85T9VKRq3cV7rXq
2026-10-23 20:26:01 +02:00
joshuaandClaude Opus 5.5 a1ecbb9617 POPIA toolkit part 3: retention, breach register, readiness
- Retention policies (Admin -> Privacy -> Retention) applied by a nightly
  `retention` job: inactive accounts anonymised after 5 years with a
  30-day warning (on by default; never staff roles or people with an
  upcoming registration), form answers, login history, payer details in
  payment notifications, audit/access logs
- User.lastLoginAt (set on login, backfilled) and inactivityWarnedAt
- Breach register (SecurityBreach) with drafts for the people affected
  and the Information Regulator
- Readiness checklist with a score
- Children's/medical flag in the access log; {retention} placeholder in
  the privacy notice
- Unit tests (retention.test.js), e2e (privacy-retention.spec.ts); docs

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012mt6TCv85T9VKRq3cV7rXq
2026-10-23 20:25:58 +02:00
joshua 6eee10b610 Merge branch 'feat/popia-admin-tools'
CI / Backend tests (push) Canceled after 0s
CI / Frontend lint, typecheck, build (push) Canceled after 0s
CI / End-to-end tests (push) Canceled after 0s
CI / Deploy tagged release (push) Canceled after 0s
v1.39.0
2026-10-23 19:11:23 +02:00
joshuaandClaude Opus 5.5 7b57fa4d04 Bump to 1.39.0
CI / Backend tests (push) Canceled after 0s
CI / Frontend lint, typecheck, build (push) Canceled after 0s
CI / End-to-end tests (push) Canceled after 0s
CI / Deploy tagged release (push) Canceled after 0s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012mt6TCv85T9VKRq3cV7rXq
2026-10-23 19:11:23 +02:00
joshuaandClaude Opus 5.5 d265047c84 POPIA toolkit part 2: privacy notice editor, operators, consent and access logs
- Admin -> Privacy (POPIA): edit the privacy notice (built-in text as the
  start, placeholders for org/IO/operators), drafts, preview, numbered
  versions; earlier versions readable on /legal/privacy?version=N
- "Significant change" versions are accepted once after login
  (NoticePrompt); sign-up records the version accepted
- Operator register (seeded with the services the app uses), listed in
  the notice via {operators}
- Consent log (news opt-ins, notice acceptances, parental consent) and
  access log (report exports, registers, certificates, group lists, form
  answers, consent/medical lists, backups, impersonation)
- Information Officer on the contact page
- Unit tests (privacyNotice.test.js), e2e (privacy-notice.spec.ts); docs

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012mt6TCv85T9VKRq3cV7rXq
2026-10-23 19:11:20 +02:00
joshua 40c5c9cc1d Merge branch 'feat/popia-privacy-centre'
CI / Backend tests (push) Canceled after 0s
CI / Frontend lint, typecheck, build (push) Canceled after 0s
CI / End-to-end tests (push) Canceled after 0s
CI / Deploy tagged release (push) Canceled after 0s
v1.38.0
2026-10-23 18:24:07 +02:00
joshuaandClaude Opus 5.5 261bc8a639 Bump to 1.38.0
CI / Backend tests (push) Canceled after 0s
CI / Frontend lint, typecheck, build (push) Canceled after 0s
CI / End-to-end tests (push) Canceled after 0s
CI / Deploy tagged release (push) Canceled after 0s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012mt6TCv85T9VKRq3cV7rXq
2026-10-23 18:24:07 +02:00
joshuaandClaude Opus 5.5 594cb35a85 POPIA toolkit part 1: privacy page, unsubscribe, WhatsApp STOP, data requests
- Privacy page: news opt-in per channel with history, download my data
  (JSON + PDF), correction and deletion requests
- Bulk messages are an Event update or News & announcements; news only
  reaches people who opted in, with unsubscribe links, one-click
  List-Unsubscribe headers and "Reply STOP" on WhatsApp
- WhatsApp STOP via WAWP's message webhook, checked against a secret in
  the webhook URL
- Sign-up opt-in box; existing accounts kept opted in and asked once
- Admin -> Data requests with 30-day due dates and a daily reminder job
- Deleting an account is a request; anonymising now covers attendees,
  form answers, consent details, payer details in provider payloads, etc.
- Unit tests (privacy.test.js) and e2e (privacy.spec.ts); docs

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012mt6TCv85T9VKRq3cV7rXq
2026-10-23 18:13:47 +02:00
joshua be5ad620da Merge branch 'fix/deploy-script'
CI / Backend tests (push) Canceled after 0s
CI / Frontend lint, typecheck, build (push) Canceled after 0s
CI / End-to-end tests (push) Canceled after 0s
CI / Deploy tagged release (push) Canceled after 0s
v1.37.4
2026-10-23 16:20:22 +02:00
joshuaandClaude Opus 5.5 96adf20cf0 Deploy script: roll back once (not inside subshells); docs; bump to 1.37.4
CI / Backend tests (push) Canceled after 0s
CI / Frontend lint, typecheck, build (push) Canceled after 0s
CI / End-to-end tests (push) Canceled after 0s
CI / Deploy tagged release (push) Canceled after 0s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 16:20:22 +02:00
joshuaandClaude Opus 5.5 a9c7a1129b Deploy: latest main without a tag; per-install PM2 config out of git
- scripts/deploy.sh with no tag deploys the latest main (detached
  origin/main); with a tag, that release.
- ecosystem.config.js is no longer in git (now ecosystem.config.example.js;
  the deploy creates the install's copy from it once). The deploy keeps the
  install's copy safe across checkouts and rollbacks, including releases
  that still tracked it.
- The script runs from a temp copy, so checking out a new deploy.sh can't
  change it mid-run; set -E so failures in functions still roll back.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 16:15:29 +02:00
joshua d8eb9f22cc Merge branch 'fix/help-entities'
CI / Backend tests (push) Canceled after 0s
CI / Frontend lint, typecheck, build (push) Canceled after 0s
CI / End-to-end tests (push) Canceled after 0s
CI / Deploy tagged release (push) Canceled after 0s
v1.37.3
2026-10-23 15:57:46 +02:00
joshuaandClaude Opus 5.5 4cae0fc672 Fix the production build: escape quotes in the Payments help text
CI / Backend tests (push) Canceled after 0s
CI / Frontend lint, typecheck, build (push) Canceled after 0s
CI / End-to-end tests (push) Canceled after 0s
CI / Deploy tagged release (push) Canceled after 0s
The deploy's next build fails on react/no-unescaped-entities. Bump to
1.37.3; the handover now says to lint the whole frontend before tagging.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 15:57:46 +02:00
joshua cd33ba281e Merge branch 'feat/payment-status'
CI / Backend tests (push) Canceled after 0s
CI / Frontend lint, typecheck, build (push) Canceled after 0s
CI / End-to-end tests (push) Canceled after 0s
CI / Deploy tagged release (push) Canceled after 0s
v1.37.2
2026-10-23 15:28:31 +02:00
joshuaandClaude Opus 5.5 220dafb400 Bump to 1.37.2
CI / Backend tests (push) Canceled after 0s
CI / Frontend lint, typecheck, build (push) Canceled after 0s
CI / End-to-end tests (push) Canceled after 0s
CI / Deploy tagged release (push) Canceled after 0s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 15:28:31 +02:00
joshuaandClaude Opus 5.5 eb0f390215 Payment status checks for notifications that never arrive
- src/payments/status.js: open checkouts, checkCheckouts and the
  payment-status job (every 15 min, checkouts 10 min - 3 days old). Found
  payments are settled exactly as their webhook would be (settlePayment,
  extracted from the webhook handler: amount check, record, emails).
- PayFast: signed API client (alphabetical signature, testing=true in
  sandbox), findPayments from the transaction history CSV matched on
  M Payment ID, and testConnection (signed /ping). Verified against a
  real sandbox account.
- Yoco documents no checkout lookup, so its checkouts are "can't check".
- Payments -> Reconcile: "Waiting for payment" with Check status /
  Check all; PayFast card: Test connection.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 15:28:28 +02:00
joshua 55a7ee2970 Merge branch 'feat/hide-pay-online'
CI / Backend tests (push) Canceled after 0s
CI / Frontend lint, typecheck, build (push) Canceled after 0s
CI / End-to-end tests (push) Canceled after 0s
CI / Deploy tagged release (push) Canceled after 0s
v1.37.1
2026-10-23 15:05:01 +02:00
joshuaandClaude Opus 5.5 4bc7dbc989 Bump to 1.37.1
CI / Backend tests (push) Canceled after 0s
CI / Frontend lint, typecheck, build (push) Canceled after 0s
CI / End-to-end tests (push) Canceled after 0s
CI / Deploy tagged release (push) Canceled after 0s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 15:05:01 +02:00
joshuaandClaude Opus 5.5 71b9da2458 Hide "Pay online" when no provider is on or the amount is under R15
A shared useOnlinePayments hook (GET /api/payments/providers) gates the
registration success page, My Events (Make payment, Make donation), the
donate page, Sponsor a spot, the register form's donation box and the
supervisor Payment Link tab; they explain why where it helps.

Roadmap: SnapScan and Zapper dropped; later versions move up by two.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 15:04:58 +02:00
joshua 654f51b86d Merge branch 'feat/payfast'
CI / Backend tests (push) Canceled after 0s
CI / Frontend lint, typecheck, build (push) Canceled after 0s
CI / End-to-end tests (push) Canceled after 0s
CI / Deploy tagged release (push) Canceled after 0s
v1.37.0
2026-10-23 14:26:09 +02:00
joshuaandClaude Opus 5.5 aeb5d6d678 Bump to 1.37.0
CI / Backend tests (push) Canceled after 0s
CI / Frontend lint, typecheck, build (push) Canceled after 0s
CI / End-to-end tests (push) Canceled after 0s
CI / Deploy tagged release (push) Canceled after 0s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 14:26:09 +02:00
joshuaandClaude Opus 5.5 f2b51dbd88 PayFast provider, Pay online page, amount checks on payments
- PayFast (custom integration): signed form (PHP-style encoding, checked
  against a real sandbox), ITN checks (signature over all posted fields,
  PayFast host, VALID from /eng/query/validate, amount vs checkout).
- ProviderCheckout records every checkout (amount, metadata, request);
  webhooks match payments to it and hold a different amount for review
  (ProviderTransaction.note).
- /pay/<id>: payers choose a provider when several are on, and the
  browser posts PayFast's form from there, so payment links work for
  every provider.
- Site settings → Payments: PayFast card (merchant ID/key, encrypted
  passphrase, sandbox/live) while PayFast is enabled.
- Payer-facing wording no longer names Yoco; legal pages name both.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 14:25:49 +02:00
joshua 25e3e9d3a6 Merge branch 'feat/payment-providers'
CI / Backend tests (push) Canceled after 0s
CI / Frontend lint, typecheck, build (push) Canceled after 0s
CI / End-to-end tests (push) Canceled after 0s
CI / Deploy tagged release (push) Canceled after 0s
v1.36.0
2026-10-23 12:17:56 +02:00
joshuaandClaude Opus 5.5 e7bed08a35 Bump to 1.36.0
CI / Backend tests (push) Canceled after 0s
CI / Frontend lint, typecheck, build (push) Canceled after 0s
CI / End-to-end tests (push) Canceled after 0s
CI / Deploy tagged release (push) Canceled after 0s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 12:17:56 +02:00
joshuaandClaude Opus 5.5 ede1ddbe00 Payment provider interface: Yoco behind it, Site settings → Payments
- src/payments: provider registry and PaymentProvider interface
  (createCheckout, verifyWebhook/parseWebhook, getTransaction, refund,
  listTransactions as capabilities), Yoco as the first provider. All four
  direct Yoco checkout calls go through payments.createCheckout.
- Webhook split into the provider part (signature, parsing) and shared
  processing; same URL (/api/webhooks/yoco), same behaviour.
- YocoTransaction renamed in place to ProviderTransaction (+ provider,
  unique per provider + externalId); Payment.provider added and
  backfilled to "yoco". Payment.method stays the wallet type so cashups
  are unchanged.
- Site settings → Payments: Enabled toggle and order per provider; Yoco's
  settings shown while it's enabled. R15 minimum for all online payments.
- New endpoint names (/api/payments/checkout, /api/provider-transactions);
  the old /yoco-checkout and /yoco-transactions paths still work.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 12:17:29 +02:00
joshua 8933a1fe77 Merge branch 'feat/session-gaps'
CI / Backend tests (push) Canceled after 0s
CI / Frontend lint, typecheck, build (push) Canceled after 0s
CI / End-to-end tests (push) Canceled after 0s
CI / Deploy tagged release (push) Canceled after 0s
v1.35.1
2026-10-23 11:38:43 +02:00
joshuaandClaude Opus 5.5 30e5db82df Bump to 1.35.1
CI / Backend tests (push) Canceled after 0s
CI / Frontend lint, typecheck, build (push) Canceled after 0s
CI / End-to-end tests (push) Canceled after 0s
CI / Deploy tagged release (push) Canceled after 0s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 11:38:43 +02:00
joshuaandClaude Opus 5.5 f99b84858e Session gaps: per-session waitlist, door session picker, all-sessions choice
- Waitlist per session: entries for pick-sessions tickets name a session;
  offers use that session's free places, never run past its start, and
  claim links tick it. Held offers count towards session capacity.
- At the door: the Select items pop-up has the session picker, with
  today's session preselected for new attendees.
- On pick-sessions events every ticket shows the session list straight
  away. "All sessions" tickets record the ticked sessions
  (RegistrationOption.sessionIds) at one price; scanning, attendance,
  certificates, reminders and capacity follow them.
- Fix: per-session stock limits weren't enforced (takenCount filtered
  WaitlistEntry by a sessionId column that didn't exist, and the error
  was swallowed), and the public event page lost its stock info.
- Fix: editing a door registration with picked sessions couldn't be saved.
- Fix: the offline-scanning service worker served /_next/static cache-first
  forever, so dev browsers that had opened the scanner ran stale code.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 11:38:21 +02:00
joshua 7c3f4694e2 Merge branch 'feat/pick-sessions'
CI / Backend tests (push) Canceled after 0s
CI / Frontend lint, typecheck, build (push) Canceled after 0s
CI / End-to-end tests (push) Canceled after 0s
CI / Deploy tagged release (push) Canceled after 0s
v1.35.0
2026-10-23 09:25:35 +02:00
joshuaandClaude Opus 5.5 b441ffac60 Bump to 1.35.0
CI / Backend tests (push) Canceled after 0s
CI / Frontend lint, typecheck, build (push) Canceled after 0s
CI / End-to-end tests (push) Canceled after 0s
CI / Deploy tagged release (push) Canceled after 0s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 09:25:32 +02:00
joshuaandClaude Opus 5.5 6010641629 Pick sessions (frontend), roadmap and handover docs
Event wizard: how people book (whole course / pick sessions), capacity
per session and per-option session mode. Session picker on the public
registration page, Manual registration and the kiosk; at the door points
to Manual registration. Register, tickets and My Events handle picked
sessions. Adds docs/ROADMAP-v2.md and docs/HANDOVER.md so work can carry
on from another machine, and a pick-sessions e2e test.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 09:25:21 +02:00
joshuaandClaude Opus 5.5 e338768f6e Pick sessions (backend): book individual sessions with per-session capacity
Options on a multi-day event can be bought per session (one line and
ticket per session, priced per session, bookable until the session
starts). Sessions can have a capacity covering picked places and
whole-course tickets. Picked tickets only scan on their own session; the
register, certificates and reminders follow the sessions booked.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 16:05:48 +02:00
joshua cc44e62379 Merge branch 'feat/ci-e2e-jobs'
CI / Backend tests (push) Canceled after 0s
CI / Frontend lint, typecheck, build (push) Canceled after 0s
CI / End-to-end tests (push) Canceled after 0s
CI / Deploy tagged release (push) Canceled after 0s
v1.34.0
2026-10-22 15:50:00 +02:00
joshuaandClaude Opus 5.5 5aba2e735f Bump to 1.34.0
CI / Backend tests (push) Canceled after 0s
CI / Frontend lint, typecheck, build (push) Canceled after 0s
CI / End-to-end tests (push) Canceled after 0s
CI / Deploy tagged release (push) Canceled after 0s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 15:49:56 +02:00
joshuaandClaude Opus 5.5 48660732fe Run background jobs on a pg-boss queue; add e2e tests, CI and deploy script
Timed jobs move from in-process timers to a job registry run by pg-boss:
once per schedule across processes, retries, run history, cron schedules
in the church timezone, and an admin Background jobs page with Run now.
Playwright end-to-end tests run against their own seeded database. A
Gitea Actions workflow runs backend tests, frontend checks and the e2e
tests, and deploys v* tags with scripts/deploy.sh once secrets are set.
Also fixes the at-the-door check-in count for multi-day tickets.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 15:49:46 +02:00
joshua 8cb2755493 Merge branch 'feat/multi-day-sessions' v1.33.0 2026-10-22 14:54:03 +02:00
joshuaandClaude Opus 5.5 e84d390393 Bump to 1.33.0
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 14:53:59 +02:00
joshuaandClaude Opus 5.5 39558577f7 Add multi-day events (frontend), attendance register and certificates
Event wizard: one date or multiple sessions with a repeat-pattern builder,
and a new Attendance step (late joining and pricing, check-in window,
session reminders, certificates with preview). Public pages show the
schedule and late prices; the scanner picks today's session; new
attendance register page, My Certificates, and a public verify page.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 14:53:39 +02:00
joshuaandClaude Opus 5.5 c79b74bc67 Add multi-day event sessions (backend)
Events can run over several sessions: per-session check-in with a whole-day
or session-time window, late joining with pro-rata or fixed late pricing,
an attendance register (Excel/PDF), session reminders, and completion
certificates (four templates, public verify page).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 14:20:52 +02:00
joshua 6a8ef62630 Merge branch 'feat/early-bird-ribbon' v1.32.0 2026-10-22 13:48:29 +02:00
joshuaandClaude Opus 5.5 dbac248d8f Bump to 1.32.0
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 13:48:28 +02:00
joshuaandClaude Opus 5.5 b1af901d6a Show an early-bird ribbon on event cards
Event cards on the home page and events list get a diagonal "Early bird"
ribbon in the top-right corner while an early-bird tier is running and
registration is open. GET /api/events returns earlyBirdEndsAt per event.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 13:48:22 +02:00
joshua 0bddb329e3 Merge branch 'feat/google-site-verification' v1.31.1 2026-10-22 11:43:01 +02:00
joshuaandClaude Opus 5.5 be4b6f02ea Bump to 1.31.1
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 11:43:00 +02:00
joshuaandClaude Opus 5.5 880e3206cc Add Google Search Console verification setting
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 11:42:56 +02:00
joshuaandClaude Opus 5.5 7b2bfc31e6 Merge branch 'feat/seo-and-traffic-sources'
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
v1.31.0
2026-10-22 09:26:17 +02:00
joshuaandClaude Opus 5.5 963c1824bf Bump to 1.31.0
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 09:26:15 +02:00
joshuaandClaude Opus 5.5 38262022ef Add event structured data, canonical URLs and referrer-based traffic sources
- Event pages emit schema.org Event JSON-LD (dates, venue, image,
  organizer, ZAR offers, sold-out status) for Google event results.
- Event pages, the events list and home page set a canonical URL so
  tagged and alias links don't split rankings.
- Funnel tracking falls back to the referring site (Facebook, Instagram,
  Google, ...) and fbclid/gclid when a link has no utm_source/src.
- Share and Save QR on the event page tag their links as share / qr.
- getServerSettings moves to lib/serverSettings.ts for reuse.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 09:26:02 +02:00