refactor(FE-344): Add Cost of Revenue Expedition table

This commit is contained in:
rstubryan
2025-12-11 14:27:29 +07:00
parent 3c03494bd3
commit 4262e8e286
4 changed files with 148 additions and 120 deletions
@@ -10,20 +10,24 @@ import ClosingGeneralInformationTable from '@/components/pages/closing/ClosingGe
import {
ClosingGeneralInformation,
BaseClosingSales,
BaseClosingCostOfRevenueExpedition,
} from '@/types/api/closing';
import ClosingSapronakTabContent from './ClosingSapronakTabContent';
import SalesReportTable from './sale/SalesReportTable';
import CostOfRevenueExpeditionReportTable from './hpp-ekspedisi/CostOfRevenueExpeditionReportTable';
interface ClosingDetailProps {
id: number;
initialValue?: ClosingGeneralInformation;
salesData?: BaseClosingSales;
costOfRevenueExpeditionData?: BaseClosingCostOfRevenueExpedition;
}
const ClosingDetail: React.FC<ClosingDetailProps> = ({
id,
initialValue,
salesData,
costOfRevenueExpeditionData,
}) => {
const [activeTab, setActiveTab] = useState<string>('sapronak');
@@ -52,7 +56,11 @@ const ClosingDetail: React.FC<ClosingDetailProps> = ({
{
id: 'hppEkspedisi',
label: 'HPP Ekspedisi',
content: 'HPP Ekspedisi',
content: (
<CostOfRevenueExpeditionReportTable
initialValues={costOfRevenueExpeditionData}
/>
),
},
{
id: 'dataProduksi',
@@ -1,119 +0,0 @@
'use client';
import React, { useMemo } from 'react';
import { ColumnDef } from '@tanstack/react-table';
import Table from '@/components/Table';
import Card from '@/components/Card';
import { formatCurrency } from '@/lib/helper';
import {
BaseClosingCosExpedition,
BaseCosExpedition,
} from '@/types/api/closing/closing';
interface CosExpeditionReportTableProps {
type?: 'detail';
initialValues?: BaseClosingCosExpedition;
}
const CosExpeditionReportTable = ({
type = 'detail',
initialValues,
}: CosExpeditionReportTableProps) => {
const cosExpeditionData: BaseCosExpedition[] = useMemo(() => {
return initialValues?.cos_expeditions || [];
}, [initialValues]);
const totals = useMemo(() => {
if (cosExpeditionData.length === 0) {
return {
totalHpp: 0,
};
}
const totalHpp = cosExpeditionData.reduce(
(sum, item) => sum + (item.hpp || 0),
0
);
return {
totalHpp,
};
}, [cosExpeditionData]);
const cosExpeditionColumns: ColumnDef<BaseCosExpedition>[] = useMemo(
() => [
{
id: 'id',
accessorKey: 'id',
header: 'No',
cell: (props) => {
return <div>{props.row.index + 1}</div>;
},
footer: () => (
<div className='font-semibold text-gray-900'>Total HPP Ekspedisi</div>
),
},
{
id: 'expedition_name',
accessorKey: 'expedition_name',
header: 'Nama Ekspedisi',
cell: (props) => props.getValue() || '-',
},
{
id: 'hpp',
accessorKey: 'hpp',
header: 'HPP Ekspedisi',
cell: (props) => {
const value = props.getValue() as number;
return <div className='text-right'>{formatCurrency(value)}</div>;
},
footer: () => (
<div className='text-right font-semibold text-gray-900'>
{formatCurrency(totals.totalHpp)}
</div>
),
},
],
[totals]
);
return (
<>
<section className='w-full'>
<div className='p-4'>
<h2 className='text-xl font-semibold mb-4'>HPP Ekspedisi</h2>
<Card
className={{
wrapper: 'w-full bg-base-100',
body: 'p-0',
}}
>
<Table
data={cosExpeditionData}
columns={cosExpeditionColumns}
renderFooter={cosExpeditionData.length > 0}
className={{
tableWrapperClassName: 'overflow-x-auto',
tableClassName: 'w-full table-auto text-sm',
headerRowClassName: 'border-b border-b-gray-200',
headerColumnClassName:
'px-4 py-3 text-xs font-semibold text-gray-500 last:flex last:flex-row last:justify-end 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',
}}
/>
</Card>
</div>
</section>
</>
);
};
export default CosExpeditionReportTable;
@@ -0,0 +1,123 @@
'use client';
import React, { useMemo } from 'react';
import { ColumnDef } from '@tanstack/react-table';
import Table from '@/components/Table';
import Card from '@/components/Card';
import { formatCurrency } from '@/lib/helper';
import {
BaseClosingCostOfRevenueExpedition,
BaseCostOfRevenueExpedition,
} from '@/types/api/closing';
interface CostOfRevenueExpeditionReportTableProps {
type?: 'detail';
initialValues?: BaseClosingCostOfRevenueExpedition;
}
const CostOfRevenueExpeditionReportTable = ({
type = 'detail',
initialValues,
}: CostOfRevenueExpeditionReportTableProps) => {
const costOfRevenueExpeditionData: BaseCostOfRevenueExpedition[] =
useMemo(() => {
return initialValues?.cos_expeditions || [];
}, [initialValues]);
const totals = useMemo(() => {
if (costOfRevenueExpeditionData.length === 0) {
return {
totalHpp: 0,
};
}
const totalHpp = costOfRevenueExpeditionData.reduce(
(sum, item) => sum + (item.hpp || 0),
0
);
return {
totalHpp,
};
}, [costOfRevenueExpeditionData]);
const costOfRevenueExpeditionColumns: ColumnDef<BaseCostOfRevenueExpedition>[] =
useMemo(
() => [
{
id: 'id',
accessorKey: 'id',
header: 'No',
cell: (props) => {
return <div>{props.row.index + 1}</div>;
},
footer: () => (
<div className='font-semibold text-gray-900'>
Total HPP Ekspedisi
</div>
),
},
{
id: 'expedition_name',
accessorKey: 'expedition_name',
header: 'Nama Ekspedisi',
cell: (props) => props.getValue() || '-',
},
{
id: 'hpp',
accessorKey: 'hpp',
header: 'HPP Ekspedisi',
cell: (props) => {
const value = props.getValue() as number;
return <div className='text-right'>{formatCurrency(value)}</div>;
},
footer: () => (
<div className='text-right font-semibold text-gray-900'>
{formatCurrency(totals.totalHpp)}
</div>
),
},
],
[totals]
);
return (
<>
<section className='w-full'>
<div className='p-4'>
<h2 className='text-xl font-semibold mb-4'>HPP Ekspedisi</h2>
<Card
className={{
wrapper: 'w-full bg-base-100',
body: 'p-0',
}}
>
<Table
data={costOfRevenueExpeditionData}
columns={costOfRevenueExpeditionColumns}
renderFooter={costOfRevenueExpeditionData.length > 0}
className={{
tableWrapperClassName: 'overflow-x-auto',
tableClassName: 'w-full table-auto text-sm',
headerRowClassName: 'border-b border-b-gray-200',
headerColumnClassName:
'px-4 py-3 text-xs font-semibold text-gray-500 last:flex last:flex-row last:justify-end 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',
}}
/>
</Card>
</div>
</section>
</>
);
};
export default CostOfRevenueExpeditionReportTable;
+16
View File
@@ -47,6 +47,22 @@ export type BaseClosing = {
export type Closing = BaseMetadata & BaseClosing;
export type BaseCostOfRevenueExpedition = {
id: number;
expedition_name: string;
hpp: number;
};
export type BaseClosingCostOfRevenueExpedition = {
project_type: string;
flock_id: number;
period: number;
cos_expeditions: BaseCostOfRevenueExpedition[];
};
export type ClosingCostOfRevenueExpedition = BaseMetadata &
BaseClosingCostOfRevenueExpedition;
export type BaseClosingGeneralInformation = BaseClosing & {
flock_id: number;
period: number;