feat(FE): Add Production Standard modal and table

This commit is contained in:
rstubryan
2026-01-15 16:24:45 +07:00
parent 00e0bc387b
commit c7b4361cb6
@@ -30,8 +30,12 @@ import {
RecordingApi,
ProjectFlockApi,
} from '@/services/api/production';
import { FcrApi } from '@/services/api/master-data';
import { FcrApi, ProductionStandardApi } from '@/services/api/master-data';
import { FcrWithStandards, FcrStandard } from '@/types/api/master-data/fcr';
import {
ProductionStandard,
StandardDetails,
} from '@/types/api/master-data/production-standard';
import { LocationApi } from '@/services/api/master-data';
import { ProductWarehouseApi } from '@/services/api/inventory';
import { ProductWarehouse } from '@/types/api/inventory/product-warehouse';
@@ -98,6 +102,94 @@ const fcrStandardColumns: ColumnDef<FcrStandard>[] = [
},
];
const productionStandardColumns: ColumnDef<StandardDetails>[] = [
{
accessorKey: 'week',
header: 'Minggu',
cell: (props) => `Minggu ${props.getValue() as number}`,
},
{
accessorKey: 'growth_standard_detail.target_mean_bw',
header: 'Target Mean BW (gram)',
cell: (props) =>
formatNumber(
(props.row.original.growth_standard_detail?.target_mean_bw as number) ||
0
),
},
{
accessorKey: 'growth_standard_detail.max_depletion',
header: 'Max Depletion (%)',
cell: (props) =>
`${
(props.row.original.growth_standard_detail?.max_depletion as number) ||
0
}%`,
},
{
accessorKey: 'growth_standard_detail.min_uniformity',
header: 'Min Uniformity (%)',
cell: (props) =>
`${
(props.row.original.growth_standard_detail?.min_uniformity as number) ||
0
}%`,
},
{
accessorKey: 'growth_standard_detail.feed_intake',
header: 'Feed Intake (gram)',
cell: (props) =>
formatNumber(
(props.row.original.growth_standard_detail?.feed_intake as number) || 0
),
},
{
accessorKey: 'egg_production_standard_detail.target_hen_day_production',
header: 'Target Hen Day (%)',
cell: (props) =>
`${
(props.row.original.egg_production_standard_detail
?.target_hen_day_production as number) || 0
}%`,
},
{
accessorKey: 'egg_production_standard_detail.target_hen_house_production',
header: 'Target Hen House (%)',
cell: (props) =>
`${
(props.row.original.egg_production_standard_detail
?.target_hen_house_production as number) || 0
}%`,
},
{
accessorKey: 'egg_production_standard_detail.target_egg_weight',
header: 'Target Egg Weight (gram)',
cell: (props) =>
formatNumber(
(props.row.original.egg_production_standard_detail
?.target_egg_weight as number) || 0
),
},
{
accessorKey: 'egg_production_standard_detail.target_egg_mass',
header: 'Target Egg Mass (gram)',
cell: (props) =>
formatNumber(
(props.row.original.egg_production_standard_detail
?.target_egg_mass as number) || 0
),
},
{
accessorKey: 'egg_production_standard_detail.standard_fcr',
header: 'Standard FCR',
cell: (props) =>
formatNumber(
(props.row.original.egg_production_standard_detail
?.standard_fcr as number) || 0
),
},
];
const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
// ===== HOOKS & ROUTER =====
const router = useRouter();
@@ -147,8 +239,12 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
const productionStandardModal = useModal();
const [fcrStandards, setFcrStandards] = useState<FcrStandard[]>([]);
const [productionStandards, setProductionStandards] =
useState<ProductionStandard | null>(null);
const [isFcrModalOpen, setIsFcrModalOpen] = useState(false);
const [isProductionStandardModalOpen, setIsProductionStandardModalOpen] =
useState(false);
useEffect(() => {
const checkFcrModalOpen = () => {
@@ -169,6 +265,25 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
return () => observer.disconnect();
}, [fcrStandardModal.ref]);
useEffect(() => {
const checkProductionStandardModalOpen = () => {
const isOpen = productionStandardModal.ref.current?.open || false;
setIsProductionStandardModalOpen(isOpen);
};
checkProductionStandardModalOpen();
const observer = new MutationObserver(checkProductionStandardModalOpen);
if (productionStandardModal.ref.current) {
observer.observe(productionStandardModal.ref.current, {
attributes: true,
attributeFilter: ['open'],
});
}
return () => observer.disconnect();
}, [productionStandardModal.ref]);
const { data: fcr, isLoading: isLoadingFcrStandards } = useSWR(
isFcrModalOpen && initialValues?.project_flock?.fcr?.id
? `fcr-detail-${initialValues.project_flock.fcr.id}`
@@ -182,6 +297,26 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
}
}, [fcr]);
const { data: productionStandard, isLoading: isLoadingProductionStandards } =
useSWR(
isProductionStandardModalOpen &&
initialValues?.project_flock?.production_standart?.id
? `production-standard-detail-${initialValues.project_flock.production_standart.id}`
: null,
() =>
ProductionStandardApi.getSingle(
initialValues!.project_flock!.production_standart!.id!
)
);
useEffect(() => {
if (productionStandard?.status === 'success') {
setProductionStandards(
productionStandard.data as ProductionStandard | null
);
}
}, [productionStandard]);
const isRecordingApproved = useCallback((recording?: Recording) => {
return (
recording?.approval?.action === 'APPROVED' &&
@@ -2828,10 +2963,11 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
{/* Production Standard Modal */}
<Modal
closeOnBackdrop={true}
ref={productionStandardModal.ref}
className={{
modal: 'p-0',
modalBox: 'p-0 rounded-2xl xl:max-w-4/12 max-w-sm',
modalBox: 'p-0 rounded-2xl xl:max-w-full max-w-sm',
}}
>
<div className='space-y-6'>
@@ -2849,81 +2985,35 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
<Icon icon='heroicons:x-mark' width={20} height={20} />
</Button>
</div>
<div className='space-y-4 px-4 pb-4'>
<div>
<span className='text-sm text-gray-600'>Nama Standard</span>
<p className='font-semibold'>
{initialValues?.project_flock?.production_standart?.name || '-'}
</p>
<div className='px-4'>
{isLoadingProductionStandards ? (
<div className='flex justify-center py-8'>
<span className='loading loading-spinner loading-lg'></span>
</div>
<div>
<span className='text-sm text-gray-600'>Minggu</span>
<p className='font-semibold'>
{initialValues?.project_flock?.production_standart?.week || '-'}
) : productionStandards?.details &&
productionStandards.details.length > 0 ? (
<Table<StandardDetails>
data={productionStandards.details}
columns={productionStandardColumns}
pageSize={100}
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 whitespace-nowrap border-l border-l-gray-200 border-r border-r-gray-200 border-t border-t-gray-200 border-gray-200 border-b-0',
bodyRowClassName:
'hover:bg-gray-50 transition-colors border-b border-gray-200 first:border-t first:border-t-gray-200 border-l border-l-gray-200 border-r border-r-gray-200',
bodyColumnClassName:
'px-4 py-3 text-xs text-gray-900 whitespace-nowrap',
paginationClassName: 'hidden',
}}
/>
) : (
<p className='text-sm text-gray-500'>
Tidak ada data Production standards
</p>
</div>
<div>
<span className='text-sm text-gray-600'>
Hen Day Standard (%)
</span>
<p className='font-semibold'>
{initialValues?.project_flock?.production_standart
?.hen_day_std != null
? `${initialValues?.project_flock?.production_standart?.hen_day_std}%`
: '-'}
</p>
</div>
<div>
<span className='text-sm text-gray-600'>
Hen House Standard (%)
</span>
<p className='font-semibold'>
{initialValues?.project_flock?.production_standart
?.hen_house_std != null
? `${initialValues?.project_flock?.production_standart?.hen_house_std}%`
: '-'}
</p>
</div>
<div>
<span className='text-sm text-gray-600'>
Feed Intake Standard (KG)
</span>
<p className='font-semibold'>
{initialValues?.project_flock?.production_standart
?.feed_intake_std != null
? formatNumber(
initialValues?.project_flock?.production_standart
?.feed_intake_std || 0
)
: '-'}
</p>
</div>
<div>
<span className='text-sm text-gray-600'>Egg Mass Standard</span>
<p className='font-semibold'>
{initialValues?.project_flock?.production_standart
?.egg_mass_std != null
? formatNumber(
initialValues?.project_flock?.production_standart
?.egg_mass_std || 0
)
: '-'}
</p>
</div>
<div>
<span className='text-sm text-gray-600'>
Egg Weight Standard (KG)
</span>
<p className='font-semibold'>
{initialValues?.project_flock?.production_standart
?.egg_weight_std != null
? formatNumber(
initialValues?.project_flock?.production_standart
?.egg_weight_std || 0
)
: '-'}
</p>
</div>
)}
</div>
</div>
</Modal>