From 7c16dec47acf593f00acf300a5e2ce119dd96e1a Mon Sep 17 00:00:00 2001 From: Malini Date: Thu, 13 Nov 2025 20:18:11 +0530 Subject: [PATCH] code merged --- .../EstablishmentInfo/EstablishmentInfo.jsx | 18 +- .../configuration/EditCompanyProfile.jsx | 326 +++++++++--------- 2 files changed, 175 insertions(+), 169 deletions(-) diff --git a/ipi-survey-platform/src/components/survey/EstablishmentInfo/EstablishmentInfo.jsx b/ipi-survey-platform/src/components/survey/EstablishmentInfo/EstablishmentInfo.jsx index 4c7d173..81993f1 100644 --- a/ipi-survey-platform/src/components/survey/EstablishmentInfo/EstablishmentInfo.jsx +++ b/ipi-survey-platform/src/components/survey/EstablishmentInfo/EstablishmentInfo.jsx @@ -401,16 +401,18 @@ const EstablishmentInfo = ({ . Changes saved there will appear here.

*/} -

+

Need to update details? Go to Profile →{' '} - Edit Profile - -

+ href={`/admin/configuration/edit-profile/${sessionStorage.getItem('establishment_id') || ''}`} + className="underline font-medium text-[#043DFF] hover:text-[#063B82]" + onClick={handleEditProfile} + > + Edit Profile + + + . Changes saved there will appear here. +

Step 1: Review Establishment Information

diff --git a/ipi-survey-platform/src/pages/Admin/configuration/EditCompanyProfile.jsx b/ipi-survey-platform/src/pages/Admin/configuration/EditCompanyProfile.jsx index c215e37..7758e58 100644 --- a/ipi-survey-platform/src/pages/Admin/configuration/EditCompanyProfile.jsx +++ b/ipi-survey-platform/src/pages/Admin/configuration/EditCompanyProfile.jsx @@ -98,6 +98,8 @@ const EditCompanyProfile = () => { const [selectedProducts, setSelectedProducts] = useState([]); const [productSearchTerm, setProductSearchTerm] = useState(''); const [isLoadingProducts, setIsLoadingProducts] = useState(false); + const [productError, setProductError] = useState(""); + const [isLoadingCities, setIsLoadingCities] = useState(false); const initialSnapshotRef = useRef(null); const formSteps = React.useMemo( @@ -126,15 +128,12 @@ const EditCompanyProfile = () => { 2: [ { field: 'contactName', label: 'Name' }, { field: 'contactAddress', label: 'Address' }, - { field: 'contactCityTown', label: 'City/Town' }, - { field: 'contactEmirate', label: 'Emirate' }, - { field: 'contactMakaniNumber', label: 'Makani Number' }, - { field: 'contactPersonName', label: 'Contact Person Name' }, - { field: 'contactMobileNumber', label: 'Mobile Number' }, + { field: 'contactEmirateId', label: 'Emirate' }, + { field: 'contactCityTownId', label: 'City/Town' }, { field: 'contactEmail', label: 'Email' }, ], 3: [ - // Products are optional, no required fields + // Products validation handled separately ], 4: [ { field: 'employmentEmiratiMale', label: 'Number of Emirati Male' }, @@ -148,6 +147,18 @@ const EditCompanyProfile = () => { const validateStepFields = useCallback((stepIndex) => { setPasswordReuseError(""); + + // Special validation for Products step + if (stepIndex === 3) { + if (selectedProducts.length === 0) { + setProductError("At least one product is required."); + return false; + } else { + setProductError(""); + return true; + } + } + const requiredFields = requiredFieldsByStep[stepIndex] || []; if (!requiredFields.length) return true; @@ -163,15 +174,15 @@ const EditCompanyProfile = () => { return nextErrors; }); return valid; - }, [form, requiredFieldsByStep]); + }, [form, requiredFieldsByStep, selectedProducts]); const handleFormChange = useCallback((field, value) => { - if (field === 'contactCityTown') { - const foundCity = contactCityOptions.find(option => option.value === value || option.label === value); + if (field === 'contactCityTownId') { + const foundCity = contactCityOptions.find(option => option.value === value); setForm(prev => ({ ...prev, - contactCityTown: foundCity?.name || '', - contactCityTownId: foundCity?.id || '' + contactCityTown: foundCity?.label || '', + contactCityTownId: value })); return; } @@ -200,63 +211,47 @@ const EditCompanyProfile = () => { if (activeStep > 0) setActiveStep(activeStep - 1); }, [activeStep]); - // Load cities based on selected emirate - const loadCities = useCallback(async (emirateId, setCityOptions) => { + // Load cities based on selected emirate using your service function + const loadCities = useCallback(async (emirateId) => { if (!emirateId) { - setCityOptions([]); + setContactCityOptions([]); return []; } try { - const response = await fetchCityTowns({ emirateId: Number(emirateId) }); - const options = Array.isArray(response) ? response : (response.data || []); + setIsLoadingCities(true); + const cities = await fetchCityTowns({ emirateId }); - const formatted = options.map((option) => ({ - label: option.name || option.label, - value: String(option.id || option.value), - name: option.name || option.label, - id: String(option.id || option.value) - })); + console.log('Loaded cities for emirate', emirateId, ':', cities); - setCityOptions(formatted); - return formatted; + setContactCityOptions(cities); + return cities; } catch (error) { console.error('Failed to load cities:', error); - setCityOptions([]); + setContactCityOptions([]); return []; + } finally { + setIsLoadingCities(false); } }, []); - // Load products + // Load products using your service function const loadProducts = useCallback(async () => { try { setIsLoadingProducts(true); - const response = await fetchProducts(); + const products = await fetchProducts(); - let productsData = []; + console.log('Processed products data:', products); - if (Array.isArray(response)) { - productsData = response; - } else if (response?.data) { - productsData = Array.isArray(response.data) ? response.data : response.data.products || []; - } - - console.log('Processed products data:', productsData); - - // Format products data - const formattedProducts = productsData.map(p => ({ - id: p.id || p.value, - product_id: p.id || p.value, - hs_code: p.hs_code || p.hsCode || '', - hsCode: p.hs_code || p.hsCode || '', - product_name: p.product_name || p.productName || p.label || '', - productName: p.product_name || p.productName || p.label || '', - label: p.product_name || p.productName || p.label || '', - value: p.id || p.value - })); - - setAvailableProducts(formattedProducts); - return formattedProducts; + setAvailableProducts(prevProducts => { + // Only update if products have actually changed to prevent unnecessary re-renders + if (JSON.stringify(prevProducts) !== JSON.stringify(products)) { + return products; + } + return prevProducts; + }); + + return products; } catch (error) { console.error('Error loading products:', error); return []; @@ -272,7 +267,7 @@ const EditCompanyProfile = () => { if (!searchTerm) { setAvailableProducts(prev => prev.filter(p => - !selectedProducts.some(sp => sp.id === p.id) + !selectedProducts.some(sp => sp.value === p.value) )); return; } @@ -280,7 +275,7 @@ const EditCompanyProfile = () => { const filtered = availableProducts.filter(product => (product.label?.toLowerCase().includes(searchTerm) || product.hs_code?.toLowerCase().includes(searchTerm)) && - !selectedProducts.some(sp => sp.id === product.id) + !selectedProducts.some(sp => sp.value === product.value) ); setAvailableProducts(filtered); @@ -294,19 +289,22 @@ const EditCompanyProfile = () => { }); // Remove from available products - setAvailableProducts(prev => prev.filter(p => p.id !== product.id)); + setAvailableProducts(prev => prev.filter(p => p.value !== product.value)); + + // Clear product error when a product is added + setProductError(""); }; // Remove product handler const handleRemoveProduct = (product) => { - setSelectedProducts(prev => prev.filter(p => p.id !== product.id)); + setSelectedProducts(prev => prev.filter(p => p.value !== product.value)); // Add back to available products if not already there and matches search if (!productSearchTerm || (product.hs_code && product.hs_code.toLowerCase().includes(productSearchTerm)) || - (product.product_name && product.product_name.toLowerCase().includes(productSearchTerm))) { + (product.label && product.label.toLowerCase().includes(productSearchTerm))) { setAvailableProducts(prev => { - if (!prev.some(p => p.id === product.id)) { + if (!prev.some(p => p.value === product.value)) { return [...prev, product]; } return prev; @@ -314,9 +312,44 @@ const EditCompanyProfile = () => { } }; + // Load emirates on component mount using your service function + useEffect(() => { + let cancelled = false; + const controller = new AbortController(); + + const loadEmirates = async () => { + try { + const emirates = await fetchEmirates({ signal: controller.signal }); + if (cancelled) return; + + console.log('Loaded emirates:', emirates); + + const lookup = {}; + emirates.forEach((emirate) => { + lookup[emirate.value] = emirate.label; + }); + + setEmirateOptions(emirates); + setEmirateLookup(lookup); + } catch (error) { + if (cancelled) return; + console.error('Failed to load emirates:', error); + } + }; + + loadEmirates(); + + return () => { + cancelled = true; + controller.abort(); + }; + }, []); + + // Load establishment data and set form with API data useEffect(() => { console.log('useEffect triggered, establishmentId:', establishmentId); - + + // Create an async function inside the effect const fetchData = async () => { if (!establishmentId) { console.log('No establishmentId found, skipping fetch'); @@ -349,98 +382,85 @@ const EditCompanyProfile = () => { console.log('Final Form Data:', formData); + // First set the form data setForm(formData); initialSnapshotRef.current = formData; - // Load products and set selected products - const allProducts = await loadProducts(); + // Then load cities and products in parallel + const [cities, allProducts] = await Promise.all([ + formData.contactEmirateId ? loadCities(formData.contactEmirateId) : Promise.resolve([]), + loadProducts() + ]); + + console.log('Loaded cities:', cities); + console.log('All products loaded:', allProducts); // Set selected products from API response const establishmentProducts = response.data?.establishment_products || response.establishment_products || []; if (Array.isArray(establishmentProducts) && establishmentProducts.length > 0) { const formattedSelectedProducts = establishmentProducts.map(ep => { const productData = ep.product || {}; - return { - id: ep.id || 0, - product_id: ep.product_id || 0, - hs_code: productData.hs_code || ep.hs_code || '', - hsCode: productData.hs_code || ep.hs_code || '', - product_name: productData.product_name || ep.product_name || 'Unnamed Product', - productName: productData.product_name || ep.product_name || 'Unnamed Product', - label: `${productData.product_name || ep.product_name || 'Unnamed Product'}${productData.hs_code ? ` (${productData.hs_code})` : ''}`, - value: ep.product_id ? String(ep.product_id) : '', - ...productData + const productId = productData.id || ep.product_id; + + // Find the product in the loaded products to get the proper format + const foundProduct = allProducts.find(p => p.value === String(productId)); + + return foundProduct || { + value: String(productId), + label: productData.product_name || productData.name || 'Unnamed Product', + hs_code: productData.hs_code || '', + product_name: productData.product_name || productData.name || 'Unnamed Product' }; }); - setSelectedProducts(formattedSelectedProducts); - // Remove selected products from available products - setAvailableProducts(prev => - prev.filter(p => !formattedSelectedProducts.some(sp => sp.id === p.id)) - ); + setSelectedProducts(formattedSelectedProducts); } } catch (error) { - console.error('Failed to load establishment details:', error); - alert('Failed to load company details. Please try again.'); + console.error('Error loading establishment:', error); } finally { setLoading(false); } }; fetchData(); - }, [establishmentId, loadProducts]); - - // Load emirates on component mount - useEffect(() => { - let cancelled = false; - const controller = new AbortController(); - - const loadEmirates = async () => { - try { - const options = await fetchEmirates({ signal: controller.signal }); - if (cancelled) return; - - const formatted = options.map((option) => ({ - label: option.name || option.label, - value: String(option.id || option.value), - name: option.name || option.label, - id: String(option.id || option.value) - })); - - const lookup = {}; - formatted.forEach((option) => { - lookup[option.value] = option.label; - }); - - setEmirateOptions(formatted); - setEmirateLookup(lookup); - } catch (error) { - if (cancelled) return; - console.error('Failed to load emirates:', error); - } - }; - - loadEmirates(); - + + // Cleanup function to prevent state updates after unmount return () => { - cancelled = true; - controller.abort(); + // Any cleanup if needed }; - }, []); + }, [establishmentId]); // Remove loadCities and loadProducts from dependencies // Load contact cities when emirate ID changes useEffect(() => { - const loadContactCities = async () => { + let isMounted = true; + + const loadCitiesForEmirate = async () => { if (form.contactEmirateId) { - await loadCities(form.contactEmirateId, setContactCityOptions); - } else { + try { + const cities = await fetchCityTowns({ emirateId: form.contactEmirateId }); + if (isMounted) { + setContactCityOptions(cities); + } + } catch (error) { + console.error('Failed to load cities:', error); + if (isMounted) { + setContactCityOptions([]); + } + } + } else if (isMounted) { setContactCityOptions([]); + // Clear city selection when emirate is cleared + handleFormChange('contactCityTownId', ''); } }; - loadContactCities(); - }, [form.contactEmirateId, loadCities]); + loadCitiesForEmirate(); + + return () => { + isMounted = false; + }; + }, [form.contactEmirateId]); // Calculate totals when employment numbers change useEffect(() => { @@ -497,7 +517,7 @@ const EditCompanyProfile = () => { updated_by: 1, establishment_products: Array.isArray(selectedProducts) && selectedProducts.length > 0 ? selectedProducts.map(p => ({ - product_id: p.product_id || p.id || 0 + product_id: Number(p.value) || 0 })) : [], }; @@ -650,23 +670,16 @@ const EditCompanyProfile = () => { /> { + onChange={(e) => { const selectedValue = e.target.value; const selectedEmirate = emirateOptions.find(opt => opt.value === selectedValue); - handleFormChange("contactEmirate", selectedEmirate?.name || ''); - handleFormChange("contactEmirateId", selectedEmirate?.id || ''); + handleFormChange("contactEmirate", selectedEmirate?.label || ''); + handleFormChange("contactEmirateId", selectedValue); handleFormChange("contactCityTown", ''); handleFormChange("contactCityTownId", ''); - - if (selectedEmirate?.id) { - const cities = await loadCities(selectedEmirate.id, setContactCityOptions); - setContactCityOptions(cities); - } else { - setContactCityOptions([]); - } }} options={emirateOptions.map(opt => ({ value: opt.value, @@ -675,28 +688,25 @@ const EditCompanyProfile = () => { placeholder="Select Emirate" width="100%" required - error={fieldErrors.contactEmirate} + error={fieldErrors.contactEmirateId} /> { const selectedValue = e.target.value; - const selectedCity = contactCityOptions.find(opt => opt.value === selectedValue); - if (selectedCity) { - handleFormChange("contactCityTown", selectedCity.value); - } + handleFormChange("contactCityTownId", selectedValue); }} options={contactCityOptions.map(opt => ({ value: opt.value, label: opt.label }))} - placeholder={form.contactEmirate ? "Select City/Town" : "Select emirate first"} + placeholder={form.contactEmirateId ? (isLoadingCities ? "Loading cities..." : "Select City/Town") : "Select emirate first"} width="100%" required - disabled={!form.contactEmirate} - error={fieldErrors.contactCityTown} + disabled={!form.contactEmirateId || isLoadingCities} + error={fieldErrors.contactCityTownId} /> { onChange={(e) => handleFormChange("contactMakaniNumber", e.target.value)} placeholder="Enter Makani Number" width="100%" - required - error={fieldErrors.contactMakaniNumber} /> { onChange={(e) => handleFormChange("contactPersonName", e.target.value)} placeholder="Enter Contact Person Name" width="100%" - required - error={fieldErrors.contactPersonName} /> { onChange={(value) => handleFormChange("contactMobileNumber", value)} placeholder="Enter Mobile Number" width="100%" - required - error={fieldErrors.contactMobileNumber} /> { case 3: return (
-

Products

+
+

Products

+ {productError && ( +

{productError}

+ )} +
{/* Available Products Panel */}
@@ -802,12 +811,10 @@ const EditCompanyProfile = () => { ) : availableProducts.length > 0 ? (
    {availableProducts.map((product) => ( -
  • +
  • - {product.hsCode || ''} - {product.hsCode && (product.productName || product.label || product.product_name) ? ' - ' : ''} - {product.productName || product.label || product.product_name || ''} + {product.label}