Files
hope-events/docs/HANDOVER.md
T
joshuaandClaude Opus 5.5 96adf20cf0
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
Deploy script: roll back once (not inside subshells); docs; bump to 1.37.4
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 16:20:22 +02:00

8.5 KiB
Raw Blame History

Handover — where things stand and how to carry on

Last updated: 2026-10-23, after 1.37.4. The plan itself is in ROADMAP-v2.md (decisions table at the end); the previous round of features is in FEATURES-ROUND-1.md.

Done so far (road to 2.0)

Version What
1.32.0 Early-bird ribbon on event cards
1.33.0 Multi-day events phase A — sessions, late joining + late pricing, per-session check-in, attendance register, session reminders, certificates (4 templates, verify page)
1.34.0 Background jobs on pg-boss (admin Background jobs page), Playwright e2e tests, Gitea CI workflow, scripts/deploy.sh
1.35.0 Multi-day events phase B — "pick sessions" (per-session tickets and prices, session capacity)
1.37.4 Deploy script: no tag = latest main; per-install ecosystem.config.js out of git (ecosystem.config.example.js)
1.37.3 Fix: the production build failed on unescaped quotes in a help text
1.37.2 Payment status checks (PayFast transaction history; job every 15 min; Payments → Reconcile → Waiting for payment), PayFast Test connection
1.37.1 "Pay online" hidden when no provider is on or under R15; SnapScan/Zapper dropped from the roadmap
1.37.0 PayFast (signed form, ITN checks), ProviderCheckout with amount checks, the "Pay online" page (/pay/<id>) where payers choose a provider
1.36.0 Payment provider interface (backend/src/payments) with Yoco behind it, ProviderTransaction, Payment.provider, Site settings → Payments (on/off and order per provider), R15 minimum
1.35.1 Waitlist per session, session picker at the door, "All sessions" tickets let people tick sessions at one price; fixes (per-session stock limits, offline service worker serving stale scripts)

Next up

  1. 1.38.0–1.39.0 — POPIA toolkit (spec §3 in the roadmap).
  2. 1.40.0 + Hub v1 — install/update tooling and the Hope Events Hub (separate repo joshua/hope-events-hub).

Working on this repo

  • Release routine: branch feat/<name> → commit → bump with npm version X.Y.Z --no-git-tag-version in ., backend and frontend → add the ## [X.Y.Z] - date heading under [Unreleased] in CHANGELOG.md → git merge --no-ff into main → git tag vX.Y.Z → git push origin main <branch> vX.Y.Z. One minor version per feature.
  • Before tagging: run npx eslint src in frontend over the whole app (the deploy runs next build, which fails on any lint error, e.g. an unescaped ' or " in JSX text). If a dev server is running, a full check build needs its own folder: NEXT_DIST_DIR=.next-buildcheck npx next build (then delete it and git checkout frontend/tsconfig.json).
  • Changelog: add entries to [Unreleased] as you go, in plain language.
  • Docs to keep in step: CHANGELOG.md, FEATURES.md, README.md, SETTINGS.md, backend/API_DOCUMENTATION.md, the /docs route list in backend/src/index.js, and in-app help in frontend/src/content/help/ (register new pages in registry.ts).
  • Prisma migrations: npx prisma migrate dev --create-only --name x against a local DB. Prisma 7 keeps adding an unrelated _EventNotifyRecipients primary-key/index change — delete those two blocks from the generated SQL. It also rewrites the comment in migration_lock.toml; git checkout that file back. New AdminAuditAction values need a migration.
  • Server Node version: the deployment notes install Node 20, so dependencies must support Node 20 (that's why pg-boss is pinned to 10.x).
  • Deploying: bash scripts/deploy.sh [tag] on the server (no tag = latest main). Each install keeps its own ecosystem.config.js (PM2 app names, port), which is not in git. The script runs from a temp copy of itself and rolls back on failure before the PM2 reload. It was tested by running it against a throwaway clone with npm/pm2 stubbed: across the tracked → untracked change for the PM2 file, deploying main, failed builds and a bad tag.
  • Pushing: pushing from some shells fails with "Failed to authenticate user" (a stale saved credential); pushing from your own terminal works.

Tests

  • Backend unit tests: cd backend && npm test (Jest).
  • PayFast e2e (e2e/payfast.spec.ts) pays into a real PayFast sandbox account and is skipped unless E2E_PAYFAST_MERCHANT_ID, E2E_PAYFAST_MERCHANT_KEY and E2E_PAYFAST_PASSPHRASE are set (from the Sandbox dashboard). PayFast's documented public test merchant (10000100) no longer accepts payments. ITNs can't reach localhost; they're covered by unit tests (backend/tests/payments.test.js) — to try one for real, use a tunnel, a public BACKEND_URL and PAYFAST_SKIP_IP_CHECK=true.
  • End-to-end: npm run test:e2e from the repo root (first time: npm install and npx playwright install chromium). They start their own backend (5100) and frontend (3100) against <your DATABASE_URL database>_e2e, which is dropped and re-seeded every run (e2e/support/prepare-db.js). The DB user must be able to create databases. ~5 minutes in dev mode. Locally, Playwright reuses servers already running on 5100/3100. If the run times out waiting for the frontend (the home page returns 500 until the e2e backend is up), start them yourself — node e2e/support/prepare-db.js, then the backend from backend/ with PORT=5100, the e2e DATABASE_URL, JOBS_ENABLED=false, TWO_FACTOR_REQUIRED_ROLES=false, and npx next dev -p 3100 from frontend/ with NEXT_DIST_DIR=.next-e2e NEXT_PUBLIC_API_URL=http://localhost:5100 — then run npx playwright test. Don't edit frontend files while it runs.
  • Careful with ad-hoc scripts against a dev/test database that has SMTP set up: registrations and check-ins send real email/WhatsApp. Use made-up @test.local people only.
  • CI: .gitea/workflows/ci.yml runs everything once a Gitea Actions runner is registered (none yet). Tag deploys need the DEPLOY_HOST, DEPLOY_USER, DEPLOY_SSH_KEY (+ optional DEPLOY_PATH) secrets.

Where the multi-day code lives

  • backend/src/utils/sessions.js — all session rules (dates, check-in window, late pricing, pick sessions, capacity, grouping); unit tests in backend/tests/sessions.test.js.
  • backend/src/utils/certificates.js, sessionReminders.js, controllers/attendanceController.js (register + certificates API).
  • backend/src/jobs/index.js — the background job registry.
  • Frontend: src/lib/sessions.ts, src/components/events/ScheduleEditor.tsx, SessionPicker.tsx, SessionSchedule.tsx, src/app/dashboard/supervisor/attendance/.

Where the payment code lives

  • backend/src/payments/index.js — the provider registry and the PaymentProvider interface (documented at the top), provider on/off and order (AppSetting payment_providers), the R15 minimum, and createCheckout. Tests: backend/tests/payments.test.js.
  • backend/src/payments/providers/yoco.js — Yoco: checkouts, webhook signature check, webhook parsing. Keys/webhook admin stays in utils/yoco.js + controllers/yocoController.js.
  • backend/src/payments/webhook.js — shared webhook processing (Payment with provider, donation split, registration status, tickets, emails, ProviderTransaction).
  • backend/src/payments/checkouts.js — registration checkouts (balance, part, + donation).
  • backend/src/payments/status.js — status checks for notifications that never arrived (payment-status job, Payments → Reconcile → "Waiting for payment"). PayFast looks payments up in its transaction history (findPayments); Yoco's Checkout API documents no lookup, so Yoco checkouts are "can't check". Tests: backend/tests/paymentStatus.test.js.
  • Adding a provider: write providers/<id>.js with the same interface, add it to PROVIDERS in index.js, mount handleProviderWebhook('<id>') at /api/webhooks/<id> (routes/webhookRoutes.js), and give it a settings card in frontend/src/components/admin/PaymentsTab.tsx. New providers start switched off.
  • Payment.method stays the wallet type (card, apple_pay…) because cashups bucket on it; the provider is Payment.provider.

Known gaps / follow-ups

  • At the door, per-session tickets are booked on new registrations only; editing an existing registration leaves its booked sessions as they are (the backend refuses changes to picked lines). "Add & charge" for a session at the scanner isn't built.
  • Ticket scanning by camera isn't covered by the e2e tests (headless browsers have no camera); scans are tested through the API.