import { useEffect, useState } from "react"; import type { StampWithStatus } from "@stamp/shared"; type RedeemModalProps = { stamp: StampWithStatus; onRedeem: (stampId: string) => Promise; onClose: () => void; }; const CONFIRM_COUNTDOWN = 5; type Mode = "uncollected" | "redeemed" | "sold-out" | "unavailable" | "ready"; function resolveMode(stamp: StampWithStatus): Mode { if (!stamp.collected) return "uncollected"; if (stamp.redeemed) return "redeemed"; if (!stamp.prize || !stamp.prize.enabled) return "unavailable"; if (stamp.prize.stock <= 0) return "sold-out"; return "ready"; } export default function RedeemModal({ stamp, onRedeem, onClose }: RedeemModalProps) { const [confirming, setConfirming] = useState(false); const [countdown, setCountdown] = useState(CONFIRM_COUNTDOWN); const [redeeming, setRedeeming] = useState(false); const [error, setError] = useState(""); const mode = resolveMode(stamp); const prize = stamp.prize; useEffect(() => { if (!confirming) return; setCountdown(CONFIRM_COUNTDOWN); const interval = setInterval(() => { setCountdown((c) => { if (c <= 1) { clearInterval(interval); return 0; } return c - 1; }); }, 1000); return () => clearInterval(interval); }, [confirming]); const openConfirm = () => { if (mode !== "ready") return; setError(""); setConfirming(true); }; const cancelConfirm = () => { if (redeeming) return; setConfirming(false); }; const doRedeem = async () => { if (countdown > 0 || redeeming || mode !== "ready") return; setRedeeming(true); setError(""); try { await onRedeem(stamp.id); onClose(); } catch (e) { setError(e instanceof Error ? e.message : "兑换失败"); setConfirming(false); } finally { setRedeeming(false); } }; const buttonCopy = () => { switch (mode) { case "uncollected": return "前往点位收集"; case "redeemed": return "已兑换"; case "sold-out": return "已兑完"; case "unavailable": return "暂无奖品"; case "ready": return "立即兑换"; } }; const buttonBg = mode === "ready" ? "var(--jade)" : mode === "redeemed" ? "var(--gold)" : "var(--border-muted)"; const buttonColor = mode === "ready" || mode === "redeemed" ? "white" : "var(--text-muted)"; return (
{ if (e.target !== e.currentTarget) return; if (confirming) return; onClose(); }} >

{mode === "uncollected" ? "品牌权益" : "兑换奖品"}

{/* Stamp header */}
{stamp.name}

Stamp

{stamp.name}

{mode === "uncollected" ? (

尚未收集

) : stamp.collectedAt ? (

收集于 {new Date(stamp.collectedAt).toLocaleDateString("zh-CN")}

) : null}
{/* Brand description (optional) */} {stamp.note && (

Brand

{stamp.note}

)} {/* Prize card */} {prize ? (

Reward

{prize.name}

{prize.description && (

{prize.description}

)}
剩余库存 0 ? "var(--jade)" : "var(--terracotta)" }} > {prize.stock}
) : (

该图章暂未配置奖品

)} {mode === "uncollected" && (

请前往线下点位触碰 NFC 收集该图章后再来兑换

)} {mode === "redeemed" && (

你已经兑换过这枚图章对应的奖品

)} {mode === "sold-out" && (

奖品库存已耗尽

)} {error &&

{error}

}
{/* Confirmation dialog */} {confirming && prize && (
e.target === e.currentTarget && cancelConfirm()} >
{/* Warning */}

Important

请在工作人员的注视下进行兑换,并确保您已领取奖品

确认兑换

Reward

{prize.name}

{prize.description && (

{prize.description}

)}

兑换后,「{stamp.name}」图章将 保持彩色点亮并标记为「已兑换」,此奖品不可再次兑换。

一旦确认,操作不可撤销

)}
); }