import { A, useNavigate } from '@solidjs/router'; import { createResource, createSignal, For, Show } from 'solid-js'; import { Search } from 'lucide-solid'; import AdminShell from '~/components/AdminShell'; const API = '/api/gateway'; type Role = { id: string; key: string; name: string; audience: string; description?: string; department_name?: string; is_active: boolean; users_assigned: number; permissions_count: number; created_at: string; }; type ListResponse = { roles: Role[]; total: number; page: number; per_page: number; }; async function loadRoles(params: { q: string; page: number }): Promise { try { const qs = new URLSearchParams({ audience: 'INTERNAL', q: params.q, page: String(params.page), per_page: '8', }); const res = await fetch(`${API}/api/admin/roles?${qs}`); if (!res.ok) throw new Error('Failed'); const data = await res.json(); if (data.roles) return data; // flat array fallback return { roles: Array.isArray(data) ? data : [], total: 0, page: 1, per_page: 8 }; } catch { return { roles: [], total: 0, page: 1, per_page: 8 }; } } function formatDate(iso: string) { try { const d = new Date(iso); return d.toISOString().slice(0, 10); } catch { return '—'; } } export default function InternalRolesListPage() { const navigate = useNavigate(); const [search, setSearch] = createSignal(''); const [debouncedSearch, setDebouncedSearch] = createSignal(''); const [page, setPage] = createSignal(1); const [deleting, setDeleting] = createSignal(''); let debounceTimer: ReturnType; const handleSearch = (val: string) => { setSearch(val); clearTimeout(debounceTimer); debounceTimer = setTimeout(() => { setDebouncedSearch(val); setPage(1); }, 300); }; const [data, { refetch }] = createResource( () => ({ q: debouncedSearch(), page: page() }), loadRoles, ); const totalPages = () => { const d = data(); if (!d || d.per_page === 0) return 1; return Math.max(1, Math.ceil(d.total / d.per_page)); }; const handleDelete = async (id: string, name: string) => { if (!confirm(`Delete role "${name}"? This cannot be undone.`)) return; setDeleting(id); try { const res = await fetch(`${API}/api/admin/roles/${id}`, { method: 'DELETE' }); if (!res.ok) throw new Error('Failed'); refetch(); } finally { setDeleting(''); } }; return (
{/* Header & Title */}

Internal Role Management

+ Add Role
{/* Tabs */}
{(t) => ( )}
{/* Filters Row */}
handleSearch(e.currentTarget.value)} class="h-11 w-full rounded-xl border border-[#d9dde6] bg-[#f9fafb] pl-11 pr-4 text-[14px] text-[#050026] outline-none transition-colors focus:border-[#050026] focus:bg-white" />
Showing {((page() - 1) * (data()?.per_page ?? 8)) + 1}–{Math.min(page() * (data()?.per_page ?? 8), data()?.total ?? 0)} of {data()?.total ?? 0} roles
{/* Table */}
{(role) => ( )}
ROLE ID ROLE NAME CATEGORY ASSOCIATED USERS ACTIVE PERMISSIONS LAST UPDATED ACTION
Loading roles...
No internal roles found.
{role.key.toUpperCase()} {role.name} {role.department_name || '—'}
{/* Placeholder for avatar group if users > 0 */} 0} fallback={0 users}>
U
+{role.users_assigned - 1}
{role.permissions_count} Permissions {formatDate(role.created_at)}
{/* Pagination */} 1}>
Page {page()} of {totalPages()}
); }