97 lines
3.0 KiB
TypeScript
97 lines
3.0 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, useState } from "react";
|
|
|
|
type Member = { id: number; name: string };
|
|
|
|
export default function MembersSection() {
|
|
const [members, setMembers] = useState<Member[]>([]);
|
|
const [name, setName] = useState("");
|
|
const [error, setError] = useState("");
|
|
|
|
async function load() {
|
|
const res = await fetch("/api/members");
|
|
if (res.ok) setMembers(await res.json());
|
|
}
|
|
useEffect(() => {
|
|
load();
|
|
}, []);
|
|
|
|
async function add() {
|
|
setError("");
|
|
const value = name.trim();
|
|
if (!value) {
|
|
setError("Bitte einen Namen eingeben.");
|
|
return;
|
|
}
|
|
const res = await fetch("/api/members", {
|
|
method: "POST",
|
|
headers: { "content-type": "application/json" },
|
|
body: JSON.stringify({ name: value }),
|
|
});
|
|
if (!res.ok) {
|
|
setError(res.status === 409 ? "Dieser Name existiert bereits." : "Speichern fehlgeschlagen.");
|
|
return;
|
|
}
|
|
setName("");
|
|
load();
|
|
}
|
|
|
|
async function remove(id: number, memberName: string) {
|
|
if (!window.confirm(`„${memberName}“ aus der Haushaltsliste entfernen? Bereits erfasste Gutscheine behalten den Namen.`)) return;
|
|
const res = await fetch(`/api/members?id=${id}`, { method: "DELETE" });
|
|
if (res.ok) load();
|
|
}
|
|
|
|
return (
|
|
<section className="rounded-card border border-line bg-white p-5">
|
|
<h2 className="text-[15px] font-semibold tracking-tight">Haushaltsmitglieder</h2>
|
|
<p className="mt-1 text-sm text-muted">Wer einen Gutschein anlegt, wählt hier seinen Namen aus.</p>
|
|
<div className="mt-3 flex gap-2">
|
|
<input
|
|
value={name}
|
|
onChange={(e) => setName(e.target.value)}
|
|
onKeyDown={(e) => {
|
|
if (e.key === "Enter") {
|
|
e.preventDefault();
|
|
add();
|
|
}
|
|
}}
|
|
placeholder="Name hinzufügen"
|
|
aria-label="Name hinzufügen"
|
|
maxLength={100}
|
|
className="h-11 min-w-0 flex-1 rounded-field border border-line bg-white px-3.5 text-sm text-ink"
|
|
/>
|
|
<button
|
|
type="button"
|
|
onClick={add}
|
|
className="h-11 shrink-0 rounded-field bg-brand-600 px-5 text-sm font-semibold text-white transition-colors hover:bg-brand-700 active:scale-[0.99]"
|
|
>
|
|
Hinzufügen
|
|
</button>
|
|
</div>
|
|
{error && (
|
|
<p role="alert" className="mt-2 text-[13px] font-medium text-danger">
|
|
{error}
|
|
</p>
|
|
)}
|
|
{members.length > 0 && (
|
|
<ul className="mt-3 divide-y divide-line">
|
|
{members.map((m) => (
|
|
<li key={m.id} className="flex items-center justify-between py-2 text-sm">
|
|
<span className="font-medium">{m.name}</span>
|
|
<button
|
|
type="button"
|
|
onClick={() => remove(m.id, m.name)}
|
|
className="rounded-lg px-2 py-1 text-[13px] font-medium text-muted hover:text-danger"
|
|
>
|
|
Entfernen
|
|
</button>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
)}
|
|
</section>
|
|
);
|
|
}
|