- Registration confirmations attach an invoice PDF (itemized breakdown,
early-bird discount, balance due, Yoco pay-now link/QR) whenever a
balance is outstanding; payment/donation confirmations attach a
payment receipt PDF. Sent as an email attachment and, over WhatsApp,
as the PDF itself with the existing message as its caption.
- Users can also (re)send either document on demand: an "Invoice"
button on the registration detail popup, and a "Receipt" button next
to each payment there and on the Payment history page, each opening
an Email/WhatsApp choice popup, via two new endpoints restricted to
the registration/payment's own owner.
- Fix: editing an event option's early-bird tiers deleted and
recreated every tier for that option with brand-new ids, silently
severing the appliedTierId link on all historical purchases (losing
early-bird attribution and undercounting stock-limit usage) even for
tiers the admin didn't touch. Tiers are now upserted by id.
- Update the "My Events" help content and the API docs index for the
new endpoints.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Multi-phase visual facelift (design tokens, dashboards, sidebar/navbar
shell, per-page help guides, and a layout/content pass across every
remaining page) plus backend fixes to the dashboard KPI stats:
- Admin/Supervisor dashboard KPIs (revenue, donations, registrations,
tickets sold) now use a rolling trailing-month window (today back one
calendar month, e.g. 9 May - 8 June if today is 8 June) instead of
calendar month-to-date, which under-counted for most of the month.
The comparison window shifts the same way, so like is still compared
with like.
- Reports deep-links from those stat tiles now match the same window
(range=trailing_month, replacing range=this_month).
- Design tokens (brand-* Tailwind scale + shadcn CSS variables), a
site-wide contextual help button, fixed dashboard sidebar/navbar,
Admin/Supervisor/Staff/User dashboard rebuilds backed by a new
GET /api/stats/overview endpoint, a dedicated Contact page, Site
Settings restyle with WhatsApp config folded in, and an Account
activity feed backed by a new SecurityEvent model.
- Every remaining page (home, events, registration flow, auth, legal,
payment results, and every Admin/Supervisor/Staff/User tool page)
restyled onto the same design tokens, several with real layout
upgrades (home hero, events list/detail, donate page, auth pages).
- 20+ new dedicated help guides so the whole site has page-specific
help content instead of falling back to a generic guide.
- Assorted fixes surfaced along the way: donation-leg double-counting
in payment stats, donations not counting toward revenue, refund
netting in per-method report breakdowns, and donation
over-allocation after a refund.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Two consistency fixes requested after the payment-method work:
1. Registration status (pending/confirmed/partial_paid/paid/cancelled)
was printed as a raw string on the user dashboard. Added
RegistrationStatusBadge mirroring the existing EventStatusBadge
pattern, using the same status colors already established on
dashboard/admin/registrations.
2. Inline success/error banners across dashboard pages persisted
indefinitely. Added a shared useDismissingState hook (drop-in
useState replacement that auto-clears a truthy value after 7s,
resetting the timer on each update) and swapped it in across ~24
dashboard files. Excluded: message-only modal dialogs (ticket-
scanning's success/error confirmations) and two states that mix
live form-validation feedback with async results inside actively-
open forms (the registration-edit modal's editError, the event
create/edit modal's error) - those keep persisting until the user
acts, since auto-hiding a "fix this field" message mid-edit would
be a regression. Also fixed at-the-door's existing bespoke
auto-dismiss timers (10s/15s, one mislabeled as "5s") to the same
consistent 7s, and removed admin/settings' manual x dismiss button
in favor of the same auto-only behavior.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Audited every place Payment.method reaches the UI. The main user
dashboard's per-registration payment list (fed by GET
/api/payments/registration/:id) was still showing the raw gateway
string, since that endpoint is shared with the staff-facing supervisor
payments page and wasn't touched by the earlier /mypayments fix.
Extracted the cash/card/eft/voucher/other normalization (matching
normalizeUserMethod on the backend) into frontend/src/lib/paymentMethod.ts
and applied it to both user-facing payment displays. Staff-facing views
(supervisor payments, at-the-door, reports, cashup) intentionally keep
showing the raw method for reconciliation and were left unchanged.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Per feedback, drop the dynamic /mypayments/methods lookup (wasn't
loading reliably) in favor of a static cash/card/eft/voucher/other
dropdown. Server-side normalization now folds any gateway-reported
method outside those four manual-entry values (apple_pay, google_pay,
yoco, etc.) into "other" instead of "card", both in the returned data
and in the filter query.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Per feedback, stop trying to surface every gateway-reported wallet
type (apple_pay, google_pay, yoco, ...) as its own filter/display
value on the user payment history page. Both /mypayments and
/mypayments/methods now fold anything outside the four manual-entry
methods into "card", both in the returned data and in the filter
query, so Apple Pay/Google Pay payments show up under Card.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The previous fix hardcoded apple_pay/google_pay as extra filter options,
but Payment.method is free-text set by whatever the gateway reports, so
guessing at literal values was fragile and still didn't surface them for
this user. Add GET /api/payments/mypayments/methods returning the
distinct method values actually present in the user's payments, and have
the dashboard filter build its options from that instead. Also switch
the method filter from a startsWith match to an exact match, since the
values now come straight from the same column being filtered.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Yoco webhook payments are tagged with whatever wallet type the gateway
reports, not just cash/card/eft/voucher. Cashup/report totals were
silently dropping those into the "other" bucket instead of "card", and
the new user payment history page couldn't filter by them and showed
raw snake_case values.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Users can now view their own payments (excluding donations) with
server-side pagination (25/page), date range, method, and
payment/refund filters, both on the API and the new client page.