import { useMutation } from "@tanstack/react-query"; import { createFileRoute, Link, redirect } from "@tanstack/react-router"; import { useCallback, useState } from "react"; import { toast } from "sonner"; import { AdminTransactionLog } from "@/components/drinkkaart/AdminTransactionLog"; import { DeductionPanel } from "@/components/drinkkaart/DeductionPanel"; import { ManualCreditForm } from "@/components/drinkkaart/ManualCreditForm"; import { QrScanner } from "@/components/drinkkaart/QrScanner"; import { ScanResultCard } from "@/components/drinkkaart/ScanResultCard"; import { Button } from "@/components/ui/button"; import { authClient } from "@/lib/auth-client"; import { formatCents } from "@/lib/drinkkaart"; import { orpc } from "@/utils/orpc"; export const Route = createFileRoute("/admin/drinkkaart")({ component: AdminDrinkkaartPage, beforeLoad: async () => { const session = await authClient.getSession(); if (!session.data?.user) { throw redirect({ to: "/login" }); } const user = session.data.user as { role?: string }; if (user.role !== "admin") { throw redirect({ to: "/" }); } }, }); type ScanState = | { step: "idle" } | { step: "scanning" } | { step: "resolving" } | { step: "resolved"; drinkkaartId: string; userId: string; userName: string; userEmail: string; balance: number; } | { step: "success"; transactionId: string; userName: string; balanceAfter: number; } | { step: "error"; message: string; retryable: boolean } | { step: "manual_credit" }; function AdminDrinkkaartPage() { const [scanState, setScanState] = useState({ step: "idle" }); const resolveQrMutation = useMutation( orpc.drinkkaart.resolveQrToken.mutationOptions(), ); const handleScan = useCallback( (token: string) => { setScanState({ step: "resolving" }); resolveQrMutation.mutate( { token }, { onSuccess: (data) => { setScanState({ step: "resolved", drinkkaartId: data.drinkkaartId, userId: data.userId, userName: data.userName, userEmail: data.userEmail, balance: data.balance, }); }, onError: (err: Error) => { setScanState({ step: "error", message: err.message ?? "Ongeldig QR-token", retryable: true, }); }, }, ); }, [resolveQrMutation], ); const handleDeductSuccess = (transactionId: string, balanceAfter: number) => { const userName = scanState.step === "resolved" ? scanState.userName : "Gebruiker"; const amountDeducted = scanState.step === "resolved" ? scanState.balance - balanceAfter : undefined; toast.success( `Afschrijving verwerkt${amountDeducted !== undefined ? ` — ${formatCents(amountDeducted)}` : ""}`, ); setScanState({ step: "success", transactionId, userName, balanceAfter }); }; return (
{/* Header */}
← Admin

Drinkkaart Beheer

{/* Scan / State machine */}
{scanState.step === "idle" && (

Scan een QR-code of laad handmatig op.

)} {scanState.step === "scanning" && ( setScanState({ step: "idle" })} /> )} {scanState.step === "resolving" && (

QR-code wordt gecontroleerd...

)} {scanState.step === "resolved" && (
setScanState({ step: "idle" })} />
)} {scanState.step === "success" && (

Betaling verwerkt

{scanState.userName} — nieuw saldo:{" "} {formatCents(scanState.balanceAfter)}

)} {scanState.step === "error" && (

Fout

{scanState.message}

{scanState.retryable && ( )}
)} {scanState.step === "manual_credit" && ( setScanState({ step: "idle" })} /> )}
{/* Transaction log */}

Transactieoverzicht

); }