mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-23 14:55:44 +00:00
refactor(FE-137): simplify stock display in MovementForm and RecordingForm, enhance input handling in SelectInput
This commit is contained in:
@@ -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 (
|
||||
<span className='text-sm text-gray-600 whitespace-nowrap'>
|
||||
(sisa: {remainingStock.toLocaleString('id-ID')})
|
||||
<span className='text-sm text-gray-500 whitespace-nowrap'>
|
||||
(Kosong)
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<span className='text-sm text-gray-600 whitespace-nowrap'>
|
||||
(tersedia: {availableStock.toLocaleString('id-ID')})
|
||||
<span className='text-sm text-gray-500 whitespace-nowrap'>
|
||||
(Tersedia {stockInfo.productCount} produk)
|
||||
</span>
|
||||
);
|
||||
},
|
||||
[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) => {
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<TextInput
|
||||
<NumberInput
|
||||
required
|
||||
type='number'
|
||||
name={`products.${idx}.product_qty`}
|
||||
value={product.product_qty ?? ''}
|
||||
onChange={formik.handleChange}
|
||||
onBlur={formik.handleBlur}
|
||||
endAdornment={getProductQtyAdornment(idx)}
|
||||
decimalScale={0}
|
||||
allowNegative={false}
|
||||
thousandSeparator=","
|
||||
decimalSeparator="."
|
||||
bottomLabel={getProductQtyBottomLabel(idx)}
|
||||
isError={
|
||||
isRepeaterInputError(
|
||||
'products',
|
||||
@@ -1240,13 +1295,16 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<TextInput
|
||||
<NumberInput
|
||||
required
|
||||
type='number'
|
||||
name={`deliveries.${idx}.products.0.product_qty`}
|
||||
value={delivery.products[0]?.product_qty ?? ''}
|
||||
onChange={formik.handleChange}
|
||||
onBlur={formik.handleBlur}
|
||||
decimalScale={0}
|
||||
allowNegative={false}
|
||||
thousandSeparator=","
|
||||
decimalSeparator="."
|
||||
isError={
|
||||
isDeliveryProductInputError(idx, 0, 'product_qty')
|
||||
.isError || Boolean(getDeliveryQtyError(idx, 0))
|
||||
@@ -1257,6 +1315,7 @@ const MovementForm = ({ type = 'add', initialValues }: MovementFormProps) => {
|
||||
getDeliveryQtyError(idx, 0) ||
|
||||
undefined
|
||||
}
|
||||
bottomLabel={getDeliveryProductQtyBottomLabel(idx, 0)}
|
||||
readOnly={type === 'detail'}
|
||||
className={{
|
||||
wrapper: 'w-full min-w-48',
|
||||
|
||||
@@ -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 (
|
||||
<span className='text-sm text-gray-600 whitespace-nowrap'>
|
||||
(sisa: {remainingStock.toLocaleString('id-ID')})
|
||||
(sisa: {remainingStock.toLocaleString('en-US')})
|
||||
</span>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<span className='text-sm text-gray-600 whitespace-nowrap'>
|
||||
(tersedia: {availableStock.toLocaleString('id-ID')})
|
||||
(tersedia: {availableStock.toLocaleString('en-US')})
|
||||
</span>
|
||||
);
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user