import { useCallback, useEffect, useMemo, useState } from "react"; import { Archive, Boxes, Building2, Inbox, Pencil, Plus, RefreshCw, Rocket, Save, Trash2 } from "lucide-react"; import { AdminPageLayout, Button, ConfirmDialog, Dialog, FormField, IconButton, SegmentedControl, SelectionList, SelectionListItem, StatusBadge, ToggleSwitch, type ApiSettings } from "@govoplan/core-webui"; import { archivePostbox, createExactPostbox, createPostboxTemplate, listAdminPostboxes, listPostboxOrganizationTargets, listPostboxTemplates, materializePostboxTemplate, publishPostboxTemplate, retirePostboxTemplate, revisePostboxTemplate, type PostboxDirectoryItem, type PostboxExactCreatePayload, type PostboxOrganizationFunction, type PostboxOrganizationUnit, type PostboxTemplate, type PostboxTemplateCreatePayload, type PostboxTemplateRevisionPayload } from "../../api/postbox"; type AdminMode = "templates" | "postboxes"; type TemplateDraft = PostboxTemplateCreatePayload & { templateId: string }; type ExactDraft = PostboxExactCreatePayload; type MaterializeDraft = { templateId: string; organization_unit_id: string; function_id: string; context_key: string; }; const templateDefaults = (): TemplateDraft => ({ templateId: "", slug: "", name: "", description: "", function_type_id: null, scope_kind: "tenant", scope_id: null, name_pattern: "{unit_name} / {function_name}", address_pattern: "{template_slug}.{unit_slug}.{function_slug}", classification: "internal", allow_vacant_delivery: true }); const exactDefaults = (): ExactDraft => ({ name: "", description: "", organization_unit_id: "", function_id: "", address_key: "", classification: "internal" }); export default function PostboxAdminPanel({ settings, canManageBindings, canManageTemplates }: { settings: ApiSettings; canManageBindings: boolean; canManageTemplates: boolean; }) { const [mode, setMode] = useState( canManageTemplates ? "templates" : "postboxes" ); const [templates, setTemplates] = useState([]); const [postboxes, setPostboxes] = useState([]); const [units, setUnits] = useState([]); const [selectedTemplateId, setSelectedTemplateId] = useState(""); const [selectedPostboxId, setSelectedPostboxId] = useState(""); const [loading, setLoading] = useState(true); const [busy, setBusy] = useState(false); const [error, setError] = useState(""); const [success, setSuccess] = useState(""); const [templateDialogOpen, setTemplateDialogOpen] = useState(false); const [templateDraft, setTemplateDraft] = useState(templateDefaults); const [exactDialogOpen, setExactDialogOpen] = useState(false); const [exactDraft, setExactDraft] = useState(exactDefaults); const [materializeDialogOpen, setMaterializeDialogOpen] = useState(false); const [materializeDraft, setMaterializeDraft] = useState({ templateId: "", organization_unit_id: "", function_id: "", context_key: "" }); const [archiveTarget, setArchiveTarget] = useState(null); const selectedTemplate = useMemo( () => templates.find((template) => template.id === selectedTemplateId) ?? templates[0] ?? null, [templates, selectedTemplateId] ); const selectedPostbox = useMemo( () => postboxes.find((postbox) => postbox.id === selectedPostboxId) ?? postboxes[0] ?? null, [postboxes, selectedPostboxId] ); const functionTypes = useMemo(() => { const values = new Map(); for (const unit of units) { for (const fn of unit.functions) { if (fn.function_type_id && !values.has(fn.function_type_id)) { values.set(fn.function_type_id, fn.name); } } } return [...values].map(([id, name]) => ({ id, name })); }, [units]); const unitTypes = useMemo(() => { const values = new Map(); for (const unit of units) { if (unit.unit_type_id && !values.has(unit.unit_type_id)) { values.set(unit.unit_type_id, unit.name); } } return [...values].map(([id, example]) => ({ id, example })); }, [units]); const load = useCallback(async () => { setLoading(true); setError(""); try { const [nextTemplates, nextPostboxes, nextUnits] = await Promise.all([ canManageTemplates ? listPostboxTemplates(settings) : Promise.resolve([]), canManageBindings ? listAdminPostboxes(settings) : Promise.resolve([]), listPostboxOrganizationTargets(settings) ]); setTemplates(nextTemplates); setPostboxes(nextPostboxes); setUnits(nextUnits); setSelectedTemplateId((current) => current && nextTemplates.some((template) => template.id === current) ? current : nextTemplates[0]?.id ?? "" ); setSelectedPostboxId((current) => current && nextPostboxes.some((postbox) => postbox.id === current) ? current : nextPostboxes[0]?.id ?? "" ); } catch (loadError) { setError(errorMessage(loadError)); } finally { setLoading(false); } }, [canManageBindings, canManageTemplates, settings]); useEffect(() => { void load(); }, [load]); function openNewTemplate() { setTemplateDraft(templateDefaults()); setTemplateDialogOpen(true); } function openTemplateRevision(template: PostboxTemplate) { const revision = currentRevision(template); if (!revision) return; setTemplateDraft({ templateId: template.id, slug: template.slug, name: template.name, description: template.description || "", function_type_id: revision.function_type_id ?? null, scope_kind: revision.scope_kind, scope_id: revision.scope_id ?? null, name_pattern: revision.name_pattern, address_pattern: revision.address_pattern, classification: revision.classification, allow_vacant_delivery: revision.allow_vacant_delivery }); setTemplateDialogOpen(true); } async function saveTemplate() { setBusy(true); setError(""); setSuccess(""); try { if (templateDraft.templateId) { await revisePostboxTemplate( settings, templateDraft.templateId, revisionPayload(templateDraft) ); setSuccess("A new immutable template revision was created."); } else { await createPostboxTemplate(settings, { slug: templateDraft.slug, name: templateDraft.name, description: templateDraft.description || null, ...revisionPayload(templateDraft) }); setSuccess("Postbox template created as a draft."); } setTemplateDialogOpen(false); await load(); } catch (actionError) { setError(errorMessage(actionError)); } finally { setBusy(false); } } async function publishSelected() { if (!selectedTemplate) return; setBusy(true); setError(""); setSuccess(""); try { await publishPostboxTemplate( settings, selectedTemplate.id, selectedTemplate.current_revision ); setSuccess(`Published revision ${selectedTemplate.current_revision}.`); await load(); } catch (actionError) { setError(errorMessage(actionError)); } finally { setBusy(false); } } async function retireSelected() { if (!selectedTemplate) return; setBusy(true); setError(""); setSuccess(""); try { await retirePostboxTemplate(settings, selectedTemplate.id); setSuccess("Postbox template retired. Existing addresses remain durable."); await load(); } catch (actionError) { setError(errorMessage(actionError)); } finally { setBusy(false); } } function openExact() { const unit = units.find((item) => item.functions.length); setExactDraft({ ...exactDefaults(), organization_unit_id: unit?.id ?? "", function_id: unit?.functions[0]?.id ?? "" }); setExactDialogOpen(true); } async function saveExact() { setBusy(true); setError(""); setSuccess(""); try { await createExactPostbox(settings, { ...exactDraft, description: exactDraft.description || null, address_key: exactDraft.address_key || null }); setExactDialogOpen(false); setSuccess("Exact function-bound Postbox created."); await load(); } catch (actionError) { setError(errorMessage(actionError)); } finally { setBusy(false); } } function openMaterialize(template: PostboxTemplate) { const revision = currentRevision(template); const compatible = compatibleTargets(units, revision?.function_type_id); const unit = compatible[0]; setMaterializeDraft({ templateId: template.id, organization_unit_id: unit?.id ?? "", function_id: unit?.functions[0]?.id ?? "", context_key: "" }); setMaterializeDialogOpen(true); } async function materialize() { setBusy(true); setError(""); setSuccess(""); try { await materializePostboxTemplate(settings, materializeDraft.templateId, { organization_unit_id: materializeDraft.organization_unit_id, function_id: materializeDraft.function_id, context_key: materializeDraft.context_key || null }); setMaterializeDialogOpen(false); setSuccess("Stable Postbox address resolved and materialized."); await load(); } catch (actionError) { setError(errorMessage(actionError)); } finally { setBusy(false); } } async function confirmArchive() { if (!archiveTarget) return; setBusy(true); setError(""); setSuccess(""); try { await archivePostbox(settings, archiveTarget.id); setSuccess("Postbox archived. Messages and delivery evidence were retained."); setArchiveTarget(null); await load(); } catch (actionError) { setError(errorMessage(actionError)); } finally { setBusy(false); } } return ( } onClick={() => void load()} disabled={busy} /> {mode === "templates" && canManageTemplates ? ( ) : null} {mode === "postboxes" && canManageBindings ? ( ) : null} } > setMode(value as AdminMode)} options={[ ...(canManageTemplates ? [{ id: "templates", label: "Templates" }] : []), ...(canManageBindings ? [{ id: "postboxes", label: "Postboxes" }] : []) ]} ariaLabel="Postbox administration section" /> {mode === "templates" ? ( void publishSelected()} onRetire={() => void retireSelected()} onMaterialize={openMaterialize} busy={busy} canManageBindings={canManageBindings} /> ) : ( )} setTemplateDialogOpen(false)} onSave={() => void saveTemplate()} /> setExactDialogOpen(false)} onSave={() => void saveExact()} /> item.id === materializeDraft.templateId) ?? null} units={units} busy={busy} onChange={setMaterializeDraft} onClose={() => setMaterializeDialogOpen(false)} onSave={() => void materialize()} /> void confirmArchive()} onCancel={() => setArchiveTarget(null)} /> ); } function TemplateWorkspace({ templates, selected, onSelect, onRevise, onPublish, onRetire, onMaterialize, busy, canManageBindings }: { templates: PostboxTemplate[]; selected: PostboxTemplate | null; onSelect: (id: string) => void; onRevise: (template: PostboxTemplate) => void; onPublish: () => void; onRetire: () => void; onMaterialize: (template: PostboxTemplate) => void; busy: boolean; canManageBindings: boolean; }) { const revision = selected ? currentRevision(selected) : null; return (
{selected && revision ? ( <>
Template

{selected.name}

{selected.description || "No description."}

{canManageBindings ? ( ) : null}
Revision
{revision.revision}{revision.published_at ? " · published" : " · draft"}
Function type
{revision.function_type_id || "Any function type"}
Scope
{revision.scope_kind}{revision.scope_id ? ` · ${revision.scope_id}` : ""}
Classification
{revision.classification}
Vacant delivery
{revision.allow_vacant_delivery ? "Accepted" : "Blocked"}
Encryption
{revision.encryption_profile}
Name pattern
{revision.name_pattern}
Address pattern
{revision.address_pattern}

Immutable revisions

{selected.revisions.map((item) => (
Revision {item.revision} {item.classification} · {item.scope_kind}
))}
) : (
Select a template

Published revisions lazily resolve stable unit-specific addresses.

)}
); } function PostboxWorkspace({ postboxes, selected, onSelect, onArchive, busy }: { postboxes: PostboxDirectoryItem[]; selected: PostboxDirectoryItem | null; onSelect: (id: string) => void; onArchive: (postbox: PostboxDirectoryItem) => void; busy: boolean; }) { return (
{selected ? ( <>
Postbox

{selected.name}

{selected.address}

Organization unit
{selected.organization_unit_name || "None"}
Function
{selected.function_name || "None"}
Address key
{selected.address_key}
Classification
{selected.classification}
Current holders
{selected.holder_count}
Vacancy
{selected.vacant ? "Vacant" : "Staffed"}
Context
{selected.context_key || "None"}
Template revision
{selected.template_revision_id || "Exact Postbox"}
) : (
Select a Postbox

Materialized Postboxes remain durable through vacancy and reassignment.

)}
); } function TemplateDialog({ open, draft, units, functionTypes, unitTypes, busy, onChange, onClose, onSave }: { open: boolean; draft: TemplateDraft; units: PostboxOrganizationUnit[]; functionTypes: Array<{ id: string; name: string }>; unitTypes: Array<{ id: string; example: string }>; busy: boolean; onChange: (draft: TemplateDraft) => void; onClose: () => void; onSave: () => void; }) { const isRevision = Boolean(draft.templateId); const scopeOptions = draft.scope_kind === "unit_type" ? unitTypes.map((item) => ({ id: item.id, label: `${item.id} (${item.example})` })) : units.map((unit) => ({ id: unit.id, label: unit.name })); const valid = draft.name.trim() && draft.slug.trim() && draft.name_pattern.trim() && draft.address_pattern.trim() && (draft.scope_kind === "tenant" || Boolean(draft.scope_id)); return ( } >
onChange({ ...draft, name: event.target.value })} /> onChange({ ...draft, slug: event.target.value })} /> onChange({ ...draft, description: event.target.value })} /> {draft.scope_kind !== "tenant" ? ( ) :
} onChange({ ...draft, name_pattern: event.target.value })} /> onChange({ ...draft, address_pattern: event.target.value })} /> onChange({ ...draft, classification: event.target.value })} />
onChange({ ...draft, allow_vacant_delivery: checked })} />

Available pattern variables include template, unit, function, and optional context names or slugs. Published revisions are immutable.

); } function ExactPostboxDialog({ open, draft, units, busy, onChange, onClose, onSave }: { open: boolean; draft: ExactDraft; units: PostboxOrganizationUnit[]; busy: boolean; onChange: (draft: ExactDraft) => void; onClose: () => void; onSave: () => void; }) { const unit = units.find((item) => item.id === draft.organization_unit_id); return ( } >
onChange({ ...draft, name: event.target.value })} /> onChange({ ...draft, address_key: event.target.value })} /> onChange({ ...draft, classification: event.target.value })} /> onChange({ ...draft, description: event.target.value })} />
); } function MaterializeDialog({ open, draft, template, units, busy, onChange, onClose, onSave }: { open: boolean; draft: MaterializeDraft; template: PostboxTemplate | null; units: PostboxOrganizationUnit[]; busy: boolean; onChange: (draft: MaterializeDraft) => void; onClose: () => void; onSave: () => void; }) { const revision = template ? currentRevision(template) : null; const compatible = compatibleTargets(units, revision?.function_type_id); const unit = compatible.find((item) => item.id === draft.organization_unit_id); return ( } >
onChange({ ...draft, context_key: event.target.value })} />
); } function currentRevision(template: PostboxTemplate) { return template.revisions.find((revision) => revision.revision === template.current_revision) ?? template.revisions.at(-1) ?? null; } function revisionPayload(draft: TemplateDraft): PostboxTemplateRevisionPayload { return { function_type_id: draft.function_type_id || null, scope_kind: draft.scope_kind, scope_id: draft.scope_kind === "tenant" ? null : draft.scope_id || null, name_pattern: draft.name_pattern, address_pattern: draft.address_pattern, classification: draft.classification, allow_vacant_delivery: draft.allow_vacant_delivery }; } function compatibleTargets( units: PostboxOrganizationUnit[], functionTypeId?: string | null ): PostboxOrganizationUnit[] { return units .map((unit) => ({ ...unit, functions: functionTypeId ? unit.functions.filter((fn) => fn.function_type_id === functionTypeId) : unit.functions })) .filter((unit) => unit.functions.length); } function errorMessage(error: unknown): string { return error instanceof Error ? error.message : "Postbox request failed"; }