bug fixed in the product data

This commit is contained in:
Malini 2025-11-12 13:56:53 +05:30
parent 8b1b09d8ba
commit 9b363d88bb
7 changed files with 169 additions and 77 deletions

View File

@ -129,7 +129,10 @@ export const DetailedOverview = ({ submission, onBack }) => {
const matchesStatus = selectedStatus === '' ||
(selectedStatus === 'pending' && !product.is_active) ||
(selectedStatus === 'approved' && product.is_active);
(selectedStatus === 'approved' && product.is_active)
(selectedStatus === 'resubmitted' && product.status === 'Resubmitted')
(selectedStatus === 'submitted' && product.status === 'Submitted')
(selectedStatus === 'rejected' && product.status === 'Rejected');
return matchesSearch && matchesStatus;
});
@ -362,6 +365,7 @@ export const DetailedOverview = ({ submission, onBack }) => {
<option value="approved">Approved</option>
<option value="submitted">Submitted</option>
<option value="rejected">Rejected</option>
<option value="resubmitted">Resubmitted</option>
</select>
<img
src={caretDownSrc}

View File

@ -115,9 +115,14 @@ const ProductDetails = ({ product, onBack, showToast: shouldShowToast, onToastSh
q2_apr_cost: product.forecast_cost_period_one || '—',
q2_may_cost: product.forecast_cost_period_two || '—',
q2_jun_cost: product.forecast_cost_period_three || '—',
variationReason: product.variation_reason?.reason || '—',
zeroTargetReason: product.zero_target_reason?.reason || '—',
remarks: product.remarks || 'NA'
// variationReason: product.variation_reason?.reason || '',
// zeroTargetReason: product.zero_target_reason?.reason || '',
remarks: product.remarks || 'NA',
variationReason: product.other_variation_reason || product.variation_reason?.reason || '—',
zeroTargetReason: product.other_zero_target_reason || product.zero_target_reason?.reason || '—',
other_variation_reason: product.other_variation_reason || '—',
other_zero_target_reason: product.other_zero_target_reason || '—',
remarks: product.remarks || 'NA'
});
const formattedProduct = formatProductData(product);

View File

@ -1,14 +1,14 @@
import React, { useState, useEffect } from 'react';
// import { useLocation } from 'react-router-dom';
import { getQuarterPeriods, getPreviousForecastData } from '@/services/submissions/submissionService';
import { getQuarterPeriods, getPreviousForecastData, getEstablishmentProducts } from '@/services/submissions/submissionService';
import {
fetchVariationReasons,
fetchZeroTargetReasons,
fetchProducts,
fetchUnits,
} from '@/services/masters/masterService.js';
import { fetchEstablishmentDetail } from '@/services/establishments/establishmentService';
// import { fetchEstablishmentDetail } from '@/services/establishments/establishmentService';
import { useLocation } from 'react-router-dom';
const caretDownSrc = '/assets/images/CaretDown-black.svg';
@ -455,48 +455,59 @@ const ProductData = ({
const fetchEstablishmentProducts = async () => {
try {
const establishmentId = sessionStorage.getItem('establishment_id');
if (!establishmentId) {
console.error('Establishment ID not found in session storage');
return;
}
if (!establishmentId) return;
const response = await fetchEstablishmentDetail(establishmentId);
if (response && response.data && Array.isArray(response.data.establishment_products)) {
// Transform products data to match the expected format
const formattedProducts = response.data.establishment_products.map(item => {
const product = item.product || {};
return {
value: item.product_id, // Use product_id as the value
label: product.product_name ?
`${product.product_name} (${product.hs_code || 'N/A'})` :
'Unknown Product',
originalData: product, // Store the full product data
hsCode: product.hs_code // Store HS code separately for display
};
});
// Update the product options state
setProductOptions(formattedProducts);
// If you need to update the form with initial products, uncomment and modify:
// if (formattedProducts.length > 0) {
// const initialProducts = formattedProducts.map((product, index) => ({
// id: index + 1,
// product: product.value,
// unit: '',
// // ... other default values
// }));
// onProductsChange(initialProducts);
// }
}
const fetchProducts = async () => {
setIsLoadingProducts(true);
setProductsError('');
try {
// Get products from getEstablishmentProducts
const apiResponse = await getEstablishmentProducts(establishmentId);
console.log('Products from API:', apiResponse); // Debug log
// Ensure we're working with an array and handle the response structure
const productsList = Array.isArray(apiResponse) ? apiResponse : (apiResponse?.data || []);
const formattedProducts = productsList.map(product => {
// Handle nested properties with dot notation
const productName = product['product.product_name'] || product.product_name || '';
const hsCode = product['product.hs_code'] || product.hs_code || '';
const unitName = product['product.unit.uom'] || '';
return {
value: product.id.toString(),
label: hsCode ? `${hsCode} - ${productName}` : productName,
originalData: product,
hsCode: hsCode,
name: productName,
productId: product.product_id, // Use product_id instead of id
product_id: product.product_id, // Add product_id directly to the object
unit: unitName
};
});
console.log('Formatted products:', formattedProducts); // Debug log
setProductOptions(formattedProducts);
} catch (error) {
console.error('Error fetching products:', error);
setProductsError('Failed to load products. Please try again.');
} finally {
setIsLoadingProducts(false);
}
};
await fetchProducts();
} catch (err) {
console.error('Error fetching establishment products:', err);
setError('Failed to load establishment products');
console.error('Error in fetchEstablishmentProducts:', err);
setProductsError('Failed to load establishment products. Please try again.');
setIsLoadingProducts(false);
}
};
fetchEstablishmentProducts();
}, []);
// Moved inside the component
React.useEffect(() => {
const maxId = products.reduce((max, item) => (item && typeof item.id === 'number' ? Math.max(max, item.id) : max), 0);
if (maxId >= nextId.current) {
@ -926,8 +937,8 @@ const handleProductSelect = async (productId, establishmentId, id) => {
<div className="space-y-6 relative">
{isLoading && (
<div className="absolute inset-0 z-40 flex items-center justify-center bg-white/80">
<div className="h-12 w-12 animate-spin rounded-full border-[3px] border-[#92722A] border-t-transparent" />
</div>
<div className="h-12 w-12 animate-spin rounded-full border-[3px] border-[#92722A] border-t-transparent" />
</div>
)}
<div className="flex justify-between items-center mb-2">
<h2 className="text-xl font-semibold text-[#232528]">Step 2: Product Data Monthly Output & Cost</h2>
@ -1084,29 +1095,52 @@ const handleProductSelect = async (productId, establishmentId, id) => {
<SearchableSelect
placeholder={isLoadingProducts ? 'Loading products...' : 'Search product HS code...'}
options={productOptions}
value={p.productId || p.product || ''}
value={p.productId?.toString() || p.product?.toString() || ''}
onChange={async (e) => {
const selectedValue = e.target.value;
console.log('Selected product value:', selectedValue); // Debug log
// Clear error when user selects a product
if (formErrors[`product_${idx}`]) {
const newErrors = { ...formErrors };
delete newErrors[`product_${idx}`];
setFormErrors(newErrors);
}
// Find the selected product from options
const selectedProduct = productOptions.find(opt => opt.value === selectedValue);
// Update the product with all necessary fields
console.log('Selected product:', selectedProduct); // Debug log
// Get unit information from the selected product
const unitId = selectedProduct?.originalData?.['product.unit.id'];
const unitName = selectedProduct?.originalData?.['product.unit.uom'] ||
selectedProduct?.originalData?.unit?.uom ||
selectedProduct?.unit || '';
// Create updated product with all necessary fields including unit info
const updatedProduct = {
...p,
product: selectedProduct?.value || selectedValue,
productName: selectedProduct?.label || '',
productId: selectedProduct?.productId || selectedValue, // Use the actual product ID
product_id: selectedProduct?.productId || selectedValue // Add product_id for submission
productId: selectedProduct?.value || selectedValue,
product_id: selectedProduct?.value || selectedValue,
hs_code: selectedProduct?.hsCode || p.hs_code,
name: selectedProduct?.name || p.name,
originalData: selectedProduct?.originalData || p.originalData,
// Set unit information
unit: unitId ? unitId.toString() : '',
unitName: unitName,
unit_id: unitId,
// Clear any previous unit-related errors
...(formErrors[`unit_${idx}`] && { unitError: undefined })
};
// Update the products array with the updated product
const updatedProducts = products.map(prod =>
prod.id === p.id ? updatedProduct : prod
const updatedProducts = products.map((prod, i) =>
i === idx ? updatedProduct : prod
);
console.log('Updated products:', updatedProducts); // Debug log
// Update the parent component's state
onProductsChange(updatedProducts);
// After updating the product, fetch forecast data if we have a valid product and establishment ID
@ -1134,8 +1168,8 @@ const handleProductSelect = async (productId, establishmentId, id) => {
capacity: data.annual_installed_capacity || ''
};
const finalUpdatedProducts = updatedProducts.map(prod =>
prod.id === p.id ? updatedWithForecast : prod
const finalUpdatedProducts = updatedProducts.map((prod, i) =>
i === idx ? updatedWithForecast : prod
);
onProductsChange(finalUpdatedProducts);
@ -1163,7 +1197,7 @@ const handleProductSelect = async (productId, establishmentId, id) => {
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Unit <span className="text-red-500">*</span></label>
<SearchableSelect
placeholder={isLoadingUnits ? 'Loading units...' : 'Search unit...'}
placeholder={p.unitName || (isLoadingUnits ? 'Loading units...' : 'Search unit...')}
options={unitOptions}
value={p.unit}
onChange={(e) => {
@ -1173,10 +1207,18 @@ const handleProductSelect = async (productId, establishmentId, id) => {
delete newErrors[`unit_${idx}`];
setFormErrors(newErrors);
}
// Find the selected unit to get its name
const selectedUnit = unitOptions.find(unit => unit.value === e.target.value);
updateProductField(p.id, 'unit', e.target.value, unitOptions);
// Also update unit name if a valid unit is selected
if (selectedUnit) {
updateProductField(p.id, 'unitName', selectedUnit.label || selectedUnit.name);
updateProductField(p.id, 'unit_id', selectedUnit.id || e.target.value);
}
}}
loading={isLoadingUnits}
error={!!formErrors[`unit_${idx}`]}
isDisabled={!!p.unitName} // Disable if unit is auto-filled from product
/>
<div className="h-5">
{formErrors[`unit_${idx}`] && (

View File

@ -170,12 +170,17 @@ const buildProductRows = (products = [], options = {}) => {
}
try {
// Find the full product details from productOptions if available
const fullProduct = productOptions.find(p => p.id === product.productId || p.value === product.productId);
// Extract product details with better fallbacks
// Find the full product details from originalData or product
const originalData = product.originalData || {};
const productId = product.id || `product-${Math.random().toString(36).substr(2, 9)}`;
const productName = fullProduct?.label || fullProduct?.name || product.productName || product.product?.name || product.product;
const productCode = fullProduct?.code || product.productCode || product.code || product.product?.code || product.id || `CODE-${index + 1}`;
// Get HS Code and Product Name from original data or product
const hsCode = originalData['product.hs_code'] || product.hs_code || '';
const productName = originalData['product.product_name'] || product.product_name || product.productName || product.product?.name || product.product;
// Format the display name as "HS Code - Product Name" if HS code exists
const displayName = hsCode ? `${hsCode} - ${productName}` : productName;
const productCode = hsCode || product.id || `CODE-${index + 1}`;
console.log("productCode",productCode)
// Use stored unitName if available, otherwise try to find it in unitOptions
let unitName = product.unitName;
@ -193,8 +198,10 @@ const buildProductRows = (products = [], options = {}) => {
// Map the quantity and cost data from the product form
const row = {
id: productId,
product: productName,
product: displayName,
productCode: productCode,
hsCode: hsCode,
productName: productName,
unit: unitName, // Use display name instead of ID
capacity: capacity,
@ -260,8 +267,20 @@ const formatSubmissionData = (establishment, products, remarks = '') => {
const totalEmployees = totalEmirati + nonEmiratiMale + nonEmiratiFemale;
// Format products data
const formattedProducts = products.map(product => ({
product_id: parseInt(product.productId) || 0,
console.log('Original products data:', JSON.stringify(products, null, 2));
const formattedProducts = products.map(product => {
// Get the actual product_id from the originalData if available
const originalData = product.originalData || {};
console.log('Original data for product:', JSON.stringify(originalData, null, 2));
// Get the product_id directly from the originalData object
// originalData has both id (establishment product ID) and product_id (actual product ID)
const productId = originalData.product_id || originalData.id; // Use product_id (56) with fallback to id
console.log('Selected product ID:', productId);
return {
product_id: parseInt(productId) || 0,
unit_id: parseInt(product.unit) || 0,
annual_installed_capacity: product.capacity?.toString() || '',
@ -301,9 +320,10 @@ const formatSubmissionData = (establishment, products, remarks = '') => {
variation_reason_master_id: product.variationReason?.toString() || '',
other_variation_reason: product.otherVariationReason?.toString() || '',
zero_target_reason_master_id: product.zeroTargetReason?.toString() || '',
other_zero_target_reason: product.otherZeroTargetReason?.toString() || '',
remarks: product.remarks || remarks || ''
}));
other_zero_target_reason: product.otherZeroTargetReason?.toString() || '',
remarks: product.remarks || remarks || ''
};
});
return {
establishment_id: parseInt(establishment.id) || 0,
@ -788,11 +808,10 @@ const ReviewSubmit = ({
</td>
<td colSpan="3" className="border border-gray-300 py-2 text-center bg-[#F3FAF4] text-[#2F663C]">
<div className="flex flex-col items-center">
<div>{product.variationReasonName || product.variationReason || '—'}</div>
{product.otherVariationReason && (
<div className="mt-1 text-xs text-gray-600">
{product.otherVariationReason}
</div>
{product.otherVariationReason ? (
<div className="text-sm">{product.otherVariationReason}</div>
) : (
<div>{product.variationReasonName?.replace('Others: ', '') || product.variationReason?.replace('Others: ', '') || '—'}</div>
)}
</div>
</td>
@ -811,11 +830,10 @@ const ReviewSubmit = ({
</td>
<td colSpan="3" className="border border-gray-300 py-2 text-center bg-[#FFF7E9] text-[#F29F0E]">
<div className="flex flex-col items-center">
<div>{product.zeroTargetReasonName || product.zeroTargetReason || '—'}</div>
{product.otherZeroTargetReason && (
<div className="mt-1 text-xs text-gray-600 ">
{product.otherZeroTargetReason}
</div>
{product.otherZeroTargetReason ? (
<div className="text-sm">{product.otherZeroTargetReason}</div>
) : (
<div>{product.zeroTargetReasonName?.replace('Others: ', '') || product.zeroTargetReason?.replace('Others: ', '') || '—'}</div>
)}
</div>
</td>

View File

@ -81,7 +81,8 @@ const Overview = () => {
!selectedStatus ||
(selectedStatus === 'approved' && record.status === 'Approved') ||
(selectedStatus === 'submitted' && record.status === 'Submitted') ||
(selectedStatus === 'rejected' && record.status === 'Rejected');
(selectedStatus === 'rejected' && record.status === 'Rejected') ||
(selectedStatus === 'resubmitted' && record.status === 'Resubmitted');
return matchesSearch && matchesStatus;
});
@ -185,6 +186,7 @@ const Overview = () => {
<option value="approved">Approved</option>
<option value="submitted">Submitted</option>
<option value="rejected">Rejected</option>
<option value="resubmitted">Resubmitted</option>
</select>
<img
src={caretDownSrc}

View File

@ -488,7 +488,7 @@ const Survey = () => {
// Map monthly data to period-specific fields
return {
...(isResubmitFlow && productId ? { id: productId } : {}), // Only include ID during resubmit
product_id: parseNumber(product.productId) || parseNumber(product.product),
product_id: parseNumber(product.originalData?.product_id) || parseNumber(product.product_id) || parseNumber(product.productId) || parseNumber(product.product),
unit_id: parseNumber(product.unitId) || parseNumber(product.unit),
annual_installed_capacity: stringify(product.capacity),

View File

@ -3,6 +3,7 @@ import resolveEstablishmentId from '@/services/utils/establishment';
import { putRequest } from '../api/CommonService';
const endpoint = '/submissions';
const ESTABLISHMENT_PRODUCTS_ENDPOINT = '/establishment-products';
const QUARTER_PERIODS_ENDPOINT = '/getQuarterPeriods';
// Add this new function to fetch submissions with pagination
@ -141,6 +142,25 @@ export const getSubmissionAuditHistory = async (establishmentId, params = {}) =>
}
};
export const getEstablishmentProducts = async (establishmentId, config = {}) => {
try {
if (!establishmentId) {
throw new Error('Establishment ID is required to fetch establishment products');
}
const response = await getRequest(ESTABLISHMENT_PRODUCTS_ENDPOINT, {
...config,
params: {
establishment_id: establishmentId,
...(config.params || {})
}
});
return response.data;
} catch (error) {
console.error('Error fetching establishment products:', error);
throw error;
}
};
export default {
getSubmissions,
submitSurvey,
@ -150,5 +170,6 @@ export default {
getQuarterPeriods,
getPreviousForecastData,
getSubmissionHistoryByEstablishment,
getSubmissionAuditHistory
getSubmissionAuditHistory,
getEstablishmentProducts
};