mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-25 07:45:47 +00:00
feat(FE-114): integrate product stock fetching and selection in RecordingForm
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useMemo, useState, useEffect } from 'react';
|
import { useMemo, useState, useEffect, useCallback } from 'react';
|
||||||
import { useFormik } from 'formik';
|
import { useFormik } from 'formik';
|
||||||
import useSWR from 'swr';
|
import useSWR from 'swr';
|
||||||
import { Icon } from '@iconify/react';
|
import { Icon } from '@iconify/react';
|
||||||
@@ -26,6 +26,7 @@ import {
|
|||||||
import { useRecordingFormHandlers } from './useRecordingFormHandlers';
|
import { useRecordingFormHandlers } from './useRecordingFormHandlers';
|
||||||
import { ProjectFlockApi } from '@/services/api/production';
|
import { ProjectFlockApi } from '@/services/api/production';
|
||||||
import { LocationApi } from '@/services/api/master-data';
|
import { LocationApi } from '@/services/api/master-data';
|
||||||
|
import { ProductWarehouseApi } from '@/services/api/inventory';
|
||||||
import { isResponseSuccess } from '@/lib/api-helper';
|
import { isResponseSuccess } from '@/lib/api-helper';
|
||||||
|
|
||||||
import Card from '@/components/Card';
|
import Card from '@/components/Card';
|
||||||
@@ -71,6 +72,22 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
|||||||
ProjectFlockApi.getAllFetcher
|
ProjectFlockApi.getAllFetcher
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Fetch Products with location filter (both PAKAN and OVK) - using selectedLocation for now
|
||||||
|
const stockProductsUrl = useMemo(() => {
|
||||||
|
if (!selectedLocation) return null;
|
||||||
|
const params = new URLSearchParams({
|
||||||
|
flags: 'PAKAN,OVK', // Fetch both flags in one request
|
||||||
|
search: '',
|
||||||
|
location_id: selectedLocation.value.toString(),
|
||||||
|
});
|
||||||
|
return `${ProductWarehouseApi.basePath}?${params.toString()}`;
|
||||||
|
}, [selectedLocation]);
|
||||||
|
|
||||||
|
const { data: stockProducts, isLoading: isLoadingStockProducts } = useSWR(
|
||||||
|
stockProductsUrl,
|
||||||
|
ProductWarehouseApi.getAllFetcher
|
||||||
|
);
|
||||||
|
|
||||||
// Extract location options from locations data
|
// Extract location options from locations data
|
||||||
const locationOptions = useMemo(() => {
|
const locationOptions = useMemo(() => {
|
||||||
if (!isResponseSuccess(locations)) return [];
|
if (!isResponseSuccess(locations)) return [];
|
||||||
@@ -99,6 +116,7 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
|||||||
return options;
|
return options;
|
||||||
}, [projectFlocks]);
|
}, [projectFlocks]);
|
||||||
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
deleteModal,
|
deleteModal,
|
||||||
recordingFormErrorMessage,
|
recordingFormErrorMessage,
|
||||||
@@ -179,6 +197,26 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Get location from selected project flock for stock filtering
|
||||||
|
const getProjectFlockLocation = useCallback((): OptionType | null => {
|
||||||
|
if (!formik.values.project_flock_kandang || !isResponseSuccess(projectFlocks)) {
|
||||||
|
return selectedLocation; // Fallback to manual location selection
|
||||||
|
}
|
||||||
|
|
||||||
|
const kandangId = formik.values.project_flock_kandang.value;
|
||||||
|
for (const projectFlock of projectFlocks.data) {
|
||||||
|
const kandang = projectFlock.kandangs.find(k => k.id === kandangId);
|
||||||
|
if (kandang && projectFlock.location) {
|
||||||
|
return {
|
||||||
|
value: projectFlock.location.id,
|
||||||
|
label: projectFlock.location.name
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return selectedLocation; // Fallback to manual location selection
|
||||||
|
}, [formik.values.project_flock_kandang, projectFlocks, selectedLocation]);
|
||||||
|
|
||||||
// EVENT HANDLERS - Select Inputs
|
// EVENT HANDLERS - Select Inputs
|
||||||
const locationChangeHandler = (val: OptionType | OptionType[] | null) => {
|
const locationChangeHandler = (val: OptionType | OptionType[] | null) => {
|
||||||
setSelectedLocation(val as OptionType);
|
setSelectedLocation(val as OptionType);
|
||||||
@@ -386,6 +424,40 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
|||||||
formik.setFieldValue('stocks', newStocks);
|
formik.setFieldValue('stocks', newStocks);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Memoized unified products for stock selection
|
||||||
|
const unifiedStockProducts = useMemo(() => {
|
||||||
|
if (!isResponseSuccess(stockProducts)) return [];
|
||||||
|
const options: OptionType[] = [];
|
||||||
|
|
||||||
|
stockProducts.data.forEach((product) => {
|
||||||
|
const warehouse = product.warehouse;
|
||||||
|
const stockText = product.quantity.toLocaleString('id-ID');
|
||||||
|
|
||||||
|
// Check if product has any of the flags
|
||||||
|
const hasPakanFlag = product.product.flags?.includes('PAKAN');
|
||||||
|
const hasOvkFlag = product.product.flags?.includes('OVK');
|
||||||
|
|
||||||
|
// Add products with warehouse and location grouping in label (similar to projectFlockKandangOptions pattern)
|
||||||
|
if (hasPakanFlag) {
|
||||||
|
options.push({
|
||||||
|
value: product.id,
|
||||||
|
label: `[PAKAN] ${product.product.name} - ${warehouse?.name || ''} (${stockText})`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasOvkFlag) {
|
||||||
|
options.push({
|
||||||
|
value: product.id,
|
||||||
|
label: `[OVK] ${product.product.name} - ${warehouse?.name || ''} (${stockText})`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return options;
|
||||||
|
}, [stockProducts, getProjectFlockLocation()]);
|
||||||
|
|
||||||
|
|
||||||
|
// Unified Stock remove handlers
|
||||||
const removeStock = (idx: number) => {
|
const removeStock = (idx: number) => {
|
||||||
const updatedStocks = formik.values.stocks?.filter((_, i) => i !== idx);
|
const updatedStocks = formik.values.stocks?.filter((_, i) => i !== idx);
|
||||||
formik.setFieldValue('stocks', updatedStocks);
|
formik.setFieldValue('stocks', updatedStocks);
|
||||||
@@ -888,13 +960,24 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
|||||||
</td>
|
</td>
|
||||||
)}
|
)}
|
||||||
<td>
|
<td>
|
||||||
<TextInput
|
<SelectInput
|
||||||
required
|
required
|
||||||
name={`stocks.${idx}.product_warehouse_id`}
|
value={
|
||||||
type='number'
|
unifiedStockProducts.find(
|
||||||
value={stock.product_warehouse_id?.toString() || ''}
|
(product) =>
|
||||||
onChange={formik.handleChange}
|
product.value === stock.product_warehouse_id
|
||||||
onBlur={formik.handleBlur}
|
) || null
|
||||||
|
}
|
||||||
|
onChange={(selectedOption) => {
|
||||||
|
const option = selectedOption as OptionType | null;
|
||||||
|
formik.setFieldValue(
|
||||||
|
`stocks.${idx}.product_warehouse_id`,
|
||||||
|
option?.value || 0
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
options={unifiedStockProducts}
|
||||||
|
placeholder='Pilih Produk'
|
||||||
|
isLoading={isLoadingStockProducts}
|
||||||
isError={
|
isError={
|
||||||
isRepeaterInputError(
|
isRepeaterInputError(
|
||||||
'stocks',
|
'stocks',
|
||||||
@@ -909,24 +992,23 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
|||||||
idx
|
idx
|
||||||
).errorMessage
|
).errorMessage
|
||||||
}
|
}
|
||||||
readOnly={type === 'detail'}
|
|
||||||
className={{
|
className={{
|
||||||
wrapper: 'w-full min-w-32',
|
wrapper: 'w-full min-w-48',
|
||||||
}}
|
}}
|
||||||
placeholder='Product Warehouse ID'
|
isSearchable
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<TextInput
|
<NumberInput
|
||||||
name={`stocks.${idx}.increase`}
|
name={`stocks.${idx}.increase`}
|
||||||
type='number'
|
value={stock.increase || 0}
|
||||||
value={
|
|
||||||
typeof stock.increase === 'number'
|
|
||||||
? stock.increase.toString()
|
|
||||||
: stock.increase
|
|
||||||
}
|
|
||||||
onChange={formik.handleChange}
|
onChange={formik.handleChange}
|
||||||
onBlur={formik.handleBlur}
|
onBlur={formik.handleBlur}
|
||||||
|
maskType='number'
|
||||||
|
decimals={0}
|
||||||
|
min={0}
|
||||||
|
thousandSeparator=','
|
||||||
|
decimalSeparator='.'
|
||||||
isError={
|
isError={
|
||||||
isRepeaterInputError('stocks', 'increase', idx)
|
isRepeaterInputError('stocks', 'increase', idx)
|
||||||
.isError
|
.isError
|
||||||
@@ -943,16 +1025,16 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
|||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<TextInput
|
<NumberInput
|
||||||
name={`stocks.${idx}.decrease`}
|
name={`stocks.${idx}.decrease`}
|
||||||
type='number'
|
value={stock.decrease || 0}
|
||||||
value={
|
|
||||||
typeof stock.decrease === 'number'
|
|
||||||
? stock.decrease.toString()
|
|
||||||
: stock.decrease
|
|
||||||
}
|
|
||||||
onChange={formik.handleChange}
|
onChange={formik.handleChange}
|
||||||
onBlur={formik.handleBlur}
|
onBlur={formik.handleBlur}
|
||||||
|
maskType='number'
|
||||||
|
decimals={0}
|
||||||
|
min={0}
|
||||||
|
thousandSeparator=','
|
||||||
|
decimalSeparator='.'
|
||||||
isError={
|
isError={
|
||||||
isRepeaterInputError('stocks', 'decrease', idx)
|
isRepeaterInputError('stocks', 'decrease', idx)
|
||||||
.isError
|
.isError
|
||||||
@@ -969,16 +1051,16 @@ const RecordingForm = ({ type = 'add', initialValues }: RecordingFormProps) => {
|
|||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<TextInput
|
<NumberInput
|
||||||
name={`stocks.${idx}.usage_amount`}
|
name={`stocks.${idx}.usage_amount`}
|
||||||
type='number'
|
value={stock.usage_amount || 0}
|
||||||
value={
|
|
||||||
typeof stock.usage_amount === 'number'
|
|
||||||
? stock.usage_amount.toString()
|
|
||||||
: stock.usage_amount
|
|
||||||
}
|
|
||||||
onChange={formik.handleChange}
|
onChange={formik.handleChange}
|
||||||
onBlur={formik.handleBlur}
|
onBlur={formik.handleBlur}
|
||||||
|
maskType='number'
|
||||||
|
decimals={0}
|
||||||
|
min={0}
|
||||||
|
thousandSeparator=','
|
||||||
|
decimalSeparator='.'
|
||||||
isError={
|
isError={
|
||||||
isRepeaterInputError('stocks', 'usage_amount', idx)
|
isRepeaterInputError('stocks', 'usage_amount', idx)
|
||||||
.isError
|
.isError
|
||||||
|
|||||||
Reference in New Issue
Block a user