mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-26 00:05:45 +00:00
262 lines
8.2 KiB
TypeScript
262 lines
8.2 KiB
TypeScript
'use client';
|
|
|
|
import { ChangeEventHandler, useEffect, useState } from 'react';
|
|
import { useSearchParams } from 'next/navigation';
|
|
import useSWR from 'swr';
|
|
import { ColumnDef, SortingState } from '@tanstack/react-table';
|
|
|
|
import { Icon } from '@iconify/react';
|
|
import Table from '@/components/Table';
|
|
import DebouncedTextInput from '@/components/input/DebouncedTextInput';
|
|
import Card from '@/components/Card';
|
|
import Badge from '@/components/Badge';
|
|
|
|
import { cn } from '@/lib/helper';
|
|
|
|
import { formatDate, formatNumber } from '@/lib/helper';
|
|
import { isResponseSuccess } from '@/lib/api-helper';
|
|
import { useTableFilter } from '@/services/hooks/useTableFilter';
|
|
import { ClosingApi } from '@/services/api/closing';
|
|
import { ClosingOutgoingSapronak } from '@/types/api/closing';
|
|
import SapronakClosingSkeleton from '@/components/pages/closing/skeleton/SapronakClosingSkeleton';
|
|
|
|
interface ClosingOutgoingSapronaksTableProps {
|
|
projectFlockId: number;
|
|
}
|
|
|
|
const ClosingOutgoingSapronaksTable = ({
|
|
projectFlockId,
|
|
}: ClosingOutgoingSapronaksTableProps) => {
|
|
const searchParams = useSearchParams();
|
|
const kandangId = searchParams.get('kandangId');
|
|
|
|
const {
|
|
state: tableFilterState,
|
|
updateFilter,
|
|
setPage,
|
|
setPageSize,
|
|
toQueryString: getTableFilterQueryString,
|
|
} = useTableFilter({
|
|
initial: {
|
|
search: '',
|
|
nameSort: '',
|
|
},
|
|
paramMap: {
|
|
page: 'page',
|
|
pageSize: 'limit',
|
|
nameSort: 'sort_name',
|
|
},
|
|
});
|
|
|
|
const { data: outgoingSapronaks, isLoading: isLoadingOutgoingSapronaks } =
|
|
useSWR(
|
|
`${ClosingApi.basePath}/${projectFlockId}/sapronak${getTableFilterQueryString()}&type=outgoing&kandang_id=${kandangId ? `${kandangId}` : ''}`,
|
|
ClosingApi.getAllOutgoingSapronakFetcher
|
|
);
|
|
|
|
const [sorting, setSorting] = useState<SortingState>([]);
|
|
const [rowSelection, setRowSelection] = useState<Record<string, boolean>>({});
|
|
|
|
const outgoingSapronaksColumns: ColumnDef<ClosingOutgoingSapronak>[] = [
|
|
{
|
|
header: 'No',
|
|
cell: (props) => props.row.index + 1,
|
|
},
|
|
{
|
|
accessorKey: 'date',
|
|
header: 'Tanggal',
|
|
cell: (props) => formatDate(props.row.original.date, 'DD MMM YYYY'),
|
|
},
|
|
{
|
|
accessorKey: 'reference_number',
|
|
header: 'No. Referensi',
|
|
},
|
|
{
|
|
accessorKey: 'transaction_type',
|
|
header: 'Jenis Transaksi',
|
|
},
|
|
{
|
|
accessorKey: 'product_name',
|
|
header: 'Produk',
|
|
},
|
|
{
|
|
accessorKey: 'product_category',
|
|
header: 'Kategori Produk',
|
|
cell: (props) => {
|
|
const categories = props.row.original.product_category
|
|
.split(' ')
|
|
.filter((cat) => cat.trim());
|
|
const maxBadges = 4;
|
|
const visibleCategories = categories.slice(0, maxBadges);
|
|
const remainingCount = categories.length - maxBadges;
|
|
|
|
return (
|
|
<div className='flex flex-wrap gap-1 whitespace-nowrap'>
|
|
{visibleCategories.map((category, index) => (
|
|
<Badge
|
|
key={index}
|
|
variant='soft'
|
|
className={{
|
|
badge: cn(
|
|
'px-2 py-1 flex flex-row justify-start gap-1 rounded-lg border border-base-content/10 text-xs font-medium text-base-content bg-base-content/5 whitespace-nowrap'
|
|
),
|
|
}}
|
|
title={category}
|
|
>
|
|
{category.length > 12
|
|
? `${category.slice(0, 12)}...`
|
|
: category}
|
|
</Badge>
|
|
))}
|
|
{remainingCount > 0 && (
|
|
<Badge
|
|
variant='soft'
|
|
className={{
|
|
badge: cn(
|
|
'px-2 py-1 flex flex-row justify-start gap-1 rounded-lg border border-base-content/10 text-xs font-medium text-base-content bg-base-content/20'
|
|
),
|
|
}}
|
|
title={categories.join(' ')}
|
|
>
|
|
+{remainingCount}
|
|
</Badge>
|
|
)}
|
|
</div>
|
|
);
|
|
},
|
|
},
|
|
{
|
|
accessorKey: 'source_warehouse',
|
|
header: 'Gudang Asal (Fisik)',
|
|
},
|
|
{
|
|
accessorKey: 'destination_warehouse',
|
|
header: 'Gudang Tujuan (Fisik)',
|
|
},
|
|
{
|
|
accessorKey: 'quantity',
|
|
header: 'Kuantitas',
|
|
cell: (props) =>
|
|
`${formatNumber(props.row.original.quantity)} ${props.row.original.unit}`,
|
|
},
|
|
{
|
|
accessorKey: 'notes',
|
|
header: 'Keterangan',
|
|
},
|
|
];
|
|
|
|
const searchChangeHandler: ChangeEventHandler<HTMLInputElement> = (e) => {
|
|
updateFilter('search', e.target.value);
|
|
};
|
|
|
|
// track sorting
|
|
useEffect(() => {
|
|
const isNameSorted = sorting.find((sortItem) => sortItem.id === 'name');
|
|
|
|
if (!isNameSorted) {
|
|
updateFilter('nameSort', '');
|
|
} else {
|
|
updateFilter('nameSort', isNameSorted.desc ? 'desc' : 'asc');
|
|
}
|
|
}, [sorting, updateFilter]);
|
|
|
|
return (
|
|
<div className='w-full'>
|
|
<Card
|
|
className={{
|
|
wrapper: 'w-full rounded-lg',
|
|
body: 'p-0',
|
|
title: 'px-2 py-1.5 font-normal text-sm bg-primary text-white',
|
|
collapsible: 'rounded-lg',
|
|
}}
|
|
variant='bordered'
|
|
title='Sapronak Keluar'
|
|
collapsible
|
|
defaultCollapsed={false}
|
|
>
|
|
<div className='flex flex-col gap-2 my-4'>
|
|
<div className='w-full flex flex-col sm:flex-row justify-start items-end sm:items-center gap-4'>
|
|
<DebouncedTextInput
|
|
name='search'
|
|
placeholder='Cari Sapronak Keluar'
|
|
value={tableFilterState.search}
|
|
onChange={searchChangeHandler}
|
|
startAdornment={
|
|
<Icon
|
|
icon='heroicons:magnifying-glass'
|
|
width={20}
|
|
height={20}
|
|
/>
|
|
}
|
|
className={{
|
|
wrapper: 'w-full min-w-24 max-w-3xs',
|
|
inputWrapper: 'rounded-xl! shadow-button-soft',
|
|
input:
|
|
'placeholder:font-semibold placeholder:text-base-content/50',
|
|
}}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
{isLoadingOutgoingSapronaks ? (
|
|
<SapronakClosingSkeleton
|
|
type='outgoing'
|
|
columns={outgoingSapronaksColumns}
|
|
/>
|
|
) : isResponseSuccess(outgoingSapronaks) &&
|
|
outgoingSapronaks.data.length === 0 ? (
|
|
<SapronakClosingSkeleton
|
|
type='outgoing'
|
|
columns={outgoingSapronaksColumns}
|
|
iconName='heroicons:chart-bar'
|
|
title='Data Sapronak Keluar Tidak Ditemukan'
|
|
subtitle='Tidak ada data sapronak keluar untuk periode ini.'
|
|
/>
|
|
) : (
|
|
<Table<ClosingOutgoingSapronak>
|
|
data={
|
|
isResponseSuccess(outgoingSapronaks)
|
|
? outgoingSapronaks?.data
|
|
: []
|
|
}
|
|
columns={outgoingSapronaksColumns}
|
|
pageSize={tableFilterState.pageSize}
|
|
onPageSizeChange={setPageSize}
|
|
rowOptions={[10, 20, 50, 100]}
|
|
page={
|
|
isResponseSuccess(outgoingSapronaks)
|
|
? outgoingSapronaks?.meta?.page
|
|
: 0
|
|
}
|
|
totalItems={
|
|
isResponseSuccess(outgoingSapronaks)
|
|
? outgoingSapronaks?.meta?.total_results
|
|
: 0
|
|
}
|
|
onPageChange={setPage}
|
|
isLoading={isLoadingOutgoingSapronaks}
|
|
sorting={sorting}
|
|
setSorting={setSorting}
|
|
rowSelection={rowSelection}
|
|
setRowSelection={setRowSelection}
|
|
className={{
|
|
containerClassName: 'w-full mb-5!',
|
|
tableWrapperClassName:
|
|
'overflow-x-auto rounded-tr-none rounded-tl-none',
|
|
tableClassName: 'w-full table-auto text-sm',
|
|
headerRowClassName: 'border-b border-b-gray-200 bg-gray-50',
|
|
headerColumnClassName:
|
|
'px-4 py-3 text-xs font-semibold text-gray-700 text-left border-b border-gray-200',
|
|
bodyRowClassName: 'hover:bg-gray-50 transition-colors',
|
|
bodyColumnClassName:
|
|
'px-4 py-3 text-xs text-gray-900 whitespace-nowrap',
|
|
}}
|
|
/>
|
|
)}
|
|
</Card>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default ClosingOutgoingSapronaksTable;
|