// components/GroupsExplorer.tsx // Client component: list groups, create/edit/delete. "use client"; import { useState, useEffect } from "react"; import Link from "next/link"; import { useQueryClient } from "@tanstack/react-query"; import { pbList, pbPatch, pbCreate, pbDelete } from "@/lib/api"; import type { YsterbaardGroup, PBList } from "@/lib/types"; interface Props { initial: PBList; } interface EditForm { id?: string; name: string; description: string; } export default function GroupsExplorer({ initial }: Props) { const qc = useQueryClient(); const [groups, setGroups] = useState(initial.items); const [showCreate, setShowCreate] = useState(false); const [editingId, setEditingId] = useState(null); const [deleteId, setDeleteId] = useState(null); const [form, setForm] = useState({ name: "", description: "" }); const [saving, setSaving] = useState(false); const [error, setError] = useState(null); const [deleteSaving, setDeleteSaving] = useState(false); // Reset form when closing create useEffect(() => { if (!showCreate) setForm({ name: "", description: "" }); }, [showCreate]); // Populate form for editing function startEdit(g: YsterbaardGroup) { setEditingId(g.id); setForm({ name: g.name, description: g.description ?? "" }); } function cancelEdit() { setEditingId(null); setForm({ name: "", description: "" }); } async function handleSave() { if (!form.name.trim()) { setError("Group name is required."); return; } setError(null); setSaving(true); try { if (editingId) { // Update existing await pbPatch({ collection: "groups", id: editingId, fields: { name: form.name.trim(), description: form.description.trim() || "", }, }); setGroups((prev) => prev.map((g) => g.id === editingId ? { ...g, name: form.name.trim(), description: form.description.trim() || null } : g ) ); cancelEdit(); } else { // Create new const created = await pbCreate({ collection: "groups", fields: { name: form.name.trim(), description: form.description.trim() || "", }, }); const newGroup = (created as YsterbaardGroup); setGroups((prev) => [...prev, newGroup].sort((a, b) => a.name.localeCompare(b.name))); setShowCreate(false); } await qc.invalidateQueries({ queryKey: ["pb-list", "groups"] }); } catch (err) { setError(err instanceof Error ? err.message : "Save failed."); } finally { setSaving(false); } } async function handleDelete() { if (!deleteId) return; setDeleteSaving(true); try { await pbDelete({ collection: "groups", id: deleteId }); setGroups((prev) => prev.filter((g) => g.id !== deleteId)); setDeleteId(null); await qc.invalidateQueries({ queryKey: ["pb-list", "groups"] }); } catch (err) { setError(err instanceof Error ? err.message : "Delete failed."); } finally { setDeleteSaving(false); } } return (
{/* Create button */} {!showCreate && editingId === null ? (
) : null} {/* Create form */} {showCreate ? (

Create Group

{error && (
{error}
)}
setForm((f) => ({ ...f, name: e.target.value.toUpperCase() }))} placeholder="e.g. CNL" autoFocus className="w-full rounded-lg border border-border bg-surface-warm px-3 py-2.5 text-sm text-fg focus:outline-none focus:border-accent focus:ring-1 focus:ring-accent" />
setForm((f) => ({ ...f, description: e.target.value }))} placeholder="e.g. CNL Group of Companies" className="w-full rounded-lg border border-border bg-surface-warm px-3 py-2.5 text-sm text-fg focus:outline-none focus:border-accent focus:ring-1 focus:ring-accent" />
) : null} {/* Error banner */} {error && !showCreate ? (
{error}
) : null} {/* Groups list */} {groups.length === 0 && !showCreate ? (
No groups yet. Click “New Group” to create one.
) : (
{groups.map((g) => ( {editingId === g.id ? ( // Editing row <> ) : ( // Display row <> )} ))}
Group Name Description
setForm((f) => ({ ...f, name: e.target.value.toUpperCase() }))} className="w-full rounded border border-border bg-surface-warm px-3 py-2 text-sm text-fg focus:outline-none focus:border-accent" autoFocus /> setForm((f) => ({ ...f, description: e.target.value }))} className="w-full rounded border border-border bg-surface-warm px-3 py-2 text-sm text-fg focus:outline-none focus:border-accent" />
{g.name} {g.description || —}
Report View PDF
)} {/* Delete confirmation modal */} {deleteId ? (

Delete Group?

This will remove this group from all companies that are linked to it.

The group record will be deleted. Companies are not deleted.

) : null}
); }