Apply payment method normalization consistently across user-facing views

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>
This commit is contained in:
2026-07-24 09:41:43 +02:00
co-authored by Claude Sonnet 5
parent 12e5dfc643
commit 193739c042
4 changed files with 31 additions and 17 deletions
+2 -1
View File
@@ -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() {
<div className="font-medium mb-1">Payments</div>
<ul className="text-sm list-disc pl-5 space-y-1">
{activeBill.payments.map((p: any) => (
<li key={p.id}>{new Date(p.createdAt).toLocaleString()} {formatRand(p.amount)} ({p.method || "Payment"})</li>
<li key={p.id}>{new Date(p.createdAt).toLocaleString()} {formatRand(p.amount)} ({formatPaymentMethod(p.method)})</li>
))}
</ul>
</div>
@@ -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<string, string> = {
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() {
</div>
<div className="text-xs text-gray-500">{formatDateTime(p.createdAt)}</div>
</div>
<div className="text-xs text-gray-600">Method: {formatMethod(p.method)}</div>
<div className="text-xs text-gray-600">Method: {formatPaymentMethod(p.method)}</div>
{eventTitle && <div className="text-xs text-gray-600">Event: {eventTitle}</div>}
</li>
);
+26
View File
@@ -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<string, string> = {
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)];
}