bug fixed

This commit is contained in:
Malini 2025-11-04 18:50:26 +05:30
parent f9c3447704
commit 01df11d1fe
4 changed files with 607 additions and 77 deletions

View File

@ -1,4 +1,5 @@
import React from 'react';
import React, { useEffect, useState } from 'react';
import { getQuarterPeriods, getPreviousForecastData } from '@/services/submissions/submissionService';
import {
fetchVariationReasons,
fetchZeroTargetReasons,
@ -12,9 +13,17 @@ const calendarIcon = '/assets/images/Duedate.svg';
const nextIcon = '/assets/images/mdi_page-next-outline.svg';
const trashActiveSrc = '/assets/images/Trash - active.svg';
const Input = ({ placeholder = '', type = 'text', value = '', onChange = () => {}, required = false }) => {
const Input = ({ placeholder = '', type = 'text', value = '', onChange = () => {}, required = false, error = false }) => {
const [isFocused, setIsFocused] = React.useState(false);
const borderColor = value || isFocused ? 'border-[#92722A]' : 'border-[#CBA344]';
let borderColor = 'border-[#CBA344]';
let bgColor = 'bg-white';
if (error) {
borderColor = 'border-red-500';
bgColor = 'bg-red-50';
} else if (value || isFocused) {
borderColor = 'border-[#92722A]';
}
return (
<div className="relative">
@ -26,7 +35,7 @@ const Input = ({ placeholder = '', type = 'text', value = '', onChange = () => {
onFocus={() => setIsFocused(true)}
onBlur={() => setIsFocused(false)}
required={required}
className={`block w-full h-10 rounded-md border-2 ${borderColor} focus:ring-0 px-4 text-sm bg-white transition-colors [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none`}
className={`block w-full h-10 rounded-md border-2 ${borderColor} focus:ring-0 px-4 text-sm ${bgColor} transition-colors [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none`}
/>
</div>
);
@ -38,24 +47,32 @@ const Select = ({
value = '',
onChange,
loading = false,
disabled = false
disabled = false,
error = false,
className = ''
}) => {
const [isFocused, setIsFocused] = React.useState(false);
let borderColor = 'border-[#CBA344]';
let bgColor = 'bg-white';
if (error) {
borderColor = 'border-red-500';
bgColor = 'bg-red-50';
} else if (value || isFocused) {
borderColor = 'border-[#92722A]';
}
return (
<div className="relative">
<select
className={`block w-full h-10 rounded-md border-2 ${
disabled ? 'bg-gray-50 cursor-not-allowed' : 'bg-white'
} ${
value ? 'border-[#92722A]' : 'border-[#CBA344]'
} ${
isFocused && !disabled ? 'border-[#92722A]' : ''
} focus:ring-0 px-4 pr-10 text-sm appearance-none`}
className={`block w-full h-10 rounded-md border-2 px-4 pr-10 text-sm appearance-none focus:ring-0 ${
disabled ? 'bg-gray-50 cursor-not-allowed' : bgColor
} ${borderColor} ${className}`}
value={value}
onChange={onChange}
onFocus={() => !disabled && setIsFocused(true)}
onBlur={() => !disabled && setIsFocused(false)}
onBlur={() => setIsFocused(false)}
disabled={disabled || loading}
>
<option value="" disabled>{loading ? 'Loading...' : placeholder}</option>
@ -139,6 +156,8 @@ const ProductData = ({
onBack = () => {},
loading = false,
error = '',
quarter = '',
year = '',
}) => {
const [showDataDefinitions, setShowDataDefinitions] = React.useState(true);
const [showEntryRules, setShowEntryRules] = React.useState(true);
@ -157,8 +176,32 @@ const ProductData = ({
const [unitOptions, setUnitOptions] = React.useState([]);
const [isLoadingUnits, setIsLoadingUnits] = React.useState(false);
const [unitsError, setUnitsError] = React.useState('');
const [activeTab, setActiveTab] = React.useState('current');
const [isInitialLoad, setIsInitialLoad] = React.useState(true);
const [remarks, setRemarks] = React.useState('');
const [formErrors, setFormErrors] = React.useState({});
const [quarterPeriods, setQuarterPeriods] = useState(null);
const [isLoadingPeriods, setIsLoadingPeriods] = useState(false);
// Fetch quarter periods when component mounts or quarter/year changes
useEffect(() => {
const fetchQuarterPeriods = async () => {
if (!quarter || !year) return;
try {
setIsLoadingPeriods(true);
const quarterNumber = quarter.replace('Q', ''); // Convert 'Q3' to '3'
const response = await getQuarterPeriods(parseInt(year), `Q${quarterNumber}`);
setQuarterPeriods(response.data);
} catch (error) {
console.error('Failed to fetch quarter periods:', error);
} finally {
setIsLoadingPeriods(false);
}
};
fetchQuarterPeriods();
}, [quarter, year]);
React.useEffect(() => {
const variationController = new AbortController();
@ -265,6 +308,65 @@ const ProductData = ({
}
}, [products]);
const requiredMessage = 'Required';
const validateForm = () => {
const errors = {};
let isValid = true;
products.forEach((product, index) => {
// Product validation
if (!product.product) {
// errors[`product_${index}`] = 'Product (HS Code - Name) is required';
errors[`product_${index}`] = requiredMessage;
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);
return isValid;
};
const handleNext = () => {
if (validateForm()) {
onNext();
}
};
const addProduct = () => {
if (products.length >= MAX_PRODUCTS) return;
const newProduct = {
@ -292,25 +394,156 @@ const ProductData = ({
onProductsChange(products.filter((p) => p.id !== id));
};
const updateProductField = (id, field, value, options = []) => {
onProductsChange(
products.map((product) => {
if (product.id !== id) return product;
// When product is selected, store both ID and name
const fetchPreviousForecastData = async (productId, establishmentId) => {
console.log('fetchPreviousForecastData called with:', {
productId,
establishmentId,
quarter,
year
});
if (!productId || !establishmentId || !quarter || !year) {
console.warn('Missing required parameters for fetchPreviousForecastData:', {
hasProductId: !!productId,
hasEstablishmentId: !!establishmentId,
hasQuarter: !!quarter,
hasYear: !!year
});
return null;
}
try {
// Ensure quarter is in the format 'Q1', 'Q2', etc.
const formattedQuarter = quarter.startsWith('Q') ? quarter : `Q${quarter}`;
const params = {
establishment_id: establishmentId,
quarter: formattedQuarter,
year: year,
product_id: productId
};
console.log('Calling getPreviousForecastData with params:', params);
const response = await getPreviousForecastData(
establishmentId,
formattedQuarter,
year,
productId
);
console.log('API Response:', response);
if (!response) {
console.warn('Empty response from getPreviousForecastData');
return null;
}
// Return the response data or the response itself if data doesn't exist
const result = response.data || response;
console.log('Processed forecast data:', result);
return result;
} catch (error) {
console.error('Error in fetchPreviousForecastData:', {
message: error.message,
response: error.response?.data,
status: error.response?.status,
config: error.config
});
return null;
}
};
// const handleProductSelect = async (productId, establishmentId, id) => {
// if (!productId || !establishmentId) return;
// try {
// const forecastData = await fetchPreviousForecastData(productId, establishmentId);
// console.log('Forecast data received:', forecastData);
// 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:', error);
// }
// };
const updateProductField = (id, field, value, options = null) => {
const updatedProducts = products.map(product => {
if (product.id === id) {
// Clear error for the field being updated
if (field === 'product') {
const selectedProduct = options.find(p => p.value === value || p.id === value);
return {
const selectedProduct = options?.find(opt => opt.value === value);
const updatedProduct = {
...product,
product: value,
productName: selectedProduct?.label || selectedProduct?.name || '',
productName: selectedProduct?.label || '',
productId: selectedProduct?.value || '',
[field]: value
};
// Fetch previous forecast data when product is selected
if (selectedProduct?.value) {
// Try to get establishmentId from sessionStorage first, then fallback to localStorage
let establishmentId = sessionStorage.getItem('establishment_id') ||
localStorage.getItem('establishmentId') ||
localStorage.getItem('establishment_id');
console.log('Retrieved establishment ID:', establishmentId);
if (establishmentId && quarter && year) {
// Get the actual product ID from the selected product object
const selectedProductId = selectedProduct.originalData?.id || selectedProduct.value;
console.log('Fetching forecast data for:', {
productId: selectedProductId,
establishmentId,
quarter,
year,
storageSource: sessionStorage.getItem('establishment_id') ? 'sessionStorage' : 'localStorage'
});
handleProductSelect(selectedProductId, establishmentId, id);
} else {
console.warn('Missing required parameters for forecast data fetch:', {
hasEstablishmentId: !!establishmentId,
establishmentIdValue: establishmentId, // Log the actual value for debugging
hasQuarter: !!quarter,
hasYear: !!year,
availableStorage: {
sessionStorage: sessionStorage.getItem('establishment_id') ? 'exists' : 'not found',
localStorage: localStorage.getItem('establishmentId') ? 'exists' : 'not found',
localStorageAlt: localStorage.getItem('establishment_id') ? 'exists' : 'not found'
}
});
}
}
return updatedProduct;
}
// When unit is selected, store both ID and name
if (field === 'unit') {
const selectedUnit = options.find(u => u.value === value || u.id === value);
const selectedUnit = options?.find(u => u.value === value || u.id === value);
return {
...product,
unit: value,
@ -342,10 +575,79 @@ const ProductData = ({
}
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) => {
console.log('handleProductSelect called with:', { productId, establishmentId, id });
if (!productId || !establishmentId) {
console.warn('Missing required parameters in handleProductSelect:', {
hasProductId: !!productId,
hasEstablishmentId: !!establishmentId
});
return;
}
try {
console.log('Fetching forecast data...');
const forecastData = await fetchPreviousForecastData(productId, establishmentId);
console.log('Forecast data received:', forecastData);
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;
console.log('Processing forecast data:', data);
const updatedProducts = products.map(product => {
if (product.id === id) {
const updatedProduct = {
...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 || ''
};
console.log('Updated product data:', updatedProduct);
return updatedProduct;
}
return product;
});
console.log('Updating products with new data');
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;
@ -358,7 +660,14 @@ const ProductData = ({
<div className="h-12 w-12 animate-spin rounded-full border-[3px] border-[#92722A] border-t-transparent" />
</div>
)}
<h2 className="text-xl font-semibold text-[#232528] mb-2">Step 2: Product Data Monthly Output & Cost</h2>
<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>
{(quarter || year) && (
<div className="text-sm font-medium text-gray-600">
Current Quarter: <span className="text-[#92722A]">Q{quarter}-{year}</span>
</div>
)}
</div>
{!showInfoCard && (
<button
onClick={() => setShowInfoCard(true)}
@ -494,12 +803,28 @@ const ProductData = ({
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Product (HS Code - Name) <span className="text-red-500">*</span></label>
<Select
placeholder={isLoadingProducts ? 'Loading products...' : 'Select product HS code'}
options={productOptions}
value={p.product || ''}
onChange={(e) => updateProductField(p.id, 'product', e.target.value, productOptions)}
/>
<div>
<Select
placeholder={isLoadingProducts ? 'Loading products...' : 'Select product HS code'}
options={productOptions}
value={p.product || ''}
onChange={(e) => {
// Clear error when user selects a product
if (formErrors[`product_${idx}`]) {
const newErrors = { ...formErrors };
delete newErrors[`product_${idx}`];
setFormErrors(newErrors);
}
updateProductField(p.id, 'product', e.target.value, productOptions);
}}
loading={isLoadingProducts}
disabled={isLoadingProducts}
error={!!formErrors[`product_${idx}`]}
/>
{formErrors[`product_${idx}`] && (
<p className="mt-1 text-sm text-red-600">{formErrors[`product_${idx}`]}</p>
)}
</div>
{productsError && productOptions.length === 0 && (
<p className="mt-1 text-xs text-red-600">{productsError}</p>
)}
@ -511,8 +836,12 @@ const ProductData = ({
options={unitOptions}
value={p.unit || ''}
onChange={(e) => updateProductField(p.id, 'unit', e.target.value, unitOptions)}
error={!!formErrors[`unit_${idx}`]}
/>
{unitsError && unitOptions.length === 0 && (
{formErrors[`unit_${idx}`] && (
<p className="mt-1 text-xs text-red-600">{formErrors[`unit_${idx}`]}</p>
)}
{!formErrors[`unit_${idx}`] && unitsError && unitOptions.length === 0 && (
<p className="mt-1 text-xs text-red-600">{unitsError}</p>
)}
</div>
@ -524,26 +853,38 @@ const ProductData = ({
value={p.capacity || ''}
onChange={(e) => updateProductField(p.id, 'capacity', e.target.value)}
required
error={!!formErrors[`capacity_${idx}`]}
/>
{formErrors[`capacity_${idx}`] && (
<p className="mt-1 text-xs text-red-600">{formErrors[`capacity_${idx}`]}</p>
)}
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<SectionBox title="PREVIOUS QUARTER (Q4-2024)" badgeBg="#E7F5FF" badgeText="#003CFF">
<SectionBox
title={quarterPeriods ? `PREVIOUS QUARTER (${quarterPeriods.previous_quarter}-${quarterPeriods.previous_year})` : 'PREVIOUS QUARTER (Q-YYYY)'}
badgeBg="#E7F5FF"
badgeText="#003CFF"
>
<div className="space-y-4">
<div className="flex items-end space-x-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Oct Qty</label>
<label className="block text-sm font-medium text-gray-700 mb-1">
{quarterPeriods?.previous_month?.previous_period_one } Qty
</label>
<Input
className="w-24"
type="number"
placeholder="Enter"
value={p.previousQuantity || ''}
onChange={(e) => updateProductField(p.id, 'previousQuantity', e.target.value)}
value={p.octQuantity || ''}
onChange={(e) => updateProductField(p.id, 'octQuantity', e.target.value)}
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Nov Qty</label>
<label className="block text-sm font-medium text-gray-700 mb-1">
{quarterPeriods?.previous_month?.previous_period_two } Qty
</label>
<Input
className="w-24"
type="number"
@ -553,7 +894,9 @@ const ProductData = ({
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Dec Qty</label>
<label className="block text-sm font-medium text-gray-700 mb-1">
{quarterPeriods?.previous_month?.previous_period_three} Qty
</label>
<Input
className="w-24"
type="number"
@ -563,10 +906,11 @@ const ProductData = ({
/>
</div>
</div>
<div className="border-t border-gray-200 my-2"></div>
<div className="flex items-end space-x-4">
<div className="flex items-end space-x-4 mt-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Oct Cost (AED)</label>
<label className="block text-sm font-medium text-gray-700 mb-1">
{quarterPeriods?.previous_month?.previous_period_one } Cost (AED)
</label>
<Input
className="w-24"
type="number"
@ -576,7 +920,9 @@ const ProductData = ({
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Nov Cost (AED)</label>
<label className="block text-sm font-medium text-gray-700 mb-1">
{quarterPeriods?.previous_month?.previous_period_two } Cost (AED)
</label>
<Input
className="w-24"
type="number"
@ -586,7 +932,9 @@ const ProductData = ({
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Dec Cost (AED)</label>
<label className="block text-sm font-medium text-gray-700 mb-1">
{quarterPeriods?.previous_month?.previous_period_three } Cost (AED)
</label>
<Input
className="w-24"
placeholder="Enter"
@ -599,76 +947,111 @@ const ProductData = ({
</div>
</SectionBox>
<SectionBox title="CURRENT QUARTER (Q1-2025)" badgeBg="#F3FAF4" badgeText="#2F663C">
<SectionBox
title={quarterPeriods ? `CURRENT QUARTER (${quarterPeriods.current_quarter} ${quarterPeriods.current_year})` : `CURRENT QUARTER (${quarter}-${year})`}
badgeBg="#F3FAF4"
badgeText="#2F663C"
>
<div className="space-y-4">
<div className="flex items-end space-x-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Jan Qty<span className="text-red-600">*</span></label>
<label className="block text-sm font-medium text-gray-700 mb-1">
{quarterPeriods?.current_month?.current_period_one} Qty<span className="text-red-600">*</span>
</label>
<Input
className="w-24"
type="number"
value={p.janQuantity || ''}
required
error={!!formErrors[`janQuantity_${idx}`]}
placeholder="Enter"
onChange={(e) => updateProductField(p.id, 'janQuantity', e.target.value)}
/>
{formErrors[`janQuantity_${idx}`] && (
<p className="mt-1 text-xs text-red-600">{formErrors[`janQuantity_${idx}`]}</p>
)}
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Feb Qty<span className="text-red-600">*</span></label>
<label className="block text-sm font-medium text-gray-700 mb-1">
{quarterPeriods?.current_month?.current_period_two } Qty<span className="text-red-600">*</span>
</label>
<Input
className="w-24"
type="number"
value={p.febQuantity || ''}
required
error={!!formErrors[`febQuantity_${idx}`]}
placeholder="Enter"
onChange={(e) => updateProductField(p.id, 'febQuantity', e.target.value)}
/>
{formErrors[`febQuantity_${idx}`] && (
<p className="mt-1 text-xs text-red-600">{formErrors[`febQuantity_${idx}`]}</p>
)}
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Mar Qty<span className="text-red-600">*</span></label>
<label className="block text-sm font-medium text-gray-700 mb-1">
{quarterPeriods?.current_month?.current_period_three } Qty<span className="text-red-600">*</span>
</label>
<Input
className="w-24"
type="number"
value={p.marQuantity || ''}
required
error={!!formErrors[`marQuantity_${idx}`]}
placeholder="Enter"
onChange={(e) => updateProductField(p.id, 'marQuantity', e.target.value)}
/>
{formErrors[`marQuantity_${idx}`] && (
<p className="mt-1 text-xs text-red-600">{formErrors[`marQuantity_${idx}`]}</p>
)}
</div>
</div>
<div className="flex items-end space-x-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Jan Cost (AED)<span className="text-red-600">*</span></label>
<label className="block text-sm font-medium text-gray-700 mb-1">
{quarterPeriods?.current_month?.current_period_one } Cost (AED)<span className="text-red-600">*</span>
</label>
<Input
className="w-24"
type="number"
value={p.janCost || ''}
required
error={!!formErrors[`janCost_${idx}`]}
placeholder="Enter"
onChange={(e) => updateProductField(p.id, 'janCost', e.target.value)}
/>
{formErrors[`janCost_${idx}`] && (
<p className="mt-1 text-xs text-red-600">{formErrors[`janCost_${idx}`]}</p>
)}
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Feb Cost (AED)<span className="text-red-600">*</span></label>
<label className="block text-sm font-medium text-gray-700 mb-1">
{quarterPeriods?.current_month?.current_period_two} Cost (AED)<span className="text-red-600">*</span>
</label>
<Input
className="w-24"
type="number"
value={p.febCost || ''}
required
error={!!formErrors[`febCost_${idx}`]}
placeholder="Enter"
onChange={(e) => updateProductField(p.id, 'febCost', e.target.value)}
/>
{formErrors[`febCost_${idx}`] && (
<p className="mt-1 text-xs text-red-600">{formErrors[`febCost_${idx}`]}</p>
)}
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Mar Cost (AED)<span className="text-red-600">*</span></label>
<label className="block text-sm font-medium text-gray-700 mb-1">
{quarterPeriods?.current_month?.current_period_three} Cost (AED)<span className="text-red-600">*</span>
</label>
<Input
className="w-24"
type="number"
value={p.marCost || ''}
required
error={!!formErrors[`marCost_${idx}`]}
placeholder="Enter"
onChange={(e) => updateProductField(p.id, 'marCost', e.target.value)}
/>
{formErrors[`marCost_${idx}`] && (
<p className="mt-1 text-xs text-red-600">{formErrors[`marCost_${idx}`]}</p>
)}
</div>
</div>
<div>
@ -686,77 +1069,116 @@ const ProductData = ({
</div>
</SectionBox>
<SectionBox title="NEXT QUARTER FORECAST (Q2-2025)" badgeBg="#FFF7E9" badgeText="#F29F0E">
<SectionBox
title={`NEXT QUARTER FORECAST (${quarterPeriods?.forecast_quarter || 'Q'}-${quarterPeriods?.forecast_year || '2025'})`}
badgeBg="#FFF7E9"
badgeText="#F29F0E"
>
<div className="space-y-4">
<div className="flex items-end space-x-3">
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Apr Qty<span className="text-red-600">*</span></label>
<label className="block text-sm font-medium text-gray-700 mb-1">
{quarterPeriods?.forecast_month?.forecast_period_one } Qty<span className="text-red-600">*</span>
</label>
<Input
className="w-24"
type="number"
required
placeholder="Enter"
value={p.aprQuantity || ''}
error={!!formErrors[`aprQuantity_${idx}`]}
onChange={(e) => updateProductField(p.id, 'aprQuantity', e.target.value)}
/>
{formErrors[`aprQuantity_${idx}`] && (
<p className="mt-1 text-xs text-red-600">{formErrors[`aprQuantity_${idx}`]}</p>
)}
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">May Qty<span className="text-red-600">*</span></label>
<label className="block text-sm font-medium text-gray-700 mb-1">
{quarterPeriods?.forecast_month?.forecast_period_two } Qty<span className="text-red-600">*</span>
</label>
<Input
className="w-24"
type="number"
required
placeholder="Enter"
value={p.mayQuantity || ''}
error={!!formErrors[`mayQuantity_${idx}`]}
onChange={(e) => updateProductField(p.id, 'mayQuantity', e.target.value)}
/>
{formErrors[`mayQuantity_${idx}`] && (
<p className="mt-1 text-xs text-red-600">{formErrors[`mayQuantity_${idx}`]}</p>
)}
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">Jun Qty<span className="text-red-600">*</span></label>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
{quarterPeriods?.forecast_month?.forecast_period_three } Qty<span className="text-red-600">*</span>
</label>
<Input
className="w-24"
type="number"
required
placeholder="Enter"
value={p.junQuantity || ''}
value={p.junQuantity || ''}
error={!!formErrors[`junQuantity_${idx}`]}
onChange={(e) => updateProductField(p.id, 'junQuantity', e.target.value)}
/>
{formErrors[`junQuantity_${idx}`] && (
<p className="mt-1 text-xs text-red-600">{formErrors[`junQuantity_${idx}`]}</p>
)}
</div>
</div>
<div className="flex items-end space-x-3">
<div>
<label className="block text-sm font-medium text-gray-700 w-26 mb-1">Apr Cost (AED)<span className="text-red-600">*</span></label>
<label className="block text-sm font-medium text-gray-700 w-26 mb-1">
{quarterPeriods?.forecast_month?.forecast_period_one} Cost (AED)<span className="text-red-600">*</span>
</label>
<Input
className="w-24"
type="number"
required
placeholder="Enter"
value={p.aprCost || ''}
error={!!formErrors[`aprCost_${idx}`]}
onChange={(e) => updateProductField(p.id, 'aprCost', e.target.value)}
/>
{formErrors[`aprCost_${idx}`] && (
<p className="mt-1 text-xs text-red-600">{formErrors[`aprCost_${idx}`]}</p>
)}
</div>
<div>
<label className="block text-sm font-medium text-gray-700 w-27 mb-1">May Cost (AED)<span className="text-red-600">*</span></label>
<label className="block text-sm font-medium text-gray-700 w-27 mb-1">
{quarterPeriods?.forecast_month?.forecast_period_two } Cost (AED)<span className="text-red-600">*</span>
</label>
<Input
className="w-24"
type="number"
required
placeholder="Enter"
value={p.mayCost || ''}
error={!!formErrors[`mayCost_${idx}`]}
onChange={(e) => updateProductField(p.id, 'mayCost', e.target.value)}
/>
{formErrors[`mayCost_${idx}`] && (
<p className="mt-1 text-xs text-red-600">{formErrors[`mayCost_${idx}`]}</p>
)}
</div>
<div>
<label className="block text-sm font-medium text-gray-700 w-27 mb-1">Jun Cost (AED)<span className="text-red-600">*</span></label>
<label className="block text-sm font-medium text-gray-700 w-27 mb-1">
{quarterPeriods?.forecast_month?.forecast_period_three } Cost (AED)<span className="text-red-600">*</span>
</label>
<Input
className="w-24"
type="number"
required
placeholder="Enter"
placeholder="Enter"
value={p.junCost || ''}
error={!!formErrors[`junCost_${idx}`]}
onChange={(e) => updateProductField(p.id, 'junCost', e.target.value)}
/>
{formErrors[`junCost_${idx}`] && (
<p className="mt-1 text-xs text-red-600">{formErrors[`junCost_${idx}`]}</p>
)}
</div>
</div>
<div>
@ -806,7 +1228,7 @@ const ProductData = ({
<span>Back</span>
</button>
<button
onClick={onNext}
onClick={handleNext}
className="inline-flex items-center gap-2 h-9 px-5 rounded-md bg-[#92722A] text-white hover:bg-[#7b5c1f] cursor-pointer"
>
<span>Next</span>

View File

@ -763,10 +763,10 @@ const ReviewSubmit = ({
<span>Back</span>
</button>
<button
disabled={!confirm || isSubmitting || hasSubmitted}
disabled={!confirm || isSubmitting}
onClick={onSubmit}
className={`inline-flex items-center justify-center h-9 px-5 rounded-md transition-colors ${
!confirm || isSubmitting || hasSubmitted
!confirm || isSubmitting
? 'bg-gray-200 text-gray-400 cursor-not-allowed'
: 'bg-[#92722A] text-white hover:bg-[#7b5c1f]'
}`}

View File

@ -62,7 +62,9 @@ const Survey = () => {
const [productsData, setProductsData] = React.useState([createEmptyProduct(1)]);
const [isSubmitting, setIsSubmitting] = React.useState(false);
const [hasSubmitted, setHasSubmitted] = React.useState(false);
const [showSuccessPopup, setShowSuccessPopup] = React.useState(false);
const [toast, setToast] = React.useState(null);
const [error, setError] = React.useState('');
const [submissionDetail, setSubmissionDetail] = React.useState(null);
const [viewLoading, setViewLoading] = React.useState(false);
const [viewError, setViewError] = React.useState('');
@ -395,6 +397,21 @@ const Survey = () => {
};
}, [establishmentData, parseNumber, productsData, stringify]);
console.log("productsData",productsData)
const handleNext = () => {
if (step < 3) {
setStep(step + 1);
}
};
const handleBack = () => {
if (step > 1) {
setStep(step - 1);
} else {
navigate('/');
}
};
const handleSubmit = React.useCallback(async () => {
setIsSubmitting(true);
try {
@ -402,9 +419,10 @@ const Survey = () => {
const response = await submitSurvey(payload);
// Navigate to overview page after successful submission
navigate('/overview');
// navigate('/overview');
console.log('Survey submission response:', response);
showToast('success', response?.message || 'You have successfully submitted the survey.');
setShowSuccessPopup(true);
setHasSubmitted(true);
} catch (error) {
console.error('Failed to submit survey', error);
@ -604,8 +622,12 @@ const Survey = () => {
<ProductData
products={productsData}
onProductsChange={setProductsData}
onBack={() => setStep(1)}
onNext={() => setStep(3)}
onNext={handleNext}
onBack={handleBack}
loading={isSubmitting}
error={error}
quarter={establishmentData.quarter}
year={establishmentData.year}
/>
)}
{step === 3 && (
@ -628,6 +650,54 @@ const Survey = () => {
)}
</div>
</main>
{/* Success Popup - Moved to root level */}
{showSuccessPopup && (
<div className="fixed inset-0 z-[9999] flex items-center justify-center bg-black/40 p-4">
<div className="bg-white w-[343px] rounded-lg shadow-2xl border border-gray-200">
<div className="p-6 text-center flex flex-col gap-6">
{/* Green Check Icon */}
<div className="mx-auto flex items-center justify-center h-12 w-12 rounded-full bg-green-100">
<svg
className="h-6 w-6 text-green-600"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M5 13l4 4L19 7"
/>
</svg>
</div>
{/* Title */}
<h3 className="text-lg font-semibold text-gray-900">
Submission successful
</h3>
{/* Message */}
<p className="text-sm text-gray-700">
Thank you. Your IP Quarterly Survey for <span className="font-semibold text-gray-900">{establishmentData?.quarter} {establishmentData?.year}</span> has been submitted.
</p>
{/* Button */}
<button
type="button"
className="px-8 py-2.5 bg-[#92722A] text-white font-medium rounded-md hover:bg-[#7b5c1f] transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-[#92722A]"
onClick={() => {
setShowSuccessPopup(false);
navigate('/overview');
}}
>
OK
</button>
</div>
</div>
</div>
)}
</div>
);
};

View File

@ -2,6 +2,7 @@ import { getRequest, postRequest } from '@/services/api/CommonService';
import resolveEstablishmentId from '@/services/utils/establishment';
const endpoint = '/submissions';
const QUARTER_PERIODS_ENDPOINT = 'https://ipi.venbait.in/api/getQuarterPeriods';
// Add this new function to fetch submissions with pagination
export const getSubmissions = async (page = 1, limit = 100, config = {}) => {
@ -51,9 +52,46 @@ export const fetchSubmissionDetail = async (submissionId, config = {}) => {
};
// Add the new function to the default export
export const getQuarterPeriods = async (currentYear, currentQuarter) => {
try {
const response = await postRequest(QUARTER_PERIODS_ENDPOINT, {
current_year: currentYear,
current_quarter: currentQuarter
});
return response.data;
} catch (error) {
console.error('Error fetching quarter periods:', error);
throw error;
}
};
export const getPreviousForecastData = async (establishmentId, quarter, year, productId) => {
try {
const response = await getRequest(
`https://ipi.venbait.in/api/submissions/getPreviousForecastData`,
{
params: {
establishment_id: establishmentId,
quarter: quarter,
year: year,
product_id: productId
}
}
);
// Check if response has data property and return it, otherwise return the response as is
return response.data || response;
} catch (error) {
console.error('Error fetching previous forecast data:', error);
// Instead of throwing, return null to handle the error gracefully
return null;
}
};
export default {
getSubmissions, // Add this line
getSubmissions,
submitSurvey,
fetchSubmissionHistory,
fetchSubmissionDetail,
getQuarterPeriods,
getPreviousForecastData
};