bug fixed

This commit is contained in:
Malini 2025-11-23 13:48:34 +05:30
parent 821c913b59
commit 2e14579ec5
2 changed files with 200 additions and 146 deletions

View File

@ -97,16 +97,39 @@ const SearchableSelect = ({
}, [options, searchTerm]); }, [options, searchTerm]);
// Get selected option label // Get selected option label
const [internalDisplayValue, setInternalDisplayValue] = React.useState(displayValue || '');
const selectedOption = options.find(opt => opt.value === value); const selectedOption = options.find(opt => opt.value === value);
const displayValueToShow = displayValue !== undefined
? displayValue // Update internal display value when value, displayValue prop or options change
: (selectedOption ? selectedOption.label : ''); React.useEffect(() => {
if (displayValue !== undefined && displayValue !== '') {
setInternalDisplayValue(displayValue);
} else if (selectedOption) {
setInternalDisplayValue(selectedOption.label);
} else if (value) {
// If we have a value but no matching option, try to find it in the options
const matchedOption = options.find(opt => opt.value === value);
setInternalDisplayValue(matchedOption?.label || '');
} else {
setInternalDisplayValue('');
}
}, [value, displayValue, selectedOption, options]);
const displayValueToShow = internalDisplayValue;
// Handle option selection // Handle option selection
const handleSelect = (option) => { const handleSelect = (option) => {
if (onChange) { if (onChange) {
const event = { target: { value: option.value } }; const event = {
target: {
value: option.value,
name: name,
selectedDisplay: option.label,
option: option
}
};
onChange(event); onChange(event);
setInternalDisplayValue(option.label);
} }
setSearchTerm(''); setSearchTerm('');
setIsOpen(false); setIsOpen(false);
@ -1235,140 +1258,135 @@ const location = useLocation();
<div className="grid grid-cols-1 md:grid-cols-3 gap-4"> <div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<div data-field={`product_${idx}`}> <div data-field={`product_${idx}`}>
<label className="block text-sm font-medium text-gray-700 mb-1">Product (HS Code - Name) <span className="text-red-500">*</span></label> <label className="block text-sm font-medium text-gray-700 mb-1">Product (HS Code - Name) <span className="text-red-500">*</span></label>
<div> <SearchableSelect
<SearchableSelect placeholder={isLoadingProducts ? 'Loading products...' : 'Search product HS code...'}
placeholder={isLoadingProducts ? 'Loading products...' : 'Search product HS code...'} options={getAvailableProducts(p.id)}
options={getAvailableProducts(p.id)} value={p.productId?.toString() || p.product_id?.toString() || p.product?.toString() || ''}
value={p.productId?.toString() || p.product_id?.toString() || p.product?.toString() || ''} displayValue={p.name || (p.hs_code && p.productName ? `${p.hs_code} - ${p.productName}` : p.productName) || ''}
displayValue={p.name || (p.hs_code && p.productName ? `${p.hs_code} - ${p.productName}` : p.productName) || ''} onChange={async (e) => {
onChange={async (e) => { const selectedValue = e.target.value;
const selectedValue = e.target.value; // Clear error when user selects a product
// Clear error when user selects a product if (formErrors[`product_${idx}`]) {
if (formErrors[`product_${idx}`]) { const newErrors = { ...formErrors };
const newErrors = { ...formErrors }; delete newErrors[`product_${idx}`];
delete newErrors[`product_${idx}`]; setFormErrors(newErrors);
setFormErrors(newErrors); }
}
// Find the selected product from options
const selectedProduct = productOptions.find(opt => opt.value === selectedValue || opt.productId?.toString() === selectedValue);
// Get unit information from the selected product
const unitId = selectedProduct?.originalData?.['product.unit.id'] ||
selectedProduct?.unitId ||
selectedProduct?.originalData?.unit_id;
const unitName = selectedProduct?.originalData?.['product.unit.uom'] ||
selectedProduct?.originalData?.unit?.uom ||
selectedProduct?.unitName ||
selectedProduct?.unit || '';
// Get the display name for the product - check multiple possible locations
let displayName = '';
if (selectedProduct) {
// Get HS code from various possible locations
const hsCode = selectedProduct.hsCode ||
selectedProduct.originalData?.['product.hs_code'] ||
selectedProduct.originalData?.hs_code ||
'';
// Find the selected product from options // Get product name from various possible locations
const selectedProduct = productOptions.find(opt => opt.value === selectedValue || opt.productId?.toString() === selectedValue); const productName = selectedProduct.name ||
selectedProduct.originalData?.['product.name'] ||
selectedProduct.originalData?.product_name ||
selectedProduct.originalData?.name ||
'';
// Get unit information from the selected product // Combine HS Code and Product Name for display
const unitId = selectedProduct?.originalData?.['product.unit.id'] || displayName = hsCode && productName ?
selectedProduct?.unitId || `${hsCode} - ${productName}` :
selectedProduct?.originalData?.unit_id; productName || selectedProduct.label ||
(selectedProduct.originalData ?
const unitName = selectedProduct?.originalData?.['product.unit.uom'] || (selectedProduct.originalData['product.product_name'] ||
selectedProduct?.originalData?.unit?.uom || selectedProduct.originalData.product_name ||
selectedProduct?.unitName || selectedProduct.originalData.name) :
selectedProduct?.unit || ''; (selectedProduct.name || '')
);
// Get the display name for the product - check multiple possible locations }
let displayName = '';
if (selectedProduct) {
// Get HS code from various possible locations
const hsCode = selectedProduct.hsCode ||
selectedProduct.originalData?.['product.hs_code'] ||
selectedProduct.originalData?.hs_code ||
'';
// Get product name from various possible locations
const productName = selectedProduct.name ||
selectedProduct.originalData?.['product.name'] ||
selectedProduct.originalData?.product_name ||
selectedProduct.originalData?.name ||
'';
// Combine HS Code and Product Name for display
displayName = hsCode && productName ?
`${hsCode} - ${productName}` :
productName || selectedProduct.label ||
(selectedProduct.originalData ?
(selectedProduct.originalData['product.product_name'] ||
selectedProduct.originalData.product_name ||
selectedProduct.originalData.name) :
(selectedProduct.name || '')
);
}
// Create updated product with all necessary fields // Create updated product with all necessary fields
const updatedProduct = { const updatedProduct = {
...p, ...p,
product: selectedValue, // Store the ID as string product: selectedValue, // Store the ID as string
productId: selectedValue, // Also store as productId for consistency productId: selectedValue, // Also store as productId for consistency
product_id: selectedValue, // For backward compatibility product_id: selectedValue, // For backward compatibility
hs_code: selectedProduct?.hsCode || hs_code: selectedProduct?.hsCode ||
(selectedProduct?.originalData ? (selectedProduct?.originalData ?
(selectedProduct.originalData['product.hs_code'] || (selectedProduct.originalData['product.hs_code'] ||
selectedProduct.originalData.hs_code) : selectedProduct.originalData.hs_code) :
'') || p.hs_code, '') || p.hs_code,
name: displayName || p.name, // Use the formatted display name name: displayName || p.name, // Use the formatted display name
originalData: selectedProduct?.originalData || p.originalData, originalData: selectedProduct?.originalData || p.originalData,
// Set unit information // Set unit information
unit: unitId ? unitId.toString() : '', unit: unitId ? unitId.toString() : '',
unitName: unitName, unitName: unitName,
unit_id: unitId ? unitId.toString() : '', unit_id: unitId ? unitId.toString() : '',
// Clear any previous unit-related errors // Clear any previous unit-related errors
...(formErrors[`unit_${idx}`] && { unitError: undefined }) ...(formErrors[`unit_${idx}`] && { unitError: undefined })
}; };
// Update the products array with the updated product // Update the products array with the updated product
const updatedProducts = products.map((prod, i) => const updatedProducts = products.map((prod, i) =>
i === idx ? updatedProduct : prod i === idx ? updatedProduct : prod
); );
// Update the parent component's state // Update the parent component's state
onProductsChange(updatedProducts); onProductsChange(updatedProducts);
// After updating the product, fetch forecast data if we have a valid product and establishment ID // After updating the product, fetch forecast data if we have a valid product and establishment ID
if (selectedProduct?.value) { if (selectedProduct?.value) {
try { try {
// Get establishment ID from sessionStorage or localStorage // Get establishment ID from sessionStorage or localStorage
const establishmentId = sessionStorage.getItem('establishment_id') || const establishmentId = sessionStorage.getItem('establishment_id') ||
localStorage.getItem('establishmentId') || localStorage.getItem('establishmentId') ||
localStorage.getItem('establishment_id'); localStorage.getItem('establishment_id');
if (establishmentId && quarter && year) {
const selectedProductId = selectedProduct.originalData?.id || selectedProduct.value;
const forecastData = await fetchPreviousForecastData(selectedProductId, establishmentId, p.id);
if (establishmentId && quarter && year) { if (forecastData) {
const selectedProductId = selectedProduct.originalData?.id || selectedProduct.value; const data = forecastData.data || forecastData;
const forecastData = await fetchPreviousForecastData(selectedProductId, establishmentId, p.id); const updatedWithForecast = {
...updatedProduct,
octQuantity: data.previous_quantity_period_one || '',
novQuantity: data.previous_quantity_period_two || '',
decQuantity: data.previous_quantity_period_three || '',
octCost: data.previous_cost_period_one || '',
novCost: data.previous_cost_period_two || '',
decCost: data.previous_cost_period_three || '',
capacity: data.annual_installed_capacity || ''
};
if (forecastData) { const finalUpdatedProducts = updatedProducts.map((prod, i) =>
const data = forecastData.data || forecastData; i === idx ? updatedWithForecast : prod
const updatedWithForecast = { );
...updatedProduct,
octQuantity: data.previous_quantity_period_one || '', onProductsChange(finalUpdatedProducts);
novQuantity: data.previous_quantity_period_two || '',
decQuantity: data.previous_quantity_period_three || '',
octCost: data.previous_cost_period_one || '',
novCost: data.previous_cost_period_two || '',
decCost: data.previous_cost_period_three || '',
capacity: data.annual_installed_capacity || ''
};
const finalUpdatedProducts = updatedProducts.map((prod, i) =>
i === idx ? updatedWithForecast : prod
);
onProductsChange(finalUpdatedProducts);
}
} }
} catch (error) {
console.error('Error fetching forecast data:', error);
} }
} catch (error) {
console.error('Error fetching forecast data:', error);
} }
}} }
loading={isLoadingProducts} }}
disabled={isLoadingProducts} loading={isLoadingProducts}
error={!!formErrors[`product_${idx}`]} disabled={isLoadingProducts}
/> error={!!formErrors[`product_${idx}`]}
<div className="h-5"> />
{formErrors[`product_${idx}`] && ( <div className="h-5">
<p className="text-sm text-red-600">{formErrors[`product_${idx}`]}</p> {formErrors[`product_${idx}`] && (
)} <p className="text-sm text-red-600">{formErrors[`product_${idx}`]}</p>
</div> )}
</div> </div>
{productsError && productOptions.length === 0 && (
<p className="mt-1 text-xs text-red-600">{productsError}</p>
)}
</div> </div>
<div data-field={`unit_${idx}`}> <div data-field={`unit_${idx}`}>
<label className="block text-sm font-medium text-gray-700 mb-1">Unit <span className="text-red-500">*</span></label> <label className="block text-sm font-medium text-gray-700 mb-1">Unit <span className="text-red-500">*</span></label>
@ -1379,17 +1397,24 @@ const location = useLocation();
displayValue={p.unitName || ''} displayValue={p.unitName || ''}
onChange={(e) => { onChange={(e) => {
const unitId = e.target.value; const unitId = e.target.value;
const selectedUnit = unitOptions.find(u => u.value === unitId); const displayName = e.target.selectedDisplay ||
const originalUnit = selectedUnit?.originalData || {}; unitOptions.find(u => u.value === unitId)?.label ||
const displayName = originalUnit.uom_short_name && originalUnit.uom '';
? `${originalUnit.uom_short_name.toUpperCase()} - ${originalUnit.uom}`
: selectedUnit?.label || ''; // Update both unit and unitName in a single update
updateProductField(idx, 'unit', unitId); const updatedProduct = {
updateProductField(idx, 'unitName', displayName); ...p,
unit: unitId,
unitName: displayName
};
const updatedProducts = [...products];
updatedProducts[idx] = updatedProduct;
onProductsChange(updatedProducts);
}} }}
loading={isLoadingUnits} loading={isLoadingUnits}
error={!!formErrors[`unit_${idx}`]} error={!!formErrors[`unit_${idx}`]}
// disabled={!!p.unitName} // Disable if unit is auto-filled from product name={`unit_${idx}`}
/> />
<div className="h-5"> <div className="h-5">
{formErrors[`unit_${idx}`] && ( {formErrors[`unit_${idx}`] && (

View File

@ -28,7 +28,7 @@ const createEmptyCodeForm = () => ({
}); });
// Toast notification component // Toast notification component
const Toast = ({ message, type = 'success', onClose }) => { const Toast = ({ message, type = 'success', onClose, position = 'page' }) => {
React.useEffect(() => { React.useEffect(() => {
const timer = setTimeout(() => { const timer = setTimeout(() => {
onClose(); onClose();
@ -49,6 +49,18 @@ const Toast = ({ message, type = 'success', onClose }) => {
} }
}; };
// For modal toasts, we'll use relative positioning
if (position === 'modal') {
return (
<div className="w-full mb-4">
<div className={`px-4 py-2 rounded-md text-sm ${getToastStyles()}`}>
{message}
</div>
</div>
);
}
// Default page-level toast
return ( return (
<div className="fixed top-0 left-0 w-full flex justify-center z-50 mt-4"> <div className="fixed top-0 left-0 w-full flex justify-center z-50 mt-4">
<div className={`px-6 py-3 rounded-md shadow-lg font-medium ${getToastStyles()}`}> <div className={`px-6 py-3 rounded-md shadow-lg font-medium ${getToastStyles()}`}>
@ -354,7 +366,12 @@ const IsicHsCodes = () => {
const [modalMode, setModalMode] = React.useState(null); const [modalMode, setModalMode] = React.useState(null);
const [form, setForm] = React.useState(createEmptyCodeForm()); const [form, setForm] = React.useState(createEmptyCodeForm());
const [unitOptions, setUnitOptions] = React.useState([]); const [unitOptions, setUnitOptions] = React.useState([]);
const [toast, setToast] = React.useState({ show: false, message: '', type: 'success' }); const [toast, setToast] = React.useState({
show: false,
message: '',
type: 'success',
position: 'page' // 'page' or 'modal'
});
const [showImportModal, setShowImportModal] = React.useState(false); const [showImportModal, setShowImportModal] = React.useState(false);
const [isRefreshing, setIsRefreshing] = React.useState(false); const [isRefreshing, setIsRefreshing] = React.useState(false);
@ -767,8 +784,8 @@ const IsicHsCodes = () => {
setForm(createEmptyCodeForm()); setForm(createEmptyCodeForm());
}; };
const showToast = (message, type = 'success') => { const showToast = (message, type = 'success', position = 'page') => {
setToast({ show: true, message, type }); setToast({ show: true, message, type, position });
setTimeout(() => { setTimeout(() => {
setToast(prev => ({ ...prev, show: false })); setToast(prev => ({ ...prev, show: false }));
}, 3000); }, 3000);
@ -833,7 +850,7 @@ const IsicHsCodes = () => {
if (!form.code || !form.product) { if (!form.code || !form.product) {
const errorMsg = 'Please fill in all required fields'; const errorMsg = 'Please fill in all required fields';
console.error(errorMsg); console.error(errorMsg);
setError(errorMsg); showToast(errorMsg, 'error');
return; return;
} }
@ -875,8 +892,9 @@ const IsicHsCodes = () => {
const productId = form.id || (editingRow !== null ? rowsData[editingRow]?.id : null); const productId = form.id || (editingRow !== null ? rowsData[editingRow]?.id : null);
if (!productId) { if (!productId) {
console.error('Product ID not found for editing'); const errorMsg = 'Cannot update product: Product ID not found';
setError('Cannot update product: Product ID not found'); console.error(errorMsg);
showToast(errorMsg, 'error');
return; return;
} }
@ -934,7 +952,7 @@ const IsicHsCodes = () => {
if (error.response?.data?.message) { if (error.response?.data?.message) {
errorMessage = error.response.data.message; errorMessage = error.response.data.message;
} }
setError(errorMessage); showToast(errorMessage, 'error');
} }
} }
@ -972,7 +990,7 @@ const IsicHsCodes = () => {
if (createError.response?.data?.message) { if (createError.response?.data?.message) {
errorMessage = createError.response.data.message; errorMessage = createError.response.data.message;
} }
setError(errorMessage); showToast(errorMessage, 'error');
} }
} catch (error) { } catch (error) {
console.error('Error in handleSaveForm:', error); console.error('Error in handleSaveForm:', error);
@ -1075,10 +1093,12 @@ const IsicHsCodes = () => {
return ( return (
<div className="relative"> <div className="relative">
{toast.show && ( {/* Page-level toasts */}
{toast.show && toast.position === 'page' && (
<Toast <Toast
message={toast.message} message={toast.message}
type={toast.type} type={toast.type}
position="page"
onClose={() => setToast(prev => ({ ...prev, show: false }))} onClose={() => setToast(prev => ({ ...prev, show: false }))}
/> />
)} )}
@ -1373,6 +1393,15 @@ const IsicHsCodes = () => {
{/* Form */} {/* Form */}
<div className="p-6 space-y-4"> <div className="p-6 space-y-4">
{/* Modal-level toast */}
{toast.show && toast.position === 'modal' && (
<Toast
message={toast.message}
type={toast.type}
position="modal"
onClose={() => setToast(prev => ({ ...prev, show: false }))}
/>
)}
<div className="grid grid-cols-2 gap-4"> <div className="grid grid-cols-2 gap-4">
<div> <div>
<label className="block text-sm font-medium text-[#374151] mb-1"> <label className="block text-sm font-medium text-[#374151] mb-1">