A setup wizard run on one server added files to backend/src that the
backend build picked up, so the deploy failed part-way and rolled back.
deploy.sh now lists files git reports as changed or added (ignored files
and the PM2 config don't count) and stops before backing up or checking
out. --check runs only that; ALLOW_LOCAL_CHANGES=1 deploys anyway; the
script also runs from a copy outside the install (the folder you're in).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Most routes answered by a v1 handler showed an empty body and
`data: unknown` in /docs/v2. Each now documents what its handler reads
and answers, and every path and query parameter is described.
- `documented: { body?, response? }` on v1Route/defineRoute, with shared
shapes in src/v2/docs.ts. Documentation only: not enforced, so nothing
that works can start failing; outside production a body or answer that
doesn't fit is logged ("doesn't match its documented schema").
- QUERY_HELP / PATH_HELP describe common parameters; empty bodies say
"No body needed".
- Declared the funnel's eventIds and webhook deliveries' status/before.
- tests/v2Docs.test.ts keeps the document complete; frontend types
regenerated, three call sites' types tightened (no payload change).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On events with a required attendee form, tickets waited for the forms, but
At the door and Manual registration never asked for them: payment was
recorded and nobody could be checked in.
- The form pops up at the door and in Manual registration; "Fill in later"
(any staff) issues the tickets once paid and sends the payer a link
(Registration.formWaivedAt, POST /api/v2/registrations/{id}/form-later).
- One form per person on picked-session bookings; a required form with no
questions no longer holds tickets back; every caller asks the ticket
generator (formsOutstanding).
- Kiosk asks for the forms the server says are missing; headings show as text.
- Admin Corrections page and /api/v2/corrections: issue missing tickets,
pay later (ticketsReleasedAt), make it free (complimentaryDiscount),
check in or undo after the event.
- Migration 20261102120000_corrections; unit and e2e tests.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The site calls AI through the Hub (protocol 3b, Hub 1.6.0): src/ai
works out whether the AI buttons show (licence token first, then the
Hub, cached a minute), runs a helper call with per-helper switches and
a per-person daily limit, logs AiCall rows, and emails admins at 80%.
v2 /ai/status, /ai/usage, /ai/settings; useAiStatus, AiButton, AiTab.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
HANDOVER: version table in order with 1.51.4/1.51.5 added; Next up
renumbered with the 2.0 clean-ups (lint rules, node-fetch, password
hashing); event picker points at fetchManagedEvents; stale lines fixed.
ROADMAP: status rewritten as a dated summary; section 14 marks what's
done and that CI was removed; Haiku prices flagged to check.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- /api/v2/webhooks/{yoco,payfast,whatsapp}; new provider set-ups use them, the v1 addresses
keep working with the same handlers.
- Calendar and one-click unsubscribe links point at v2; old links answer 308 (a one-click POST
to the old address is still handled there).
- /api/webhooks/whatsapp runs the real WhatsApp handler (was an empty stub).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ls1vMdg2GYqbnKZmPaX1ot
- v2: my privacy, unsubscribe links, the privacy admin, audit log, jobs, impersonation,
settings and first-run set-up, the Hub, outbound webhooks, backups, Yoco/PayFast settings.
- Every call the app makes is now on /api/v2 (incoming webhooks and message links: 1.51.16).
- v1Route handles res.download/sendFile; impersonation end/log allowed on v2.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ls1vMdg2GYqbnKZmPaX1ot
- v2: tickets (mine, one, send, issue, transfers, scans), scan locks, the attendance register
and its export, certificates, allocations.
- Frontend: apiV2Raw() for string-path helpers; every ticket/attendance screen on v2.
- v1Route can run a v1 router by URL; an unanswered chain is a 404.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ls1vMdg2GYqbnKZmPaX1ot
- v2: payments, refunds, donations, payment links and the pay page, providers, cash-ups,
provider transactions, waitlist, sponsorship, consent, form responses, feedback.
- src/v2/viaV1.ts: v1Route() answers a v2 route with the v1 handler in-process.
- /docs/v2 opens collapsed with a filter (Swagger UI stopped drawing past ~130 endpoints).
- Frontend: every money screen on v2.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ls1vMdg2GYqbnKZmPaX1ot
- v2: staff list (paged), event registrations, registration in full (+ /me), status, manual
registration, bulk, items, invoice, coupon, attendees and form links, form drafts and responses.
- src/services/registrationAdmin.ts holds the rest of registrationController.js (now v1 wrappers).
- Frontend: fetchAllRegistrations(); every registration screen on v2.
- Roadmap: money gets its own release (1.51.11); webhooks and links move to 1.51.16.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ls1vMdg2GYqbnKZmPaX1ot
- v2: series (+ page, compare, follow), season passes (+ buy, pay, mine), bundle deals
(+ quote), coupon codes (+ check, redemptions), campuses (+ admin, enabled, order, audience).
- src/services/series.ts, deals.ts, campuses.ts; the five controllers are thin v1 wrappers.
- v2 errors carry a service's own code as `reason`.
- Frontend: apiV2Data() for screens with their own record types; these screens on v2.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ls1vMdg2GYqbnKZmPaX1ot
- v2: /events/manage, create, PATCH, deactivate, copy-from, notify-recipients.
- src/services/events.ts (from eventController.js, with the schedule helpers); v1 uses it.
- fetchManagedEvents() for every picker; the event wizard writes through v2.
- pg-boss 11 in its own schema (pgboss_v11); old pgboss schema left in place.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ls1vMdg2GYqbnKZmPaX1ot
- Next 16 (Turbopack dev and build), React 19.3; tsconfig jsx react-jsx; images remotePatterns.
- ESLint 10 with eslint-config-next 16 flat configs; npm run lint = eslint src. React
Compiler-era react-hooks rules set to warn (181 existing places) until screens move to v2.
- Removed the empty pass-through middleware.ts.
- e2e: wait for the palette to be ready before Ctrl+K on the staff dashboard.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ls1vMdg2GYqbnKZmPaX1ot
- Express 5 (qs query parser and req.body = {} kept as in Express 4).
- Scanner and at-the-door check-in use /api/v2/checkin; look-up gives attendee, bookedBy,
ticketOptionId.
- Shared middleware errors on /api/v2 rewritten to { error: { code, message, reason } };
apiFetch treats reason like v1's code.
- Deprecation + Link: rel="successor-version" on v1 endpoints that have a v2 replacement.
- Roadmap: open question on how much of v1 to retire in 2.0.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ls1vMdg2GYqbnKZmPaX1ot
- v2: auth/signup, auth/login (+ code step, rate limited), PATCH /me; check-in look-up and
scan returning an outcome for every normal result. Campus rule as v1.
- Sign up/login/2FA/profile moved to src/services/auth.ts; scan preview and scan to
src/services/checkin.ts. v1 answers unchanged (ServiceError v1Status).
- Login, 2FA step, sign up and profile/password pages use v2.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ls1vMdg2GYqbnKZmPaX1ot
- v2: register, get, cancel and checkout a registration (owner/admin only).
- Registration create/cancel moved to src/services/registrations.ts and online checkout to
src/services/payments.ts; v1 controllers call the same services (ServiceError carries status).
- Typed frontend calls: npm run api:types writes openapi.json and schema.d.ts; apiV2() helper.
Register page, Pay now and My Events cancel use v2; apiFetch reads v2 errors.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ls1vMdg2GYqbnKZmPaX1ot
- tsconfig (allowJs, strict .ts), npm run build -> dist/, typecheck, dev via tsx, ts-jest.
- src/index.js is now a launcher (dist build when it matches, else tsx); server moved to app.js.
- /api/v2: defineRoute with zod -> request validation, response stripping, OpenAPI 3.1 at
/docs/v2; one error shape and one paged-list shape. First endpoints: events, event, me,
my registrations.
- deploy.sh/install.sh build the backend; rollback to older releases skips the build.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ls1vMdg2GYqbnKZmPaX1ot
- EventPicker replaces the plain event dropdowns on staff/supervisor pages and the kiosk:
upcoming first, past behind "Show past events", search across all, keyboard support.
- Reports events list can be searched ("Select these N").
- Site settings -> Webhooks: "How webhooks work" guide with headers, signature checking
(Node.js/PHP) and an example of every event type (EVENT_TYPES[].example, shape-tested).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ls1vMdg2GYqbnKZmPaX1ot
Audit log: search (person, target, IP), role filter, every action in the
filter, inclusive To date. Data requests: search and paging (was cut at
200). Webhook deliveries: status filter and Load older. Supervisor Payments
loads only what it needs (unpaid registrations, recent/today/donation
payments, a person's own for refunds) instead of everything.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ls1vMdg2GYqbnKZmPaX1ot
Public pages, auth, registration, payment, My Events and the kiosk: skip
link, focus ring, labels tied to fields, named quantity buttons, announced
errors, ModalOverlay popups (focus trap, Escape, focus return), contrast
and underlined in-text links, reduced motion. Branding warns about a primary
colour too light for white text and offers a darker shade. axe-core and
keyboard checks in e2e/accessibility.spec.ts.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The accessibility pass becomes 1.50.0 and the TypeScript backend /
OpenAPI / api v2 1.51.0. Section 10 is kept for reference.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Manage registrations, Users, Events and Reports: save the current
filters under a name, apply one with a click, star one as the default
for the page (not applied over a deep link). Per person, stored on the
server, removed when an account is anonymised. /api/saved-filters.
Migration 20261028180000_saved_filters. Completes roadmap section 9.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Manage registrations: tick registrations (or all shown) and message
them (email/WhatsApp event update), mark them paid (the balance, by
method), move them to another ticket (same tickets and QR codes,
repriced, places checked), put their people in a room or group, export
to Excel, or cancel. Each runs the same handlers as doing it one at a
time, with a result per registration. POST /api/registrations/bulk,
campus-checked per id. Migration 20261028170000_registrations_bulk.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Staff, supervisor and admin dashboards start with what's on today
(checked in vs places, children in now, volunteers here; money in today
and this week for supervisors) and, for supervisors and admins, what's
waiting for someone (unpaid registrations, consent, waitlists, cover
requests, roster gaps; for admins cashups, reconciling, privacy
requests, failed webhooks), each linking to where it's dealt with.
Ctrl+K opens a palette of pages, settings, reports and actions for the
role, with search: events, payments by amount, people and
registrations (admins). All campus-scoped. Bulk actions and saved
filters follow as 1.49.1 and 1.49.2.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
npm run demo:small | demo:events | demo:ministry | demo:multicampus
(demo:list describes them; set-demo -- --profile=<name> in backend/).
New builders: courses with sessions, attendance and certificates, a
pick-your-days conference, kids church with families, rooms and live
check-ins, volunteer rosters, a series with followers and a season
pass, and three campuses with campus-limited staff.
set-demo had failed since 1.43.0: TRUNCATE CASCADE on Campus also
emptied User (homeCampusId), deleting the admin it keeps. Tables that
User points at are now emptied with DELETE.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The built-in notice now covers volunteer rosters, event series, season
passes, webhooks, how people agree to it, and the provider-notices
storage item. Sites with their own published version see what changed
in the editor (sections marked, use new text per section or all, add
missing sections, "Done - hide this"). Account activation asks for
agreement to the terms and notice; anyone who never accepted is asked
once after login. The cookie button shows above any cookie section.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Site settings -> Webhooks: admins add https endpoints and choose event
types (registration created/paid/cancelled, payment received/refunded,
ticket checked in, account created, event published). Events are
noticed by a Prisma client extension so every write path is covered,
re-checked before sending, and delivered as signed JSON
(X-CrossEvents-Signature, HMAC-SHA256) with retries, a 30-day delivery
log, Send test and Resend. Public https addresses only; each endpoint
is added to the operator register; kids and volunteer tickets are never
sent. New `webhooks` job. Migration 20261028160000_webhooks.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
After asking for an update, a newer version coming out made the Licence
page claim the newer version was requested, with no way to ask for it.
Now it shows the version asked for, that a newer one is out, and "Ask
for X instead"; the banner shows again. Hub 1.5.0 moves the open request
on (`changed`); with an older Hub the site withdraws and asks again.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A pass sells a ticket at each of several events in a series for one
price. Supervisors set passes up on the Event series page (events and
ticket per event; fixed once sold). Buying on the series page registers
the buyer at every event at once with one checkout (free passes give
tickets straight away; Pay now if online payment isn't possible). The
price is split over the events in proportion to their normal prices:
each registration line is priced at its share and the webhook records
one Payment per event, so cashups stay per event. Pass registrations
aren't repriced, take no coupons and can't be edited. Migration
20261028150000_season_pass.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
New report (Reports -> Other, or "Compare" on the Event series page):
each event in a series side by side, oldest first - registrations,
tickets, income (dashboard definition), people checked in (average per
session on multi-session events) and attendance rate, with the change
from the event before. Exports like the other reports; campus-scoped
staff see only their campuses' events. GET /api/series/:id/compare.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Group separate events (Camp 2025, Camp 2026) on a series page at
/series/<slug> with upcoming and past events. Events join a series from
the event editor (copies keep it); a series can span campuses. Event
pages show "Part of ..." and "Next in this series". Signed-in people can
follow a series; the new `series` job emails followers once when a new
event in it goes live, as News & announcements (opted-in only, with
unsubscribe). Follows are in the data export and removed on
anonymising. Migration 20261028140000_event_series.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
"Copy" on each event card opens the event editor filled in from that
event (campus, sessions, options, variants, early birds, sections, form,
recipients, feedback questions, certificates, reminders). "New start"
moves every date with it. Allocation groups and, for admins, planned
costs are copied by POST /api/events/:id/copy-from. Registrations,
coupons, attachments, kids check-in and volunteer roles are not copied.
Roadmap: event series and cloning ships as 1.47.0-1.47.3.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Hub reads this file from main (Hub 1.4.1), so these show on sites' Licence
page without a new release.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Client-facing release notes the Hub reads from git with the version tags, so
the latest version and its notes reach installs without touching the Hub.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Site settings -> Licence shows the release notes for every newer version from
the Hub; urgent (security/serious) updates are marked and can't be put off.
- Withdraw an update request (the provider is told through the Hub).
- "Not now" on the banner or Licence page hides update reminders until the
next version.
- Notices from the provider show as a dashboard banner for site admins and
supervisors, in their time window, and on the Licence page.
- New audit actions (migration), API docs, help, protocol doc, tests.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There's no Actions runner (no machine that can run Docker), so every push
left a run stuck on "Waiting". Tests and lint are run by hand before a
release, and releases are deployed with scripts/deploy.sh on the server.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>