import { A, useNavigate } from '@solidjs/router'; import { createResource, createSignal, For, Show } from 'solid-js'; import AdminShell from '~/components/AdminShell'; import { ArrowUpDown, Filter, Download, Eye, Pencil } from 'lucide-solid'; 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 }; const FALLBACK_ROLES: Role[] = [ { id: 'r1', key: 'ADM-SYS-001', name: 'System Administrator', audience: 'INTERNAL', department_name: 'Information Technology', is_active: true, users_assigned: 12, permissions_count: 0, created_at: '2024-01-12' }, { id: 'r2', key: 'FIN-MGR-002', name: 'Finance Manager', audience: 'INTERNAL', department_name: 'Accounting & Finance', is_active: true, users_assigned: 4, permissions_count: 42, created_at: '2024-02-05' }, { id: 'r3', key: 'HR-COOR-003', name: 'HR Coordinator', audience: 'INTERNAL', department_name: 'Human Resources', is_active: false, users_assigned: 8, permissions_count: 28, created_at: '2024-03-18' }, { id: 'r4', key: 'ENG-LEAD-004', name: 'Lead Developer', audience: 'INTERNAL', department_name: 'Engineering', is_active: true, users_assigned: 25, permissions_count: 64, created_at: '2024-04-02' }, { id: 'r5', key: 'SLS-ASSC-005', name: 'Sales Associate', audience: 'INTERNAL', department_name: 'Growth & Sales', is_active: true, users_assigned: 48, permissions_count: 15, created_at: '2024-05-11' }, ]; 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: '10' }); const res = await fetch(`${API}/api/admin/roles?${qs}`); if (!res.ok) throw new Error('Failed'); const data = await res.json(); if (data.roles?.length > 0) return data; throw new Error('empty'); } catch { const q = params.q.toLowerCase(); const filtered = q ? FALLBACK_ROLES.filter((r) => r.name.toLowerCase().includes(q) || r.key.toLowerCase().includes(q)) : FALLBACK_ROLES; return { roles: filtered, total: filtered.length, page: 1, per_page: 10 }; } } function formatDate(iso: string) { try { const d = new Date(iso); return d.toLocaleDateString('en-US', { month: 'short', day: '2-digit', year: 'numeric' }); } catch { return '—'; } } function StatusBadge(props: { active: boolean }) { return ( {props.active ? 'Active' : 'Inactive'} ); } function UsersBadge(props: { count: number }) { return ( {props.count > 99 ? '99+' : String(props.count).padStart(2, '0')} ); } const TABS = [ { label: 'All Roles', href: '/admin/roles' }, { label: 'Create Role', href: '/admin/roles/create' }, { label: 'View Role', href: '/admin/roles/view' }, { label: 'Edit Role', href: '/admin/roles/edit' }, ]; export default function InternalRolesListPage() { const navigate = useNavigate(); const [search, setSearch] = createSignal(''); const [debouncedSearch, setDebouncedSearch] = createSignal(''); const [page, setPage] = createSignal(1); let debounceTimer: ReturnType; const handleSearch = (val: string) => { setSearch(val); clearTimeout(debounceTimer); debounceTimer = setTimeout(() => { setDebouncedSearch(val); setPage(1); }, 300); }; const [data] = 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 roles = () => data()?.roles ?? []; return (
{/* Page header */}

Internal Role Management

Define and manage organizational access levels with granular permission control.

{/* Tabs */}
{(tab) => { const active = () => tab.href === '/admin/roles'; return ( {tab.label} ); }}
{/* Table card */}
{/* Filter bar */}
handleSearch(e.currentTarget.value)} class="h-[34px] flex-1 rounded-lg border border-[#E5E7EB] bg-white px-3 text-[13px] text-[#111827] outline-none placeholder:text-[#9CA3AF] focus:border-[#FF5E13] focus:ring-2 focus:ring-[rgba(255,94,19,0.08)] transition-colors" />
{/* Table */}
{() => ( )} {(role) => ( )}
Role Name Role Code Department Users Assigned Permissions Status Created Date AC

No roles found

Create your first internal role to control admin access.

Create Role

{role.name}

{role.key.split('-').map((seg, i, arr) => ( {seg}{i < arr.length - 1 ? '-' : ''} ))}
{role.department_name || '—'} {role.permissions_count} Controls }> All Access {formatDate(role.created_at)}
{/* Pagination */} 0}>

Showing {roles().length} of {data()?.total ?? 0} roles

i + 1)}> {(p) => ( )}
{/* Bottom stats row */}
{/* Distribution card */}

Distribution

{[ { h: 55, color: '#FFD4C2' }, { h: 80, color: '#FF5E13' }, { h: 45, color: '#FFD4C2' }, { h: 65, color: '#FFD4C2' }, { h: 38, color: '#FFD4C2' }, ].map((bar) => (
))}

Most users are concentrated in Sales and{' '} Engineering departments.

{/* Audit Readiness Score card */}

Audit Readiness Score

Your organizational permissions currently align with 94% of compliance standards. Review inactive roles to reach 100%.

94%
); }