From f31eb8db59f0b47b2dc7c358a3cb46fc50246f0b Mon Sep 17 00:00:00 2001 From: rstubryan Date: Tue, 3 Feb 2026 14:20:32 +0700 Subject: [PATCH] refactor(FE): Show pending stock usage and depletions in detail --- .../recording/form/RecordingForm.tsx | 794 ++++++++++-------- 1 file changed, 421 insertions(+), 373 deletions(-) diff --git a/src/components/pages/production/recording/form/RecordingForm.tsx b/src/components/pages/production/recording/form/RecordingForm.tsx index 94f078c1..b8b6b1fc 100644 --- a/src/components/pages/production/recording/form/RecordingForm.tsx +++ b/src/components/pages/production/recording/form/RecordingForm.tsx @@ -48,6 +48,7 @@ import { UpdateLayingRecordingPayload, Recording, NextDayRecording, + RecordingStock, } from '@/types/api/production/recording'; import { type BaseApiResponse } from '@/types/api/api-general'; import { @@ -1103,14 +1104,51 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { [formik.values.stocks, type] ); + const getStockPendingInfo = useCallback( + (productWarehouseId: number) => { + if ((type === 'edit' || type === 'detail') && initialValues?.stocks) { + const existingStock = initialValues.stocks.find( + (s) => s.product_warehouse_id === productWarehouseId + ) as RecordingStock | undefined; + if (existingStock) { + return { + usageAmount: existingStock.usage_amount ?? 0, + pendingQty: existingStock.pending_qty ?? 0, + }; + } + } + return { + usageAmount: 0, + pendingQty: 0, + }; + }, + [initialValues, type] + ); + const getStockUsageAdornment = useCallback( (stockIdx: number) => { - if ((type as 'add' | 'edit' | 'detail') === 'detail') return null; const stock = formik.values.stocks?.[stockIdx]; if (!stock || !stock.product_warehouse_id) return null; + + const isDetail = (type as 'add' | 'edit' | 'detail') === 'detail'; const availableStock = getAvailableStock(stock.product_warehouse_id); const requestedUsage = Number(stock.qty) || 0; const remainingStock = availableStock - requestedUsage; + const { pendingQty } = getStockPendingInfo(stock.product_warehouse_id); + + if (isDetail) { + if (pendingQty > 0) { + return ( + + (tersedia: {formatNumber(requestedUsage)} | pending:{' '} + {formatNumber(pendingQty)} | + pakai: {formatNumber(requestedUsage + pendingQty)}) + + ); + } + return null; + } + if (requestedUsage > 0) { return ( @@ -1127,7 +1165,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { ); }, - [formik.values.stocks, getAvailableStock, type] + [formik.values.stocks, getAvailableStock, getStockPendingInfo, type] ); const getProjectFlockBadgeAdornment = useCallback(() => { @@ -2550,8 +2588,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { : null } /> - {(type as 'add' | 'edit' | 'detail') !== 'detail' && - getStockUsageAdornment(idx)} + {getStockUsageAdornment(idx)} {(type as 'add' | 'edit' | 'detail') !== 'detail' && ( @@ -2604,209 +2641,10 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { {/* Depletions Table */} - -
- - - - {(type as 'add' | 'edit' | 'detail') !== 'detail' && ( - - )} - - - {(type as 'add' | 'edit' | 'detail') !== 'detail' && ( - - )} - - - - {formik.values.depletions?.map((depletion, idx) => ( - - {(type as 'add' | 'edit' | 'detail') !== 'detail' && ( - - )} - - - {(type as 'add' | 'edit' | 'detail') !== 'detail' && ( - - )} - - ))} - -
- 0 - } - onChange={( - e: React.ChangeEvent - ) => { - if (e.target.checked) { - setSelectedDepletions( - formik.values.depletions?.map( - (_, idx) => idx - ) ?? [] - ); - } else { - setSelectedDepletions([]); - } - }} - classNames={{ - wrapper: 'flex justify-center', - checkbox: 'checkbox checkbox-sm', - }} - /> - KondisiJumlahAction
- - ) => { - if (e.target.checked) { - setSelectedDepletions([ - ...selectedDepletions, - idx, - ]); - } else { - setSelectedDepletions( - selectedDepletions.filter((i) => i !== idx) - ); - } - }} - classNames={{ - wrapper: 'flex justify-center', - checkbox: 'checkbox checkbox-sm', - }} - /> - - - product.value === depletion.product_warehouse_id - ) || null - } - onChange={(selectedOption) => { - const option = selectedOption as OptionType | null; - formik.setFieldValue( - `depletions.${idx}.product_warehouse_id`, - option?.value || 0 - ); - }} - options={getAvailableDepletionProductOptions(idx)} - placeholder='Pilih Kondisi' - isLoading={isLoadingDepletionProducts} - onMenuScrollToBottom={loadMoreDepletionProducts} - isError={ - isRepeaterInputError( - 'depletions', - 'product_warehouse_id', - idx - ).isError - } - errorMessage={ - isRepeaterInputError( - 'depletions', - 'product_warehouse_id', - idx - ).errorMessage - } - isDisabled={type === 'detail'} - className={{ - wrapper: 'w-full min-w-48', - }} - isSearchable - isClearable={type !== 'detail'} - /> - - - -
- -
-
-
- {(type as 'add' | 'edit' | 'detail') !== 'detail' && ( -
- {selectedDepletions.length > 0 && ( - - )} - -
- )} -
- - {/* Eggs Table - Only for LAYING Category */} - {isLayingCategory && ( + {((type as 'add' | 'edit' | 'detail') !== 'detail' || + (formik.values.depletions?.length ?? 0) > 0) && ( { {(type as 'add' | 'edit' | 'detail') !== 'detail' && ( 0 + formik.values.depletions?.length === + selectedDepletions.length && + formik.values.depletions?.length > 0 } onChange={( e: React.ChangeEvent ) => { if (e.target.checked) { - setSelectedEggs( - ( - formik.values as RecordingLayingFormValues - ).eggs?.map((_, idx) => idx) ?? [] + setSelectedDepletions( + formik.values.depletions?.map( + (_, idx) => idx + ) ?? [] ); } else { - setSelectedEggs([]); + setSelectedDepletions([]); } }} classNames={{ @@ -2846,201 +2683,412 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { /> )} - Kondisi Telur + Kondisi Jumlah - - Total Berat (Kilogram) - - - - {(type as 'add' | 'edit' | 'detail') !== 'detail' && ( Action )} - {(formik.values as RecordingLayingFormValues).eggs?.map( - (egg, idx) => ( - - {(type as 'add' | 'edit' | 'detail') !== 'detail' && ( - - - ) => { - if (e.target.checked) { - setSelectedEggs([...selectedEggs, idx]); - } else { - setSelectedEggs( - selectedEggs.filter((i) => i !== idx) - ); - } - }} - classNames={{ - wrapper: 'flex justify-center', - checkbox: 'checkbox checkbox-sm', - }} - /> - - )} - - - product.value === egg.product_warehouse_id - ) || null - } - onChange={(selectedOption) => { - const option = - selectedOption as OptionType | null; - formik.setFieldValue( - `eggs.${idx}.product_warehouse_id`, - option?.value || 0 - ); + {formik.values.depletions?.map((depletion, idx) => ( + + {(type as 'add' | 'edit' | 'detail') !== 'detail' && ( + + + ) => { + if (e.target.checked) { + setSelectedDepletions([ + ...selectedDepletions, + idx, + ]); + } else { + setSelectedDepletions( + selectedDepletions.filter((i) => i !== idx) + ); + } }} - options={getAvailableEggProductOptions(idx)} - placeholder='Pilih Kondisi Telur' - isLoading={isLoadingEggProducts} - onMenuScrollToBottom={loadMoreEggProducts} - isError={ - isRepeaterInputError( - 'eggs', - 'product_warehouse_id', - idx - ).isError - } - errorMessage={ - isRepeaterInputError( - 'eggs', - 'product_warehouse_id', - idx - ).errorMessage - } - isDisabled={type === 'detail'} - className={{ - wrapper: 'w-full min-w-48', + classNames={{ + wrapper: 'flex justify-center', + checkbox: 'checkbox checkbox-sm', }} - isSearchable - isClearable={type !== 'detail'} /> + )} + + + product.value === + depletion.product_warehouse_id + ) || null + } + onChange={(selectedOption) => { + const option = + selectedOption as OptionType | null; + formik.setFieldValue( + `depletions.${idx}.product_warehouse_id`, + option?.value || 0 + ); + }} + options={getAvailableDepletionProductOptions(idx)} + placeholder='Pilih Kondisi' + isLoading={isLoadingDepletionProducts} + onMenuScrollToBottom={loadMoreDepletionProducts} + isError={ + isRepeaterInputError( + 'depletions', + 'product_warehouse_id', + idx + ).isError + } + errorMessage={ + isRepeaterInputError( + 'depletions', + 'product_warehouse_id', + idx + ).errorMessage + } + isDisabled={type === 'detail'} + className={{ + wrapper: 'w-full min-w-48', + }} + isSearchable + isClearable={type !== 'detail'} + /> + + + + + {(type as 'add' | 'edit' | 'detail') !== 'detail' && ( - +
+ +
- - - - {(type as 'add' | 'edit' | 'detail') !== 'detail' && ( - -
- -
- - )} - - ) - )} + )} + + ))} {(type as 'add' | 'edit' | 'detail') !== 'detail' && (
- {selectedEggs.length > 0 && ( + {selectedDepletions.length > 0 && ( )}
)}
)} + {/* Eggs Table - Only for LAYING Category */} + {isLayingCategory && + ((type as 'add' | 'edit' | 'detail') !== 'detail' || + ((formik.values as RecordingLayingFormValues).eggs?.length ?? 0) > + 0) && ( + +
+ + + + {(type as 'add' | 'edit' | 'detail') !== 'detail' && ( + + )} + + + + {(type as 'add' | 'edit' | 'detail') !== 'detail' && ( + + )} + + + + {(formik.values as RecordingLayingFormValues).eggs?.map( + (egg, idx) => ( + + {(type as 'add' | 'edit' | 'detail') !== + 'detail' && ( + + )} + + + + {(type as 'add' | 'edit' | 'detail') !== + 'detail' && ( + + )} + + ) + )} + +
+ 0 + } + onChange={( + e: React.ChangeEvent + ) => { + if (e.target.checked) { + setSelectedEggs( + ( + formik.values as RecordingLayingFormValues + ).eggs?.map((_, idx) => idx) ?? [] + ); + } else { + setSelectedEggs([]); + } + }} + classNames={{ + wrapper: 'flex justify-center', + checkbox: 'checkbox checkbox-sm', + }} + /> + Kondisi TelurJumlah + Total Berat (Kilogram) + + + + Action
+ + ) => { + if (e.target.checked) { + setSelectedEggs([...selectedEggs, idx]); + } else { + setSelectedEggs( + selectedEggs.filter((i) => i !== idx) + ); + } + }} + classNames={{ + wrapper: 'flex justify-center', + checkbox: 'checkbox checkbox-sm', + }} + /> + + + product.value === egg.product_warehouse_id + ) || null + } + onChange={(selectedOption) => { + const option = + selectedOption as OptionType | null; + formik.setFieldValue( + `eggs.${idx}.product_warehouse_id`, + option?.value || 0 + ); + }} + options={getAvailableEggProductOptions(idx)} + placeholder='Pilih Kondisi Telur' + isLoading={isLoadingEggProducts} + onMenuScrollToBottom={loadMoreEggProducts} + isError={ + isRepeaterInputError( + 'eggs', + 'product_warehouse_id', + idx + ).isError + } + errorMessage={ + isRepeaterInputError( + 'eggs', + 'product_warehouse_id', + idx + ).errorMessage + } + isDisabled={type === 'detail'} + className={{ + wrapper: 'w-full min-w-48', + }} + isSearchable + isClearable={type !== 'detail'} + /> + + + + + +
+ +
+
+
+ {(type as 'add' | 'edit' | 'detail') !== 'detail' && ( +
+ {selectedEggs.length > 0 && ( + + )} + +
+ )} +
+ )} +
{recordingFormErrorMessage && (