From 11abb7d77c6fe970441b3d4ac5afe06d9fa81ef9 Mon Sep 17 00:00:00 2001 From: Malini Date: Mon, 17 Nov 2025 16:54:25 +0530 Subject: [PATCH] validation added --- .../survey/ProductData/ProductData.jsx | 288 ++++++++++-------- 1 file changed, 161 insertions(+), 127 deletions(-) diff --git a/ipi-survey-platform/src/components/survey/ProductData/ProductData.jsx b/ipi-survey-platform/src/components/survey/ProductData/ProductData.jsx index 0eb5298..a4274bc 100644 --- a/ipi-survey-platform/src/components/survey/ProductData/ProductData.jsx +++ b/ipi-survey-platform/src/components/survey/ProductData/ProductData.jsx @@ -20,8 +20,10 @@ const uae_currency = '/assets/images/UAE_Dirham_Symbol.svg'; const Input = ({ placeholder = '', type = 'text', value = '', onChange = () => {}, required = false, error = false }) => { const [isFocused, setIsFocused] = React.useState(false); - let borderColor = 'border-[#CBA344]'; - let bgColor = 'bg-white'; + + // Simple red background when there's an error + let borderColor = error ? 'border-red-500' : 'border-[#CBA344]'; + let bgColor = error ? 'bg-red-50' : 'bg-white'; if (error) { borderColor = 'border-red-500'; @@ -46,8 +48,6 @@ const Input = ({ placeholder = '', type = 'text', value = '', onChange = () => { ); }; - - const SearchableSelect = ({ placeholder = '', options = [], @@ -112,9 +112,10 @@ const SearchableSelect = ({ setIsOpen(false); }; - let borderColor = 'border-[#CBA344]'; - let bgColor = 'bg-white'; - + // Simple red background when there's an error + let borderColor = error ? 'border-red-500' : 'border-[#CBA344]'; + let bgColor = error ? 'bg-red-50' : 'bg-white'; + if (error) { borderColor = 'border-red-500'; bgColor = 'bg-red-50'; @@ -192,8 +193,9 @@ const Select = ({ }) => { const [isFocused, setIsFocused] = React.useState(false); - let borderColor = 'border-[#CBA344]'; - let bgColor = 'bg-white'; + // Simple red background when there's an error + let borderColor = error ? 'border-red-500' : 'border-[#CBA344]'; + let bgColor = error ? 'bg-red-50' : 'bg-white'; if (error) { borderColor = 'border-red-500'; @@ -571,53 +573,82 @@ const location = useLocation(); ); }; - const validateProduct = (product, index) => { - const errors = {}; - let hasError = false; + // Simple validation function + const validateField = (fieldName, value, productIndex) => { + const errors = { ...formErrors }; + const fieldKey = `${fieldName}_${productIndex}`; - // Check for duplicate products - const duplicateProductIndex = products.findIndex( - (p, i) => p.product === product.product && i !== index - ); - - if (duplicateProductIndex !== -1) { - errors[`product-${product.id}`] = 'This product has already been selected'; - hasError = true; + if (!value || value.toString().trim() === '') { + errors[fieldKey] = 'This field is required'; + } else { + delete errors[fieldKey]; } - // Validate product selection - if (!product.product) { - errors[`product-${product.id}`] = 'Product is required'; - hasError = true; - } - - // Validate unit selection - if (!product.unit) { - errors[`unit-${product.id}`] = 'Unit is required'; - hasError = true; - } - - // Validate capacity - if (!product.capacity) { - errors[`capacity-${product.id}`] = 'Capacity is required'; - hasError = true; - } - - return { errors, hasError }; + setFormErrors(errors); + return Object.keys(errors).length === 0; }; + // Simple validation for all required fields const validateForm = () => { - let isValid = true; const errors = {}; + let isValid = true; products.forEach((product, index) => { - const { errors: productErrors, hasError } = validateProduct(product, index); - if (hasError) { - Object.assign(errors, productErrors); + // Validate product selection + if (!product.product) { + errors[`product_${index}`] = 'Product is required'; isValid = false; } + + // Validate unit selection + if (!product.unit) { + errors[`unit_${index}`] = 'Unit is required'; + isValid = false; + } + + // Validate capacity + if (!product.capacity) { + errors[`capacity_${index}`] = 'Capacity is required'; + isValid = false; + } + + // Validate current quarter quantities + const currentQuantityFields = ['janQuantity', 'febQuantity', 'marQuantity']; + currentQuantityFields.forEach(field => { + if (!product[field] && product[field] !== 0) { + errors[`${field}_${index}`] = 'Required'; + isValid = false; + } + }); + + // Validate current quarter costs + const currentCostFields = ['janCost', 'febCost', 'marCost']; + currentCostFields.forEach(field => { + if (!product[field] && product[field] !== 0) { + errors[`${field}_${index}`] = 'Required'; + isValid = false; + } + }); + + // Validate forecast quantities + const forecastQuantityFields = ['aprQuantity', 'mayQuantity', 'junQuantity']; + forecastQuantityFields.forEach(field => { + if (!product[field] && product[field] !== 0) { + errors[`${field}_${index}`] = 'Required'; + isValid = false; + } + }); + + // Validate forecast costs + const forecastCostFields = ['aprCost', 'mayCost', 'junCost']; + forecastCostFields.forEach(field => { + if (!product[field] && product[field] !== 0) { + errors[`${field}_${index}`] = 'Required'; + isValid = false; + } + }); }); - + setFormErrors(errors); return isValid; }; @@ -625,6 +656,15 @@ const location = useLocation(); const handleNext = () => { if (validateForm()) { onNext(); + } else { + // Scroll to first error + const firstErrorKey = Object.keys(formErrors)[0]; + if (firstErrorKey) { + const element = document.querySelector(`[data-field="${firstErrorKey}"]`); + if (element) { + element.scrollIntoView({ behavior: 'smooth', block: 'center' }); + } + } } }; @@ -825,13 +865,6 @@ const location = useLocation(); [field]: value }; } - - return { - ...product, - variationReason: value, - variationReasonName: selectedReason?.label || selectedReason?.name || '', - [field]: value - }; } // Handle product selection @@ -953,67 +986,68 @@ const location = useLocation(); }; } - return { ...product, [field]: value }; - } - return product; - }); - - // Clear error for the field being updated - const productIndex = products.findIndex(p => p.id === id); - if (productIndex !== -1) { - const errorKey = `${field}_${productIndex}`; - if (formErrors[errorKey] && value) { - const newErrors = { ...formErrors }; - delete newErrors[errorKey]; - setFormErrors(newErrors); - } - } - - onProductsChange(updatedProducts); -}; - -const handleProductSelect = async (productId, establishmentId, id) => { - if (!productId || !establishmentId) { - console.warn('Missing required parameters in handleProductSelect:', { - hasProductId: !!productId, - hasEstablishmentId: !!establishmentId - }); - return; - } - - try { - const forecastData = await fetchPreviousForecastData(productId, establishmentId); - if (!forecastData) { - console.warn('No forecast data received for product:', productId); - return; - } - - // Check if the response has a data property (nested response) or is the data itself - const data = forecastData.data || forecastData; - const updatedProducts = products.map(product => { - if (product.id === id) { - return { - ...product, - 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 || '' - }; + return { ...product, [field]: value }; } return product; }); + + // Clear error for the field being updated + const productIndex = products.findIndex(p => p.id === id); + if (productIndex !== -1) { + const errorKey = `${field}_${productIndex}`; + if (formErrors[errorKey] && value) { + const newErrors = { ...formErrors }; + delete newErrors[errorKey]; + setFormErrors(newErrors); + } + } + onProductsChange(updatedProducts); - } catch (error) { - console.error('Error in handleProductSelect:', { - message: error.message, - error: error, - stack: error.stack - }); - } -}; + }; + + const handleProductSelect = async (productId, establishmentId, id) => { + if (!productId || !establishmentId) { + console.warn('Missing required parameters in handleProductSelect:', { + hasProductId: !!productId, + hasEstablishmentId: !!establishmentId + }); + return; + } + + try { + const forecastData = await fetchPreviousForecastData(productId, establishmentId); + if (!forecastData) { + console.warn('No forecast data received for product:', productId); + return; + } + + // Check if the response has a data property (nested response) or is the data itself + const data = forecastData.data || forecastData; + const updatedProducts = products.map(product => { + if (product.id === id) { + return { + ...product, + 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 || '' + }; + } + return product; + }); + onProductsChange(updatedProducts); + } catch (error) { + console.error('Error in handleProductSelect:', { + message: error.message, + error: error, + stack: error.stack + }); + } + }; + const canAddMoreProducts = products.length < MAX_PRODUCTS; const isLoading = loading || isLoadingReasons || isLoadingZeroReasons || isLoadingProducts || isLoadingUnits; @@ -1142,7 +1176,7 @@ const handleProductSelect = async (productId, establishmentId, id) => {

Data definitions

Product (HS Code – Name): Standard trade classification. Start typing to search by code or name.

Quantity (Qty): Physical output produced in the month, measured in the selected unit.

-

Cost (AED): Total production cost for that month, excluding VAT (e.g., materials, energy, direct labour, and manufacturing expenses). Do not include sales margins.

+

Cost (AED AED): Total production cost for that month, excluding VAT (e.g., materials, energy, direct labour, and manufacturing expenses). Do not include sales margins.

Annual Installed Capacity: Maximum achievable annual output under normal operating conditions with existing equipment; do not include extraordinary overtime.

)} @@ -1199,7 +1233,7 @@ const handleProductSelect = async (productId, establishmentId, id) => { )}
-
+
{

{productsError}

)}
-
+
{ )}
-
+
{
{p.octCost || '0'} @@ -1425,7 +1459,7 @@ const handleProductSelect = async (productId, establishmentId, id) => {
{p.novCost || '0'} @@ -1450,7 +1484,7 @@ const handleProductSelect = async (productId, establishmentId, id) => { >
-
+
@@ -1466,7 +1500,7 @@ const handleProductSelect = async (productId, establishmentId, id) => {

{formErrors[`janQuantity_${idx}`]}

)}
-
+
@@ -1482,7 +1516,7 @@ const handleProductSelect = async (productId, establishmentId, id) => {

{formErrors[`febQuantity_${idx}`]}

)}
-
+
@@ -1500,7 +1534,7 @@ const handleProductSelect = async (productId, establishmentId, id) => {
-
+
@@ -1516,7 +1550,7 @@ const handleProductSelect = async (productId, establishmentId, id) => {

{formErrors[`janCost_${idx}`]}

)}
-
+
@@ -1532,7 +1566,7 @@ const handleProductSelect = async (productId, establishmentId, id) => {

{formErrors[`febCost_${idx}`]}

)}
-
+
@@ -1592,7 +1626,7 @@ const handleProductSelect = async (productId, establishmentId, id) => { >
-
+
@@ -1609,7 +1643,7 @@ const handleProductSelect = async (productId, establishmentId, id) => {

{formErrors[`aprQuantity_${idx}`]}

)}
-
+
@@ -1626,7 +1660,7 @@ const handleProductSelect = async (productId, establishmentId, id) => {

{formErrors[`mayQuantity_${idx}`]}

)}
-
+
@@ -1645,7 +1679,7 @@ const handleProductSelect = async (productId, establishmentId, id) => {
-
+
@@ -1662,7 +1696,7 @@ const handleProductSelect = async (productId, establishmentId, id) => {

{formErrors[`aprCost_${idx}`]}

)}
-
+
@@ -1679,7 +1713,7 @@ const handleProductSelect = async (productId, establishmentId, id) => {

{formErrors[`mayCost_${idx}`]}

)}
-
+
@@ -1786,4 +1820,4 @@ const handleProductSelect = async (productId, establishmentId, id) => { ); }; -export default ProductData; +export default ProductData; \ No newline at end of file