import { A } from '@solidjs/router'; import { createResource, createSignal, createMemo, Show, For } from 'solid-js'; import AdminShell from '~/components/AdminShell'; const API = '/api/gateway'; type Department = { id: string; name?: string; departmentName?: string; description?: string; is_archived?: boolean; status?: string | number; created_at?: string; createdAt?: string; }; async function loadDepartments(): Promise { try { const res = await fetch(`${API}/api/admin/departments`); if (!res.ok) throw new Error('Failed to load'); const data = await res.json(); return Array.isArray(data) ? data : (data.departments ?? []); } catch { return []; } } function isArchived(item: Department): boolean { if (item.is_archived !== undefined) return item.is_archived; if (item.status !== undefined) { const s = String(item.status).toUpperCase(); return s === 'ARCHIVED' || s === '2'; } return false; } function deptLabel(item: Department): string { return item.departmentName || item.name || '—'; } function fmtDate(val?: string): string { if (!val) return '—'; try { return new Date(val).toLocaleDateString(); } catch { return val; } } export default function DepartmentPage() { const [departments, { refetch }] = createResource(loadDepartments); // tabs const [tab, setTab] = createSignal<'active' | 'archived'>('active'); // create form const [showCreate, setShowCreate] = createSignal(false); const [createName, setCreateName] = createSignal(''); const [createDesc, setCreateDesc] = createSignal(''); const [creating, setCreating] = createSignal(false); const [createError, setCreateError] = createSignal(''); // inline edit const [editingId, setEditingId] = createSignal(''); const [editName, setEditName] = createSignal(''); const [editDesc, setEditDesc] = createSignal(''); const [saving, setSaving] = createSignal(false); const [editError, setEditError] = createSignal(''); // row-level busy const [busy, setBusy] = createSignal(''); const [actionError, setActionError] = createSignal(''); const filtered = createMemo(() => { const all = departments() ?? []; return tab() === 'archived' ? all.filter((d) => isArchived(d)) : all.filter((d) => !isArchived(d)); }); // ---------- CREATE ---------- const handleCreate = async (e: Event) => { e.preventDefault(); if (!createName().trim()) return; setCreating(true); setCreateError(''); try { const res = await fetch(`${API}/api/admin/departments`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name: createName().trim(), description: createDesc().trim(), }), }); if (!res.ok) { const err = await res.json().catch(() => ({})); throw new Error((err as any).message || 'Failed to create'); } setCreateName(''); setCreateDesc(''); setShowCreate(false); setTab('active'); refetch(); } catch (err: any) { setCreateError(err.message || 'Failed to create department'); } finally { setCreating(false); } }; // ---------- EDIT ---------- const startEdit = (item: Department) => { setEditingId(item.id); setEditName(deptLabel(item)); setEditDesc(item.description ?? ''); setEditError(''); }; const cancelEdit = () => { setEditingId(''); setEditError(''); }; const handleUpdate = async (id: string) => { if (!editName().trim()) return; setSaving(true); setEditError(''); try { const res = await fetch(`${API}/api/admin/departments/${id}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name: editName().trim(), description: editDesc().trim(), }), }); if (!res.ok) { const err = await res.json().catch(() => ({})); throw new Error((err as any).message || 'Failed to update'); } setEditingId(''); refetch(); } catch (err: any) { setEditError(err.message || 'Failed to update department'); } finally { setSaving(false); } }; // ---------- ARCHIVE / RESTORE ---------- const handleArchive = async (id: string) => { setBusy(id); setActionError(''); try { const res = await fetch(`${API}/api/admin/departments/${id}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ is_archived: true }), }); if (!res.ok) throw new Error('Failed to archive'); refetch(); } catch (err: any) { setActionError(err.message || 'Failed to archive department'); } finally { setBusy(''); } }; const handleRestore = async (id: string) => { setBusy(id); setActionError(''); try { const res = await fetch(`${API}/api/admin/departments/${id}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ is_archived: false }), }); if (!res.ok) throw new Error('Failed to restore'); refetch(); } catch (err: any) { setActionError(err.message || 'Failed to restore department'); } finally { setBusy(''); } }; // ---------- DELETE ---------- const handleDelete = async (id: string, name: string) => { if (!confirm(`Delete department "${name}"?`)) return; setBusy(id); setActionError(''); try { const res = await fetch(`${API}/api/admin/departments/${id}`, { method: 'DELETE', }); if (!res.ok) throw new Error('Failed to delete'); refetch(); } catch (err: any) { setActionError(err.message || 'Failed to delete department'); } finally { setBusy(''); } }; return ( {/* Header */}

Departments

Manage organization departments

{/* Create form */}
setCreateName(e.currentTarget.value)} />
setCreateDesc(e.currentTarget.value)} />

{createError()}

{/* Tabs */}
{/* Action error */}
{actionError()}
{/* Table */}
0}> {(item) => ( <> {/* Inline edit row */} )}
Name Description Created At Actions
Loading...
Failed to load. Is the backend running?
No departments found.
{deptLabel(item)} {item.description || '—'} {fmtDate(item.createdAt || item.created_at)}
setEditName(e.currentTarget.value)} />
setEditDesc(e.currentTarget.value)} />

{editError()}

); }