212 lines
6.7 KiB
TypeScript
212 lines
6.7 KiB
TypeScript
import { useState, useMemo } from 'react';
|
||
import { DataTable } from '~/components/ui/DataTable';
|
||
import { Button } from '~/components/ui/Button';
|
||
import { Text } from '~/components/ui/Text';
|
||
import { processTableData, type TableState } from '~/lib/table-utils';
|
||
import { useSettings } from '~/contexts/SettingsContext';
|
||
import type { Customer } from '~/types/database';
|
||
|
||
interface EnhancedCustomerTableProps {
|
||
customers: Customer[];
|
||
loading?: boolean;
|
||
onEdit?: (customer: Customer) => void;
|
||
onDelete?: (customer: Customer) => void;
|
||
onView?: (customer: Customer) => void;
|
||
}
|
||
|
||
export function EnhancedCustomerTable({
|
||
customers,
|
||
loading = false,
|
||
onEdit,
|
||
onDelete,
|
||
onView,
|
||
}: EnhancedCustomerTableProps) {
|
||
const { formatDate } = useSettings();
|
||
const [tableState, setTableState] = useState<TableState>({
|
||
search: '',
|
||
filters: {},
|
||
sort: { key: 'name', direction: 'asc' },
|
||
pagination: { page: 1, pageSize: 10 },
|
||
});
|
||
|
||
// Define searchable fields
|
||
const searchableFields = ['name', 'phone', 'email', 'address'] as const;
|
||
|
||
// Process table data
|
||
const processedData = useMemo(() => {
|
||
return processTableData(customers, tableState, searchableFields);
|
||
}, [customers, tableState]);
|
||
|
||
// Handle sorting
|
||
const handleSort = (key: string, direction: 'asc' | 'desc') => {
|
||
setTableState(prev => ({
|
||
...prev,
|
||
sort: { key, direction },
|
||
}));
|
||
};
|
||
|
||
// Handle page change
|
||
const handlePageChange = (page: number) => {
|
||
setTableState(prev => ({
|
||
...prev,
|
||
pagination: { ...prev.pagination, page },
|
||
}));
|
||
};
|
||
|
||
// Define table columns
|
||
const columns = [
|
||
{
|
||
key: 'name' as keyof Customer,
|
||
header: 'اسم العميل',
|
||
sortable: true,
|
||
filterable: true,
|
||
render: (customer: Customer) => (
|
||
<div>
|
||
<Text weight="medium">{customer.name}</Text>
|
||
</div>
|
||
),
|
||
},
|
||
{
|
||
key: 'phone' as keyof Customer,
|
||
header: 'رقم الهاتف',
|
||
sortable: true,
|
||
filterable: true,
|
||
render: (customer: Customer) => (
|
||
<Text dir="ltr" className="text-left">
|
||
{customer.phone || '-'}
|
||
</Text>
|
||
),
|
||
},
|
||
{
|
||
key: 'email' as keyof Customer,
|
||
header: 'البريد الإلكتروني',
|
||
sortable: true,
|
||
filterable: true,
|
||
render: (customer: Customer) => (
|
||
<Text dir="ltr" className="text-left">
|
||
{customer.email || '-'}
|
||
</Text>
|
||
),
|
||
},
|
||
{
|
||
key: 'address' as keyof Customer,
|
||
header: 'العنوان',
|
||
filterable: true,
|
||
render: (customer: Customer) => (
|
||
<Text className="max-w-xs truncate" title={customer.address || undefined}>
|
||
{customer.address || '-'}
|
||
</Text>
|
||
),
|
||
},
|
||
{
|
||
key: 'createdDate' as keyof Customer,
|
||
header: 'تاريخ الإنشاء',
|
||
sortable: true,
|
||
render: (customer: Customer) => (
|
||
<Text size="sm" color="secondary">
|
||
{formatDate(customer.createdDate)}
|
||
</Text>
|
||
),
|
||
},
|
||
];
|
||
|
||
return (
|
||
<div className="space-y-4">
|
||
{/* Table Header */}
|
||
<div className="flex justify-between items-center">
|
||
<div>
|
||
<Text size="lg" weight="semibold">
|
||
قائمة العملاء
|
||
</Text>
|
||
<Text size="sm" color="secondary">
|
||
إدارة بيانات العملاء
|
||
</Text>
|
||
</div>
|
||
|
||
<div className="flex items-center space-x-2 space-x-reverse">
|
||
<Text size="sm" color="secondary">
|
||
المجموع: {processedData.originalCount}
|
||
</Text>
|
||
{processedData.filteredCount !== processedData.originalCount && (
|
||
<Text size="sm" color="secondary">
|
||
(مفلتر: {processedData.filteredCount})
|
||
</Text>
|
||
)}
|
||
</div>
|
||
</div>
|
||
|
||
{/* Enhanced Data Table */}
|
||
<DataTable
|
||
data={processedData.data}
|
||
columns={columns}
|
||
loading={loading}
|
||
emptyMessage="لا يوجد عملاء مسجلين"
|
||
searchable
|
||
searchPlaceholder="البحث في العملاء..."
|
||
filterable
|
||
onSort={handleSort}
|
||
sortKey={tableState.sort?.key}
|
||
sortDirection={tableState.sort?.direction}
|
||
pagination={{
|
||
enabled: true,
|
||
currentPage: tableState.pagination.page,
|
||
pageSize: tableState.pagination.pageSize,
|
||
totalItems: processedData.filteredCount,
|
||
onPageChange: handlePageChange,
|
||
}}
|
||
actions={{
|
||
label: 'الإجراءات',
|
||
render: (customer: Customer) => (
|
||
<div className="flex items-center space-x-2 space-x-reverse">
|
||
{onView && (
|
||
<Button
|
||
size="sm"
|
||
variant="ghost"
|
||
onClick={() => onView(customer)}
|
||
icon={
|
||
<svg className="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
|
||
</svg>
|
||
}
|
||
>
|
||
عرض
|
||
</Button>
|
||
)}
|
||
|
||
{onEdit && (
|
||
<Button
|
||
size="sm"
|
||
variant="ghost"
|
||
onClick={() => onEdit(customer)}
|
||
icon={
|
||
<svg className="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" />
|
||
</svg>
|
||
}
|
||
>
|
||
تعديل
|
||
</Button>
|
||
)}
|
||
|
||
{onDelete && (
|
||
<Button
|
||
size="sm"
|
||
variant="ghost"
|
||
onClick={() => onDelete(customer)}
|
||
icon={
|
||
<svg className="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
|
||
</svg>
|
||
}
|
||
>
|
||
حذف
|
||
</Button>
|
||
)}
|
||
</div>
|
||
),
|
||
}}
|
||
/>
|
||
</div>
|
||
);
|
||
} |