diff --git a/CHANGELOG.md b/CHANGELOG.md index 6ec83c5..1b17a58 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ and this project follows [Semantic Versioning](https://semver.org/). - Cashup/reports: payments tagged with a digital wallet method (e.g. `apple_pay`, `google_pay` from online checkouts) are now bucketed as "card" for reconciliation instead of silently falling into "other". - User dashboard payment history: `GET /api/payments/mypayments` now normalizes `method` to the fixed set cash/card/eft/voucher/other. Card-network wallet payments (`apple_pay`, `google_pay`) are reported and filterable as "card"; any other gateway-reported value falls under "other" — instead of exposing raw, inconsistent gateway strings the filter dropdown didn't know about. +- User dashboard: the registration payment list (shown on the main dashboard when viewing a registration's bill) applies the same cash/card/eft/voucher/other normalization client-side, so it no longer shows a raw `apple_pay`/`google_pay` string. Staff-facing payment views (supervisor payments, reports, cashup) are unaffected — they still show the raw method, which is what reconciliation needs. ## [1.0.1] - 2026-07-23 diff --git a/frontend/src/app/dashboard/user/page.tsx b/frontend/src/app/dashboard/user/page.tsx index 981b290..8180b5f 100644 --- a/frontend/src/app/dashboard/user/page.tsx +++ b/frontend/src/app/dashboard/user/page.tsx @@ -4,6 +4,7 @@ import { useAuth } from "@/hooks/useAuth"; import { apiFetch } from "@/lib/api"; import { useRouter } from "next/navigation"; import { formatDate } from "@/lib/date"; +import { formatPaymentMethod } from "@/lib/paymentMethod"; import { QrImage } from "@/components/shared/QrImage"; // Helper formatters @@ -1008,7 +1009,7 @@ export default function UserDashboardPage() {
Payments
diff --git a/frontend/src/app/dashboard/user/payments/page.tsx b/frontend/src/app/dashboard/user/payments/page.tsx index 4208c3d..26af19e 100644 --- a/frontend/src/app/dashboard/user/payments/page.tsx +++ b/frontend/src/app/dashboard/user/payments/page.tsx @@ -4,6 +4,7 @@ import { useAuth } from "@/hooks/useAuth"; import { useRouter } from "next/navigation"; import { apiFetch } from "@/lib/api"; import { formatDateTime } from "@/lib/date"; +import { formatPaymentMethod } from "@/lib/paymentMethod"; interface PaymentItem { id: string; @@ -18,21 +19,6 @@ interface PaymentItem { const formatRand = (n: number) => `R ${Math.abs(n).toFixed(2)}`; -// The API normalizes Payment.method to this set before it ever reaches the dashboard — any -// gateway-reported wallet type (apple_pay, google_pay, yoco, ...) is folded into "other". -const METHOD_LABELS: Record = { - cash: "Cash", - card: "Card", - eft: "EFT", - voucher: "Voucher", - other: "Other", -}; - -const formatMethod = (method: string | null | undefined) => { - if (!method) return "Payment"; - return METHOD_LABELS[method] || method; -}; - export default function UserPaymentsPage() { const { user, loading, token } = useAuth(); const router = useRouter(); @@ -163,7 +149,7 @@ export default function UserPaymentsPage() {
{formatDateTime(p.createdAt)}
-
Method: {formatMethod(p.method)}
+
Method: {formatPaymentMethod(p.method)}
{eventTitle &&
Event: {eventTitle}
} ); diff --git a/frontend/src/lib/paymentMethod.ts b/frontend/src/lib/paymentMethod.ts new file mode 100644 index 0000000..3a2e4ea --- /dev/null +++ b/frontend/src/lib/paymentMethod.ts @@ -0,0 +1,26 @@ +// Payment.method is free-text — online checkouts get tagged with whatever wallet type the +// gateway reports (apple_pay, google_pay, ...), not just the manual-entry methods below. +// Mirrors normalizeUserMethod in backend/src/controllers/paymentController.js: card-network +// wallets count as "card" (same settlement, no separate float); anything else is "other". +const USER_FACING_METHODS = ["cash", "card", "eft", "voucher"]; +const CARD_ALIASES = ["apple_pay", "google_pay"]; + +const METHOD_LABELS: Record = { + cash: "Cash", + card: "Card", + eft: "EFT", + voucher: "Voucher", + other: "Other", +}; + +export function normalizePaymentMethod(method: string | null | undefined): string { + const m = String(method || "").toLowerCase(); + if (USER_FACING_METHODS.includes(m)) return m; + if (CARD_ALIASES.includes(m)) return "card"; + return "other"; +} + +export function formatPaymentMethod(method: string | null | undefined): string { + if (!method) return "Payment"; + return METHOD_LABELS[normalizePaymentMethod(method)]; +}