Files
alla-allaos-fullstack/src/features/crm-demo/components/enquiries-table.tsx
phaichayon b8cd39eaa4 task-b
task-b.1
complere
2026-06-15 11:19:31 +07:00

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>
);
}