Populate payment method filter from real data instead of a guessed list

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>
This commit is contained in:
2026-07-24 08:55:58 +02:00
co-authored by Claude Sonnet 5
parent 90bea25338
commit 59194349d2
7 changed files with 53 additions and 11 deletions
@@ -57,9 +57,20 @@ export default function UserPaymentsPage() {
// Filters
const [startDate, setStartDate] = useState("");
const [endDate, setEndDate] = useState("");
const [method, setMethod] = useState<"" | "cash" | "card" | "eft" | "voucher" | "apple_pay" | "google_pay">("");
const [method, setMethod] = useState("");
const [kind, setKind] = useState<"" | "payment" | "refund">("");
// Method is free-text (gateways can report values like apple_pay/google_pay beyond the
// manual-entry set), so the filter options come from what's actually in the user's payments
// rather than a hardcoded guess.
const [availableMethods, setAvailableMethods] = useState<string[]>([]);
useEffect(() => {
if (!token) return;
apiFetch<any>("/api/payments/mypayments/methods", { authToken: token })
.then(res => setAvailableMethods(Array.isArray(res?.methods) ? res.methods : []))
.catch(() => {});
}, [token]);
const buildQuery = useCallback((p: number) => {
const qs = new URLSearchParams({ page: String(p), limit: "25" });
if (startDate) qs.set("startDate", startDate);
@@ -124,14 +135,11 @@ export default function UserPaymentsPage() {
</div>
<div>
<label className="block text-xs text-gray-600 mb-1">Method</label>
<select className="border rounded px-2 py-1.5 text-sm" value={method} onChange={e => setMethod(e.target.value as any)}>
<select className="border rounded px-2 py-1.5 text-sm" value={method} onChange={e => setMethod(e.target.value)}>
<option value="">All methods</option>
<option value="cash">Cash</option>
<option value="card">Card</option>
<option value="eft">EFT</option>
<option value="voucher">Voucher</option>
<option value="apple_pay">Apple Pay</option>
<option value="google_pay">Google Pay</option>
{availableMethods.map(m => (
<option key={m} value={m}>{formatMethod(m)}</option>
))}
</select>
</div>
<div>