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 fetchProfessionList(endpoint: string): Promise { try { const res = await fetch(`${API}${endpoint}`); if (!res.ok) throw new Error(`Failed to load: ${res.status}`); const data = await res.json(); return Array.isArray(data) ? data : []; } catch (e) { console.error(`Failed to fetch from ${endpoint}:`, e); 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 ProfessionAdminListPage(props: { endpoint: string; title: string; subtitle: string; emptyLabel: string; viewHref: (id: string) => string; nameField?: string; }) { const nameField = props.nameField || 'first_name'; const [items] = createResource(() => props.endpoint, fetchProfessionList); 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 = items() ?? []; const q = search().toLowerCase().trim(); const f = statusFilter(); const sorted = list.filter((u) => { const firstName = String(u.first_name || '').toLowerCase(); const lastName = String(u.last_name || '').toLowerCase(); const fullName = `${firstName} ${lastName}`.trim(); 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 `${a.first_name || ''} ${a.last_name || ''}`.localeCompare(`${b.first_name || ''} ${b.last_name || ''}`); if (sortBy() === 'name_desc') return `${b.first_name || ''} ${b.last_name || ''}`.localeCompare(`${a.first_name || ''} ${a.last_name || ''}`); return bDate - aDate; }); return sorted; }); const exportCsv = () => { const headers = ['First Name', 'Last Name', 'Email', 'Phone', 'Status', 'Registered']; const rows = filtered().map((item) => [ String(item.first_name || ''), String(item.last_name || ''), String(item.email || ''), String(item.phone || ''), 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.title.toLowerCase().replace(/\s+/g, '-')}-export.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 Phone Status Registered Actions
Loading...
Failed to load. Is the backend running?
{props.emptyLabel}
{item.first_name || ''} {item.last_name || ''} {item.email} {item.phone || '—'} {item.status?.toUpperCase() || '—'} {item.created_at ? new Date(item.created_at).toLocaleDateString() : '—'}
); }