import { For, Show, createMemo, createSignal, onMount } from 'solid-js'; import AdminShell from '~/components/AdminShell'; import type { CrudRecord } from '~/lib/admin/types'; const API = '/api/gateway'; type CustomerRecord = CrudRecord & { email: string; phone?: string; location?: string; totalOrders?: number; status: 'ACTIVE' | 'INACTIVE' | 'PENDING'; registeredDate?: string; }; function StatusBadge(props: { status: string }) { const active = () => props.status === 'ACTIVE'; const pending = () => props.status === 'PENDING'; return ( {props.status.charAt(0) + props.status.slice(1).toLowerCase()} ); } export default function CustomerManagementPage() { const [view, setView] = createSignal<'list' | 'detail'>('list'); const [listTab, setListTab] = createSignal<'all' | 'active' | 'pending' | 'view'>('all'); const [detailTab, setDetailTab] = createSignal<'overview' | 'orders' | 'support'>('overview'); const [search, setSearch] = createSignal(''); const [statusFilter, setStatusFilter] = createSignal('all'); const [sortBy, setSortBy] = createSignal<'name_asc' | 'name_desc' | 'orders_desc' | 'orders_asc'>('name_asc'); const [sortMenuOpen, setSortMenuOpen] = createSignal(false); const [filterMenuOpen, setFilterMenuOpen] = createSignal(false); const [rows, setRows] = createSignal([]); const [selectedCustomer, setSelectedCustomer] = createSignal(null); const [openMenuId, setOpenMenuId] = createSignal(null); const load = async () => { try { const res = await fetch(`${API}/api/admin/users?role=CUSTOMER`); if (!res.ok) throw new Error('Fetch failed'); const data = await res.json(); const list = (Array.isArray(data) ? data : []).map((u: any) => ({ id: u.id, name: u.full_name || u.email.split('@')[0], email: u.email, status: (u.status || 'ACTIVE').toUpperCase(), registeredDate: u.created_at ? new Date(u.created_at).toLocaleDateString() : '—', updatedAt: u.updated_at || u.created_at || '—', location: 'Not Specified', totalOrders: 0 } as CustomerRecord)); setRows(list); } catch (e) { console.error('Customer load error:', e); setRows([]); } }; onMount(() => void load()); const filteredRows = createMemo(() => { let r = rows(); if (statusFilter() !== 'all') r = r.filter((d) => d.status === statusFilter().toUpperCase()); const q = search().toLowerCase(); if (q) { r = r.filter(it => it.name.toLowerCase().includes(q) || it.email.toLowerCase().includes(q)); } const sorted = [...r]; const mode = sortBy(); sorted.sort((a, b) => { if (mode === 'name_desc') return b.name.localeCompare(a.name); if (mode === 'orders_desc') return (b.totalOrders || 0) - (a.totalOrders || 0); if (mode === 'orders_asc') return (a.totalOrders || 0) - (b.totalOrders || 0); return a.name.localeCompare(b.name); }); return sorted; }); const exportCsv = () => { const headers = ['Customer Name', 'Email', 'Location', 'Orders', 'Status']; const body = filteredRows().map((row) => [ row.name || '', row.email || '', row.location || '', String(row.totalOrders || 0), row.status || '', ]); const csv = [headers, ...body] .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 = 'customers.csv'; link.click(); URL.revokeObjectURL(url); }; const openDetail = (row: CustomerRecord) => { setSelectedCustomer(row); setListTab('view'); setOpenMenuId(null); }; return (

Customer Management

Manage and monitor all customer accounts on the platform

{/* ── LIST VIEW ── */}
{/* Tabs */}
{([ { key: 'all', label: 'All Customers', action: () => { setListTab('all'); setStatusFilter('all'); } }, { key: 'active', label: 'Active', action: () => { setListTab('active'); setStatusFilter('active'); } }, { key: 'pending', label: 'Pending', action: () => { setListTab('pending'); setStatusFilter('pending'); } }, { key: 'view', label: 'View Profile', action: () => setListTab('view') }, ] as const).map((tab) => ( ))}
{/* View Profile panel */}

No customer selected

Click the menu on any customer row and choose View Profile.

{selectedCustomer()!.name}

{selectedCustomer()!.email} • Joined {selectedCustomer()!.registeredDate}

{(['overview', 'orders', 'support'] as const).map((tab, i) => { const labels = ['Overview', 'Order History', 'Support Cases']; const active = () => detailTab() === tab; return ( ); })}

Customer Summary

{[ { l: 'Location', v: selectedCustomer()!.location || '—' }, { l: 'Total Orders', v: selectedCustomer()!.totalOrders || '0' }, { l: 'Phone', v: selectedCustomer()!.phone || '—' }, { l: 'Last Active', v: selectedCustomer()!.updatedAt || '—' }, ].map(item => (
{item.l} {item.v}
))}
setSearch(e.currentTarget.value)} placeholder="Search customers..." style="height:34px;flex:1;border-radius:8px;border:1px solid #E5E7EB;background:white;padding:0 12px;font-size:13px;color:#111827;outline:none" />
{(['name_asc', 'name_desc', 'orders_desc', 'orders_asc'] as const).map((s, i) => ( ))}
{(['all', 'active', 'pending', 'inactive'] as const).map((s) => ( ))}
{['Customer Name', 'Email', 'Location', 'Orders', 'Status', 'Actions'].map(h => ( ))} {(row) => ( )}
{h}
{row.name} {row.email} {row.location || '—'} {row.totalOrders || 0} orders
); }