import { A } from '@solidjs/router'; import { createMemo, createResource, createSignal, For, Show } from 'solid-js'; const API = '/api/gateway'; type SortMode = 'newest' | 'oldest' | 'name_asc' | 'name_desc'; async function fetchUsers(role: string): Promise { try { const res = await fetch(`${API}/api/admin/users?role=${role.toUpperCase()}`); if (!res.ok) throw new Error('Failed to load'); const data = await res.json(); return Array.isArray(data) ? data : (data.users || []); } catch { return []; } } function statusBadge(status?: string) { const normalized = (status || '').toUpperCase(); if (normalized === 'ACTIVE') return 'inline-flex items-center rounded-full bg-green-100 px-2.5 py-0.5 text-xs font-medium text-green-700'; if (normalized === 'PENDING') return 'inline-flex items-center rounded-full bg-orange-50 px-2.5 py-0.5 text-xs font-medium text-orange-700'; return 'inline-flex items-center rounded-full bg-gray-100 px-2.5 py-0.5 text-xs font-medium text-gray-600'; } export default function RoleUserManagementTablePage(props: { role: string; title: string; subtitle: string; emptyLabel: string; viewHref: (id: string) => string; }) { const [users] = createResource(() => props.role, fetchUsers); const [search, setSearch] = createSignal(''); const [statusFilter, setStatusFilter] = createSignal<'all' | 'ACTIVE' | 'INACTIVE' | 'PENDING'>('all'); const [sortBy, setSortBy] = createSignal('newest'); const [sortMenuOpen, setSortMenuOpen] = createSignal(false); const [filterMenuOpen, setFilterMenuOpen] = createSignal(false); const filtered = createMemo(() => { const list = users() ?? []; const q = search().toLowerCase().trim(); const f = statusFilter(); const sorted = list.filter((u) => { const fullName = String(u.name || u.full_name || '').toLowerCase(); const email = String(u.email || '').toLowerCase(); const st = String(u.status || '').toUpperCase(); const matchesSearch = !q || fullName.includes(q) || email.includes(q); const matchesStatus = f === 'all' || st === f; return matchesSearch && matchesStatus; }); sorted.sort((a, b) => { const aDate = new Date(a.created_at || 0).getTime(); const bDate = new Date(b.created_at || 0).getTime(); if (sortBy() === 'oldest') return aDate - bDate; if (sortBy() === 'name_asc') return String(a.name || a.full_name || '').localeCompare(String(b.name || b.full_name || '')); if (sortBy() === 'name_desc') return String(b.name || b.full_name || '').localeCompare(String(a.name || a.full_name || '')); return bDate - aDate; }); return sorted; }); const exportCsv = () => { const headers = ['Name', 'Email', 'Status', 'Registered']; const rows = filtered().map((item) => [ String(item.name || item.full_name || ''), String(item.email || ''), String(item.status || ''), item.created_at ? new Date(item.created_at).toLocaleDateString() : '', ]); const csv = [headers, ...rows] .map((line) => line.map((cell) => `"${String(cell).replace(/"/g, '""')}"`).join(',')) .join('\n'); const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' }); const url = URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = `${props.role}-users.csv`; link.click(); URL.revokeObjectURL(url); }; return (

{props.title}

{props.subtitle}

setSearch(e.currentTarget.value)} class="rounded-lg border border-gray-200 px-3 py-2 text-sm outline-none focus:border-[#FF5E13] w-72" />
{(item) => ( )}
{(item) => ( )}
0}> {(item) => ( )}
Name Email Status Registered Actions
Loading...
Failed to load. Is the backend running?
{props.emptyLabel}
{item.name || item.full_name || '—'} {item.email} {item.status?.toUpperCase() || '—'} {item.created_at ? new Date(item.created_at).toLocaleDateString() : '—'}
); }