122 lines
8.5 KiB
Markdown
122 lines
8.5 KiB
Markdown
# 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](./ROADMAP-v2.md) (decisions table at the end); the previous round of
|
||
features is in [FEATURES-ROUND-1.md](./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.
|