Files
lti-web-client/src/components/pages/closing/table/OverheadClosingTable.tsx
T

349 lines
12 KiB
TypeScript

import Card from '@/components/Card';
import Table from '@/components/Table';
import { isResponseSuccess } from '@/lib/api-helper';
import { cn, formatCurrency, formatDate, formatNumber } from '@/lib/helper';
import { ClosingApi } from '@/services/api/closing';
import {
ClosingGeneralInformation,
Overhead,
OverheadTotal,
} from '@/types/api/closing';
import { ProjectFlockKandang } from '@/types/api/production/project-flock-kandang';
import { Icon } from '@iconify/react';
import { ColumnDef } from '@tanstack/react-table';
import { useSearchParams } from 'next/navigation';
import { useMemo } from 'react';
import useSWR from 'swr';
import OverheadClosingSkeleton from '@/components/pages/closing/skeleton/OverheadClosingSkeleton';
interface OverheadClosingTableProps {
projectFlockId: number;
generalInformation?: ClosingGeneralInformation;
kandangData?: ProjectFlockKandang;
}
const OverheadClosingTable = ({
projectFlockId,
generalInformation,
kandangData,
}: OverheadClosingTableProps) => {
const searchParams = useSearchParams();
const kandangId = searchParams.get('kandangId');
const { data: overhead, isLoading: isLoadingOverhead } = useSWR(
`${ClosingApi.basePath}/${projectFlockId}/overhead`,
() => ClosingApi.getOverhead(projectFlockId),
{
keepPreviousData: true,
}
);
const { data: overheadKandang } = useSWR(
kandangId
? `${ClosingApi.basePath}/${projectFlockId}/${kandangId}/overhead`
: undefined,
() =>
ClosingApi.getOverhead(
projectFlockId,
kandangId ? Number(kandangId) : undefined
),
{
keepPreviousData: true,
}
);
const chickinPopulation = useMemo(() => {
if (kandangData) {
return kandangData?.chickins?.reduce(
(acc, chickin) => acc + chickin.usage_qty,
0
);
}
return 0;
}, [kandangData]);
const kandangTotal = useMemo(() => {
if (!isResponseSuccess(overhead)) {
return 0;
}
const total =
((chickinPopulation ?? 0) * overhead.data.total.actual_total_amount) /
(generalInformation?.population ?? 0);
return total;
}, [overhead, chickinPopulation, generalInformation]);
// Helper function to create columns with footer support
const createColumns = (
total?: OverheadTotal,
kandangId?: number
): ColumnDef<Overhead>[] => {
const flockColumn: ColumnDef<Overhead>[] = [
{
header: 'Budget Pengajuan',
footer: '',
columns: [
{
id: 'budget_quantity',
header: 'Jumlah',
accessorFn: (props) => formatNumber(props.budget_quantity),
footer: total ? () => formatNumber(total.budget_quantity) : '',
},
{
id: 'budget_unit_price',
header: 'Harga Satuan',
accessorFn: (props) => formatCurrency(props.budget_unit_price),
footer: '',
},
{
id: 'budget_total_amount',
header: 'Total',
accessorFn: (props) => formatCurrency(props.budget_total_amount),
footer: total
? () => formatCurrency(total.budget_total_amount)
: '0',
},
],
},
{
header: 'Realisasi',
footer: '',
columns: [
{
id: 'actual_date',
header: 'Tanggal',
accessorFn: (props) =>
formatDate(props.actual_date, 'DD MMM, YYYY'),
footer: '',
},
{
id: 'actual_quantity',
header: 'Jumlah',
accessorFn: (props) => formatNumber(props.actual_quantity),
footer: total ? () => formatNumber(total.actual_quantity) : '0',
},
{
id: 'actual_unit_price',
header: 'Harga Satuan',
accessorFn: (props) => formatCurrency(props.actual_unit_price),
footer: '',
},
{
id: 'actual_total_amount',
header: 'Total',
accessorFn: (props) => formatCurrency(props.actual_total_amount),
footer: total
? () => formatCurrency(total.actual_total_amount)
: '0',
},
],
},
];
const kandangColumn: ColumnDef<Overhead>[] = [
{
id: 'actual_date',
header: 'Tanggal',
accessorFn: (props) => formatDate(props.actual_date, 'DD MMM, YYYY'),
footer: '',
},
{
id: 'actual_quantity',
header: 'Jumlah',
accessorFn: (props) => formatNumber(props.actual_quantity),
footer: total ? () => formatNumber(total.actual_quantity) : '',
},
{
id: 'actual_unit_price',
header: 'Harga Satuan',
accessorFn: (props) => formatCurrency(props.actual_unit_price),
footer: '',
},
{
id: 'actual_total_amount',
header: 'Total',
accessorFn: (props) => formatCurrency(props.actual_total_amount),
footer: total ? () => formatCurrency(total.actual_total_amount) : '',
},
];
const finalColumns: ColumnDef<Overhead>[] = [
// Group untuk kolom tanpa footer
{
header: 'No',
accessorFn: (_, index) => index,
cell: (props) => props.row.index + 1,
},
{
header: 'Nama Item',
accessorFn: (props) => props.item_name,
footer: 'Total Pengeluaran Overhead',
},
{
header: 'Satuan',
accessorFn: (props) => props.uom_name,
},
...(kandangId ? kandangColumn : flockColumn),
{
id: 'cost_per_bird',
header: 'Rp/Ekor',
accessorFn: (props) => formatCurrency(props.cost_per_bird),
footer: total ? () => formatCurrency(total.cost_per_bird) : '',
},
];
return finalColumns;
};
const columns = useMemo(
() =>
isResponseSuccess(overhead)
? createColumns(
kandangId
? isResponseSuccess(overheadKandang)
? overheadKandang.data?.total
: undefined
: overhead.data?.total,
kandangId ? Number(kandangId) : undefined
)
: createColumns(),
[overhead, kandangId, overheadKandang]
);
return (
<div className='w-full pt-3'>
<Card
className={{
wrapper: 'w-full rounded-lg border-none',
body: 'p-0',
title: 'px-2 py-1.5 font-normal text-sm bg-primary text-white',
collapsible: 'rounded-lg',
}}
variant='bordered'
title='Pengeluaran Overhead'
collapsible
defaultCollapsed={false}
>
{isLoadingOverhead ? (
<OverheadClosingSkeleton columns={columns} />
) : !isResponseSuccess(overhead) ? (
<OverheadClosingSkeleton
columns={columns}
iconName='heroicons:chart-bar'
title='Data Overhead Tidak Ditemukan'
subtitle='Tidak ada data overhead untuk periode ini.'
/>
) : kandangId && !isResponseSuccess(overheadKandang) ? (
<OverheadClosingSkeleton
columns={columns}
iconName='heroicons:chart-bar'
title='Data Overhead Tidak Ditemukan'
subtitle='Tidak ada data overhead untuk periode ini.'
/>
) : (!kandangId && overhead.data?.overheads.length === 0) ||
(kandangId &&
isResponseSuccess(overheadKandang) &&
overheadKandang.data?.overheads.length === 0) ? (
<OverheadClosingSkeleton
columns={columns}
iconName='heroicons:chart-bar'
/>
) : (
<Table<Overhead>
data={
kandangId
? isResponseSuccess(overheadKandang)
? (overheadKandang.data?.overheads ?? [])
: []
: isResponseSuccess(overhead)
? (overhead.data?.overheads ?? [])
: []
}
columns={columns}
className={{
containerClassName: 'w-full mb-0!',
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: cn(
'px-4 py-3 text-xs font-semibold text-gray-700 text-left border border-gray-200',
'whitespace-nowrap'
),
bodyRowClassName:
'hover:bg-gray-50 transition-colors border-b border-l border-r border-b-gray-200 border-l-gray-200 border-r-gray-200',
bodyColumnClassName:
'px-4 py-3 text-xs text-gray-900 whitespace-nowrap',
tableFooterClassName:
'bg-gray-100 font-semibold border border-gray-200',
footerRowClassName: 'border-t-2 border-gray-300',
footerColumnClassName:
'px-4 py-3 text-xs text-gray-900 whitespace-nowrap',
}}
isLoading={isLoadingOverhead}
renderFooter={
isResponseSuccess(overhead)
? overhead.data?.overheads.length > 0
: false
}
/>
)}
{kandangId && !isLoadingOverhead && isResponseSuccess(overhead) && (
<Card
className={{
wrapper: 'w-full',
body: 'p-4 shadow-button-soft border border-base-content/10 rounded-lg',
}}
>
<div className='flex flex-row gap-3 w-full justify-center items-stretch'>
<div className='flex flex-row items-center justify-between'>
<h2 className='text-base font-bold'>Pembelian Kandang </h2>
</div>
<div className='flex flex-col items-center justify-center'>
<Icon icon='heroicons:equals' className='inline' />
</div>
<div className='flex flex-col flex-1 gap-1.5'>
<div className='flex flex-row gap-1.5 text-center items-center justify-center font-medium'>
Populasi Akhir KANDANG{' '}
<Icon icon='heroicons:x-mark' className='inline' /> Pemakaian
Di FARM
</div>
<hr className='w-full h-fit m-0 p-0 text-base-content/65' />
<div className='flex flex-row gap-1.5 text-center items-center justify-center font-medium'>
Populasi Akhir Proyek
</div>
</div>
<div className='flex flex-col items-center justify-center'>
<Icon icon='heroicons:equals' className='inline' />
</div>
<div className='flex flex-col flex-1 gap-1.5'>
<div className='flex flex-row gap-1.5 text-center items-center justify-center font-medium'>
{formatNumber(chickinPopulation ?? 0)}
<Icon icon='heroicons:x-mark' className='inline' />
{formatCurrency(
isResponseSuccess(overhead)
? overhead.data?.total.actual_total_amount
: 0
)}
</div>
<hr className='w-full h-fit m-0 p-0 text-base-content/65' />
<div className='flex flex-row gap-1.5 text-center items-center justify-center font-medium'>
{formatNumber(generalInformation?.population ?? 0)}
</div>
</div>
<div className='flex flex-col items-center justify-center'>
<Icon icon='heroicons:equals' className='inline' />
</div>
<div className='flex flex-row items-center justify-between'>
<h2 className='text-base font-bold'>
{formatNumber(kandangTotal || 0)}
</h2>
</div>
</div>
</Card>
)}
</Card>
</div>
);
};
export default OverheadClosingTable;