import { useEffect, useState } from "react"; import { Loader2 } from "lucide-react"; interface Service { id: string; name: string; description?: string; basePriceCents: number; durationMinutes: number; active: boolean; } interface BufferRule { id: string; serviceId: string; serviceName: string; sizeCategory?: string; coatType?: string; bufferMinutes: number; createdAt: string; updatedAt: string; } interface BufferRuleForm { serviceId: string; sizeCategory: string; coatType: string; bufferMinutes: string; } const EMPTY_FORM: BufferRuleForm = { serviceId: "", sizeCategory: "", coatType: "", bufferMinutes: "", }; const SIZE_OPTIONS = ["", "small", "medium", "large", "xlarge"] as const; const COAT_OPTIONS = ["", "smooth", "double", "wire", "curly", "long", "hairless"] as const; export function BufferRulesSection() { const [rules, setRules] = useState([]); const [services, setServices] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [showForm, setShowForm] = useState(false); const [form, setForm] = useState(EMPTY_FORM); const [saving, setSaving] = useState(false); const [formError, setFormError] = useState(null); const [deletingId, setDeletingId] = useState(null); const [confirmDeleteId, setConfirmDeleteId] = useState(null); const [editingId, setEditingId] = useState(null); const [editBuffer, setEditBuffer] = useState(""); useEffect(() => { Promise.all([ fetch("/api/buffer-rules").then(r => r.ok ? r.json() : []), fetch("/api/services?includeInactive=true").then(r => r.ok ? r.json() : []), ]).then(([rulesData, servicesData]) => { setRules(rulesData as BufferRule[]); setServices(servicesData as Service[]); }).catch(() => setError("Failed to load")).finally(() => setLoading(false)); }, []); async function handleCreate(e: React.FormEvent) { e.preventDefault(); const mins = parseInt(form.bufferMinutes); if (!form.serviceId || isNaN(mins) || mins <= 0) { setFormError("Service and valid buffer minutes are required."); return; } setSaving(true); setFormError(null); try { const body: Record = { serviceId: form.serviceId, bufferMinutes: mins, }; if (form.sizeCategory) body.sizeCategory = form.sizeCategory; if (form.coatType) body.coatType = form.coatType; const res = await fetch("/api/buffer-rules", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body), }); if (!res.ok) { const err = await res.json().catch(() => ({})) as { error?: string }; throw new Error(err.error ?? `HTTP ${res.status}`); } const newRule = await res.json() as BufferRule; setRules(prev => [...prev, newRule]); setShowForm(false); setForm(EMPTY_FORM); } catch (e: unknown) { setFormError(e instanceof Error ? e.message : "Failed to create rule"); } finally { setSaving(false); } } async function handleDelete(id: string) { setDeletingId(id); try { await fetch(`/api/buffer-rules/${id}`, { method: "DELETE" }); setRules(prev => prev.filter(r => r.id !== id)); } finally { setDeletingId(null); setConfirmDeleteId(null); } } function startEdit(rule: BufferRule) { setEditingId(rule.id); setEditBuffer(String(rule.bufferMinutes)); } async function saveEdit(rule: BufferRule) { const mins = parseInt(editBuffer); if (isNaN(mins) || mins <= 0) return; setSaving(true); try { const res = await fetch(`/api/buffer-rules/${rule.id}`, { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ bufferMinutes: mins }), }); if (!res.ok) throw new Error(`HTTP ${res.status}`); const updated = await res.json() as BufferRule; setRules(prev => prev.map(r => r.id === updated.id ? updated : r)); } catch { // silent fail } finally { setSaving(false); setEditingId(null); setEditBuffer(""); } } if (loading) { return (
); } if (error) { return (
{error}
); } return (

Buffer Rules

Extra time rules per service / pet size / coat type

{showForm && (
setForm(f => ({ ...f, bufferMinutes: e.target.value }))} required className="w-full border border-stone-200 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-(--color-accent)" />
{formError &&

{formError}

}
)} {rules.length === 0 && !showForm ? (

No buffer rules configured yet.

) : (
{rules.map(rule => (
{rule.serviceName}
{rule.sizeCategory && Size: {rule.sizeCategory}} {rule.coatType && Coat: {rule.coatType}}
{editingId === rule.id ? (
setEditBuffer(e.target.value)} className="w-20 border border-stone-200 rounded px-2 py-1 text-sm" /> min
) : ( <> {rule.bufferMinutes} min )} {confirmDeleteId === rule.id ? (
Delete?
) : ( )}
))}
)}
); }