158 lines
5.7 KiB
TypeScript
158 lines
5.7 KiB
TypeScript
'use client';
|
|
|
|
import Link from 'next/link';
|
|
import { useMutation, useSuspenseQuery } from '@tanstack/react-query';
|
|
import { type ColumnDef } from '@tanstack/react-table';
|
|
import { parseAsInteger, parseAsString, useQueryStates } from 'nuqs';
|
|
import { getSortingStateParser } from '@/lib/parsers';
|
|
import { useDataTable } from '@/hooks/use-data-table';
|
|
import { DataTable } from '@/components/ui/table/data-table';
|
|
import { Input } from '@/components/ui/input';
|
|
import { Button } from '@/components/ui/button';
|
|
import { toast } from 'sonner';
|
|
import { crmReferenceQueryOptions, enquiriesQueryOptions } from '../api/queries';
|
|
import { convertEnquiryMutation } from '../api/mutations';
|
|
import type { Enquiry } from '../api/types';
|
|
import { ChancePill, EnquiryStatusBadge } from './shared';
|
|
|
|
const columns: ColumnDef<Enquiry>[] = [
|
|
{ accessorKey: 'code', header: 'Code' },
|
|
{ accessorKey: 'title', header: 'Enquiry' },
|
|
{ accessorKey: 'productType', header: 'Product' },
|
|
{
|
|
accessorKey: 'status',
|
|
header: 'Status',
|
|
cell: ({ row }) => <EnquiryStatusBadge status={row.original.status} />
|
|
},
|
|
{
|
|
accessorKey: 'chancePercent',
|
|
header: 'Chance',
|
|
cell: ({ row }) => <ChancePill value={row.original.chancePercent} />
|
|
},
|
|
{
|
|
id: 'actions',
|
|
header: '',
|
|
cell: ({ row }) => <RowActions enquiry={row.original} />
|
|
}
|
|
];
|
|
|
|
const columnIds = ['code', 'title', 'productType', 'status', 'chancePercent', 'actions'];
|
|
|
|
function RowActions({ enquiry }: { enquiry: Enquiry }) {
|
|
const mutation = useMutation({
|
|
...convertEnquiryMutation,
|
|
onSuccess: () => toast.success('สร้าง quotation mock จาก enquiry แล้ว')
|
|
});
|
|
|
|
return (
|
|
<div className='flex justify-end gap-2'>
|
|
<Button variant='outline' size='sm' asChild>
|
|
<Link href={`/dashboard/crm-demo/enquiries/${enquiry.id}`}>View</Link>
|
|
</Button>
|
|
<Button size='sm' isLoading={mutation.isPending} onClick={() => mutation.mutate(enquiry.id)}>
|
|
Convert
|
|
</Button>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export function EnquiriesTablePage() {
|
|
const [params, setParams] = useQueryStates({
|
|
page: parseAsInteger.withDefault(1),
|
|
perPage: parseAsInteger.withDefault(10),
|
|
name: parseAsString,
|
|
status: parseAsString,
|
|
productType: parseAsString,
|
|
salesman: parseAsString,
|
|
dateFrom: parseAsString,
|
|
dateTo: parseAsString,
|
|
sort: getSortingStateParser(columnIds).withDefault([])
|
|
});
|
|
const { data: reference } = useSuspenseQuery(crmReferenceQueryOptions());
|
|
|
|
const filters = {
|
|
page: params.page,
|
|
limit: params.perPage,
|
|
...(params.name && { search: params.name }),
|
|
...(params.status && { status: params.status }),
|
|
...(params.productType && { productType: params.productType }),
|
|
...(params.salesman && { salesman: params.salesman }),
|
|
...(params.dateFrom && { dateFrom: params.dateFrom }),
|
|
...(params.dateTo && { dateTo: params.dateTo }),
|
|
...(params.sort.length ? { sort: JSON.stringify(params.sort) } : {})
|
|
};
|
|
|
|
const { data } = useSuspenseQuery(enquiriesQueryOptions(filters));
|
|
const pageCount = Math.ceil(data.totalItems / params.perPage);
|
|
const { table } = useDataTable({
|
|
data: data.items,
|
|
columns,
|
|
pageCount,
|
|
initialState: {
|
|
columnPinning: { right: ['actions'] }
|
|
}
|
|
});
|
|
|
|
return (
|
|
<div className='flex flex-1 flex-col space-y-4'>
|
|
<div className='grid gap-3 md:grid-cols-2 xl:grid-cols-6'>
|
|
<Input
|
|
placeholder='ค้นหา enquiry หรือ code'
|
|
value={params.name ?? ''}
|
|
onChange={(event) => void setParams({ name: event.target.value || null, page: 1 })}
|
|
/>
|
|
<select
|
|
className='border-input bg-background rounded-md border px-3 text-sm'
|
|
value={params.status ?? ''}
|
|
onChange={(event) => void setParams({ status: event.target.value || null, page: 1 })}
|
|
>
|
|
<option value=''>ทุกสถานะ</option>
|
|
<option value='new'>New</option>
|
|
<option value='qualifying'>Qualifying</option>
|
|
<option value='requirement'>Requirement</option>
|
|
<option value='follow_up'>Follow Up</option>
|
|
<option value='converted'>Converted</option>
|
|
</select>
|
|
<select
|
|
className='border-input bg-background rounded-md border px-3 text-sm'
|
|
value={params.productType ?? ''}
|
|
onChange={(event) => void setParams({ productType: event.target.value || null, page: 1 })}
|
|
>
|
|
<option value=''>ทุกสินค้า</option>
|
|
<option value='crane'>Crane</option>
|
|
<option value='dockdoor'>Dock Door</option>
|
|
<option value='solarcell'>Solar Cell</option>
|
|
</select>
|
|
<select
|
|
className='border-input bg-background rounded-md border px-3 text-sm'
|
|
value={params.salesman ?? ''}
|
|
onChange={(event) => void setParams({ salesman: event.target.value || null, page: 1 })}
|
|
>
|
|
<option value=''>ทุก salesperson</option>
|
|
{reference.salespersons.map((sales) => (
|
|
<option key={sales.id} value={sales.id}>
|
|
{sales.name}
|
|
</option>
|
|
))}
|
|
</select>
|
|
<Input
|
|
type='date'
|
|
value={params.dateFrom ?? ''}
|
|
onChange={(event) => void setParams({ dateFrom: event.target.value || null, page: 1 })}
|
|
/>
|
|
<Input
|
|
type='date'
|
|
value={params.dateTo ?? ''}
|
|
onChange={(event) => void setParams({ dateTo: event.target.value || null, page: 1 })}
|
|
/>
|
|
</div>
|
|
<div className='flex justify-end'>
|
|
<Button variant='outline' asChild>
|
|
<Link href='/dashboard/crm-demo/customers'>Create Enquiry</Link>
|
|
</Button>
|
|
</div>
|
|
<DataTable table={table} />
|
|
</div>
|
|
);
|
|
}
|