3import { useState } from 'react';
4import { apiClient } from '@/lib/client/apiClient';
6interface CustomerData {
13export default function SalesByCustomerPage() {
14 const [customerData, setCustomerData] = useState<CustomerData[]>([]);
15 const [isLoading, setIsLoading] = useState(false);
16 const [sortColumn, setSortColumn] = useState<keyof CustomerData>('name');
17 const [sortDirection, setSortDirection] = useState<'asc' | 'desc'>('asc');
18 const [maxRows, setMaxRows] = useState(100);
20 const loadReport = async () => {
23 const response = await apiClient.getCustomers({ limit: maxRows, source: 'openapi' });
25 if (response.success && response.data) {
26 const customers: CustomerData[] = response.data.map((cust: any) => ({
27 cid: String(cust.cid || cust.CustomerId || ''),
28 name: cust.Name || cust.CustomerName || 'Unknown Customer',
29 email: cust.Email || '',
30 phone: cust.Mobile || cust.Phone || ''
33 setCustomerData(customers);
36 console.error('Error loading customers:', error);
42 const handleSort = (column: keyof CustomerData) => {
43 if (sortColumn === column) {
44 setSortDirection(sortDirection === 'asc' ? 'desc' : 'asc');
46 setSortColumn(column);
47 setSortDirection('asc');
51 const sortedData = [...customerData].sort((a, b) => {
52 const aVal = a[sortColumn];
53 const bVal = b[sortColumn];
55 if (aVal === undefined || bVal === undefined) return 0;
57 const aStr = String(aVal);
58 const bStr = String(bVal);
59 return sortDirection === 'asc'
60 ? aStr.localeCompare(bStr)
61 : bStr.localeCompare(aStr);
64 const SortIcon = ({ column }: { column: keyof CustomerData }) => {
65 if (sortColumn !== column) return <span className="text-muted/50">⇅</span>;
66 return <span>{sortDirection === 'asc' ? '↑' : '↓'}</span>;
72 <div className="mb-6">
73 <h1 className="text-3xl font-bold mb-2">Sales by Customer 👥</h1>
74 <p className="text-sm text-muted mb-2">
75 Customer list and information
77 <div className="text-sm text-muted">
78 <a href="/pages/reports/sales-reports" className="text-brand hover:underline">Sales</a>
80 <span>Sales by Customer</span>
85 <div className="bg-surface rounded-lg shadow p-6 mb-6">
86 <div className="mb-4 p-3 bg-info/10 border-l-4 border-info">
87 <p className="text-sm text-info">
88 <strong>Note:</strong> This report shows customer directory. Sales analytics by customer is not available in the current API.
92 onSubmit={(e) => { e.preventDefault(); loadReport(); }}
93 className="flex items-end gap-4"
96 <label className="block text-sm font-medium text-text mb-2">
101 onChange={(e) => setMaxRows(Number(e.target.value))}
102 className="border rounded px-3 py-2"
104 <option value="50">50</option>
105 <option value="100">100</option>
106 <option value="200">200</option>
107 <option value="500">500</option>
114 className="bg-brand text-white px-6 py-2 rounded hover:bg-brand2 disabled:bg-muted/50"
116 {isLoading ? 'Loading...' : 'Load Customers'}
122 {/* Results Table */}
123 <div className="bg-surface rounded-lg shadow overflow-hidden">
125 <div className="p-8 text-center">
126 <div className="inline-block animate-spin rounded-full h-8 w-8 border-b-2 border-info"></div>
127 <p className="mt-2 text-muted">Loading customers...</p>
131 <div className="overflow-x-auto">
132 <table className="w-full text-sm">
133 <thead className="bg-surface-2 sticky top-0">
136 className="px-4 py-3 text-left font-semibold cursor-pointer hover:bg-surface-2/80"
137 onClick={() => handleSort('cid')}
139 ID <SortIcon column="cid" />
142 className="px-4 py-3 text-left font-semibold cursor-pointer hover:bg-surface-2/80"
143 onClick={() => handleSort('name')}
145 Name <SortIcon column="name" />
148 className="px-4 py-3 text-left font-semibold cursor-pointer hover:bg-surface-2/80"
149 onClick={() => handleSort('email')}
151 Email <SortIcon column="email" />
154 className="px-4 py-3 text-left font-semibold cursor-pointer hover:bg-surface-2/80"
155 onClick={() => handleSort('phone')}
157 Phone <SortIcon column="phone" />
161 <tbody className="divide-y divide-border">
162 {sortedData.length === 0 ? (
164 <td colSpan={4} className="px-4 py-8 text-center text-muted">
165 {isLoading ? 'Loading...' : 'No customer data found. Click "Load Customers" to fetch data.'}
169 sortedData.map((customer, idx) => (
170 <tr key={idx} className="hover:bg-surface-2">
171 <td className="px-4 py-3 font-mono text-sm">
174 <td className="px-4 py-3 font-medium">
177 <td className="px-4 py-3 text-muted">
178 {customer.email || '-'}
180 <td className="px-4 py-3 text-muted">
181 {customer.phone || '-'}
190 {customerData.length > 0 && (
191 <div className="px-4 py-3 bg-surface-2 border-t">
192 <p className="text-sm text-muted">
193 Showing {customerData.length} customer{customerData.length !== 1 ? 's' : ''}.