From fde8142acb0b5313e6ae100bbde319d70bc95a0a Mon Sep 17 00:00:00 2001 From: Malini Date: Thu, 13 Nov 2025 19:01:28 +0530 Subject: [PATCH] fcsc bug fixed --- .../src/components/admin/SubmissionTable.jsx | 11 +- .../components/overview/ProductDetails.jsx | 2 +- .../survey/ProductData/ProductData.jsx | 147 ++++++++++++------ .../survey/ReviewSubmit/ReviewSubmit.jsx | 1 + .../Admin/configuration/CompanyProfile.jsx | 46 +++++- .../pages/Admin/configuration/UnitMaster.jsx | 6 +- .../src/pages/Survey/Survey.jsx | 9 +- 7 files changed, 159 insertions(+), 63 deletions(-) diff --git a/ipi-survey-platform/src/components/admin/SubmissionTable.jsx b/ipi-survey-platform/src/components/admin/SubmissionTable.jsx index 2ed0e26..8ce769e 100644 --- a/ipi-survey-platform/src/components/admin/SubmissionTable.jsx +++ b/ipi-survey-platform/src/components/admin/SubmissionTable.jsx @@ -86,14 +86,15 @@ const SubmissionTable = ({ selectedQuarter, selectedYear }) => { year: 'numeric', hour: '2-digit', minute: '2-digit', - hour12: true, + hour12: false }; - // Format the date in Dubai time - let formatted = new Intl.DateTimeFormat('en-GB', options).format(date); + // Format the date in Dubai time with 24-hour format + const formatter = new Intl.DateTimeFormat('en-GB', options); + let formatted = formatter.format(date); - // Convert to uppercase AM/PM and ensure consistent formatting - formatted = formatted.replace(/\s?(am|pm)$/i, (match) => match.toUpperCase()); + // Remove any AM/PM indicator since we're using 24-hour format + formatted = formatted.replace(/\s*[AP]M$/, '').trim(); return formatted; }; diff --git a/ipi-survey-platform/src/components/overview/ProductDetails.jsx b/ipi-survey-platform/src/components/overview/ProductDetails.jsx index c957adb..f6cc50d 100644 --- a/ipi-survey-platform/src/components/overview/ProductDetails.jsx +++ b/ipi-survey-platform/src/components/overview/ProductDetails.jsx @@ -410,7 +410,7 @@ const ProductDetails = ({
- +
{(() => { const status = String(submissionStatus).toLowerCase(); diff --git a/ipi-survey-platform/src/components/survey/ProductData/ProductData.jsx b/ipi-survey-platform/src/components/survey/ProductData/ProductData.jsx index 6adeb44..adc1637 100644 --- a/ipi-survey-platform/src/components/survey/ProductData/ProductData.jsx +++ b/ipi-survey-platform/src/components/survey/ProductData/ProductData.jsx @@ -328,6 +328,7 @@ const ProductData = ({ const [showOtherVariationReason, setShowOtherVariationReason] = React.useState({}); const [showOtherZeroTargetReason, setShowOtherZeroTargetReason] = React.useState({}); const [formErrors, setFormErrors] = React.useState({}); + const [selectedProductIds, setSelectedProductIds] = React.useState([]); const [remarks, setRemarks] = React.useState(() => { if (typeof window !== 'undefined') { return localStorage.getItem('surveyRemarks') || ''; @@ -554,52 +555,68 @@ const location = useLocation(); console.log("surveyDatatest", surveyData); const requiredMessage = 'Required'; - const validateForm = () => { - const errors = {}; - let isValid = true; + const getAvailableProducts = (currentProductId) => { + // Get the current product's selected value + const currentProduct = products.find(p => p.id === currentProductId); + const currentProductValue = currentProduct?.product || ''; + // Get all selected product IDs except the current product's ID + const otherSelectedProductIds = products + .filter(p => p.id !== currentProductId && p.product) + .map(p => p.product); + + // Filter out selected products, but include the current product's selection + return productOptions.filter(option => + !otherSelectedProductIds.includes(option.value) || + option.value === currentProductValue + ); + }; + + const validateProduct = (product, index) => { + const errors = {}; + let hasError = false; + + // 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; + } + + // 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 }; + }; + + const validateForm = () => { + let isValid = true; + const errors = {}; + products.forEach((product, index) => { - // Product validation - if (!product.product) { - // errors[`product_${index}`] = 'Product (HS Code - Name) is required'; - errors[`product_${index}`] = requiredMessage; + const { errors: productErrors, hasError } = validateProduct(product, index); + if (hasError) { + Object.assign(errors, productErrors); isValid = false; } - - // Unit validation - if (!product.unit) { - errors[`unit_${index}`] = requiredMessage; - isValid = false; - } - - // Capacity validation - if (!product.capacity) { - errors[`capacity_${index}`] = requiredMessage; - isValid = false; - } - - // Monthly quantity validations - const requiredFields = [ - { key: 'janQuantity', label: 'Jan Qty' }, - { key: 'febQuantity', label: 'Feb Qty' }, - { key: 'marQuantity', label: 'Mar Qty' }, - { key: 'aprQuantity', label: 'Apr Qty' }, - { key: 'mayQuantity', label: 'May Qty' }, - { key: 'junQuantity', label: 'Jun Qty' }, - { key: 'janCost', label: 'Jan Cost' }, - { key: 'febCost', label: 'Feb Cost' }, - { key: 'marCost', label: 'Mar Cost' }, - { key: 'aprCost', label: 'Apr Cost' }, - { key: 'mayCost', label: 'May Cost' }, - { key: 'junCost', label: 'Jun Cost' } - ]; - - requiredFields.forEach(field => { - if (product[field.key] === undefined || product[field.key] === '') { - errors[`${field.key}_${index}`] = requiredMessage; - isValid = false; - } - }); }); setFormErrors(errors); @@ -636,7 +653,13 @@ const location = useLocation(); const removeProduct = (id) => { if (products.length === 1) return; + const productToRemove = products.find(p => p.id === id); onProductsChange(products.filter((p) => p.id !== id)); + + // Remove the product ID from selectedProductIds if it exists + if (productToRemove?.product) { + setSelectedProductIds(prev => prev.filter(pid => pid !== productToRemove.product)); + } }; const fetchPreviousForecastData = async (productId, establishmentId) => { @@ -698,7 +721,43 @@ const location = useLocation(); }; const updateProductField = (id, field, value, options = null) => { - const updatedProducts = products.map(product => { + // Create a copy of the current products array to work with + let updatedProducts = [...products]; + + // If this is a product selection change, validate for duplicates + if (field === 'product' && value) { + // Check if this product is already selected in another row + const isDuplicate = products.some( + (p, idx) => p.product === value && p.id !== id + ); + + if (isDuplicate) { + // Set error for this field + setFormErrors(prev => ({ + ...prev, + [`product-${id}`]: 'This product has already been selected' + })); + return; // Don't update the product if it's a duplicate + } + + // Clear any existing error for this field + const newErrors = { ...formErrors }; + delete newErrors[`product-${id}`]; + setFormErrors(newErrors); + + // Update selected product IDs + const currentProduct = products.find(p => p.id === id); + if (currentProduct) { + // Remove the old product ID if it exists + setSelectedProductIds(prev => + prev.filter(pid => pid !== currentProduct.product) + ); + } + // Add the new product ID + setSelectedProductIds(prev => [...prev, value]); + } + + updatedProducts = products.map(product => { if (product.id === id) { // Check if 'Others' is selected for variation reason if (field === 'variationReason' || field === 'otherVariationReason') { @@ -1118,7 +1177,7 @@ const handleProductSelect = async (productId, establishmentId, id) => {
{ diff --git a/ipi-survey-platform/src/components/survey/ReviewSubmit/ReviewSubmit.jsx b/ipi-survey-platform/src/components/survey/ReviewSubmit/ReviewSubmit.jsx index f1bb238..cf3a145 100644 --- a/ipi-survey-platform/src/components/survey/ReviewSubmit/ReviewSubmit.jsx +++ b/ipi-survey-platform/src/components/survey/ReviewSubmit/ReviewSubmit.jsx @@ -340,6 +340,7 @@ const ReviewSubmit = ({ hasSubmitted = false, submitButtonText = 'Submit', quarter = 'Q1', + // remarks = '', year = new Date().getFullYear() }) => { const [quarterPeriods, setQuarterPeriods] = useState(null); diff --git a/ipi-survey-platform/src/pages/Admin/configuration/CompanyProfile.jsx b/ipi-survey-platform/src/pages/Admin/configuration/CompanyProfile.jsx index 398272c..e328fea 100644 --- a/ipi-survey-platform/src/pages/Admin/configuration/CompanyProfile.jsx +++ b/ipi-survey-platform/src/pages/Admin/configuration/CompanyProfile.jsx @@ -187,6 +187,7 @@ const CompanyProfile = () => { const [isSaving, setIsSaving] = React.useState(false); const [saveWarning, setSaveWarning] = React.useState(false); const [saveError, setSaveError] = React.useState(''); + const [productError, setProductError] = React.useState(''); const [toast, setToast] = React.useState(null); const [emirateOptions, setEmirateOptions] = React.useState([]); const [emirateLookup, setEmirateLookup] = React.useState({}); @@ -890,7 +891,12 @@ const CompanyProfile = () => { case 3: return (
-

Products

+ {productError && ( +
+ {productError} +
+ )} +

Products *

{/* Available Products Panel */}
@@ -1133,6 +1139,9 @@ const loadProducts = async () => { }; const handleAddProduct = (product) => { + // Clear any previous product errors + setProductError(''); + setSaveError(''); setSelectedProducts(prev => { const updated = [...prev, product]; return updated; @@ -1146,6 +1155,9 @@ const loadProducts = async () => { }; const handleRemoveProduct = (product) => { + // Clear any previous product errors + setProductError(''); + setSaveError(''); setSelectedProducts(prev => { const updated = prev.filter(p => p.id !== product.id); return updated; @@ -1171,6 +1183,13 @@ const loadProducts = async () => { }; const handleNextStep = React.useCallback(() => { + if (activeStep === 3 && selectedProducts.length === 0) { + // Show error if on products step and no products are selected + setProductError('Please select at least one product'); + showToast('error', 'Please select at least one product'); + return; + } + if (!validateStepFields(activeStep)) { return; } @@ -2856,13 +2875,24 @@ const requiredFields = [
) : ( - +
+ {activeStep > 0 && ( + + )} + +
)}
diff --git a/ipi-survey-platform/src/pages/Admin/configuration/UnitMaster.jsx b/ipi-survey-platform/src/pages/Admin/configuration/UnitMaster.jsx index c08f82e..a481684 100644 --- a/ipi-survey-platform/src/pages/Admin/configuration/UnitMaster.jsx +++ b/ipi-survey-platform/src/pages/Admin/configuration/UnitMaster.jsx @@ -474,7 +474,7 @@ const UnitMaster = () => { const downloadSampleCSV = () => { const sampleData = [ - ['uom', 'description'], + ['Unit Name', 'Description'], ['Kilogram', 'Weight measurement in kilograms'], ['Gram', 'Weight measurement in grams'], ['Liter', 'Volume measurement in liters'], @@ -720,7 +720,7 @@ const UnitMaster = () => { >

- View Unit + View Unit Master

diff --git a/ipi-survey-platform/src/pages/Survey/Survey.jsx b/ipi-survey-platform/src/pages/Survey/Survey.jsx index 281bd6f..9be28ad 100644 --- a/ipi-survey-platform/src/pages/Survey/Survey.jsx +++ b/ipi-survey-platform/src/pages/Survey/Survey.jsx @@ -1,4 +1,4 @@ -import React, { useEffect } from 'react'; +import React, { useEffect,useState } from 'react'; import { useLocation, useNavigate } from 'react-router-dom'; import { submitSurvey, fetchSubmissionDetail, resubmitSurvey } from '@/services/submissions/submissionService'; import { fetchEstablishmentDetail, fetchEstablishmentDashboard } from '@/services/establishments/establishmentService'; @@ -66,6 +66,7 @@ const Survey = () => { const [toast, setToast] = React.useState(null); const [error, setError] = React.useState(''); const [submissionDetail, setSubmissionDetail] = React.useState(null); + const [remarks, setRemarks] = useState(''); const [viewLoading, setViewLoading] = React.useState(false); const [viewError, setViewError] = React.useState(''); const [establishmentLoading, setEstablishmentLoading] = React.useState(false); @@ -606,6 +607,7 @@ const Survey = () => { non_emirati_female: parseNumber(info.nonEmiratiFemale), total_emirati: parseNumber(info.totalEmirati), total_employees: parseNumber(info.totalEmployees), + remarks: remarks, // Add remarks to the payload products: productsData.map((product) => { const variationId = parseNumber(product.variationReason, null); const zeroTargetId = parseNumber(product.zeroTargetReason, null); @@ -683,7 +685,7 @@ const Survey = () => { }; }), }; - }, [establishmentData, parseNumber, productsData, stringify]); + }, [establishmentData, parseNumber, productsData,remarks, stringify]); const handleNext = () => { if (step < 3) { @@ -1011,6 +1013,8 @@ const Survey = () => { error={error} quarter={establishmentData.quarter} year={establishmentData.year} + remarks={remarks} + onRemarksChange={setRemarks} /> )} {step === 3 && ( @@ -1030,6 +1034,7 @@ const Survey = () => { }} isSubmitting={isSubmitting} hasSubmitted={false} + remarks={remarks} /> )}