diff --git a/src/components/input/SelectInput.tsx b/src/components/input/SelectInput.tsx index 28eb9786..912d4622 100644 --- a/src/components/input/SelectInput.tsx +++ b/src/components/input/SelectInput.tsx @@ -48,6 +48,7 @@ interface SelectInputBaseProps { openMenu?: boolean; delay?: number; onInputChange?: (search: string) => void; + startAdornment?: ReactNode; } interface SelectInputProps extends SelectInputBaseProps { @@ -82,6 +83,7 @@ const SelectInput = (props: SelectInputProps) => { delay = 300, createables = false, onInputChange, + startAdornment, } = props; const [internalInputValue, setInternalInputValue] = useState(''); @@ -205,6 +207,28 @@ const SelectInput = (props: SelectInputProps) => { components={{ ...components, ...(optionComponent ? { Option: optionComponent } : {}), + ...(startAdornment ? { + Control: ({ children, innerRef, innerProps, menuIsOpen, isFocused, isDisabled }) => ( +
+
+ {startAdornment} + {children} +
+
+ ), + } : {}), }} menuPortalTarget={ typeof document !== 'undefined' ? document.body : undefined diff --git a/src/components/pages/inventory/movement/form/MovementForm.tsx b/src/components/pages/inventory/movement/form/MovementForm.tsx index 9a2b3b97..deb304df 100644 --- a/src/components/pages/inventory/movement/form/MovementForm.tsx +++ b/src/components/pages/inventory/movement/form/MovementForm.tsx @@ -111,14 +111,10 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => { const warehouseOptions = isResponseSuccess(warehouses) ? warehouses?.data.map((w) => { - const stockInfo = warehouseStockMap.get(w.id); - const stockLabel = stockInfo - ? ` (Stock: ${stockInfo.totalQty.toLocaleString('id-ID')} items, ${stockInfo.productCount} produk)` - : ' (Kosong)'; - - return { - value: w.id, - label: `${w.name}${stockLabel}`, + warehouseStockMap.get(w.id); + return { + value: w.id, + label: w.name, area: w.area?.name, location: 'type' in w && (w.type === 'LOKASI' || w.type === 'KANDANG') @@ -223,7 +219,7 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => { const productWarehouseOptions = isResponseSuccess(productWarehouses) ? productWarehouses?.data.map((pw) => ({ value: pw.product.id, - label: `${pw.product.name} - ${pw.warehouse.name} (Stock: ${pw.quantity.toLocaleString('id-ID')})`, + label: pw.product.name, product_id: pw.product.id, warehouse_id: pw.warehouse.id, warehouse_name: pw.warehouse.name, @@ -464,31 +460,77 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => { [productWarehouseOptions, type] ); - const getProductQtyAdornment = useCallback( + const getProductQtyBottomLabel = useCallback( (productIdx: number) => { - if (type === 'detail') return null; + if (type === 'detail') return undefined; const product = formik.values.products?.[productIdx]; - if (!product || !product.product_id) return null; + if (!product || !product.product_id) return undefined; const availableStock = getAvailableStock(product.product_id); const requestedQty = Number(product.product_qty) || 0; const remainingStock = availableStock - requestedQty; if (requestedQty > 0) { + return `Sisa: ${remainingStock.toLocaleString('en-US')}`; + } + + return `Tersedia: ${availableStock.toLocaleString('en-US')}`; + }, + [formik.values.products, getAvailableStock, type] + ); + + const getDeliveryProductQtyBottomLabel = useCallback( + (deliveryIdx: number, productIdx: number) => { + if (type === 'detail') return undefined; + const delivery = formik.values.deliveries?.[deliveryIdx]; + if (!delivery) return undefined; + + const deliveryProduct = delivery.products[productIdx]; + if (!deliveryProduct || !deliveryProduct.product_id) return undefined; + + const relatedProduct = formik.values.products?.find( + (p) => p.product_id === deliveryProduct.product_id + ); + if (!relatedProduct) return undefined; + + const totalQtyUsed = + formik.values.deliveries?.reduce((total, d, dIdx) => { + const productQty = d.products.reduce((sum, p, pIdx) => { + if ( + p.product_id === deliveryProduct.product_id && + !(dIdx === deliveryIdx && pIdx === productIdx) + ) { + return sum + (Number(p.product_qty) || 0); + } + return sum; + }, 0); + return total + productQty; + }, 0) || 0; + + const availableQty = Number(relatedProduct.product_qty) - totalQtyUsed; + return `Tersedia: ${availableQty.toLocaleString('en-US')}`; + }, + [formik.values.deliveries, formik.values.products, type] + ); + + const getWarehouseStockAdornment = useCallback( + (warehouseId: number) => { + const stockInfo = warehouseStockMap.get(warehouseId); + if (!stockInfo) { return ( - - (sisa: {remainingStock.toLocaleString('id-ID')}) + + (Kosong) ); } return ( - - (tersedia: {availableStock.toLocaleString('id-ID')}) + + (Tersedia {stockInfo.productCount} produk) ); }, - [formik.values.products, getAvailableStock, type] + [warehouseStockMap] ); const getProductQtyError = useCallback( @@ -501,7 +543,7 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => { const requestedQty = Number(product.product_qty) || 0; if (requestedQty > availableStock) { - return `Qty melebihi stok tersedia! Maksimal: ${availableStock.toLocaleString('id-ID')}`; + return `Qty melebihi stok tersedia! Maksimal: ${availableStock.toLocaleString('en-US')}`; } return null; @@ -746,6 +788,11 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => { errorMessage={formik.errors.source_warehouse_id as string} isDisabled={type === 'detail'} isClearable + startAdornment={ + formik.values.source_warehouse_id + ? getWarehouseStockAdornment(formik.values.source_warehouse_id) + : undefined + } /> {/* Area and Location Info */} @@ -808,6 +855,11 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => { } isDisabled={type === 'detail'} isClearable + startAdornment={ + formik.values.destination_warehouse_id + ? getWarehouseStockAdornment(formik.values.destination_warehouse_id) + : undefined + } /> {/* Area and Location Info */} @@ -981,14 +1033,17 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => { /> - { /> - { getDeliveryQtyError(idx, 0) || undefined } + bottomLabel={getDeliveryProductQtyBottomLabel(idx, 0)} readOnly={type === 'detail'} className={{ wrapper: 'w-full min-w-48', diff --git a/src/components/pages/production/recording/form/RecordingForm.tsx b/src/components/pages/production/recording/form/RecordingForm.tsx index 17af9515..031303e2 100644 --- a/src/components/pages/production/recording/form/RecordingForm.tsx +++ b/src/components/pages/production/recording/form/RecordingForm.tsx @@ -123,8 +123,8 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { projectFlock.kandangs.forEach((kandang) => { const isAlreadyRecorded = recordedProjectFlockIds.has(projectFlock.id); const label = isAlreadyRecorded - ? `${projectFlock.flock.name} - ${projectFlock.area.name} - ${kandang.name} (Sudah Direcord)` - : `${projectFlock.flock.name} - ${projectFlock.area.name} - ${kandang.name}`; + ? `${projectFlock.flock.name} - ${kandang.name} (Sudah Direcord)` + : `${projectFlock.flock.name} - ${kandang.name}`; options.push({ value: projectFlock.id, @@ -140,22 +140,21 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { if (isResponseSuccess(stockProducts)) { stockProducts.data.forEach((product) => { const warehouse = product.warehouse; - const stockText = product.quantity.toLocaleString('id-ID'); - + product.quantity.toLocaleString('en-US'); const hasPakanFlag = product.product.flags?.includes('PAKAN'); const hasOvkFlag = product.product.flags?.includes('OVK'); if (hasPakanFlag) { options.push({ value: product.id, - label: `[PAKAN] ${product.product.name} - ${warehouse?.name || ''} (${stockText})` + label: `[PAKAN] ${product.product.name} - ${warehouse?.name || ''}` }); } if (hasOvkFlag) { options.push({ value: product.id, - label: `[OVK] ${product.product.name} - ${warehouse?.name || ''} (${stockText})` + label: `[OVK] ${product.product.name} - ${warehouse?.name || ''}` }); } }); @@ -293,7 +292,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { const availableStock = getAvailableStock(stock.product_warehouse_id); const requestedUsage = Number(stock.usage_amount) || 0; if (requestedUsage > availableStock) { - return `Jumlah pakai melebihi stok tersedia! Maksimal: ${availableStock.toLocaleString('id-ID')}`; + return `Jumlah pakai melebihi stok tersedia! Maksimal: ${availableStock.toLocaleString('en-US')}`; } return null; }, @@ -311,13 +310,13 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => { if (requestedUsage > 0) { return ( - (sisa: {remainingStock.toLocaleString('id-ID')}) + (sisa: {remainingStock.toLocaleString('en-US')}) ); } return ( - (tersedia: {availableStock.toLocaleString('id-ID')}) + (tersedia: {availableStock.toLocaleString('en-US')}) ); },