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 { import {
fetchVariationReasons, fetchVariationReasons,
fetchZeroTargetReasons, fetchZeroTargetReasons,
@ -12,9 +13,17 @@ const calendarIcon = '/assets/images/Duedate.svg';
const nextIcon = '/assets/images/mdi_page-next-outline.svg'; const nextIcon = '/assets/images/mdi_page-next-outline.svg';
const trashActiveSrc = '/assets/images/Trash - active.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 [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 ( return (
<div className="relative"> <div className="relative">
@ -26,7 +35,7 @@ const Input = ({ placeholder = '', type = 'text', value = '', onChange = () => {
onFocus={() => setIsFocused(true)} onFocus={() => setIsFocused(true)}
onBlur={() => setIsFocused(false)} onBlur={() => setIsFocused(false)}
required={required} 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> </div>
); );
@ -38,24 +47,32 @@ const Select = ({
value = '', value = '',
onChange, onChange,
loading = false, loading = false,
disabled = false disabled = false,
error = false,
className = ''
}) => { }) => {
const [isFocused, setIsFocused] = React.useState(false); 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 ( return (
<div className="relative"> <div className="relative">
<select <select
className={`block w-full h-10 rounded-md border-2 ${ 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' : 'bg-white' disabled ? 'bg-gray-50 cursor-not-allowed' : bgColor
} ${ } ${borderColor} ${className}`}
value ? 'border-[#92722A]' : 'border-[#CBA344]'
} ${
isFocused && !disabled ? 'border-[#92722A]' : ''
} focus:ring-0 px-4 pr-10 text-sm appearance-none`}
value={value} value={value}
onChange={onChange} onChange={onChange}
onFocus={() => !disabled && setIsFocused(true)} onFocus={() => !disabled && setIsFocused(true)}
onBlur={() => !disabled && setIsFocused(false)} onBlur={() => setIsFocused(false)}
disabled={disabled || loading} disabled={disabled || loading}
> >
<option value="" disabled>{loading ? 'Loading...' : placeholder}</option> <option value="" disabled>{loading ? 'Loading...' : placeholder}</option>
@ -139,6 +156,8 @@ const ProductData = ({
onBack = () => {}, onBack = () => {},
loading = false, loading = false,
error = '', error = '',
quarter = '',
year = '',
}) => { }) => {
const [showDataDefinitions, setShowDataDefinitions] = React.useState(true); const [showDataDefinitions, setShowDataDefinitions] = React.useState(true);
const [showEntryRules, setShowEntryRules] = React.useState(true); const [showEntryRules, setShowEntryRules] = React.useState(true);
@ -157,8 +176,32 @@ const ProductData = ({
const [unitOptions, setUnitOptions] = React.useState([]); const [unitOptions, setUnitOptions] = React.useState([]);
const [isLoadingUnits, setIsLoadingUnits] = React.useState(false); const [isLoadingUnits, setIsLoadingUnits] = React.useState(false);
const [unitsError, setUnitsError] = React.useState(''); const [unitsError, setUnitsError] = React.useState('');
const [activeTab, setActiveTab] = React.useState('current');
const [isInitialLoad, setIsInitialLoad] = React.useState(true); const [isInitialLoad, setIsInitialLoad] = React.useState(true);
const [remarks, setRemarks] = React.useState(''); 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(() => { React.useEffect(() => {
const variationController = new AbortController(); const variationController = new AbortController();
@ -265,6 +308,65 @@ const ProductData = ({
} }
}, [products]); }, [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 = () => { const addProduct = () => {
if (products.length >= MAX_PRODUCTS) return; if (products.length >= MAX_PRODUCTS) return;
const newProduct = { const newProduct = {
@ -292,25 +394,156 @@ const ProductData = ({
onProductsChange(products.filter((p) => p.id !== id)); onProductsChange(products.filter((p) => p.id !== id));
}; };
const updateProductField = (id, field, value, options = []) => { const fetchPreviousForecastData = async (productId, establishmentId) => {
onProductsChange( console.log('fetchPreviousForecastData called with:', {
products.map((product) => { productId,
if (product.id !== id) return product; establishmentId,
quarter,
// When product is selected, store both ID and name 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') { if (field === 'product') {
const selectedProduct = options.find(p => p.value === value || p.id === value); const selectedProduct = options?.find(opt => opt.value === value);
return { const updatedProduct = {
...product, ...product,
product: value, product: value,
productName: selectedProduct?.label || selectedProduct?.name || '', productName: selectedProduct?.label || '',
productId: selectedProduct?.value || '',
[field]: 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') { 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 { return {
...product, ...product,
unit: value, unit: value,
@ -342,10 +575,79 @@ const ProductData = ({
} }
return { ...product, [field]: value }; 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 canAddMoreProducts = products.length < MAX_PRODUCTS;
const isLoading = loading || isLoadingReasons || isLoadingZeroReasons || isLoadingProducts || isLoadingUnits; 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 className="h-12 w-12 animate-spin rounded-full border-[3px] border-[#92722A] border-t-transparent" />
</div> </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 && ( {!showInfoCard && (
<button <button
onClick={() => setShowInfoCard(true)} onClick={() => setShowInfoCard(true)}
@ -494,12 +803,28 @@ const ProductData = ({
<div className="grid grid-cols-1 md:grid-cols-3 gap-4"> <div className="grid grid-cols-1 md:grid-cols-3 gap-4">
<div> <div>
<label className="block text-sm font-medium text-gray-700 mb-1">Product (HS Code - Name) <span className="text-red-500">*</span></label> <label className="block text-sm font-medium text-gray-700 mb-1">Product (HS Code - Name) <span className="text-red-500">*</span></label>
<Select <div>
placeholder={isLoadingProducts ? 'Loading products...' : 'Select product HS code'} <Select
options={productOptions} placeholder={isLoadingProducts ? 'Loading products...' : 'Select product HS code'}
value={p.product || ''} options={productOptions}
onChange={(e) => updateProductField(p.id, 'product', e.target.value, 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 && ( {productsError && productOptions.length === 0 && (
<p className="mt-1 text-xs text-red-600">{productsError}</p> <p className="mt-1 text-xs text-red-600">{productsError}</p>
)} )}
@ -511,8 +836,12 @@ const ProductData = ({
options={unitOptions} options={unitOptions}
value={p.unit || ''} value={p.unit || ''}
onChange={(e) => updateProductField(p.id, 'unit', e.target.value, unitOptions)} 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> <p className="mt-1 text-xs text-red-600">{unitsError}</p>
)} )}
</div> </div>
@ -524,26 +853,38 @@ const ProductData = ({
value={p.capacity || ''} value={p.capacity || ''}
onChange={(e) => updateProductField(p.id, 'capacity', e.target.value)} onChange={(e) => updateProductField(p.id, 'capacity', e.target.value)}
required required
error={!!formErrors[`capacity_${idx}`]}
/> />
{formErrors[`capacity_${idx}`] && (
<p className="mt-1 text-xs text-red-600">{formErrors[`capacity_${idx}`]}</p>
)}
</div> </div>
</div> </div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4"> <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="space-y-4">
<div className="flex items-end space-x-4"> <div className="flex items-end space-x-4">
<div> <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 <Input
className="w-24" className="w-24"
type="number" type="number"
placeholder="Enter" placeholder="Enter"
value={p.previousQuantity || ''} value={p.octQuantity || ''}
onChange={(e) => updateProductField(p.id, 'previousQuantity', e.target.value)} onChange={(e) => updateProductField(p.id, 'octQuantity', e.target.value)}
/> />
</div> </div>
<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 <Input
className="w-24" className="w-24"
type="number" type="number"
@ -553,7 +894,9 @@ const ProductData = ({
/> />
</div> </div>
<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 <Input
className="w-24" className="w-24"
type="number" type="number"
@ -563,10 +906,11 @@ const ProductData = ({
/> />
</div> </div>
</div> </div>
<div className="border-t border-gray-200 my-2"></div> <div className="flex items-end space-x-4 mt-4">
<div className="flex items-end space-x-4">
<div> <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 <Input
className="w-24" className="w-24"
type="number" type="number"
@ -576,7 +920,9 @@ const ProductData = ({
/> />
</div> </div>
<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 <Input
className="w-24" className="w-24"
type="number" type="number"
@ -586,7 +932,9 @@ const ProductData = ({
/> />
</div> </div>
<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 <Input
className="w-24" className="w-24"
placeholder="Enter" placeholder="Enter"
@ -599,76 +947,111 @@ const ProductData = ({
</div> </div>
</SectionBox> </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="space-y-4">
<div className="flex items-end space-x-4"> <div className="flex items-end space-x-4">
<div> <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 <Input
className="w-24" className="w-24"
type="number" type="number"
value={p.janQuantity || ''} value={p.janQuantity || ''}
required error={!!formErrors[`janQuantity_${idx}`]}
placeholder="Enter" placeholder="Enter"
onChange={(e) => updateProductField(p.id, 'janQuantity', e.target.value)} 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>
<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 <Input
className="w-24" className="w-24"
type="number" type="number"
value={p.febQuantity || ''} value={p.febQuantity || ''}
required error={!!formErrors[`febQuantity_${idx}`]}
placeholder="Enter" placeholder="Enter"
onChange={(e) => updateProductField(p.id, 'febQuantity', e.target.value)} 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>
<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 <Input
className="w-24" className="w-24"
type="number" type="number"
value={p.marQuantity || ''} value={p.marQuantity || ''}
required error={!!formErrors[`marQuantity_${idx}`]}
placeholder="Enter" placeholder="Enter"
onChange={(e) => updateProductField(p.id, 'marQuantity', e.target.value)} 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> </div>
<div className="flex items-end space-x-4"> <div className="flex items-end space-x-4">
<div> <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 <Input
className="w-24" className="w-24"
type="number" type="number"
value={p.janCost || ''} value={p.janCost || ''}
required error={!!formErrors[`janCost_${idx}`]}
placeholder="Enter" placeholder="Enter"
onChange={(e) => updateProductField(p.id, 'janCost', e.target.value)} 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>
<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 <Input
className="w-24" className="w-24"
type="number" type="number"
value={p.febCost || ''} value={p.febCost || ''}
required error={!!formErrors[`febCost_${idx}`]}
placeholder="Enter" placeholder="Enter"
onChange={(e) => updateProductField(p.id, 'febCost', e.target.value)} 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>
<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 <Input
className="w-24" className="w-24"
type="number" type="number"
value={p.marCost || ''} value={p.marCost || ''}
required error={!!formErrors[`marCost_${idx}`]}
placeholder="Enter" placeholder="Enter"
onChange={(e) => updateProductField(p.id, 'marCost', e.target.value)} 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> </div>
<div> <div>
@ -686,77 +1069,116 @@ const ProductData = ({
</div> </div>
</SectionBox> </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="space-y-4">
<div className="flex items-end space-x-3"> <div className="flex items-end space-x-3">
<div> <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 <Input
className="w-24" className="w-24"
type="number" type="number"
required required
placeholder="Enter" placeholder="Enter"
value={p.aprQuantity || ''} value={p.aprQuantity || ''}
error={!!formErrors[`aprQuantity_${idx}`]}
onChange={(e) => updateProductField(p.id, 'aprQuantity', e.target.value)} 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>
<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 <Input
className="w-24" className="w-24"
type="number" type="number"
required required
placeholder="Enter" placeholder="Enter"
value={p.mayQuantity || ''} value={p.mayQuantity || ''}
error={!!formErrors[`mayQuantity_${idx}`]}
onChange={(e) => updateProductField(p.id, 'mayQuantity', e.target.value)} 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>
<div>
<div> <label className="block text-sm font-medium text-gray-700 mb-1">
<label className="block text-sm font-medium text-gray-700 mb-1">Jun Qty<span className="text-red-600">*</span></label> {quarterPeriods?.forecast_month?.forecast_period_three } Qty<span className="text-red-600">*</span>
</label>
<Input <Input
className="w-24" className="w-24"
type="number" type="number"
required required
placeholder="Enter" placeholder="Enter"
value={p.junQuantity || ''} value={p.junQuantity || ''}
error={!!formErrors[`junQuantity_${idx}`]}
onChange={(e) => updateProductField(p.id, 'junQuantity', e.target.value)} 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> </div>
<div className="flex items-end space-x-3"> <div className="flex items-end space-x-3">
<div> <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 <Input
className="w-24" className="w-24"
type="number" type="number"
required required
placeholder="Enter" placeholder="Enter"
value={p.aprCost || ''} value={p.aprCost || ''}
error={!!formErrors[`aprCost_${idx}`]}
onChange={(e) => updateProductField(p.id, 'aprCost', e.target.value)} 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>
<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 <Input
className="w-24" className="w-24"
type="number" type="number"
required required
placeholder="Enter" placeholder="Enter"
value={p.mayCost || ''} value={p.mayCost || ''}
error={!!formErrors[`mayCost_${idx}`]}
onChange={(e) => updateProductField(p.id, 'mayCost', e.target.value)} 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>
<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 <Input
className="w-24" className="w-24"
type="number" type="number"
required required
placeholder="Enter" placeholder="Enter"
value={p.junCost || ''} value={p.junCost || ''}
error={!!formErrors[`junCost_${idx}`]}
onChange={(e) => updateProductField(p.id, 'junCost', e.target.value)} 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> </div>
<div> <div>
@ -806,7 +1228,7 @@ const ProductData = ({
<span>Back</span> <span>Back</span>
</button> </button>
<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" 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> <span>Next</span>

View File

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

View File

@ -62,7 +62,9 @@ const Survey = () => {
const [productsData, setProductsData] = React.useState([createEmptyProduct(1)]); const [productsData, setProductsData] = React.useState([createEmptyProduct(1)]);
const [isSubmitting, setIsSubmitting] = React.useState(false); const [isSubmitting, setIsSubmitting] = React.useState(false);
const [hasSubmitted, setHasSubmitted] = React.useState(false); const [hasSubmitted, setHasSubmitted] = React.useState(false);
const [showSuccessPopup, setShowSuccessPopup] = React.useState(false);
const [toast, setToast] = React.useState(null); const [toast, setToast] = React.useState(null);
const [error, setError] = React.useState('');
const [submissionDetail, setSubmissionDetail] = React.useState(null); const [submissionDetail, setSubmissionDetail] = React.useState(null);
const [viewLoading, setViewLoading] = React.useState(false); const [viewLoading, setViewLoading] = React.useState(false);
const [viewError, setViewError] = React.useState(''); const [viewError, setViewError] = React.useState('');
@ -395,6 +397,21 @@ const Survey = () => {
}; };
}, [establishmentData, parseNumber, productsData, stringify]); }, [establishmentData, parseNumber, productsData, stringify]);
console.log("productsData",productsData) 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 () => { const handleSubmit = React.useCallback(async () => {
setIsSubmitting(true); setIsSubmitting(true);
try { try {
@ -402,9 +419,10 @@ const Survey = () => {
const response = await submitSurvey(payload); const response = await submitSurvey(payload);
// Navigate to overview page after successful submission // Navigate to overview page after successful submission
navigate('/overview'); // navigate('/overview');
console.log('Survey submission response:', response); console.log('Survey submission response:', response);
showToast('success', response?.message || 'You have successfully submitted the survey.'); showToast('success', response?.message || 'You have successfully submitted the survey.');
setShowSuccessPopup(true);
setHasSubmitted(true); setHasSubmitted(true);
} catch (error) { } catch (error) {
console.error('Failed to submit survey', error); console.error('Failed to submit survey', error);
@ -604,8 +622,12 @@ const Survey = () => {
<ProductData <ProductData
products={productsData} products={productsData}
onProductsChange={setProductsData} onProductsChange={setProductsData}
onBack={() => setStep(1)} onNext={handleNext}
onNext={() => setStep(3)} onBack={handleBack}
loading={isSubmitting}
error={error}
quarter={establishmentData.quarter}
year={establishmentData.year}
/> />
)} )}
{step === 3 && ( {step === 3 && (
@ -628,6 +650,54 @@ const Survey = () => {
)} )}
</div> </div>
</main> </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> </div>
); );
}; };

View File

@ -2,6 +2,7 @@ import { getRequest, postRequest } from '@/services/api/CommonService';
import resolveEstablishmentId from '@/services/utils/establishment'; import resolveEstablishmentId from '@/services/utils/establishment';
const endpoint = '/submissions'; const endpoint = '/submissions';
const QUARTER_PERIODS_ENDPOINT = 'https://ipi.venbait.in/api/getQuarterPeriods';
// Add this new function to fetch submissions with pagination // Add this new function to fetch submissions with pagination
export const getSubmissions = async (page = 1, limit = 100, config = {}) => { 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 // 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 { export default {
getSubmissions, // Add this line getSubmissions,
submitSurvey, submitSurvey,
fetchSubmissionHistory, fetchSubmissionHistory,
fetchSubmissionDetail, fetchSubmissionDetail,
getQuarterPeriods,
getPreviousForecastData
}; };