256 lines
7.3 KiB
TypeScript
256 lines
7.3 KiB
TypeScript
import * as React from 'react';
|
|
import type { Metadata } from 'next';
|
|
import Box from '@mui/material/Box';
|
|
import Button from '@mui/material/Button';
|
|
import Card from '@mui/material/Card';
|
|
import Divider from '@mui/material/Divider';
|
|
import Stack from '@mui/material/Stack';
|
|
import Typography from '@mui/material/Typography';
|
|
import { Plus as PlusIcon } from '@phosphor-icons/react/dist/ssr/Plus';
|
|
|
|
import { config } from '@/config';
|
|
import { dayjs } from '@/lib/dayjs';
|
|
import { CustomersFilters } from '@/components/dashboard/customer/customers-filters';
|
|
import type { Filters } from '@/components/dashboard/customer/customers-filters';
|
|
import { CustomersPagination } from '@/components/dashboard/customer/customers-pagination';
|
|
import { CustomersSelectionProvider } from '@/components/dashboard/customer/customers-selection-context';
|
|
import { CustomersTable } from '@/components/dashboard/customer/customers-table';
|
|
import type { Customer } from '@/components/dashboard/customer/customers-table';
|
|
|
|
export const metadata = { title: `List | Customers | Dashboard | ${config.site.name}` } satisfies Metadata;
|
|
|
|
const customers = [
|
|
{
|
|
id: 'USR-005',
|
|
name: 'Fran Perez',
|
|
avatar: '/assets/avatar-5.png',
|
|
email: 'fran.perez@domain.com',
|
|
phone: '(815) 704-0045',
|
|
quota: 50,
|
|
status: 'active',
|
|
createdAt: dayjs().subtract(1, 'hour').toDate(),
|
|
},
|
|
{
|
|
id: 'USR-004',
|
|
name: 'Penjani Inyene',
|
|
avatar: '/assets/avatar-4.png',
|
|
email: 'penjani.inyene@domain.com',
|
|
phone: '(803) 937-8925',
|
|
quota: 100,
|
|
status: 'active',
|
|
createdAt: dayjs().subtract(3, 'hour').toDate(),
|
|
},
|
|
{
|
|
id: 'USR-003',
|
|
name: 'Carson Darrin',
|
|
avatar: '/assets/avatar-3.png',
|
|
email: 'carson.darrin@domain.com',
|
|
phone: '(715) 278-5041',
|
|
quota: 10,
|
|
status: 'blocked',
|
|
createdAt: dayjs().subtract(1, 'hour').subtract(1, 'day').toDate(),
|
|
},
|
|
{
|
|
id: 'USR-002',
|
|
name: 'Siegbert Gottfried',
|
|
avatar: '/assets/avatar-2.png',
|
|
email: 'siegbert.gottfried@domain.com',
|
|
phone: '(603) 766-0431',
|
|
quota: 0,
|
|
status: 'pending',
|
|
createdAt: dayjs().subtract(7, 'hour').subtract(1, 'day').toDate(),
|
|
},
|
|
{
|
|
id: 'USR-001',
|
|
name: 'Miron Vitold',
|
|
avatar: '/assets/avatar-1.png',
|
|
email: 'miron.vitold@domain.com',
|
|
phone: '(425) 434-5535',
|
|
quota: 50,
|
|
status: 'active',
|
|
createdAt: dayjs().subtract(2, 'hour').subtract(2, 'day').toDate(),
|
|
},
|
|
{
|
|
id: 'USR-005',
|
|
name: 'Fran Perez',
|
|
avatar: '/assets/avatar-5.png',
|
|
email: 'fran.perez@domain.com',
|
|
phone: '(815) 704-0045',
|
|
quota: 50,
|
|
status: 'active',
|
|
createdAt: dayjs().subtract(1, 'hour').toDate(),
|
|
},
|
|
{
|
|
id: 'USR-004',
|
|
name: 'Penjani Inyene',
|
|
avatar: '/assets/avatar-4.png',
|
|
email: 'penjani.inyene@domain.com',
|
|
phone: '(803) 937-8925',
|
|
quota: 100,
|
|
status: 'active',
|
|
createdAt: dayjs().subtract(3, 'hour').toDate(),
|
|
},
|
|
{
|
|
id: 'USR-003',
|
|
name: 'Carson Darrin',
|
|
avatar: '/assets/avatar-3.png',
|
|
email: 'carson.darrin@domain.com',
|
|
phone: '(715) 278-5041',
|
|
quota: 10,
|
|
status: 'blocked',
|
|
createdAt: dayjs().subtract(1, 'hour').subtract(1, 'day').toDate(),
|
|
},
|
|
{
|
|
id: 'USR-002',
|
|
name: 'Siegbert Gottfried',
|
|
avatar: '/assets/avatar-2.png',
|
|
email: 'siegbert.gottfried@domain.com',
|
|
phone: '(603) 766-0431',
|
|
quota: 0,
|
|
status: 'pending',
|
|
createdAt: dayjs().subtract(7, 'hour').subtract(1, 'day').toDate(),
|
|
},
|
|
{
|
|
id: 'USR-001',
|
|
name: 'Miron Vitold',
|
|
avatar: '/assets/avatar-1.png',
|
|
email: 'miron.vitold@domain.com',
|
|
phone: '(425) 434-5535',
|
|
quota: 50,
|
|
status: 'active',
|
|
createdAt: dayjs().subtract(2, 'hour').subtract(2, 'day').toDate(),
|
|
},
|
|
{
|
|
id: 'USR-005',
|
|
name: 'Fran Perez',
|
|
avatar: '/assets/avatar-5.png',
|
|
email: 'fran.perez@domain.com',
|
|
phone: '(815) 704-0045',
|
|
quota: 50,
|
|
status: 'active',
|
|
createdAt: dayjs().subtract(1, 'hour').toDate(),
|
|
},
|
|
{
|
|
id: 'USR-004',
|
|
name: 'Penjani Inyene',
|
|
avatar: '/assets/avatar-4.png',
|
|
email: 'penjani.inyene@domain.com',
|
|
phone: '(803) 937-8925',
|
|
quota: 100,
|
|
status: 'active',
|
|
createdAt: dayjs().subtract(3, 'hour').toDate(),
|
|
},
|
|
{
|
|
id: 'USR-003',
|
|
name: 'Carson Darrin',
|
|
avatar: '/assets/avatar-3.png',
|
|
email: 'carson.darrin@domain.com',
|
|
phone: '(715) 278-5041',
|
|
quota: 10,
|
|
status: 'blocked',
|
|
createdAt: dayjs().subtract(1, 'hour').subtract(1, 'day').toDate(),
|
|
},
|
|
{
|
|
id: 'USR-002',
|
|
name: 'Siegbert Gottfried',
|
|
avatar: '/assets/avatar-2.png',
|
|
email: 'siegbert.gottfried@domain.com',
|
|
phone: '(603) 766-0431',
|
|
quota: 0,
|
|
status: 'pending',
|
|
createdAt: dayjs().subtract(7, 'hour').subtract(1, 'day').toDate(),
|
|
},
|
|
{
|
|
id: 'USR-001',
|
|
name: 'Miron Vitold',
|
|
avatar: '/assets/avatar-1.png',
|
|
email: 'miron.vitold@domain.com',
|
|
phone: '(425) 434-5535',
|
|
quota: 50,
|
|
status: 'active',
|
|
createdAt: dayjs().subtract(2, 'hour').subtract(2, 'day').toDate(),
|
|
},
|
|
] satisfies Customer[];
|
|
|
|
interface PageProps {
|
|
searchParams: { email?: string; phone?: string; sortDir?: 'asc' | 'desc'; status?: string };
|
|
}
|
|
|
|
export default function Page({ searchParams }: PageProps): React.JSX.Element {
|
|
const { email, phone, sortDir, status } = searchParams;
|
|
|
|
const sortedCustomers = applySort(customers, sortDir);
|
|
const filteredCustomers = applyFilters(sortedCustomers, { email, phone, status });
|
|
|
|
return (
|
|
<Box
|
|
sx={{
|
|
maxWidth: 'var(--Content-maxWidth)',
|
|
m: 'var(--Content-margin)',
|
|
p: 'var(--Content-padding)',
|
|
width: 'var(--Content-width)',
|
|
}}
|
|
>
|
|
<Stack spacing={4}>
|
|
<Stack direction={{ xs: 'column', sm: 'row' }} spacing={3} sx={{ alignItems: 'flex-start' }}>
|
|
<Box sx={{ flex: '1 1 auto' }}>
|
|
<Typography variant="h4">Customers</Typography>
|
|
</Box>
|
|
<Box sx={{ display: 'flex', justifyContent: 'flex-end' }}>
|
|
<Button startIcon={<PlusIcon />} variant="contained">
|
|
Add
|
|
</Button>
|
|
</Box>
|
|
</Stack>
|
|
<CustomersSelectionProvider customers={filteredCustomers}>
|
|
<Card>
|
|
<CustomersFilters filters={{ email, phone, status }} sortDir={sortDir} />
|
|
<Divider />
|
|
<Box sx={{ overflowX: 'auto' }}>
|
|
<CustomersTable rows={filteredCustomers} />
|
|
</Box>
|
|
<Divider />
|
|
<CustomersPagination count={filteredCustomers.length + 100} page={0} />
|
|
</Card>
|
|
</CustomersSelectionProvider>
|
|
</Stack>
|
|
</Box>
|
|
);
|
|
}
|
|
|
|
// Sorting and filtering has to be done on the server.
|
|
|
|
function applySort(row: Customer[], sortDir: 'asc' | 'desc' | undefined): Customer[] {
|
|
return row.sort((a, b) => {
|
|
if (sortDir === 'asc') {
|
|
return a.createdAt.getTime() - b.createdAt.getTime();
|
|
}
|
|
|
|
return b.createdAt.getTime() - a.createdAt.getTime();
|
|
});
|
|
}
|
|
|
|
function applyFilters(row: Customer[], { email, phone, status }: Filters): Customer[] {
|
|
return row.filter((item) => {
|
|
if (email) {
|
|
if (!item.email?.toLowerCase().includes(email.toLowerCase())) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (phone) {
|
|
if (!item.phone?.toLowerCase().includes(phone.toLowerCase())) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (status) {
|
|
if (item.status !== status) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
});
|
|
}
|