Merge branch 'fix/closing-overhead' into 'development'

[FIX/FE] Fixing Closing Overhead Per Kandang

See merge request mbugroup/lti-web-client!300
This commit is contained in:
Rivaldi A N S
2026-02-03 05:04:43 +00:00
4 changed files with 145 additions and 51 deletions
@@ -66,7 +66,13 @@ const ClosingDetail: React.FC<ClosingDetailProps> = ({
{
id: 'overhead',
label: 'Overhead',
content: <ClosingOverheadTabContent projectFlockId={id} />,
content: (
<ClosingOverheadTabContent
projectFlockId={id}
generalInformation={initialValue}
kandangData={kandangData}
/>
),
},
{
id: 'hppEkspedisi',
@@ -1,3 +1,4 @@
import { formatNumber } from '@/lib/helper';
import { ClosingGeneralInformation } from '@/types/api/closing';
import { ProjectFlock } from '@/types/api/production/project-flock';
import { ProjectFlockKandang } from '@/types/api/production/project-flock-kandang';
@@ -56,8 +57,8 @@ const ClosingGeneralInformationTable = ({
<td>:</td>
<td>
{!kandangData
? (initialValue?.population ?? 0)
: (chickinPopulation ?? 0)}{' '}
? formatNumber(initialValue?.population || 0)
: formatNumber(chickinPopulation || 0)}{' '}
Ekor
</td>
</tr>
@@ -1,16 +1,26 @@
import ClosingOverheadTable from '@/components/pages/closing/ClosingOverheadTable';
import { ClosingGeneralInformation } from '@/types/api/closing';
import { ProjectFlockKandang } from '@/types/api/production/project-flock-kandang';
interface ClosingOverheadTabContentProps {
projectFlockId: number;
generalInformation?: ClosingGeneralInformation;
kandangData?: ProjectFlockKandang;
}
const ClosingOverheadTabContent = ({
projectFlockId,
generalInformation,
kandangData,
}: ClosingOverheadTabContentProps) => {
return (
<div className='flex flex-col gap-4'>
{projectFlockId && (
<ClosingOverheadTable projectFlockId={projectFlockId} />
<ClosingOverheadTable
projectFlockId={projectFlockId}
generalInformation={generalInformation}
kandangData={kandangData}
/>
)}
</div>
);
@@ -3,7 +3,13 @@ import Table, { TABLE_DEFAULT_STYLING } from '@/components/Table';
import { isResponseSuccess } from '@/lib/api-helper';
import { cn, formatCurrency, formatDate, formatNumber } from '@/lib/helper';
import { ClosingApi } from '@/services/api/closing';
import { Overhead, OverheadTotal } from '@/types/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';
@@ -11,16 +17,30 @@ import useSWR from 'swr';
interface ClosingOverheadTableProps {
projectFlockId: number;
generalInformation?: ClosingGeneralInformation;
kandangData?: ProjectFlockKandang;
}
const ClosingOverheadTable = ({
projectFlockId,
generalInformation,
kandangData,
}: ClosingOverheadTableProps) => {
const searchParams = useSearchParams();
const kandangId = searchParams.get('kandangId');
const { data: overhead, isLoading: isLoadingOverhead } = useSWR(
`${ClosingApi.basePath}/${projectFlockId}${kandangId ? `/${kandangId}` : ''}/overhead`,
`${ClosingApi.basePath}/${projectFlockId}/overhead`,
() => ClosingApi.getOverhead(projectFlockId),
{
keepPreviousData: true,
}
);
const { data: overheadKandang, isLoading: isLoadingOverheadKandang } = useSWR(
kandangId
? `${ClosingApi.basePath}/${projectFlockId}/${kandangId}/overhead`
: undefined,
() =>
ClosingApi.getOverhead(
projectFlockId,
@@ -31,6 +51,26 @@ const ClosingOverheadTable = ({
}
);
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,
@@ -44,29 +84,22 @@ const ClosingOverheadTable = ({
{
id: 'budget_quantity',
header: 'Jumlah',
accessorFn: (props) =>
props.budget_quantity ? formatNumber(props.budget_quantity) : '-',
accessorFn: (props) => formatNumber(props.budget_quantity),
footer: total ? () => formatNumber(total.budget_quantity) : '',
},
{
id: 'budget_unit_price',
header: 'Harga Satuan',
accessorFn: (props) =>
props.budget_unit_price
? formatCurrency(props.budget_unit_price)
: '-',
accessorFn: (props) => formatCurrency(props.budget_unit_price),
footer: '',
},
{
id: 'budget_total_amount',
header: 'Total',
accessorFn: (props) =>
props.budget_total_amount
? formatCurrency(props.budget_total_amount)
: '-',
accessorFn: (props) => formatCurrency(props.budget_total_amount),
footer: total
? () => formatCurrency(total.budget_total_amount)
: '',
: '0',
},
],
},
@@ -78,37 +111,28 @@ const ClosingOverheadTable = ({
id: 'actual_date',
header: 'Tanggal',
accessorFn: (props) =>
props.actual_date
? formatDate(props.actual_date, 'DD MMM, YYYY')
: '-',
formatDate(props.actual_date, 'DD MMM, YYYY'),
footer: '',
},
{
id: 'actual_quantity',
header: 'Jumlah',
accessorFn: (props) =>
props.actual_quantity ? formatNumber(props.actual_quantity) : '-',
footer: total ? () => formatNumber(total.actual_quantity) : '',
accessorFn: (props) => formatNumber(props.actual_quantity),
footer: total ? () => formatNumber(total.actual_quantity) : '0',
},
{
id: 'actual_unit_price',
header: 'Harga Satuan',
accessorFn: (props) =>
props.actual_unit_price
? formatCurrency(props.actual_unit_price)
: '-',
accessorFn: (props) => formatCurrency(props.actual_unit_price),
footer: '',
},
{
id: 'actual_total_amount',
header: 'Total',
accessorFn: (props) =>
props.actual_total_amount
? formatCurrency(props.actual_total_amount)
: '-',
accessorFn: (props) => formatCurrency(props.actual_total_amount),
footer: total
? () => formatCurrency(total.actual_total_amount)
: '',
: '0',
},
],
},
@@ -118,35 +142,25 @@ const ClosingOverheadTable = ({
{
id: 'actual_date',
header: 'Tanggal',
accessorFn: (props) =>
props.actual_date
? formatDate(props.actual_date, 'DD MMM, YYYY')
: '-',
accessorFn: (props) => formatDate(props.actual_date, 'DD MMM, YYYY'),
footer: '',
},
{
id: 'actual_quantity',
header: 'Jumlah',
accessorFn: (props) =>
props.actual_quantity ? formatNumber(props.actual_quantity) : '-',
accessorFn: (props) => formatNumber(props.actual_quantity),
footer: total ? () => formatNumber(total.actual_quantity) : '',
},
{
id: 'actual_unit_price',
header: 'Harga Satuan',
accessorFn: (props) =>
props.actual_unit_price
? formatCurrency(props.actual_unit_price)
: '-',
accessorFn: (props) => formatCurrency(props.actual_unit_price),
footer: '',
},
{
id: 'actual_total_amount',
header: 'Total',
accessorFn: (props) =>
props.actual_total_amount
? formatCurrency(props.actual_total_amount)
: '-',
accessorFn: (props) => formatCurrency(props.actual_total_amount),
footer: total ? () => formatCurrency(total.actual_total_amount) : '',
},
];
@@ -171,8 +185,7 @@ const ClosingOverheadTable = ({
{
id: 'cost_per_bird',
header: 'Rp/Ekor',
accessorFn: (props) =>
props.cost_per_bird ? formatCurrency(props.cost_per_bird) : '-',
accessorFn: (props) => formatCurrency(props.cost_per_bird),
footer: total ? () => formatCurrency(total.cost_per_bird) : '',
},
];
@@ -183,11 +196,15 @@ const ClosingOverheadTable = ({
() =>
isResponseSuccess(overhead)
? createColumns(
overhead.data?.total,
kandangId
? isResponseSuccess(overheadKandang)
? overheadKandang.data?.total
: undefined
: overhead.data?.total,
kandangId ? Number(kandangId) : undefined
)
: createColumns(),
[overhead]
[overhead, kandangId, overheadKandang]
);
return (
@@ -203,7 +220,13 @@ const ClosingOverheadTable = ({
>
<Table<Overhead>
data={
isResponseSuccess(overhead) ? (overhead.data?.overheads ?? []) : []
kandangId
? isResponseSuccess(overheadKandang)
? (overheadKandang.data?.overheads ?? [])
: []
: isResponseSuccess(overhead)
? (overhead.data?.overheads ?? [])
: []
}
columns={columns}
className={{
@@ -220,6 +243,60 @@ const ClosingOverheadTable = ({
: false
}
/>
{kandangId && (
<Card
className={{
wrapper: 'w-full',
body: 'p-4 shadow-button-soft border border-base-content/10 rounded-lg',
}}
>
<div className='flex flex-row gap-4 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'>
<div className='flex flex-row 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-1' />
<div className='flex flex-row 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'>
<div className='flex flex-row 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-1' />
<div className='flex flex-row 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>
</>
);