78 lines
3.1 KiB
TypeScript
78 lines
3.1 KiB
TypeScript
"use client";
|
|
import { use, useEffect, useState } from "react";
|
|
import Link from "next/link";
|
|
import { formatEuro } from "@/lib/vouchers";
|
|
import { getVoucherStatus } from "@/lib/voucher-status";
|
|
|
|
type Voucher = {
|
|
id: number;
|
|
name: string;
|
|
anbieter: string;
|
|
wertCents: number;
|
|
restCents: number;
|
|
ablaufdatum: string | null;
|
|
codeText: string | null;
|
|
kategorie: string | null;
|
|
notiz: string | null;
|
|
status: string;
|
|
contacts?: { contact: { id: number; name: string; email: string | null; telegramChatId: string | null } }[];
|
|
};
|
|
|
|
export default function VoucherDetailPage({ params }: { params: Promise<{ id: string }> }) {
|
|
const { id } = use(params);
|
|
const [v, setV] = useState<Voucher | null>(null);
|
|
const [loading, setLoading] = useState(true);
|
|
const [error, setError] = useState("");
|
|
|
|
useEffect(() => {
|
|
fetch(`/api/vouchers/${id}`)
|
|
.then((r) => (r.ok ? r.json() : Promise.reject(new Error("not found"))))
|
|
.then((d) => setV(d))
|
|
.catch(() => setError("Gutschein nicht gefunden"))
|
|
.finally(() => setLoading(false));
|
|
}, [id]);
|
|
|
|
async function redeem() {
|
|
const res = await fetch(`/api/vouchers/${id}`, {
|
|
method: "POST",
|
|
headers: { "content-type": "application/json" },
|
|
body: JSON.stringify({ action: "redeem" }),
|
|
});
|
|
if (res.ok) setV(await res.json());
|
|
}
|
|
|
|
if (loading) return <main className="mx-auto max-w-xl animate-pulse space-y-2 p-6"><div className="h-6 w-1/2 bg-gray-200" /><div className="h-4 w-1/3 bg-gray-100" /></main>;
|
|
if (error || !v)
|
|
return (
|
|
<main className="mx-auto max-w-xl space-y-4 p-6">
|
|
<p>{error}</p>
|
|
<Link href="/" className="underline">Zurück zur Liste</Link>
|
|
</main>
|
|
);
|
|
|
|
return (
|
|
<main className="mx-auto max-w-xl space-y-3 p-6">
|
|
<Link href="/" className="text-sm underline">← Zurück</Link>
|
|
<h1 className="text-xl font-bold">{v.name}</h1>
|
|
<dl className="space-y-1 text-sm">
|
|
<div><dt className="font-medium">Anbieter</dt><dd>{v.anbieter}</dd></div>
|
|
<div><dt className="font-medium">Wert</dt><dd>{formatEuro(v.wertCents)}</dd></div>
|
|
<div><dt className="font-medium">Rest</dt><dd>{formatEuro(v.restCents)}</dd></div>
|
|
<div><dt className="font-medium">Status</dt><dd>{getVoucherStatus({ status: v.status, ablaufdatum: v.ablaufdatum ? new Date(v.ablaufdatum) : null })}</dd></div>
|
|
{v.ablaufdatum && <div><dt className="font-medium">Ablaufdatum</dt><dd>{new Date(v.ablaufdatum).toLocaleDateString("de-DE")}</dd></div>}
|
|
{v.codeText && <div><dt className="font-medium">Code</dt><dd>{v.codeText}</dd></div>}
|
|
{v.kategorie && <div><dt className="font-medium">Kategorie</dt><dd>{v.kategorie}</dd></div>}
|
|
{v.notiz && <div><dt className="font-medium">Notiz</dt><dd>{v.notiz}</dd></div>}
|
|
{v.contacts && v.contacts.length > 0 && (
|
|
<div><dt className="font-medium">Empfänger</dt><dd>{v.contacts.map((c) => c.contact.name).join(", ")}</dd></div>
|
|
)}
|
|
</dl>
|
|
{v.status !== "eingeloest" && (
|
|
<button onClick={redeem} className="rounded bg-black px-4 py-2 text-white">
|
|
Einlösen
|
|
</button>
|
|
)}
|
|
</main>
|
|
);
|
|
}
|