Purchase Order table
This commit is contained in:
@@ -42,6 +42,9 @@ import Loading from '@/components/layout/shared/Loading'
|
||||
import { PurchaseOrderType } from '@/types/apps/purchaseOrderTypes'
|
||||
import { purchaseOrdersData } from '@/data/dummy/purchase-order'
|
||||
import { getLocalizedUrl } from '@/utils/i18n'
|
||||
import { PurchaseOrder } from '@/types/services/purchaseOrder'
|
||||
import { usePurchaseOrders } from '@/services/queries/purchaseOrder'
|
||||
import StatusFilterTabs from '@/components/StatusFilterTab'
|
||||
|
||||
declare module '@tanstack/table-core' {
|
||||
interface FilterFns {
|
||||
@@ -52,7 +55,7 @@ declare module '@tanstack/table-core' {
|
||||
}
|
||||
}
|
||||
|
||||
type PurchaseOrderTypeWithAction = PurchaseOrderType & {
|
||||
type PurchaseOrderTypeWithAction = PurchaseOrder & {
|
||||
actions?: string
|
||||
}
|
||||
|
||||
@@ -135,46 +138,24 @@ const PurchaseOrderListTable = () => {
|
||||
// States
|
||||
const [addPOOpen, setAddPOOpen] = useState(false)
|
||||
const [rowSelection, setRowSelection] = useState({})
|
||||
const [currentPage, setCurrentPage] = useState(0)
|
||||
const [currentPage, setCurrentPage] = useState(1)
|
||||
const [pageSize, setPageSize] = useState(10)
|
||||
const [openConfirm, setOpenConfirm] = useState(false)
|
||||
const [poId, setPOId] = useState('')
|
||||
const [search, setSearch] = useState('')
|
||||
const [statusFilter, setStatusFilter] = useState<string>('Semua')
|
||||
const [filteredData, setFilteredData] = useState<PurchaseOrderType[]>(purchaseOrdersData)
|
||||
|
||||
// Hooks
|
||||
const { lang: locale } = useParams()
|
||||
|
||||
// Filter data based on search and status
|
||||
useEffect(() => {
|
||||
let filtered = purchaseOrdersData
|
||||
const { data, isLoading, error, isFetching } = usePurchaseOrders({
|
||||
page: currentPage,
|
||||
limit: pageSize,
|
||||
search,
|
||||
status: statusFilter === 'Semua' ? '' : statusFilter
|
||||
})
|
||||
|
||||
// Filter by search
|
||||
if (search) {
|
||||
filtered = filtered.filter(
|
||||
po =>
|
||||
po.number.toLowerCase().includes(search.toLowerCase()) ||
|
||||
po.vendorName.toLowerCase().includes(search.toLowerCase()) ||
|
||||
po.vendorCompany.toLowerCase().includes(search.toLowerCase()) ||
|
||||
po.status.toLowerCase().includes(search.toLowerCase())
|
||||
)
|
||||
}
|
||||
|
||||
// Filter by status
|
||||
if (statusFilter !== 'Semua') {
|
||||
filtered = filtered.filter(po => po.status === statusFilter)
|
||||
}
|
||||
|
||||
setFilteredData(filtered)
|
||||
setCurrentPage(0)
|
||||
}, [search, statusFilter])
|
||||
|
||||
const totalCount = filteredData.length
|
||||
const paginatedData = useMemo(() => {
|
||||
const startIndex = currentPage * pageSize
|
||||
return filteredData.slice(startIndex, startIndex + pageSize)
|
||||
}, [filteredData, currentPage, pageSize])
|
||||
const purchaseOrders = data?.purchase_orders ?? []
|
||||
const totalCount = data?.total_count ?? 0
|
||||
|
||||
const handlePageChange = useCallback((event: unknown, newPage: number) => {
|
||||
setCurrentPage(newPage)
|
||||
@@ -222,7 +203,7 @@ const PurchaseOrderListTable = () => {
|
||||
/>
|
||||
)
|
||||
},
|
||||
columnHelper.accessor('number', {
|
||||
columnHelper.accessor('po_number', {
|
||||
header: 'Nomor PO',
|
||||
cell: ({ row }) => (
|
||||
<Button
|
||||
@@ -239,19 +220,19 @@ const PurchaseOrderListTable = () => {
|
||||
}
|
||||
}}
|
||||
>
|
||||
{row.original.number}
|
||||
{row.original.po_number}
|
||||
</Button>
|
||||
)
|
||||
}),
|
||||
columnHelper.accessor('vendorName', {
|
||||
columnHelper.accessor('vendor.name', {
|
||||
header: 'Vendor',
|
||||
cell: ({ row }) => (
|
||||
<div className='flex flex-col'>
|
||||
<Typography color='text.primary' className='font-medium'>
|
||||
{row.original.vendorName}
|
||||
{row.original.vendor.contact_person}
|
||||
</Typography>
|
||||
<Typography variant='body2' color='text.secondary'>
|
||||
{row.original.vendorCompany}
|
||||
{row.original.vendor.name}
|
||||
</Typography>
|
||||
</div>
|
||||
)
|
||||
@@ -260,13 +241,13 @@ const PurchaseOrderListTable = () => {
|
||||
header: 'Referensi',
|
||||
cell: ({ row }) => <Typography color='text.secondary'>{row.original.reference || '-'}</Typography>
|
||||
}),
|
||||
columnHelper.accessor('date', {
|
||||
columnHelper.accessor('transaction_date', {
|
||||
header: 'Tanggal',
|
||||
cell: ({ row }) => <Typography>{row.original.date}</Typography>
|
||||
cell: ({ row }) => <Typography>{row.original.transaction_date}</Typography>
|
||||
}),
|
||||
columnHelper.accessor('dueDate', {
|
||||
columnHelper.accessor('due_date', {
|
||||
header: 'Tanggal Jatuh Tempo',
|
||||
cell: ({ row }) => <Typography>{row.original.dueDate}</Typography>
|
||||
cell: ({ row }) => <Typography>{row.original.due_date}</Typography>
|
||||
}),
|
||||
columnHelper.accessor('status', {
|
||||
header: 'Status',
|
||||
@@ -282,16 +263,16 @@ const PurchaseOrderListTable = () => {
|
||||
</div>
|
||||
)
|
||||
}),
|
||||
columnHelper.accessor('total', {
|
||||
columnHelper.accessor('total_amount', {
|
||||
header: 'Total',
|
||||
cell: ({ row }) => <Typography className='font-medium'>{formatCurrency(row.original.total)}</Typography>
|
||||
cell: ({ row }) => <Typography className='font-medium'>{formatCurrency(row.original.total_amount)}</Typography>
|
||||
})
|
||||
],
|
||||
[]
|
||||
)
|
||||
|
||||
const table = useReactTable({
|
||||
data: paginatedData as PurchaseOrderType[],
|
||||
data: purchaseOrders as PurchaseOrder[],
|
||||
columns,
|
||||
filterFns: {
|
||||
fuzzy: fuzzyFilter
|
||||
@@ -316,27 +297,11 @@ const PurchaseOrderListTable = () => {
|
||||
{/* Filter Status Tabs */}
|
||||
<div className='p-6 border-bs'>
|
||||
<div className='flex flex-wrap gap-2'>
|
||||
{['Semua', 'Draft', 'Disetujui', 'Dikirim Sebagian', 'Selesai', 'Lainnya'].map(status => (
|
||||
<Button
|
||||
key={status}
|
||||
variant={statusFilter === status ? 'contained' : 'outlined'}
|
||||
color={statusFilter === status ? 'primary' : 'inherit'}
|
||||
onClick={() => handleStatusFilter(status)}
|
||||
size='small'
|
||||
className='rounded-lg'
|
||||
sx={{
|
||||
textTransform: 'none',
|
||||
fontWeight: statusFilter === status ? 600 : 400,
|
||||
borderRadius: '8px',
|
||||
...(statusFilter !== status && {
|
||||
borderColor: '#e0e0e0',
|
||||
color: '#666'
|
||||
})
|
||||
}}
|
||||
>
|
||||
{status}
|
||||
</Button>
|
||||
))}
|
||||
<StatusFilterTabs
|
||||
statusOptions={['Semua', 'draft', 'sent', 'approved', 'received', 'cancelled']}
|
||||
selectedStatus={statusFilter}
|
||||
onStatusChange={handleStatusFilter}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -378,56 +343,60 @@ const PurchaseOrderListTable = () => {
|
||||
</div>
|
||||
</div>
|
||||
<div className='overflow-x-auto'>
|
||||
<table className={tableStyles.table}>
|
||||
<thead>
|
||||
{table.getHeaderGroups().map(headerGroup => (
|
||||
<tr key={headerGroup.id}>
|
||||
{headerGroup.headers.map(header => (
|
||||
<th key={header.id}>
|
||||
{header.isPlaceholder ? null : (
|
||||
<>
|
||||
<div
|
||||
className={classnames({
|
||||
'flex items-center': header.column.getIsSorted(),
|
||||
'cursor-pointer select-none': header.column.getCanSort()
|
||||
})}
|
||||
onClick={header.column.getToggleSortingHandler()}
|
||||
>
|
||||
{flexRender(header.column.columnDef.header, header.getContext())}
|
||||
{{
|
||||
asc: <i className='tabler-chevron-up text-xl' />,
|
||||
desc: <i className='tabler-chevron-down text-xl' />
|
||||
}[header.column.getIsSorted() as 'asc' | 'desc'] ?? null}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</thead>
|
||||
{filteredData.length === 0 ? (
|
||||
<tbody>
|
||||
<tr>
|
||||
<td colSpan={table.getVisibleFlatColumns().length} className='text-center'>
|
||||
Tidak ada data tersedia
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
) : (
|
||||
<tbody>
|
||||
{table.getRowModel().rows.map(row => {
|
||||
return (
|
||||
<tr key={row.id} className={classnames({ selected: row.getIsSelected() })}>
|
||||
{row.getVisibleCells().map(cell => (
|
||||
<td key={cell.id}>{flexRender(cell.column.columnDef.cell, cell.getContext())}</td>
|
||||
))}
|
||||
</tr>
|
||||
)
|
||||
})}
|
||||
</tbody>
|
||||
)}
|
||||
</table>
|
||||
{isLoading ? (
|
||||
<Loading />
|
||||
) : (
|
||||
<table className={tableStyles.table}>
|
||||
<thead>
|
||||
{table.getHeaderGroups().map(headerGroup => (
|
||||
<tr key={headerGroup.id}>
|
||||
{headerGroup.headers.map(header => (
|
||||
<th key={header.id}>
|
||||
{header.isPlaceholder ? null : (
|
||||
<>
|
||||
<div
|
||||
className={classnames({
|
||||
'flex items-center': header.column.getIsSorted(),
|
||||
'cursor-pointer select-none': header.column.getCanSort()
|
||||
})}
|
||||
onClick={header.column.getToggleSortingHandler()}
|
||||
>
|
||||
{flexRender(header.column.columnDef.header, header.getContext())}
|
||||
{{
|
||||
asc: <i className='tabler-chevron-up text-xl' />,
|
||||
desc: <i className='tabler-chevron-down text-xl' />
|
||||
}[header.column.getIsSorted() as 'asc' | 'desc'] ?? null}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</th>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</thead>
|
||||
{purchaseOrders.length === 0 ? (
|
||||
<tbody>
|
||||
<tr>
|
||||
<td colSpan={table.getVisibleFlatColumns().length} className='text-center'>
|
||||
Tidak ada data tersedia
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
) : (
|
||||
<tbody>
|
||||
{table.getRowModel().rows.map(row => {
|
||||
return (
|
||||
<tr key={row.id} className={classnames({ selected: row.getIsSelected() })}>
|
||||
{row.getVisibleCells().map(cell => (
|
||||
<td key={cell.id}>{flexRender(cell.column.columnDef.cell, cell.getContext())}</td>
|
||||
))}
|
||||
</tr>
|
||||
)
|
||||
})}
|
||||
</tbody>
|
||||
)}
|
||||
</table>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<TablePagination
|
||||
@@ -445,6 +414,7 @@ const PurchaseOrderListTable = () => {
|
||||
onPageChange={handlePageChange}
|
||||
onRowsPerPageChange={handlePageSizeChange}
|
||||
rowsPerPageOptions={[10, 25, 50]}
|
||||
disabled={isLoading}
|
||||
/>
|
||||
</Card>
|
||||
</>
|
||||
|
||||
Reference in New Issue
Block a user