feat: contacts and voucher assignment

This commit is contained in:
opencode committed 2026-10-18 10:33:05 +02:00
1 parent c86ec8bd36
commit f2b1d0293a
8 files changed
+154 -4

No files matched your search

+4
View File
@@ -15,6 +15,7 @@ type Voucher = {
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 }> }) {
@@ -62,6 +63,9 @@ export default function VoucherDetailPage({ params }: { params: Promise<{ id: st
{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">
+26 -3
View File
@@ -1,5 +1,5 @@
"use client";
import { useState } from "react";
import { useEffect, useState } from "react";
import { useRouter } from "next/navigation";
import { validateVoucher } from "@/lib/vouchers";
@@ -21,6 +21,15 @@ export default function NewVoucherPage() {
const [values, setValues] = useState<Record<string, string>>({});
const [errors, setErrors] = useState<string[]>([]);
const [serverError, setServerError] = useState("");
const [contacts, setContacts] = useState<{ id: number; name: string }[]>([]);
const [contactIds, setContactIds] = useState<number[]>([]);
useEffect(() => {
fetch("/api/contacts")
.then((r) => (r.ok ? r.json() : []))
.then(setContacts)
.catch(() => {});
}, []);
function set(name: string, value: string) {
setValues((v) => ({ ...v, [name]: value }));
@@ -46,7 +55,7 @@ export default function NewVoucherPage() {
const res = await fetch("/api/vouchers", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify(input),
body: JSON.stringify({ ...input, contactIds }),
});
if (!res.ok) {
const data = await res.json().catch(() => ({}));
@@ -78,7 +87,21 @@ export default function NewVoucherPage() {
{errors.includes(f.name) && <p className="mt-1 text-sm text-red-600">Ungültig: {f.name}</p>}
</div>
))}
{/* Task 5 hook: <section id="kontakte">…ContactSelect…</section> */}
<section id="kontakte">
<h2 className="text-sm font-medium">Empfänger</h2>
{contacts.map((c) => (
<label key={c.id} className="flex items-center gap-2 text-sm">
<input
type="checkbox"
checked={contactIds.includes(c.id)}
onChange={(e) =>
setContactIds((ids) => (e.target.checked ? [...ids, c.id] : ids.filter((x) => x !== c.id)))
}
/>
{c.name}
</label>
))}
</section>
{serverError && <p className="text-sm text-red-600">{serverError}</p>}
<button type="submit" className="rounded bg-black px-4 py-2 text-white">
Speichern