From cc0b051a0a3f157a03b69975a3a744e61b036eaf Mon Sep 17 00:00:00 2001 From: rstubryan Date: Tue, 30 Dec 2025 21:55:37 +0700 Subject: [PATCH 1/9] refactor(FE-440): Remove body_weights handling from recording forms --- .../recording/form/RecordingForm.schema.ts | 43 -- .../recording/form/RecordingForm.tsx | 487 ------------------ src/types/api/production/recording.d.ts | 11 - 3 files changed, 541 deletions(-) diff --git a/src/components/pages/production/recording/form/RecordingForm.schema.ts b/src/components/pages/production/recording/form/RecordingForm.schema.ts index 30013001..4901b349 100644 --- a/src/components/pages/production/recording/form/RecordingForm.schema.ts +++ b/src/components/pages/production/recording/form/RecordingForm.schema.ts @@ -12,11 +12,6 @@ type RecordingGrowingFormSchemaType = { label: string; } | null; project_flock_kandang_id: number; - body_weights: { - weight: number | string; - avg_weight: number | string; - qty: number | string; - }[]; stocks: { product_warehouse_id: number; qty: number | string; @@ -35,12 +30,6 @@ type RecordingLayingFormSchemaType = RecordingGrowingFormSchemaType & { }[]; }; -export type BodyWeightSchema = { - weight: number | string; - avg_weight: number | string; - qty: number | string; -}; - export type StockSchema = { product_warehouse_id: number; qty: number | string; @@ -57,20 +46,6 @@ export type EggSchema = { weight: number | string; }; -const BodyWeightObjectSchema: Yup.ObjectSchema = Yup.object({ - weight: Yup.number() - .required('Berat ayam total wajib diisi!') - .min(1, 'Berat ayam total minimal 1 gram!') - .typeError('Berat ayam total harus berupa angka!'), - avg_weight: Yup.number() - .required('Berat ayam rata-rata wajib diisi!') - .typeError('Berat ayam rata-rata harus berupa angka!'), - qty: Yup.number() - .required('Jumlah ayam wajib diisi!') - .min(1, 'Jumlah ayam minimal 1 ekor!') - .typeError('Jumlah ayam harus berupa angka!'), -}); - const StockObjectSchema: Yup.ObjectSchema = Yup.object({ product_warehouse_id: Yup.number() .required('Produk wajib diisi!') @@ -140,10 +115,6 @@ export const RecordingGrowingFormSchema: Yup.ObjectSchema; type RecordingFormData = Partial & { - body_weights?: CreateGrowingRecordingPayload['body_weights']; stocks?: CreateGrowingRecordingPayload['stocks'] | Recording['stocks']; depletions?: | CreateGrowingRecordingPayload['depletions'] @@ -216,19 +186,6 @@ export const getRecordingGrowingFormInitialValues = ( } : null, project_flock_kandang_id: initialValues?.project_flock_kandang_id ?? 0, - body_weights: initialValues?.body_weights?.map( - (bw: NonNullable[0]) => ({ - weight: bw.avg_weight * bw.qty, - avg_weight: bw.avg_weight, - qty: bw.qty, - }) - ) ?? [ - { - weight: '', - avg_weight: '', - qty: '', - }, - ], stocks: initialValues?.stocks?.map((stock) => ({ product_warehouse_id: stock.product_warehouse_id, qty: diff --git a/src/components/pages/production/recording/form/RecordingForm.tsx b/src/components/pages/production/recording/form/RecordingForm.tsx index c8fa3ca0..f9182cab 100644 --- a/src/components/pages/production/recording/form/RecordingForm.tsx +++ b/src/components/pages/production/recording/form/RecordingForm.tsx @@ -71,16 +71,10 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { const router = useRouter(); // ===== STATE MANAGEMENT ===== - const [selectedBodyWeights, setSelectedBodyWeights] = useState([]); const [selectedStocks, setSelectedStocks] = useState([]); const [selectedDepletions, setSelectedDepletions] = useState([]); const [selectedEggs, setSelectedEggs] = useState([]); - const [editingAverageIndex] = useState(null); - const [manuallyEditedRows, setManuallyEditedRows] = useState>( - new Set() - ); - const [locationSearchValue, setLocationSearchValue] = useState(''); const [selectedLocation, setSelectedLocation] = useState( null @@ -122,19 +116,6 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { (values: RecordingGrowingFormValues) => { return { project_flock_kandang_id: values.project_flock_kandang_id, - body_weights: (values.body_weights ?? []).map((bw) => { - const qty = Number(bw.qty) || 0; - const weight = Number(bw.weight) || 0; - const totalWeight = qty * weight; - return { - avg_weight: - typeof bw.avg_weight === 'number' - ? bw.avg_weight - : parseFloat(String(bw.avg_weight)) || 0, - qty: qty, - total_weight: parseFloat(String(totalWeight)) || 0, - }; - }), stocks: (values.stocks ?? []).map((stock) => ({ product_warehouse_id: stock.product_warehouse_id, qty: Number(stock.qty) || 0, @@ -152,15 +133,6 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { (values: RecordingLayingFormValues) => { return { project_flock_kandang_id: values.project_flock_kandang_id, - body_weights: (values.body_weights ?? []).map((bw) => { - return { - avg_weight: - typeof bw.avg_weight === 'number' - ? bw.avg_weight - : parseFloat(String(bw.avg_weight)) || 0, - qty: Number(bw.qty) || 0, - }; - }), stocks: (values.stocks ?? []).map((stock) => ({ product_warehouse_id: stock.product_warehouse_id, qty: Number(stock.qty) || 0, @@ -587,28 +559,6 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { return recordedIds; }, [existingRecordings, today]); - const getLatestTotalChickQty = useCallback( - (projectFlockKandangId: number) => { - if (!isResponseSuccess(existingRecordings)) return null; - - const projectFlockRecordings = existingRecordings.data.filter( - (recording) => - recording.project_flock_kandang_id === projectFlockKandangId - ); - - if (projectFlockRecordings.length === 0) return null; - - projectFlockRecordings.sort( - (a, b) => - new Date(b.record_datetime).getTime() - - new Date(a.record_datetime).getTime() - ); - - return projectFlockRecordings[0].total_chick_qty; - }, - [existingRecordings] - ); - const unifiedStockProducts = useMemo(() => { const options: OptionType[] = []; if (isResponseSuccess(stockProducts) && selectedKandang) { @@ -808,25 +758,6 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { }); // ===== HELPER FUNCTIONS ===== - const getTotalChickQtyError = useCallback( - (qty: number) => { - if (type === 'detail') return null; - if (!formik.values.project_flock_kandang_id) return null; - - const totalChickQty = getLatestTotalChickQty( - formik.values.project_flock_kandang_id - ); - if (!totalChickQty) return null; - - if (qty > totalChickQty) { - return `Jumlah ayam tidak boleh melebihi total ayam tersedia! Maksimal: ${formatNumber(totalChickQty)}`; - } - - return null; - }, - [formik.values.project_flock_kandang_id, getLatestTotalChickQty, type] - ); - useCallback((): OptionType | null => { if ( !formik.values.project_flock_kandang || @@ -1193,124 +1124,6 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { setIsRejectLoading(false); }; - const addBodyWeight = () => { - const newBodyWeights = [ - ...(formik.values.body_weights || []), - { - weight: '', - avg_weight: '', - qty: '', - }, - ]; - formik.setFieldValue('body_weights', newBodyWeights); - }; - - const handleWeightChange = (idx: number, value: number) => { - formik.setFieldValue(`body_weights.${idx}.weight`, value); - - setManuallyEditedRows((prev) => { - const newSet = new Set(prev); - newSet.delete(idx); - return newSet; - }); - - const currentWeight = formik.values.body_weights?.[idx]; - if (currentWeight) { - const qty = Number(currentWeight.qty) || 0; - const totalWeight = qty * value; - - if (qty > 0 && value > 0) { - const avgWeight = parseFloat((value / qty).toFixed(2)); - formik.setFieldValue(`body_weights.${idx}.avg_weight`, avgWeight); - } else { - formik.setFieldValue(`body_weights.${idx}.avg_weight`, ''); - } - - formik.setFieldValue(`body_weights.${idx}.total_weight`, totalWeight); - } - }; - - const handleAvgWeightChange = (idx: number, value: number) => { - formik.setFieldValue(`body_weights.${idx}.avg_weight`, value); - - setManuallyEditedRows((prev) => { - const newSet = new Set(prev); - newSet.add(idx); - return newSet; - }); - - const currentWeight = formik.values.body_weights?.[idx]; - if (currentWeight) { - const qty = Number(currentWeight.qty) || 0; - if (qty > 0 && value > 0) { - const totalWeight = value * qty; - formik.setFieldValue(`body_weights.${idx}.weight`, totalWeight); - formik.setFieldValue(`body_weights.${idx}.total_weight`, totalWeight); - } else { - formik.setFieldValue(`body_weights.${idx}.weight`, 0); - formik.setFieldValue(`body_weights.${idx}.total_weight`, 0); - } - } - }; - - const handleQtyChange = (idx: number, value: number) => { - formik.setFieldValue(`body_weights.${idx}.qty`, value); - - setManuallyEditedRows((prev) => { - const newSet = new Set(prev); - newSet.delete(idx); - return newSet; - }); - - const currentWeight = formik.values.body_weights?.[idx]; - if (currentWeight) { - const weight = Number(currentWeight.weight) || 0; - const totalWeight = value * weight; - - if (value > 0 && weight > 0) { - const avgWeight = parseFloat((weight / value).toFixed(2)); - formik.setFieldValue(`body_weights.${idx}.avg_weight`, avgWeight); - } else { - formik.setFieldValue(`body_weights.${idx}.avg_weight`, ''); - } - - formik.setFieldValue(`body_weights.${idx}.total_weight`, totalWeight); - } - }; - - const handleWeightChangeWrapper = - (idx: number) => (e: React.ChangeEvent) => { - const value = parseFloat(e.target.value) || 0; - handleWeightChange(idx, value); - }; - - const handleAvgWeightChangeWrapper = - (idx: number) => (e: React.ChangeEvent) => { - const value = parseFloat(e.target.value) || 0; - handleAvgWeightChange(idx, value); - }; - - const handleQtyChangeWrapper = - (idx: number) => (e: React.ChangeEvent) => { - const value = parseFloat(e.target.value) || 0; - handleQtyChange(idx, value); - }; - - const removeBodyWeight = (idx: number) => { - const updatedBodyWeights = formik.values.body_weights?.filter( - (_, i) => i !== idx - ); - formik.setFieldValue('body_weights', updatedBodyWeights); - }; - - const removeSelectedBodyWeights = () => { - const updatedBodyWeights = formik.values.body_weights?.filter( - (_, idx) => !selectedBodyWeights.includes(idx) - ); - formik.setFieldValue('body_weights', updatedBodyWeights); - setSelectedBodyWeights([]); - }; - const addStock = () => { const newStocks = [ ...(formik.values.stocks || []), @@ -1435,45 +1248,6 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { } }, [isLayingCategory, type]); - const bodyWeightValues = useMemo(() => { - if (!formik.values.body_weights) return []; - return formik.values.body_weights.map((w) => ({ - weight: w.weight, - qty: w.qty, - })); - }, [formik.values.body_weights]); - - useEffect(() => { - if (formik.values.body_weights && editingAverageIndex === null) { - const updatedBodyWeights = formik.values.body_weights.map( - (weight, idx) => { - if (idx === editingAverageIndex || manuallyEditedRows.has(idx)) { - return weight; - } - const qty = Number(weight.qty) || 0; - const weightValue = Number(weight.weight) || 0; - return { - ...weight, - avg_weight: - qty > 0 && weightValue > 0 - ? parseFloat((weightValue / qty).toFixed(2)) - : 0, - }; - } - ); - const hasChanges = updatedBodyWeights.some( - (updated, idx) => - idx !== editingAverageIndex && - !manuallyEditedRows.has(idx) && - updated.avg_weight !== - (formik.values.body_weights[idx]?.avg_weight || 0) - ); - if (hasChanges) { - formik.setFieldValue('body_weights', updatedBodyWeights, false); - } - } - }, [bodyWeightValues, editingAverageIndex, manuallyEditedRows]); - return ( <>
@@ -1763,267 +1537,6 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { )} - {/* Body Weights Table */} - -
- - - - {(type as 'add' | 'edit' | 'detail') !== 'detail' && ( - - )} - - - - {(type as 'add' | 'edit' | 'detail') !== 'detail' && ( - - )} - - - - {formik.values.body_weights?.map((bw, idx) => ( - - {(type as 'add' | 'edit' | 'detail') !== 'detail' && ( - - )} - - - - - {(type as 'add' | 'edit' | 'detail') !== 'detail' && ( - - )} - - ))} - -
- 0 - } - onChange={( - e: React.ChangeEvent - ) => { - if (e.target.checked) { - setSelectedBodyWeights( - formik.values.body_weights?.map( - (_, idx) => idx - ) ?? [] - ); - } else { - setSelectedBodyWeights([]); - } - }} - classNames={{ - wrapper: 'flex justify-center', - checkbox: 'checkbox checkbox-sm', - }} - /> - - Berat Ayam (gram) - - * - - - Jumlah Ayam - - * - - - Rata-rata Berat Ayam (gram) - - * - - Action
- - ) => { - if (e.target.checked) { - setSelectedBodyWeights([ - ...selectedBodyWeights, - idx, - ]); - } else { - setSelectedBodyWeights( - selectedBodyWeights.filter((i) => i !== idx) - ); - } - }} - classNames={{ - wrapper: 'flex justify-center', - checkbox: 'checkbox checkbox-sm', - }} - /> - - - - - - - -
- -
-
-
- {(type as 'add' | 'edit' | 'detail') !== 'detail' && ( -
- {selectedBodyWeights.length > 0 && ( - - )} - -
- )} -
- {/* Stocks Table */} Date: Tue, 30 Dec 2025 22:18:51 +0700 Subject: [PATCH 2/9] feat(FE-441): Add FCR and mortality tables to detail view --- .../recording/form/RecordingForm.tsx | 169 ++++++++++++++++++ 1 file changed, 169 insertions(+) diff --git a/src/components/pages/production/recording/form/RecordingForm.tsx b/src/components/pages/production/recording/form/RecordingForm.tsx index f9182cab..205eada7 100644 --- a/src/components/pages/production/recording/form/RecordingForm.tsx +++ b/src/components/pages/production/recording/form/RecordingForm.tsx @@ -60,6 +60,7 @@ import { GROWING_RECORDING_APPROVAL_LINE, LAYING_RECORDING_APPROVAL_LINE, } from '@/config/approval-line'; +import Table from '@/components/Table'; interface RecordingFormProps { type?: 'add' | 'edit' | 'detail'; @@ -1537,6 +1538,174 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { )} + {/* FCR & Mortality Metrics - Detail View Only - Grid Layout */} + {type === 'detail' && initialValues && ( +
+ {/* FCR Metrics */} +
+

FCR

+ ( + + {props.row.original.label} + + ), + }, + { + accessorKey: 'aktual', + header: 'Aktual', + cell: (props) => ( + + {props.row.original.aktual} + + ), + }, + { + accessorKey: 'standar', + header: 'Standar', + cell: (props) => ( + {props.row.original.standar} + ), + }, + { + accessorKey: 'totalPakan', + header: 'Total Pemakaian Pakan (KG)', + cell: (props) => ( + {props.row.original.totalPakan} + ), + }, + ]} + pageSize={2} + className={{ + containerClassName: 'mb-0', + paginationClassName: 'hidden', + }} + /> + + + {/* Mortality Metrics */} +
+

Mortalitas

+
( + + {props.row.original.jumlahAyamTotal} + + ), + }, + { + accessorKey: 'jumlahAyamPct', + header: '(%)', + cell: (props) => ( + + {props.row.original.jumlahAyamPct} + + ), + }, + ], + }, + { + id: 'deplesi-harian', + header: 'Deplesi Harian', + columns: [ + { + accessorKey: 'deplesiHarianTotal', + header: 'Total', + cell: (props) => ( + + {props.row.original.deplesiHarianTotal} + + ), + }, + { + accessorKey: 'deplesiHarianPct', + header: '(%)', + cell: (props) => ( + + {props.row.original.deplesiHarianPct} + + ), + }, + ], + }, + { + id: 'deplesi-kumulatif', + header: 'Deplesi Kumulatif', + columns: [ + { + accessorKey: 'deplesiKumulatifTotal', + header: 'Total', + cell: (props) => ( + + {props.row.original.deplesiKumulatifTotal} + + ), + }, + { + accessorKey: 'deplesiKumulatifPct', + header: '(%)', + cell: (props) => ( + + {props.row.original.deplesiKumulatifPct} + + ), + }, + ], + }, + ]} + pageSize={1} + className={{ + containerClassName: 'mb-0', + paginationClassName: 'hidden', + }} + /> + + + )} + {/* Stocks Table */} Date: Tue, 30 Dec 2025 22:29:15 +0700 Subject: [PATCH 3/9] refactor(FE-440): Use styled HTML tables for FCR and mortality --- .../recording/form/RecordingForm.tsx | 291 ++++++++---------- 1 file changed, 131 insertions(+), 160 deletions(-) diff --git a/src/components/pages/production/recording/form/RecordingForm.tsx b/src/components/pages/production/recording/form/RecordingForm.tsx index 205eada7..f83e9286 100644 --- a/src/components/pages/production/recording/form/RecordingForm.tsx +++ b/src/components/pages/production/recording/form/RecordingForm.tsx @@ -1538,170 +1538,141 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { )} - {/* FCR & Mortality Metrics - Detail View Only - Grid Layout */} + {/* FCR & Mortality Metrics - Detail View Only */} {type === 'detail' && initialValues && (
- {/* FCR Metrics */} -
-

FCR

-
( - - {props.row.original.label} - - ), - }, - { - accessorKey: 'aktual', - header: 'Aktual', - cell: (props) => ( - - {props.row.original.aktual} - - ), - }, - { - accessorKey: 'standar', - header: 'Standar', - cell: (props) => ( - {props.row.original.standar} - ), - }, - { - accessorKey: 'totalPakan', - header: 'Total Pemakaian Pakan (KG)', - cell: (props) => ( - {props.row.original.totalPakan} - ), - }, - ]} - pageSize={2} - className={{ - containerClassName: 'mb-0', - paginationClassName: 'hidden', - }} - /> + {/* FCR Section */} +
+
+ FCR +
+
+
+ + + + + + + + + + + + + + + + + + + + + + +
+ AKTUAL + + STANDAR + + TOTAL PEMAKAIAN PAKAN (KG) +
Bobot + + {initialValues.avg_daily_gain?.toFixed(2) || '-'} + + - + {formatNumber(initialValues.cum_intake || 0)} +
FCR + + {initialValues.fcr_value?.toFixed(2) || '-'} + + -
+
- {/* Mortality Metrics */} -
-

Mortalitas

- ( - - {props.row.original.jumlahAyamTotal} - - ), - }, - { - accessorKey: 'jumlahAyamPct', - header: '(%)', - cell: (props) => ( - - {props.row.original.jumlahAyamPct} - - ), - }, - ], - }, - { - id: 'deplesi-harian', - header: 'Deplesi Harian', - columns: [ - { - accessorKey: 'deplesiHarianTotal', - header: 'Total', - cell: (props) => ( - - {props.row.original.deplesiHarianTotal} - - ), - }, - { - accessorKey: 'deplesiHarianPct', - header: '(%)', - cell: (props) => ( - - {props.row.original.deplesiHarianPct} - - ), - }, - ], - }, - { - id: 'deplesi-kumulatif', - header: 'Deplesi Kumulatif', - columns: [ - { - accessorKey: 'deplesiKumulatifTotal', - header: 'Total', - cell: (props) => ( - - {props.row.original.deplesiKumulatifTotal} - - ), - }, - { - accessorKey: 'deplesiKumulatifPct', - header: '(%)', - cell: (props) => ( - - {props.row.original.deplesiKumulatifPct} - - ), - }, - ], - }, - ]} - pageSize={1} - className={{ - containerClassName: 'mb-0', - paginationClassName: 'hidden', - }} - /> + {/* Mortality Section */} +
+
+ + Mortalitas + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ JUMLAH AYAM + + DEPLESI HARIAN + + DEPLESI KUMULATIF +
+ Total + + (%) + + Total + + (%) + + Total + + (%) +
+ + {formatNumber(initialValues.total_chick_qty || 0)} + + + - + + + {formatNumber(initialValues.daily_gain || 0)} + + + - + + + {formatNumber( + initialValues.total_depletion_qty || 0 + )} + + + {initialValues.cum_depletion_rate?.toFixed(2) || '-'} +
+
)} From 4f571f1c16b2e5e882632b9b4b7dccd48b53e966 Mon Sep 17 00:00:00 2001 From: rstubryan Date: Wed, 31 Dec 2025 08:40:56 +0700 Subject: [PATCH 4/9] feat(FE-441): Show laying metrics and extend ProductionMetrics --- .../recording/form/RecordingForm.tsx | 115 ++++++++++++------ src/types/api/production/recording.d.ts | 16 ++- 2 files changed, 89 insertions(+), 42 deletions(-) diff --git a/src/components/pages/production/recording/form/RecordingForm.tsx b/src/components/pages/production/recording/form/RecordingForm.tsx index f83e9286..eec9156c 100644 --- a/src/components/pages/production/recording/form/RecordingForm.tsx +++ b/src/components/pages/production/recording/form/RecordingForm.tsx @@ -1540,7 +1540,13 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { {/* FCR & Mortality Metrics - Detail View Only */} {type === 'detail' && initialValues && ( -
+
{/* FCR Section */}
@@ -1557,24 +1563,9 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { STANDAR - - TOTAL PEMAKAIAN PAKAN (KG) - - - Bobot - - - {initialValues.avg_daily_gain?.toFixed(2) || '-'} - - - - - - {formatNumber(initialValues.cum_intake || 0)} - - FCR @@ -1583,7 +1574,17 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { - - + + + Feed Intake (KG) + + + {formatNumber(initialValues.feed_intake || 0)} + + + + {formatNumber(initialValues.feed_intake_std || 0)} + @@ -1601,12 +1602,6 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { - - - @@ -1645,19 +1634,13 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { - -
- JUMLAH AYAM - { Total - (%) - - Total - (%)
- {formatNumber(initialValues.total_chick_qty || 0)} + {formatNumber(initialValues.hand_day || 0)} - - - - - {formatNumber(initialValues.daily_gain || 0)} - - - - + {initialValues.hand_day_std !== undefined + ? `${initialValues.hand_day_std}%` + : '-'} @@ -1674,6 +1657,60 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
+ + {/* Egg Production Section - Only for LAYING category */} + {type === 'detail' && + initialValues && + initialValues.project_flock_category === 'LAYING' && ( +
+
+ + Produksi Telur + +
+
+ + + + + + + + + + + + + + + + + + + + +
+ AKTUAL + + STANDAR +
Egg Mesh + + {formatNumber(initialValues.egg_mesh || 0)} + + + {formatNumber(initialValues.egg_mesh_std || 0)} +
+ Egg Weight (KG) + + + {formatNumber(initialValues.egg_weight || 0)} + + + {formatNumber(initialValues.egg_weight_std || 0)} +
+
+
+ )}
)} diff --git a/src/types/api/production/recording.d.ts b/src/types/api/production/recording.d.ts index cda76e47..83bdf3f2 100644 --- a/src/types/api/production/recording.d.ts +++ b/src/types/api/production/recording.d.ts @@ -4,12 +4,22 @@ import { ProductWarehouse } from '@/types/api/inventory/product-warehouse'; export type ProductionMetrics = { total_depletion_qty: number; cum_depletion_rate: number; - daily_gain: number; - avg_daily_gain: number; cum_intake: number; fcr_value: number; total_chick_qty: number; - cum_depletion: number; + hand_day?: number; + hand_house?: number; + feed_intake?: number; + egg_mesh?: number; + egg_weight?: number; + hand_day_std?: number; + hand_house_std?: number; + feed_intake_std?: number; + egg_mesh_std?: number; + egg_weight_std?: number; + daily_gain?: number; + avg_daily_gain?: number; + cum_depletion?: number; }; export type BaseRecording = { From afb79b0589696fcff895ea30558ad3d33d91fa74 Mon Sep 17 00:00:00 2001 From: rstubryan Date: Wed, 31 Dec 2025 08:50:51 +0700 Subject: [PATCH 5/9] feat(FE-441): Format and display intake, hand house, and totals --- .../recording/form/RecordingForm.tsx | 43 ++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/src/components/pages/production/recording/form/RecordingForm.tsx b/src/components/pages/production/recording/form/RecordingForm.tsx index eec9156c..552cd98f 100644 --- a/src/components/pages/production/recording/form/RecordingForm.tsx +++ b/src/components/pages/production/recording/form/RecordingForm.tsx @@ -1570,7 +1570,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { FCR - {initialValues.fcr_value?.toFixed(2) || '-'} + {formatNumber(initialValues.fcr_value || 0)} - @@ -1586,6 +1586,15 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { {formatNumber(initialValues.feed_intake_std || 0)} + + Cum. Intake (KG) + + + {formatNumber(initialValues.cum_intake || 0)} + + + - +
@@ -1653,6 +1662,38 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { {initialValues.cum_depletion_rate?.toFixed(2) || '-'} + + + + {formatNumber(initialValues.hand_house || 0)} + + + + {initialValues.hand_house_std !== undefined + ? `${initialValues.hand_house_std}%` + : '-'} + + + - + + + + + Total Ayam + + + {formatNumber(initialValues.total_chick_qty || 0)} + + From 6c3285f624f4f50b6663db35272ed1cb7ef8ecb7 Mon Sep 17 00:00:00 2001 From: rstubryan Date: Wed, 31 Dec 2025 08:54:15 +0700 Subject: [PATCH 6/9] refactor(FE-441): Make recording metrics grid responsive --- .../pages/production/recording/form/RecordingForm.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/pages/production/recording/form/RecordingForm.tsx b/src/components/pages/production/recording/form/RecordingForm.tsx index 552cd98f..1501ed62 100644 --- a/src/components/pages/production/recording/form/RecordingForm.tsx +++ b/src/components/pages/production/recording/form/RecordingForm.tsx @@ -1541,10 +1541,10 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { {/* FCR & Mortality Metrics - Detail View Only */} {type === 'detail' && initialValues && (
{/* FCR Section */} From 2bf764a05c164689a63a46fa224dec63ae7d9607 Mon Sep 17 00:00:00 2001 From: rstubryan Date: Wed, 31 Dec 2025 09:02:15 +0700 Subject: [PATCH 7/9] refactor(FE-441): Display '-' for empty/zero numeric fields --- .../recording/form/RecordingForm.tsx | 73 ++++++++++++++----- 1 file changed, 56 insertions(+), 17 deletions(-) diff --git a/src/components/pages/production/recording/form/RecordingForm.tsx b/src/components/pages/production/recording/form/RecordingForm.tsx index 1501ed62..e0c16fda 100644 --- a/src/components/pages/production/recording/form/RecordingForm.tsx +++ b/src/components/pages/production/recording/form/RecordingForm.tsx @@ -1570,7 +1570,10 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { FCR - {formatNumber(initialValues.fcr_value || 0)} + {initialValues.fcr_value && + initialValues.fcr_value > 0 + ? formatNumber(initialValues.fcr_value) + : '-'} - @@ -1579,18 +1582,27 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { Feed Intake (KG) - {formatNumber(initialValues.feed_intake || 0)} + {initialValues.feed_intake && + initialValues.feed_intake > 0 + ? formatNumber(initialValues.feed_intake) + : '-'} - {formatNumber(initialValues.feed_intake_std || 0)} + {initialValues.feed_intake_std && + initialValues.feed_intake_std > 0 + ? formatNumber(initialValues.feed_intake_std) + : '-'} Cum. Intake (KG) - {formatNumber(initialValues.cum_intake || 0)} + {initialValues.cum_intake && + initialValues.cum_intake > 0 + ? formatNumber(initialValues.cum_intake) + : '-'} - @@ -1643,33 +1655,45 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { - {formatNumber(initialValues.hand_day || 0)} + {initialValues.hand_day && + initialValues.hand_day > 0 + ? formatNumber(initialValues.hand_day) + : '-'} - {initialValues.hand_day_std !== undefined + {initialValues.hand_day_std !== undefined && + initialValues.hand_day_std > 0 ? `${initialValues.hand_day_std}%` : '-'} - {formatNumber( - initialValues.total_depletion_qty || 0 - )} + {initialValues.total_depletion_qty && + initialValues.total_depletion_qty > 0 + ? formatNumber(initialValues.total_depletion_qty) + : '-'} - {initialValues.cum_depletion_rate?.toFixed(2) || '-'} + {initialValues.cum_depletion_rate && + initialValues.cum_depletion_rate > 0 + ? initialValues.cum_depletion_rate.toFixed(2) + : '-'} - {formatNumber(initialValues.hand_house || 0)} + {initialValues.hand_house && + initialValues.hand_house > 0 + ? formatNumber(initialValues.hand_house) + : '-'} - {initialValues.hand_house_std !== undefined + {initialValues.hand_house_std !== undefined && + initialValues.hand_house_std > 0 ? `${initialValues.hand_house_std}%` : '-'} @@ -1691,7 +1715,10 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { colSpan={2} className='text-center py-3 font-semibold' > - {formatNumber(initialValues.total_chick_qty || 0)} + {initialValues.total_chick_qty && + initialValues.total_chick_qty > 0 + ? formatNumber(initialValues.total_chick_qty) + : '-'} @@ -1727,11 +1754,17 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { Egg Mesh - {formatNumber(initialValues.egg_mesh || 0)} + {initialValues.egg_mesh && + initialValues.egg_mesh > 0 + ? formatNumber(initialValues.egg_mesh) + : '-'} - {formatNumber(initialValues.egg_mesh_std || 0)} + {initialValues.egg_mesh_std && + initialValues.egg_mesh_std > 0 + ? formatNumber(initialValues.egg_mesh_std) + : '-'} @@ -1740,11 +1773,17 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { - {formatNumber(initialValues.egg_weight || 0)} + {initialValues.egg_weight && + initialValues.egg_weight > 0 + ? formatNumber(initialValues.egg_weight) + : '-'} - {formatNumber(initialValues.egg_weight_std || 0)} + {initialValues.egg_weight_std && + initialValues.egg_weight_std > 0 + ? formatNumber(initialValues.egg_weight_std) + : '-'} From d8daf09844227702ac3b01521d8a3446c99bdd2a Mon Sep 17 00:00:00 2001 From: rstubryan Date: Wed, 31 Dec 2025 09:20:49 +0700 Subject: [PATCH 8/9] refactorF(FE-441): Display formatted fcr_std in RecordingForm --- .../pages/production/recording/form/RecordingForm.tsx | 6 +++++- src/types/api/production/recording.d.ts | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/components/pages/production/recording/form/RecordingForm.tsx b/src/components/pages/production/recording/form/RecordingForm.tsx index e0c16fda..127af83d 100644 --- a/src/components/pages/production/recording/form/RecordingForm.tsx +++ b/src/components/pages/production/recording/form/RecordingForm.tsx @@ -1576,7 +1576,11 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { : '-'} - - + + {initialValues.fcr_std && initialValues.fcr_std > 0 + ? formatNumber(initialValues.fcr_std) + : '-'} + Feed Intake (KG) diff --git a/src/types/api/production/recording.d.ts b/src/types/api/production/recording.d.ts index 83bdf3f2..9cf9a625 100644 --- a/src/types/api/production/recording.d.ts +++ b/src/types/api/production/recording.d.ts @@ -6,6 +6,7 @@ export type ProductionMetrics = { cum_depletion_rate: number; cum_intake: number; fcr_value: number; + fcr_std?: number; total_chick_qty: number; hand_day?: number; hand_house?: number; From 8e8835589499a81ae4c5b48701bb87bb5f9ead0c Mon Sep 17 00:00:00 2001 From: rstubryan Date: Wed, 31 Dec 2025 09:43:05 +0700 Subject: [PATCH 9/9] refactor(FE-441): Refactor RecordingForm layout and labels --- .../recording/form/RecordingForm.tsx | 100 +++++++----------- 1 file changed, 38 insertions(+), 62 deletions(-) diff --git a/src/components/pages/production/recording/form/RecordingForm.tsx b/src/components/pages/production/recording/form/RecordingForm.tsx index 127af83d..3a6f8071 100644 --- a/src/components/pages/production/recording/form/RecordingForm.tsx +++ b/src/components/pages/production/recording/form/RecordingForm.tsx @@ -1599,18 +1599,6 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { : '-'} - - Cum. Intake (KG) - - - {initialValues.cum_intake && - initialValues.cum_intake > 0 - ? formatNumber(initialValues.cum_intake) - : '-'} - - - - -
@@ -1627,12 +1615,6 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { - - - @@ -1657,20 +1633,6 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { - - - - - - - + + - + + + + + + + + + + +
- DEPLESI HARIAN - {
- Total - - (%) - Total
- - {initialValues.hand_day && - initialValues.hand_day > 0 - ? formatNumber(initialValues.hand_day) - : '-'} - - - {initialValues.hand_day_std !== undefined && - initialValues.hand_day_std > 0 - ? `${initialValues.hand_day_std}%` - : '-'} - {initialValues.total_depletion_qty && @@ -1686,28 +1648,6 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { : '-'}
- - {initialValues.hand_house && - initialValues.hand_house > 0 - ? formatNumber(initialValues.hand_house) - : '-'} - - - {initialValues.hand_house_std !== undefined && - initialValues.hand_house_std > 0 - ? `${initialValues.hand_house_std}%` - : '-'} - - - -
{ > Total Ayam
{
- Produksi Telur + Produksi
@@ -1755,7 +1697,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
Egg MeshEgg Mass {initialValues.egg_mesh && @@ -1790,6 +1732,40 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { : '-'}
Hen Day + + {initialValues.hand_day && + initialValues.hand_day > 0 + ? formatNumber(initialValues.hand_day) + : '-'} + + + {initialValues.hand_day_std !== undefined && + initialValues.hand_day_std > 0 + ? `${initialValues.hand_day_std}%` + : '-'} +
Hen House + + {initialValues.hand_house && + initialValues.hand_house > 0 + ? formatNumber(initialValues.hand_house) + : '-'} + + + {initialValues.hand_house_std !== undefined && + initialValues.hand_house_std > 0 + ? `${initialValues.hand_house_std}%` + : '-'} +