diff --git a/ipi-survey-platform/src/components/survey/ProductData/ProductData.jsx b/ipi-survey-platform/src/components/survey/ProductData/ProductData.jsx index 70258e7..b4b6fac 100644 --- a/ipi-survey-platform/src/components/survey/ProductData/ProductData.jsx +++ b/ipi-survey-platform/src/components/survey/ProductData/ProductData.jsx @@ -1603,10 +1603,25 @@ useEffect(() => { const errors = { ...formErrors }; const fieldKey = `${fieldName}_${productIndex}`; - if (!value || value.toString().trim() === '') { - errors[fieldKey] = 'This field is required'; + // Check if this is a mandatory quantity or cost field + const mandatoryQuantityFields = ['janQuantity', 'febQuantity', 'marQuantity', 'aprQuantity', 'mayQuantity', 'junQuantity']; + const mandatoryCostFields = ['janCost', 'febCost', 'marCost', 'aprCost', 'mayCost', 'junCost']; + const isMandatoryQuantityOrCost = mandatoryQuantityFields.includes(fieldName) || mandatoryCostFields.includes(fieldName); + + if (isMandatoryQuantityOrCost) { + // For mandatory quantity and cost fields, value must be greater than 0 + if (!value || value.toString().trim() === '' || parseFloat(value) <= 0) { + errors[fieldKey] = 'Required'; + } else { + delete errors[fieldKey]; + } } else { - delete errors[fieldKey]; + // For other fields, just check if not empty + if (!value || value.toString().trim() === '') { + errors[fieldKey] = 'This field is required'; + } else { + delete errors[fieldKey]; + } } setFormErrors(errors); @@ -1617,52 +1632,49 @@ useEffect(() => { const validateForm = () => { const errors = {}; let isValid = true; + let hasFieldErrors = false; - // Check if all available products have been added - const availableProductsCount = productOptions.length; - const currentProductsCount = products.length; - const remainingProducts = availableProductsCount - currentProductsCount; - - if (currentProductsCount < availableProductsCount) { - errors['products_general'] = 'Please complete all products to continue.'; - isValid = false; - } - + // First validate all individual fields for each product products.forEach((product, index) => { // Only validate fields that are marked as mandatory (with asterisk in UI) // Product selection is mandatory if (!product.product) { errors[`product_${index}`] = 'Product is required'; isValid = false; + hasFieldErrors = true; } // Unit selection is mandatory - if (!product.unit) { + if (!product.unit || product.unit === '') { errors[`unit_${index}`] = 'Unit is required'; isValid = false; + hasFieldErrors = true; } - // Annual installed capacity is mandatory - if (!product.capacity || product.capacity === '0' || parseFloat(product.capacity) === 0) { + // Annual installed capacity is mandatory (0 should be valid) + if (!product.capacity || product.capacity === '') { errors[`capacity_${index}`] = 'Capacity is required'; isValid = false; + hasFieldErrors = true; } // Only quantity fields with asterisk (*) are mandatory (current and next quarter) const mandatoryQuantityFields = ['janQuantity', 'febQuantity', 'marQuantity', 'aprQuantity', 'mayQuantity', 'junQuantity']; mandatoryQuantityFields.forEach(field => { - if (!product[field] || product[field] === '0' || parseFloat(product[field]) === 0) { + if (!product[field] || product[field] === '' || parseFloat(product[field]) <= 0) { errors[`${field}_${index}`] = 'Required'; isValid = false; + hasFieldErrors = true; } }); // Only value fields with asterisk (*) are mandatory (current and next quarter) const mandatoryCostFields = ['janCost', 'febCost', 'marCost', 'aprCost', 'mayCost', 'junCost']; mandatoryCostFields.forEach(field => { - if (!product[field] || product[field] === '0' || parseFloat(product[field]) === 0) { + if (!product[field] || product[field] === '' || parseFloat(product[field]) <= 0) { errors[`${field}_${index}`] = 'Required'; isValid = false; + hasFieldErrors = true; } }); @@ -1673,6 +1685,7 @@ useEffect(() => { if (product.variationReason === null || product.variationReason === undefined || product.variationReason === '') { errors[`variationReason_${index}`] = 'Reason for variation is required when cost variation exceeds ±10%'; isValid = false; + hasFieldErrors = true; } // If "Other" is selected, validate the other variation reason field @@ -1685,12 +1698,26 @@ useEffect(() => { if (isOtherSelected && (!product.otherVariationReason || product.otherVariationReason.trim() === '')) { errors[`otherVariationReason_${index}`] = 'Please specify the reason for variation'; isValid = false; + hasFieldErrors = true; } } }); + // Only check product count if there are no field errors + if (!hasFieldErrors) { + // Check if all available products have been added + const availableProductsCount = productOptions.length; + const currentProductsCount = products.length; + const remainingProducts = availableProductsCount - currentProductsCount; + + if (currentProductsCount < availableProductsCount) { + errors['products_general'] = 'Please complete all products to continue.'; + isValid = false; + } + } + setFormErrors(errors); - return isValid; + return { isValid, hasFieldErrors, errors }; }; // Validation function for Save as Draft - requires at least one field to be filled @@ -1743,32 +1770,20 @@ useEffect(() => { }; const handleNext = () => { - // First run validation to get errors - const errors = {}; - let isValid = true; - - // Check if all available products have been added - const availableProductsCount = productOptions.length; - const currentProductsCount = products.length; + // Run validation to get errors and field error status + const validation = validateForm(); - if (currentProductsCount < availableProductsCount) { - errors['products_general'] = 'Please complete all products to continue.'; - isValid = false; - } - - // Update formErrors state - setFormErrors(errors); - - if (isValid) { + if (validation.isValid) { onNext(); } else { - // Show toast notification for validation error - if (errors['products_general']) { - setValidationErrorMessage(errors['products_general']); + // Only show toast for product count error if there are no field errors + if (!validation.hasFieldErrors && validation.errors['products_general']) { + setValidationErrorMessage(validation.errors['products_general']); setShowValidationErrorToast(true); } + // Scroll to first error - const firstErrorKey = Object.keys(errors)[0]; + const firstErrorKey = Object.keys(validation.errors)[0]; if (firstErrorKey) { const element = document.querySelector(`[data-field="${firstErrorKey}"]`); if (element) { @@ -1871,6 +1886,18 @@ useEffect(() => { // Create a copy of the current products array to work with let updatedProducts = [...products]; + // Clear error for this field when value is entered + const newErrors = { ...formErrors }; + const fieldIndex = products.findIndex(p => p.id === id); + const errorKey = `${field}_${fieldIndex}`; + + // Debug logging + + if (newErrors[errorKey] && value && value !== '') { + delete newErrors[errorKey]; + setFormErrors(newErrors); + } + // Format value for cost fields to treat last two digits as decimals if (field.endsWith('Cost') && value !== '') { // Remove all non-digit characters @@ -2095,12 +2122,29 @@ useEffect(() => { 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); + const newErrors = { ...formErrors }; + + // Check if this is a mandatory quantity or cost field + const mandatoryQuantityFields = ['janQuantity', 'febQuantity', 'marQuantity', 'aprQuantity', 'mayQuantity', 'junQuantity']; + const mandatoryCostFields = ['janCost', 'febCost', 'marCost', 'aprCost', 'mayCost', 'junCost']; + const isMandatoryQuantityOrCost = mandatoryQuantityFields.includes(field) || mandatoryCostFields.includes(field); + + if (isMandatoryQuantityOrCost) { + // For mandatory quantity and cost fields, validate that value is greater than 0 + if (!value || value.toString().trim() === '' || parseFloat(value) <= 0) { + newErrors[errorKey] = 'Required'; + } else { + delete newErrors[errorKey]; + } + } else { + // For other fields, just clear error if value exists + if (formErrors[errorKey] && value) { + delete newErrors[errorKey]; + } } + setFormErrors(newErrors); + // Real-time validation for variation reason when cost fields change const costFields = ['octCost', 'novCost', 'decCost', 'janCost', 'febCost', 'marCost']; if (costFields.includes(field)) { diff --git a/ipi-survey-platform/src/pages/Admin/configuration/EditCompanyProfile.jsx b/ipi-survey-platform/src/pages/Admin/configuration/EditCompanyProfile.jsx index d2419cf..fccb311 100644 --- a/ipi-survey-platform/src/pages/Admin/configuration/EditCompanyProfile.jsx +++ b/ipi-survey-platform/src/pages/Admin/configuration/EditCompanyProfile.jsx @@ -491,7 +491,7 @@ const EditCompanyProfile = () => { if (allowedAddCount === 0) { setProductLimitMessage(`Maximum of ${MAX_TOTAL_SELECTED_PRODUCTS} products can be selected.`); } else { - setProductLimitMessage(`You can add up to ${allowedAddCount} new product${allowedAddCount !== 1 ? 's' : ''}.`); + setProductLimitMessage(`You have reached the maximum limit`); } return; } @@ -1182,7 +1182,7 @@ const EditCompanyProfile = () => { if (newlyAddedCheckedCount >= allowedAddCount) { return ( -
You can add up to {allowedAddCount} new product{allowedAddCount !== 1 ? 's' : ''}.
+You have reached the maximum limit.
); } @@ -1194,7 +1194,7 @@ const EditCompanyProfile = () => { if (allowedAddCount < MAX_NEW_PRODUCTS_PER_ACTION) { return ( -You can add up to {allowedAddCount} new product{allowedAddCount !== 1 ? 's' : ''}.
+You have reached the maximum limit.
); }