initial: standalone repo from monorepo split
This commit is contained in:
95
src/app/(dashboard)/dashboard/clients/page.tsx
Normal file
95
src/app/(dashboard)/dashboard/clients/page.tsx
Normal file
@@ -0,0 +1,95 @@
|
||||
'use client';
|
||||
|
||||
import { useState, useEffect, useCallback } from 'react';
|
||||
import Link from 'next/link';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { api } from '@/lib/api';
|
||||
import { PageHeader } from '@/components/ui/page-header';
|
||||
import { DataTable } from '@/components/ui/data-table';
|
||||
import { Badge, statusBadgeVariant } from '@/components/ui/badge';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { ActionIcon } from '@/components/ui/action-icon';
|
||||
import { CreateClientModal } from '@/components/modals/create-client-modal';
|
||||
|
||||
interface Client {
|
||||
id: string;
|
||||
accountNumber: string;
|
||||
firstName: string;
|
||||
lastName: string;
|
||||
email: string | null;
|
||||
phone: string | null;
|
||||
status: string;
|
||||
area: { id: string; name: string } | null;
|
||||
_count: { subscriptions: number; tickets: number };
|
||||
}
|
||||
|
||||
export default function ClientsPage() {
|
||||
const router = useRouter();
|
||||
const [clients, setClients] = useState<Client[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [search, setSearch] = useState('');
|
||||
const [showCreate, setShowCreate] = useState(false);
|
||||
const [filters, setFilters] = useState<Record<string, string>>({});
|
||||
|
||||
const loadClients = useCallback(async () => {
|
||||
try {
|
||||
const res = await api.get('/clients?limit=100');
|
||||
const d = res.data.data;
|
||||
setClients(Array.isArray(d) ? d : d.items);
|
||||
} finally { setLoading(false); }
|
||||
}, []);
|
||||
|
||||
useEffect(() => { loadClients(); }, [loadClients]);
|
||||
|
||||
const filtered = clients.filter((c) => {
|
||||
if (search && !`${c.firstName} ${c.lastName} ${c.accountNumber} ${c.phone || ''} ${c.email || ''}`.toLowerCase().includes(search.toLowerCase())) return false;
|
||||
if (filters.status && c.status !== filters.status) return false;
|
||||
return true;
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
<PageHeader title="Clients" description="Manage subscriber accounts"
|
||||
action={<Button onClick={() => setShowCreate(true)}>New Client</Button>} />
|
||||
|
||||
<div className="mt-5">
|
||||
<DataTable
|
||||
data={filtered}
|
||||
loading={loading}
|
||||
keyExtractor={(c) => c.id}
|
||||
emptyTitle="No clients found"
|
||||
emptyDescription="Create your first client to get started."
|
||||
searchPlaceholder="Search by name, account #, phone, email..."
|
||||
searchValue={search}
|
||||
onSearchChange={setSearch}
|
||||
quickFilters={[
|
||||
{ key: 'status', label: 'Status', options: [
|
||||
{ label: 'Active', value: 'active' },
|
||||
{ label: 'Inactive', value: 'inactive' },
|
||||
{ label: 'Suspended', value: 'suspended' },
|
||||
]},
|
||||
]}
|
||||
activeFilters={filters}
|
||||
onFilterChange={(k, v) => setFilters((f) => ({ ...f, [k]: v }))}
|
||||
onRowClick={(c) => router.push(`/dashboard/clients/${c.id}`)}
|
||||
columns={[
|
||||
{ key: 'accountNumber', label: 'Account', sortable: true, render: (c) => <span className="font-mono text-surface-700 dark:text-surface-300">{c.accountNumber}</span> },
|
||||
{ key: 'firstName', label: 'Name', sortable: true, render: (c) => (
|
||||
<Link href={`/dashboard/clients/${c.id}`} className="font-medium text-surface-800 dark:text-surface-200 hover:text-primary-600 transition-colors">{c.firstName} {c.lastName}</Link>
|
||||
)},
|
||||
{ key: 'area', label: 'Area', render: (c) => <span className="text-surface-500 dark:text-surface-400">{c.area?.name || '—'}</span> },
|
||||
{ key: 'phone', label: 'Contact', render: (c) => <span className="text-surface-500 dark:text-surface-400">{c.phone || c.email || '—'}</span> },
|
||||
{ key: 'status', label: 'Status', sortable: true, render: (c) => <Badge label={c.status} variant={statusBadgeVariant(c.status)} /> },
|
||||
{ key: 'actions', label: '', align: 'right', render: (c) => (
|
||||
<Link href={`/dashboard/clients/${c.id}`} onClick={(e) => e.stopPropagation()}>
|
||||
<ActionIcon icon="eye" variant="ghost" label="View details" />
|
||||
</Link>
|
||||
)},
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<CreateClientModal open={showCreate} onClose={() => setShowCreate(false)} onSuccess={loadClients} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user