parent
ec1eca181f
commit
0a3d8e6b5b
@ -6,7 +6,10 @@ import HeaderBar from '@/components/layout/HeaderBar';
|
|||||||
import DetailedOverview from '@/components/overview/DetailedOverview';
|
import DetailedOverview from '@/components/overview/DetailedOverview';
|
||||||
|
|
||||||
const searchIconSrc = '/assets/images/material-symbols_search-rounded.svg';
|
const searchIconSrc = '/assets/images/material-symbols_search-rounded.svg';
|
||||||
|
const pencilIconSrc = '/assets/images/PencilSimple.svg';
|
||||||
|
import { fetchSubmissionDetail } from '@/services/submissions/submissionService';
|
||||||
|
const trashActiveSrc = '/assets/images/Trash - active.svg';
|
||||||
|
const trashInactiveSrc = '/assets/images/Trash - Inactive.svg';
|
||||||
const formatDateTime = (value) => {
|
const formatDateTime = (value) => {
|
||||||
if (!value) return '—';
|
if (!value) return '—';
|
||||||
const date = new Date(value);
|
const date = new Date(value);
|
||||||
@ -138,13 +141,45 @@ export const SubmissionHistory = ({ history = [], loading = false, error = '' })
|
|||||||
const [searchTerm, setSearchTerm] = React.useState('');
|
const [searchTerm, setSearchTerm] = React.useState('');
|
||||||
const [selectedSubmission, setSelectedSubmission] = React.useState(null);
|
const [selectedSubmission, setSelectedSubmission] = React.useState(null);
|
||||||
const [currentPage, setCurrentPage] = React.useState(1);
|
const [currentPage, setCurrentPage] = React.useState(1);
|
||||||
|
const [hoveredDelete, setHoveredDelete] = React.useState(null);
|
||||||
|
|
||||||
const pageSize = 10;
|
const pageSize = 10;
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const handleViewDetails = (submission) => {
|
const handleViewDetails = (submission) => {
|
||||||
navigate('/overview', { state: { selectedSubmission: submission } });
|
navigate('/overview', { state: { selectedSubmission: submission } });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleEdit = async (submission) => {
|
||||||
|
if (submission.status.toLowerCase() === 'draft') {
|
||||||
|
try {
|
||||||
|
const submissionData = await fetchSubmissionDetail(submission.id);
|
||||||
|
navigate('/survey', {
|
||||||
|
state: {
|
||||||
|
submission: submissionData,
|
||||||
|
isEdit: true,
|
||||||
|
fromDraft: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching submission details:', error);
|
||||||
|
// Optionally show an error message to the user
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDeleteDraft = async (e, submission) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
try {
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error deleting submission:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const normalizedHistory = React.useMemo(() => {
|
const normalizedHistory = React.useMemo(() => {
|
||||||
if (!Array.isArray(history)) return [];
|
if (!Array.isArray(history)) return [];
|
||||||
return history.map((entry, index) => {
|
return history.map((entry, index) => {
|
||||||
@ -186,6 +221,7 @@ export const SubmissionHistory = ({ history = [], loading = false, error = '' })
|
|||||||
|
|
||||||
const tableRows = filtered.map((item) => {
|
const tableRows = filtered.map((item) => {
|
||||||
const productsLabel = `${item.products}`;
|
const productsLabel = `${item.products}`;
|
||||||
|
const isDraft = item.status.toLowerCase() === 'draft';
|
||||||
return [
|
return [
|
||||||
item.survey_name,
|
item.survey_name,
|
||||||
item.year,
|
item.year,
|
||||||
@ -193,12 +229,47 @@ export const SubmissionHistory = ({ history = [], loading = false, error = '' })
|
|||||||
getStatusBadge(item.status),
|
getStatusBadge(item.status),
|
||||||
<span className="whitespace-nowrap">{item.submission_on}</span>,
|
<span className="whitespace-nowrap">{item.submission_on}</span>,
|
||||||
productsLabel,
|
productsLabel,
|
||||||
<button
|
<div className="flex items-center gap-2">
|
||||||
onClick={() => handleViewDetails(item)}
|
{isDraft ? (
|
||||||
className="text-[#9E792B] hover:text-[#7b5f22] font-medium"
|
<>
|
||||||
>
|
<button
|
||||||
View Details
|
onClick={(e) => {
|
||||||
</button>,
|
e.stopPropagation();
|
||||||
|
handleEdit(item);
|
||||||
|
}}
|
||||||
|
className="p-1 focus:outline-none"
|
||||||
|
title="Edit Draft"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src={pencilIconSrc}
|
||||||
|
alt="Edit"
|
||||||
|
className="h-5 w-5 opacity-60 hover:opacity-100 transition-opacity"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={(e) => handleDeleteDraft(e, item)}
|
||||||
|
onMouseEnter={() => setHoveredDelete(item.id)}
|
||||||
|
onMouseLeave={() => setHoveredDelete(null)}
|
||||||
|
className="p-1 focus:outline-none"
|
||||||
|
title="Delete Draft"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src={hoveredDelete === item.id ? trashInactiveSrc : trashActiveSrc}
|
||||||
|
alt="Delete"
|
||||||
|
className="h-5 w-5 transition-opacity"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<button
|
||||||
|
onClick={() => handleViewDetails(item)}
|
||||||
|
className="text-[#9E792B] hover:text-[#7b5f22] font-medium"
|
||||||
|
>
|
||||||
|
View Details
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -175,6 +175,65 @@ const EstablishmentInfo = ({
|
|||||||
localStorage.setItem('surveyRemarks', remarksresubmit)
|
localStorage.setItem('surveyRemarks', remarksresubmit)
|
||||||
|
|
||||||
const [surveyData, setSurveyData] = React.useState(() => {
|
const [surveyData, setSurveyData] = React.useState(() => {
|
||||||
|
// For draft submission, use the data from the submission
|
||||||
|
|
||||||
|
if (location.state?.fromDraft && location.state?.submission) {
|
||||||
|
const submission = location.state.submission;
|
||||||
|
// Extract data from submission
|
||||||
|
const submissionData = submission.data || {};
|
||||||
|
const quarter = submissionData.quarter || '';
|
||||||
|
const year = submissionData.year || '';
|
||||||
|
const endDate = submission.quarter_window?.end_date || '';
|
||||||
|
|
||||||
|
// Extract establishment info
|
||||||
|
const establishmentData = submissionData.establishment || {};
|
||||||
|
|
||||||
|
// Extract employee data from establishment object
|
||||||
|
const employeeData = establishmentData.employee_info || establishmentData || {};
|
||||||
|
|
||||||
|
// Update the parent component with the submission data
|
||||||
|
onChange({
|
||||||
|
...data,
|
||||||
|
...submissionData,
|
||||||
|
establishmentId: establishmentData.id,
|
||||||
|
establishmentName: establishmentData.factory_name,
|
||||||
|
permanentFactoryCode: establishmentData.permanent_factory_code,
|
||||||
|
|
||||||
|
industryCode: establishmentData.industry_code,
|
||||||
|
licenseNumber: establishmentData.license_number,
|
||||||
|
isicCode: establishmentData.isic_code,
|
||||||
|
emirate: establishmentData.establishment_emirate_id,
|
||||||
|
establishment_contact_email: establishmentData.establishment_contact_email,
|
||||||
|
employeeInfo: {
|
||||||
|
...defaultEmployeeInfo,
|
||||||
|
emiratiMale: employeeData.emirati_male || '',
|
||||||
|
nonEmiratiMale: employeeData.non_emirati_male || '',
|
||||||
|
emiratiFemale: employeeData.emirati_female || '',
|
||||||
|
nonEmiratiFemale: employeeData.non_emirati_female || '',
|
||||||
|
totalEmployees: employeeData.total_employees || '',
|
||||||
|
totalEmirati: employeeData.total_emirati || ''
|
||||||
|
},
|
||||||
|
quarter,
|
||||||
|
year,
|
||||||
|
end_date: endDate
|
||||||
|
});
|
||||||
|
|
||||||
|
const surveyPeriod = {
|
||||||
|
quarter,
|
||||||
|
year,
|
||||||
|
endDate
|
||||||
|
};
|
||||||
|
|
||||||
|
localStorage.setItem('currentSurveyPeriod', JSON.stringify(surveyPeriod));
|
||||||
|
localStorage.setItem('currentQuarter', quarter);
|
||||||
|
localStorage.setItem('currentYear', year);
|
||||||
|
|
||||||
|
// Store the full submission data for reference
|
||||||
|
localStorage.setItem('currentSubmission', JSON.stringify(submissionData));
|
||||||
|
|
||||||
|
return surveyPeriod;
|
||||||
|
}
|
||||||
|
|
||||||
// For resubmission, use the quarter/year from the submission data
|
// For resubmission, use the quarter/year from the submission data
|
||||||
if (location.state?.fromResubmit && data?.quarter && data?.year) {
|
if (location.state?.fromResubmit && data?.quarter && data?.year) {
|
||||||
const surveyPeriod = {
|
const surveyPeriod = {
|
||||||
@ -183,7 +242,7 @@ const EstablishmentInfo = ({
|
|||||||
endDate: data.end_date || ''
|
endDate: data.end_date || ''
|
||||||
};
|
};
|
||||||
localStorage.setItem('currentSurveyPeriod', JSON.stringify(surveyPeriod));
|
localStorage.setItem('currentSurveyPeriod', JSON.stringify(surveyPeriod));
|
||||||
localStorage.setItem('currentQuarter', data.quarter); // Store quarter separately
|
localStorage.setItem('currentQuarter', data.quarter);
|
||||||
localStorage.setItem('currentYear', data.year);
|
localStorage.setItem('currentYear', data.year);
|
||||||
return surveyPeriod;
|
return surveyPeriod;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import { toast, ToastContainer } from 'react-toastify';
|
||||||
|
import 'react-toastify/dist/ReactToastify.css';
|
||||||
// import { useLocation } from 'react-router-dom';
|
// import { useLocation } from 'react-router-dom';
|
||||||
import { getQuarterPeriods, getPreviousForecastData, getEstablishmentProducts } from '@/services/submissions/submissionService';
|
import { getQuarterPeriods, getPreviousForecastData, getEstablishmentProducts , submitSurvey,resubmitSurvey} from '@/services/submissions/submissionService';
|
||||||
import {
|
import {
|
||||||
fetchVariationReasons,
|
fetchVariationReasons,
|
||||||
fetchZeroTargetReasons,
|
fetchZeroTargetReasons,
|
||||||
@ -302,6 +304,17 @@ const Select = ({
|
|||||||
<img src={caretDownSrc} alt="open" className="h-4 w-4" />
|
<img src={caretDownSrc} alt="open" className="h-4 w-4" />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
<ToastContainer
|
||||||
|
position="top-right"
|
||||||
|
autoClose={3000}
|
||||||
|
hideProgressBar={false}
|
||||||
|
newestOnTop={false}
|
||||||
|
closeOnClick
|
||||||
|
rtl={false}
|
||||||
|
pauseOnFocusLoss
|
||||||
|
draggable
|
||||||
|
pauseOnHover
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -378,6 +391,10 @@ 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(null);
|
const [unitsError, setUnitsError] = React.useState(null);
|
||||||
|
const [isSavingDraft, setIsSavingDraft] = React.useState(false);
|
||||||
|
|
||||||
|
|
||||||
|
const location = useLocation(); // Add this line
|
||||||
|
|
||||||
// Debug: Log products data when it changes
|
// Debug: Log products data when it changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -454,6 +471,256 @@ const ProductData = ({
|
|||||||
fetchQuarterPeriods();
|
fetchQuarterPeriods();
|
||||||
}, [quarter, year]);
|
}, [quarter, year]);
|
||||||
|
|
||||||
|
const handleSaveDraft = async () => {
|
||||||
|
try {
|
||||||
|
setIsSavingDraft(true);
|
||||||
|
|
||||||
|
// Get current submission from localStorage
|
||||||
|
const currentSubmissionStr = localStorage.getItem('currentSubmission');
|
||||||
|
if (!currentSubmissionStr) {
|
||||||
|
throw new Error('Current submission data not found');
|
||||||
|
}
|
||||||
|
const currentSubmission = JSON.parse(currentSubmissionStr);
|
||||||
|
const currentQuarter = localStorage.getItem('currentQuarter');
|
||||||
|
const currentYear = localStorage.getItem('currentYear')
|
||||||
|
// Get establishment ID from current submission
|
||||||
|
const submissionId = currentSubmission.id; // Changed this line to get ID from currentSubmission
|
||||||
|
|
||||||
|
const establishmentId = currentSubmission.establishment_id;
|
||||||
|
if (!establishmentId) {
|
||||||
|
throw new Error('Establishment ID not found in submission');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get user profile from localStorage
|
||||||
|
const userProfileStr = localStorage.getItem('user_profile');
|
||||||
|
if (!userProfileStr) {
|
||||||
|
throw new Error('User profile not found');
|
||||||
|
}
|
||||||
|
const userProfile = JSON.parse(userProfileStr);
|
||||||
|
const createdById = userProfile?.id;
|
||||||
|
if (!createdById) {
|
||||||
|
throw new Error('User ID not found in profile');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get quarter and year from current submission
|
||||||
|
const effectiveQuarter = currentQuarter;
|
||||||
|
const effectiveYear = currentYear;
|
||||||
|
if (!effectiveQuarter || !effectiveYear) {
|
||||||
|
throw new Error('Quarter and year are required in submission');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get establishment data
|
||||||
|
const establishment = currentSubmission.establishment;
|
||||||
|
if (!establishment) {
|
||||||
|
throw new Error('Establishment data not found in submission');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare products data in the required format
|
||||||
|
const formattedProducts = products.map(product => {
|
||||||
|
// Get the selected product option to access product_id
|
||||||
|
const selectedProduct = productOptions.find(p => p.value === product.product);
|
||||||
|
const existingProduct = currentSubmission.products?.find(p =>
|
||||||
|
p.product_id === product.product_id ||
|
||||||
|
p.product?.id === product.product_id
|
||||||
|
);
|
||||||
|
return {
|
||||||
|
id: existingProduct?.id || 0,
|
||||||
|
product_id: selectedProduct?.product_id || product.product_id || null,
|
||||||
|
unit_id: product.unit?.toString() || '', // Ensure unit_id is a string
|
||||||
|
annual_installed_capacity: product.capacity || '0',
|
||||||
|
previous_quantity_period_one: product.octQuantity || '0',
|
||||||
|
previous_quantity_period_two: product.novQuantity || '0',
|
||||||
|
previous_quantity_period_three: product.decQuantity || '0',
|
||||||
|
previous_cost_period_one: product.octCost || '0',
|
||||||
|
previous_cost_period_two: product.novCost || '0',
|
||||||
|
previous_cost_period_three: product.decCost || '0',
|
||||||
|
current_quantity_period_one: product.janQuantity || '0',
|
||||||
|
current_quantity_period_two: product.febQuantity || '0',
|
||||||
|
current_quantity_period_three: product.marQuantity || '0',
|
||||||
|
current_cost_period_one: product.janCost || '0',
|
||||||
|
current_cost_period_two: product.febCost || '0',
|
||||||
|
current_cost_period_three: product.marCost || '0',
|
||||||
|
forecast_quantity_period_one: product.aprQuantity || '0',
|
||||||
|
forecast_quantity_period_two: product.mayQuantity || '0',
|
||||||
|
forecast_quantity_period_three: product.junQuantity || '0',
|
||||||
|
forecast_cost_period_one: product.aprCost || '0',
|
||||||
|
forecast_cost_period_two: product.mayCost || '0',
|
||||||
|
forecast_cost_period_three: product.junCost || '0',
|
||||||
|
// Previous quarter (Q4) - October
|
||||||
|
previous_quantity: product.octQuantity || '0',
|
||||||
|
previous_cost: product.octCost || '0',
|
||||||
|
// Current quarter (Q1) - Using November as current (as per previous implementation)
|
||||||
|
// If you want to use a different field for current, replace novQuantity/novCost with the appropriate field
|
||||||
|
current_quantity: product.novQuantity || '0',
|
||||||
|
current_cost: product.novCost || '0',
|
||||||
|
// Next quarter forecast (Q2) - December
|
||||||
|
forecast_quantity: product.decQuantity || '0',
|
||||||
|
forecast_cost: product.decCost || '0',
|
||||||
|
variation_reason_master_id: product.variationReason || '',
|
||||||
|
other_variation_reason: product.otherVariationReason || '',
|
||||||
|
zero_target_reason_master_id: product.zeroTargetReason || '',
|
||||||
|
other_zero_target_reason: product.otherZeroTargetReason || '',
|
||||||
|
remarks: product.remarks || remarks || '' // Use product.remarks if available, otherwise use the component's remarks state
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// Prepare the complete payload with values from currentSubmission
|
||||||
|
const payload = {
|
||||||
|
establishment_id: establishmentId,
|
||||||
|
quarter: effectiveQuarter,
|
||||||
|
year: effectiveYear,
|
||||||
|
emirati_male: establishment.emirati_male || 0,
|
||||||
|
emirati_female: establishment.emirati_female || 0,
|
||||||
|
non_emirati_male: establishment.non_emirati_male || 0,
|
||||||
|
non_emirati_female: establishment.non_emirati_female || 0,
|
||||||
|
total_emirati: establishment.total_emirati || 0,
|
||||||
|
total_employees: establishment.total_employees || 0,
|
||||||
|
status: "Draft",
|
||||||
|
created_by: createdById,
|
||||||
|
products: formattedProducts
|
||||||
|
};
|
||||||
|
|
||||||
|
// Example: const response = await api.saveDraft(payload);
|
||||||
|
let response;
|
||||||
|
if (submissionId) {
|
||||||
|
// Update existing submission
|
||||||
|
response = await resubmitSurvey(submissionId, payload);
|
||||||
|
toast.success('Draft updated successfully', {
|
||||||
|
position: "top-right",
|
||||||
|
autoClose: 3000,
|
||||||
|
hideProgressBar: false,
|
||||||
|
closeOnClick: true,
|
||||||
|
pauseOnHover: true,
|
||||||
|
draggable: true
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Create new submission
|
||||||
|
response = await submitSurvey(payload);
|
||||||
|
|
||||||
|
// Update local storage with the new submission ID if this was a create operation
|
||||||
|
if (response && response.id) {
|
||||||
|
const updatedSubmission = { ...currentSubmission, id: response.id };
|
||||||
|
localStorage.setItem('currentSubmission', JSON.stringify(updatedSubmission));
|
||||||
|
}
|
||||||
|
|
||||||
|
toast.success('Draft saved successfully', {
|
||||||
|
position: "top-right",
|
||||||
|
autoClose: 3000,
|
||||||
|
hideProgressBar: false,
|
||||||
|
closeOnClick: true,
|
||||||
|
pauseOnHover: true,
|
||||||
|
draggable: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error saving draft:', error);
|
||||||
|
toast.error(error.message || 'Failed to save draft');
|
||||||
|
} finally {
|
||||||
|
setIsSavingDraft(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Add this useEffect hook after the existing hooks
|
||||||
|
useEffect(() => {
|
||||||
|
const loadDraftData = () => {
|
||||||
|
try {
|
||||||
|
const isEditMode = location.state?.isEdit || false;
|
||||||
|
|
||||||
|
// Clear existing data if not in edit mode
|
||||||
|
if (!isEditMode) {
|
||||||
|
onProductsChange([{ id: Date.now() }]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentSubmissionStr = localStorage.getItem('currentSubmission');
|
||||||
|
if (!currentSubmissionStr) return;
|
||||||
|
|
||||||
|
const currentSubmission = JSON.parse(currentSubmissionStr);
|
||||||
|
|
||||||
|
if (currentSubmission.status === 'Draft' && currentSubmission.products && currentSubmission.products.length > 0) {
|
||||||
|
const formattedProducts = currentSubmission.products.map(product => {
|
||||||
|
// Get product name and HS code from the nested product object
|
||||||
|
const productName = product.product?.product_name || '';
|
||||||
|
const hsCode = product.product?.hs_code || '';
|
||||||
|
const displayName = productName ? `${hsCode} - ${productName}` : '';
|
||||||
|
return {
|
||||||
|
id: product.id,
|
||||||
|
product_id: product.product_id,
|
||||||
|
product: product.product_id?.toString() || '', // This should be the ID as string
|
||||||
|
productId: product.product_id?.toString() || '', // Also include as productId
|
||||||
|
name: displayName, // This will be used in displayValue
|
||||||
|
product_name: product.product?.product_name || '', // Raw name if needed
|
||||||
|
hs_code: hsCode, // Include HS code separately
|
||||||
|
productName: productName, // Include product name separately
|
||||||
|
product_value: product.product_id?.toString() || '',
|
||||||
|
unit: product.unit_id ? product.unit_id.toString() : '',
|
||||||
|
unit_name: product.unit?.uom || '', // Add unit name for display
|
||||||
|
capacity: product.annual_installed_capacity || '0',
|
||||||
|
|
||||||
|
// Previous quarter (Q4) - October, November, December
|
||||||
|
octQuantity: product.previous_quantity_period_one || '0',
|
||||||
|
octCost: product.previous_cost_period_one || '0',
|
||||||
|
novQuantity: product.previous_quantity_period_two || '0',
|
||||||
|
novCost: product.previous_cost_period_two || '0',
|
||||||
|
decQuantity: product.previous_quantity_period_three || '0',
|
||||||
|
decCost: product.previous_cost_period_three || '0',
|
||||||
|
|
||||||
|
// Current quarter (Q1) - January, February, March
|
||||||
|
janQuantity: product.current_quantity_period_one || '0',
|
||||||
|
janCost: product.current_cost_period_one || '0',
|
||||||
|
febQuantity: product.current_quantity_period_two || '0',
|
||||||
|
febCost: product.current_cost_period_two || '0',
|
||||||
|
marQuantity: product.current_quantity_period_three || '0',
|
||||||
|
marCost: product.current_cost_period_three || '0',
|
||||||
|
|
||||||
|
// Forecast quarter (Q2) - April, May, June
|
||||||
|
aprQuantity: product.forecast_quantity_period_one || '0',
|
||||||
|
aprCost: product.forecast_cost_period_one || '0',
|
||||||
|
mayQuantity: product.forecast_quantity_period_two || '0',
|
||||||
|
mayCost: product.forecast_cost_period_two || '0',
|
||||||
|
junQuantity: product.forecast_quantity_period_three || '0',
|
||||||
|
junCost: product.forecast_cost_period_three || '0',
|
||||||
|
|
||||||
|
// Variation reasons
|
||||||
|
variationReason: product.variation_reason_master_id?.toString() || '',
|
||||||
|
otherVariationReason: product.other_variation_reason || '',
|
||||||
|
zeroTargetReason: product.zero_target_reason_master_id?.toString() || '',
|
||||||
|
otherZeroTargetReason: product.other_zero_target_reason || '',
|
||||||
|
remarks: product.remarks || ''
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
onProductsChange(formattedProducts);
|
||||||
|
setSelectedProductIds(formattedProducts.map(p => p.product_id));
|
||||||
|
|
||||||
|
// Set remarks if available
|
||||||
|
if (currentSubmission.products[0]?.remarks) {
|
||||||
|
setRemarks(currentSubmission.products[0].remarks);
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
localStorage.setItem('surveyRemarks', currentSubmission.products[0].remarks);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set quarter and year in localStorage if not already set
|
||||||
|
if (currentSubmission.quarter && currentSubmission.year) {
|
||||||
|
localStorage.setItem('currentQuarter', currentSubmission.quarter);
|
||||||
|
localStorage.setItem('currentYear', currentSubmission.year.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error loading draft data:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isInitialLoad) {
|
||||||
|
loadDraftData();
|
||||||
|
setIsInitialLoad(false);
|
||||||
|
}
|
||||||
|
}, [isInitialLoad, onProductsChange, location.state]);
|
||||||
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const variationController = new AbortController();
|
const variationController = new AbortController();
|
||||||
const zeroController = new AbortController();
|
const zeroController = new AbortController();
|
||||||
@ -616,7 +883,7 @@ const ProductData = ({
|
|||||||
endDate: ''
|
endDate: ''
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const location = useLocation();
|
// const location = useLocation();
|
||||||
|
|
||||||
// Update surveyData when props or location changes
|
// Update surveyData when props or location changes
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
@ -1195,7 +1462,7 @@ const location = useLocation();
|
|||||||
<div className="flex items-center space-x-1">
|
<div className="flex items-center space-x-1">
|
||||||
<img src={calendarIcon} alt="" className="h-4 w-4" />
|
<img src={calendarIcon} alt="" className="h-4 w-4" />
|
||||||
<span className="font-medium">Due:</span>
|
<span className="font-medium">Due:</span>
|
||||||
<span className="text-sm text-[#6C4527] whitespace-nowrap">
|
<span className="text-sm text-[#6C4527] whitespace-nowrap">
|
||||||
{surveyData.endDate ? (
|
{surveyData.endDate ? (
|
||||||
new Date(surveyData.endDate).toLocaleDateString('en-US', {
|
new Date(surveyData.endDate).toLocaleDateString('en-US', {
|
||||||
year: 'numeric',
|
year: 'numeric',
|
||||||
@ -1905,21 +2172,26 @@ const location = useLocation();
|
|||||||
|
|
||||||
|
|
||||||
<div className="flex flex-col sm:flex-row justify-between gap-5 mt-6">
|
<div className="flex flex-col sm:flex-row justify-between gap-5 mt-6">
|
||||||
<button
|
<div className="flex-1">
|
||||||
onClick={onBack}
|
|
||||||
className="inline-flex items-center gap-2 h-9 px-4 rounded-md border border-[#92722A] text-[#92722A] bg-transparent hover:bg-[#92722A]/10"
|
|
||||||
>
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
|
||||||
<path strokeLinecap="round" strokeLinejoin="round" d="M15 19l-7-7 7-7" />
|
|
||||||
</svg>
|
|
||||||
<span>Back</span>
|
|
||||||
</button>
|
|
||||||
<div className="flex gap-3">
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
onClick={onBack}
|
||||||
className="inline-flex items-center gap-2 h-9 px-5 rounded-md border border-[#92722A] text-[#92722A] bg-transparent hover:bg-[#92722A]/10 cursor-pointer"
|
className="inline-flex items-center justify-center gap-2 h-9 px-4 rounded-md border border-[#92722A] text-[#92722A] bg-transparent hover:bg-[#92722A]/10"
|
||||||
>
|
>
|
||||||
<span>Save as Draft</span>
|
<svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
|
||||||
|
<path strokeLinecap="round" strokeLinejoin="round" d="M15 19l-7-7 7-7" />
|
||||||
|
</svg>
|
||||||
|
<span>Back</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<button
|
||||||
|
className="inline-flex items-center justify-center h-9 px-4 rounded-md border border-[#92722A] text-[#92722A] bg-transparent hover:bg-[#F5F0E1] transition-colors"
|
||||||
|
onClick={handleSaveDraft}
|
||||||
|
disabled={isSavingDraft}
|
||||||
|
>
|
||||||
|
{/* <span>Save as Draft</span> */}
|
||||||
|
{isSavingDraft ? 'Saving...' : 'Save as Draft'}
|
||||||
|
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={handleNext}
|
onClick={handleNext}
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import Table from '@/components/common/Table';
|
import Table from '@/components/common/Table';
|
||||||
import { getQuarterPeriods } from '@/services/submissions/submissionService';
|
import { getQuarterPeriods , resubmitSurvey } from '@/services/submissions/submissionService';
|
||||||
import { getEmirates, fetchVariationReasons, fetchZeroTargetReasons } from '@/services/masters/masterService';
|
import { getEmirates, fetchVariationReasons, fetchZeroTargetReasons } from '@/services/masters/masterService';
|
||||||
const caretUpSrc = '/assets/images/caret-up.svg';
|
const caretUpSrc = '/assets/images/caret-up.svg';
|
||||||
const caretDownSrc = '/assets/images/CaretDown-black.svg';
|
const caretDownSrc = '/assets/images/CaretDown-black.svg';
|
||||||
@ -360,7 +360,9 @@ const ReviewSubmit = ({
|
|||||||
const [emirateName, setEmirateName] = useState('');
|
const [emirateName, setEmirateName] = useState('');
|
||||||
const [variationReasons, setVariationReasons] = useState([]);
|
const [variationReasons, setVariationReasons] = useState([]);
|
||||||
const [zeroTargetReasons, setZeroTargetReasons] = useState([]);
|
const [zeroTargetReasons, setZeroTargetReasons] = useState([]);
|
||||||
|
const [isSavingDraft, setIsSavingDraft] = React.useState(false);
|
||||||
|
const [productOptions, setProductOptions] = React.useState([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchEmiratesData = async () => {
|
const fetchEmiratesData = async () => {
|
||||||
try {
|
try {
|
||||||
@ -443,6 +445,193 @@ const ReviewSubmit = ({
|
|||||||
return '';
|
return '';
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const handleSaveDraft = async () => {
|
||||||
|
try {
|
||||||
|
setIsSavingDraft(true);
|
||||||
|
|
||||||
|
// Get current submission from localStorage
|
||||||
|
const currentSubmissionStr = localStorage.getItem('currentSubmission');
|
||||||
|
if (!currentSubmissionStr) {
|
||||||
|
throw new Error('Current submission data not found');
|
||||||
|
}
|
||||||
|
const currentSubmission = JSON.parse(currentSubmissionStr);
|
||||||
|
const currentQuarter = localStorage.getItem('currentQuarter');
|
||||||
|
const currentYear = localStorage.getItem('currentYear');
|
||||||
|
|
||||||
|
// Get establishment ID from current submission
|
||||||
|
const submissionId = currentSubmission.id;
|
||||||
|
const establishmentId = currentSubmission.establishment_id;
|
||||||
|
|
||||||
|
if (!establishmentId) {
|
||||||
|
throw new Error('Establishment ID not found in submission');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get user profile from localStorage
|
||||||
|
const userProfileStr = localStorage.getItem('user_profile');
|
||||||
|
if (!userProfileStr) {
|
||||||
|
throw new Error('User profile not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
const userProfile = JSON.parse(userProfileStr);
|
||||||
|
const createdById = userProfile?.id;
|
||||||
|
|
||||||
|
if (!createdById) {
|
||||||
|
throw new Error('User ID not found in profile');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get quarter and year from current submission
|
||||||
|
const effectiveQuarter = currentQuarter;
|
||||||
|
const effectiveYear = currentYear;
|
||||||
|
|
||||||
|
if (!effectiveQuarter || !effectiveYear) {
|
||||||
|
throw new Error('Quarter and year are required in submission');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get establishment data
|
||||||
|
const establishment = currentSubmission.establishment;
|
||||||
|
if (!establishment) {
|
||||||
|
throw new Error('Establishment data not found in submission');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare products data
|
||||||
|
const formattedProducts = products.map(product => ({
|
||||||
|
id: product.id || 0,
|
||||||
|
product_id: product.product_id || null,
|
||||||
|
unit_id: product.unit || '',
|
||||||
|
annual_installed_capacity: product.capacity || '0',
|
||||||
|
previous_quantity_period_one: product.octQuantity || '0',
|
||||||
|
previous_quantity_period_two: product.novQuantity || '0',
|
||||||
|
previous_quantity_period_three: product.decQuantity || '0',
|
||||||
|
previous_cost_period_one: product.octCost || '0',
|
||||||
|
previous_cost_period_two: product.novCost || '0',
|
||||||
|
previous_cost_period_three: product.decCost || '0',
|
||||||
|
current_quantity_period_one: product.janQuantity || '0',
|
||||||
|
current_quantity_period_two: product.febQuantity || '0',
|
||||||
|
current_quantity_period_three: product.marQuantity || '0',
|
||||||
|
current_cost_period_one: product.janCost || '0',
|
||||||
|
current_cost_period_two: product.febCost || '0',
|
||||||
|
current_cost_period_three: product.marCost || '0',
|
||||||
|
forecast_quantity_period_one: product.aprQuantity || '0',
|
||||||
|
forecast_quantity_period_two: product.mayQuantity || '0',
|
||||||
|
forecast_quantity_period_three: product.junQuantity || '0',
|
||||||
|
forecast_cost_period_one: product.aprCost || '0',
|
||||||
|
forecast_cost_period_two: product.mayCost || '0',
|
||||||
|
forecast_cost_period_three: product.junCost || '0',
|
||||||
|
variation_reason_master_id: product.variationReason || null,
|
||||||
|
other_variation_reason: product.otherVariationReason || '',
|
||||||
|
zero_target_reason_master_id: product.zeroTargetReason || null,
|
||||||
|
other_zero_target_reason: product.otherZeroTargetReason || '',
|
||||||
|
remarks: product.remarks || ''
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Prepare the complete payload
|
||||||
|
const payload = {
|
||||||
|
establishment_id: establishmentId,
|
||||||
|
quarter: effectiveQuarter,
|
||||||
|
year: effectiveYear,
|
||||||
|
emirati_male: establishment.emirati_male || 0,
|
||||||
|
emirati_female: establishment.emirati_female || 0,
|
||||||
|
non_emirati_male: establishment.non_emirati_male || 0,
|
||||||
|
non_emirati_female: establishment.non_emirati_female || 0,
|
||||||
|
total_emirati: establishment.total_emirati || 0,
|
||||||
|
total_employees: establishment.total_employees || 0,
|
||||||
|
status: "Draft",
|
||||||
|
created_by: createdById,
|
||||||
|
products: formattedProducts
|
||||||
|
};
|
||||||
|
let response;
|
||||||
|
try {
|
||||||
|
if (submissionId) {
|
||||||
|
// Update existing submission
|
||||||
|
response = await resubmitSurvey(submissionId, payload);
|
||||||
|
// Handle different response formats
|
||||||
|
const responseData = response?.data || response;
|
||||||
|
|
||||||
|
if (responseData) {
|
||||||
|
// Update local storage with the updated submission
|
||||||
|
const updatedSubmission = {
|
||||||
|
...currentSubmission,
|
||||||
|
...responseData,
|
||||||
|
updated_at: new Date().toISOString()
|
||||||
|
};
|
||||||
|
localStorage.setItem('currentSubmission', JSON.stringify(updatedSubmission));
|
||||||
|
|
||||||
|
toast.success('Draft updated successfully', {
|
||||||
|
position: "top-right",
|
||||||
|
autoClose: 3000,
|
||||||
|
hideProgressBar: false,
|
||||||
|
closeOnClick: true,
|
||||||
|
pauseOnHover: true,
|
||||||
|
draggable: true
|
||||||
|
});
|
||||||
|
return; // Success, exit the function
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Create new submission
|
||||||
|
response = await submitSurvey(payload);
|
||||||
|
// Handle different response formats
|
||||||
|
const responseData = response?.data || response;
|
||||||
|
|
||||||
|
if (responseData && (responseData.id || responseData.submission_id)) {
|
||||||
|
// Update local storage with the new submission ID
|
||||||
|
const updatedSubmission = {
|
||||||
|
...currentSubmission,
|
||||||
|
id: responseData.id || responseData.submission_id,
|
||||||
|
status: 'Draft',
|
||||||
|
created_at: new Date().toISOString()
|
||||||
|
};
|
||||||
|
localStorage.setItem('currentSubmission', JSON.stringify(updatedSubmission));
|
||||||
|
|
||||||
|
toast.success('Draft saved successfully', {
|
||||||
|
position: "top-right",
|
||||||
|
autoClose: 3000,
|
||||||
|
hideProgressBar: false,
|
||||||
|
closeOnClick: true,
|
||||||
|
pauseOnHover: true,
|
||||||
|
draggable: true
|
||||||
|
});
|
||||||
|
return; // Success, exit the function
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we get here, the response wasn't in the expected format
|
||||||
|
throw new Error('Unexpected response format from server');
|
||||||
|
|
||||||
|
} catch (apiError) {
|
||||||
|
console.error('API Error details:', {
|
||||||
|
message: apiError.message,
|
||||||
|
response: apiError.response,
|
||||||
|
stack: apiError.stack
|
||||||
|
});
|
||||||
|
|
||||||
|
// If we have a response with error details, use that
|
||||||
|
if (apiError.response?.data) {
|
||||||
|
const errorMessage = apiError.response.data.message || apiError.response.data.error || 'Unknown server error';
|
||||||
|
throw new Error(`Server error: ${errorMessage}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, rethrow the original error
|
||||||
|
throw apiError;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error in handleSaveDraft:', {
|
||||||
|
name: error.name,
|
||||||
|
message: error.message,
|
||||||
|
stack: error.stack
|
||||||
|
});
|
||||||
|
|
||||||
|
toast.error(error.message || 'Failed to save draft. Please try again.', {
|
||||||
|
position: "top-right",
|
||||||
|
autoClose: 5000,
|
||||||
|
hideProgressBar: false,
|
||||||
|
closeOnClick: true,
|
||||||
|
pauseOnHover: true,
|
||||||
|
draggable: true
|
||||||
|
});
|
||||||
|
} finally {
|
||||||
|
setIsSavingDraft(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
const handleSubmit = () => {
|
const handleSubmit = () => {
|
||||||
try {
|
try {
|
||||||
const submissionData = formatSubmissionData(
|
const submissionData = formatSubmissionData(
|
||||||
@ -1042,27 +1231,30 @@ const ReviewSubmit = ({
|
|||||||
<img src={caretDownSrc} alt="" className="h-4 w-4 rotate-90" />
|
<img src={caretDownSrc} alt="" className="h-4 w-4 rotate-90" />
|
||||||
<span>Back</span>
|
<span>Back</span>
|
||||||
</button>
|
</button>
|
||||||
<div className="flex gap-3">
|
<div className="flex gap-4">
|
||||||
<button
|
<button
|
||||||
type="button"
|
className="inline-flex items-center justify-center h-9 px-4 rounded-md border border-[#92722A] text-[#92722A] bg-transparent hover:bg-[#F5F0E1] transition-colors"
|
||||||
className="inline-flex items-center justify-center h-9 px-5 rounded-md border border-[#92722A] text-[#92722A] bg-transparent hover:bg-[#92722A]/10 transition-colors"
|
onClick={handleSaveDraft}
|
||||||
>
|
disabled={isSavingDraft}
|
||||||
<span>Save as Draft</span>
|
>
|
||||||
</button>
|
{/* <span>Save as Draft</span> */}
|
||||||
<button
|
{isSavingDraft ? 'Saving...' : 'Save as Draft'}
|
||||||
disabled={!confirm || isSubmitting}
|
|
||||||
onClick={onSubmit}
|
</button>
|
||||||
className={`inline-flex items-center justify-center h-9 px-5 rounded-md transition-colors ${
|
<button
|
||||||
!confirm || isSubmitting
|
disabled={!confirm || isSubmitting}
|
||||||
? 'bg-gray-200 text-gray-400 cursor-not-allowed'
|
onClick={onSubmit}
|
||||||
: 'bg-[#92722A] text-white hover:bg-[#7b5c1f]'
|
className={`inline-flex items-center justify-center h-9 px-5 rounded-md transition-colors ${
|
||||||
}`}
|
!confirm || isSubmitting
|
||||||
>
|
? 'bg-gray-200 text-gray-400 cursor-not-allowed'
|
||||||
<span>{submitButtonText}</span>
|
: 'bg-[#92722A] text-white hover:bg-[#7b5c1f]'
|
||||||
</button>
|
}`}
|
||||||
</div>
|
>
|
||||||
|
<span>{submitButtonText}</span>
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,13 +1,18 @@
|
|||||||
// src/pages/Overview/Overview.jsx
|
// src/pages/Overview/Overview.jsx
|
||||||
import React, { useState, useEffect, useMemo } from 'react';
|
import React, { useState, useEffect, useMemo } from 'react';
|
||||||
import { useNavigate, useLocation } from 'react-router-dom';
|
import { useNavigate, useLocation } from 'react-router-dom';
|
||||||
import { getSubmissions, getSubmissionHistoryByEstablishment } from '@/services/submissions/submissionService';
|
import { getSubmissions, getSubmissionHistoryByEstablishment, fetchSubmissionDetail } from '@/services/submissions/submissionService';
|
||||||
import HeaderBar from '@/components/layout/HeaderBar';
|
import HeaderBar from '@/components/layout/HeaderBar';
|
||||||
import Table from '@/components/common/Table';
|
import Table from '@/components/common/Table';
|
||||||
import DetailedOverview from '@/components/overview/DetailedOverview';
|
import DetailedOverview from '@/components/overview/DetailedOverview';
|
||||||
const searchIconSrc = '/assets/images/material-symbols_search-rounded.svg';
|
const searchIconSrc = '/assets/images/material-symbols_search-rounded.svg';
|
||||||
const caretDownSrc = '/assets/images/CaretDown.svg';
|
const caretDownSrc = '/assets/images/CaretDown.svg';
|
||||||
const downloadIconSrc = '/assets/images/DownloadSimple.svg';
|
const downloadIconSrc = '/assets/images/DownloadSimple.svg';
|
||||||
|
const pencilActiveSrc = '/assets/images/PencilSimple.svg';
|
||||||
|
const pencilInactiveSrc = '/assets/images/pencilsimple-inactive.svg';
|
||||||
|
// const deleteIconSrc = '/assets/images/delete.svg';
|
||||||
|
const deleteIconSrc = '/assets/images/Trash - active.svg';
|
||||||
|
const trashInactiveSrc = '/assets/images/Trash - Inactive.svg';
|
||||||
|
|
||||||
const statusStyles = {
|
const statusStyles = {
|
||||||
approved: 'text-[#2F663C] bg-[#F3FAF4]',
|
approved: 'text-[#2F663C] bg-[#F3FAF4]',
|
||||||
@ -46,6 +51,18 @@ const Overview = () => {
|
|||||||
setSelectedSubmission(location.state.selectedSubmission);
|
setSelectedSubmission(location.state.selectedSubmission);
|
||||||
}
|
}
|
||||||
}, [location.state]);
|
}, [location.state]);
|
||||||
|
const handleDeleteDraft = async (e, record) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
try {
|
||||||
|
// Add your delete logic here
|
||||||
|
// Example: await deleteSubmission(record.id);
|
||||||
|
// You might want to add a confirmation dialog before deleting
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error deleting submission:', error);
|
||||||
|
// Optionally show an error message to the user
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchSubmissions = async () => {
|
const fetchSubmissions = async () => {
|
||||||
@ -113,9 +130,48 @@ const Overview = () => {
|
|||||||
) : '-',
|
) : '-',
|
||||||
record.status,
|
record.status,
|
||||||
formatDate(record.created_at),
|
formatDate(record.created_at),
|
||||||
'View Details',
|
record.status === 'Draft' ? (
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<button
|
||||||
|
onClick={async (e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
try {
|
||||||
|
const submissionData = await fetchSubmissionDetail(record.id);
|
||||||
|
navigate('/survey', {
|
||||||
|
state: {
|
||||||
|
submission: submissionData,
|
||||||
|
isEdit: true,
|
||||||
|
fromDraft: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching submission details:', error);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
className="focus:outline-none"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src={pencilActiveSrc}
|
||||||
|
alt="Edit"
|
||||||
|
className="h-5 w-5 opacity-60 hover:opacity-100 transition-opacity"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={(e) => handleDeleteDraft(e, record)}
|
||||||
|
className="focus:outline-none"
|
||||||
|
title="Delete Draft"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src={deleteIconSrc}
|
||||||
|
alt="Delete"
|
||||||
|
className="h-5 w-5 opacity-60 hover:opacity-100 transition-opacity text-red-500"
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
) : 'View Details',
|
||||||
]);
|
]);
|
||||||
}, [filteredRows]);
|
}, [filteredRows, uae_currency]);
|
||||||
|
|
||||||
const handlePageChange = (newPage) => {
|
const handlePageChange = (newPage) => {
|
||||||
setPagination(prev => ({
|
setPagination(prev => ({
|
||||||
|
|||||||
@ -799,36 +799,114 @@ const Survey = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleFinalSubmit = async () => {
|
// const handleFinalSubmit = async () => {
|
||||||
if (isResubmit && submissionId) {
|
// if (isResubmit && submissionId) {
|
||||||
await handleResubmit();
|
// await handleResubmit();
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
|
|
||||||
setIsSubmitting(true);
|
// setIsSubmitting(true);
|
||||||
setError('');
|
// setError('');
|
||||||
|
|
||||||
try {
|
// try {
|
||||||
const payload = buildSubmissionPayload();
|
// const payload = buildSubmissionPayload();
|
||||||
await submitSurvey(payload);
|
// await submitSurvey(payload);
|
||||||
setShowSuccessPopup(true);
|
// setShowSuccessPopup(true);
|
||||||
setHasSubmitted(true);
|
// setHasSubmitted(true);
|
||||||
showToast('success', 'Survey submitted successfully!');
|
// showToast('success', 'Survey submitted successfully!');
|
||||||
localStorage.removeItem('surveyRemarks');
|
// localStorage.removeItem('surveyRemarks');
|
||||||
// Clear the stored survey data after successful submission
|
// // Clear the stored survey data after successful submission
|
||||||
localStorage.removeItem('currentSurvey');
|
// localStorage.removeItem('currentSurvey');
|
||||||
// Remove currentSurveyPeriod from localStorage
|
// // Remove currentSurveyPeriod from localStorage
|
||||||
localStorage.removeItem('currentSurveyPeriod');
|
// localStorage.removeItem('currentSurveyPeriod');
|
||||||
} catch (error) {
|
// } catch (error) {
|
||||||
const message = error?.response?.data?.message || error?.message || 'Failed to submit survey. Please try again.';
|
// const message = error?.response?.data?.message || error?.message || 'Failed to submit survey. Please try again.';
|
||||||
console.error('Submission Error:', error);
|
// console.error('Submission Error:', error);
|
||||||
setError(message);
|
// setError(message);
|
||||||
showToast('error', message);
|
// showToast('error', message);
|
||||||
} finally {
|
// } finally {
|
||||||
setIsSubmitting(false);
|
// setIsSubmitting(false);
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
|
const handleFinalSubmit = async () => {
|
||||||
|
if (isResubmit && submissionId) {
|
||||||
|
await handleResubmit();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setIsSubmitting(true);
|
||||||
|
setError('');
|
||||||
|
|
||||||
|
try {
|
||||||
|
const payload = buildSubmissionPayload();
|
||||||
|
const currentSubmissionStr = localStorage.getItem('currentSubmission');
|
||||||
|
let submissionId = null;
|
||||||
|
let currentSubmission = null;
|
||||||
|
|
||||||
|
if (currentSubmissionStr) {
|
||||||
|
currentSubmission = JSON.parse(currentSubmissionStr);
|
||||||
|
submissionId = currentSubmission.id;
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
|
if (currentSubmission?.products?.length) {
|
||||||
|
payload.products = payload.products.map((product, index) => {
|
||||||
|
const existingProduct = currentSubmission.products[index];
|
||||||
|
return {
|
||||||
|
...product,
|
||||||
|
id: existingProduct?.id || 0, // Include existing product ID or 0 for new products
|
||||||
|
product_id: product.product_id || existingProduct?.product_id // Ensure product_id is included
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let response;
|
||||||
|
|
||||||
|
if (submissionId) {
|
||||||
|
// Update existing draft submission
|
||||||
|
response = await resubmitSurvey(submissionId, {
|
||||||
|
...payload,
|
||||||
|
status: 'Submitted'
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Create new submission
|
||||||
|
response = await submitSurvey({
|
||||||
|
...payload,
|
||||||
|
status: 'Submitted'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update local storage with the submitted submission and product IDs
|
||||||
|
if (response && (response.data || response.id)) {
|
||||||
|
const responseData = response.data || response;
|
||||||
|
const updatedSubmission = {
|
||||||
|
...(currentSubmission || {}),
|
||||||
|
id: responseData.id || responseData.submission_id,
|
||||||
|
status: 'Submitted',
|
||||||
|
submitted_at: new Date().toISOString()
|
||||||
|
};
|
||||||
|
|
||||||
|
// Update product IDs if they exist in the response
|
||||||
|
if (responseData.products && Array.isArray(responseData.products)) {
|
||||||
|
updatedSubmission.products = responseData.products.map((product, index) => ({
|
||||||
|
...(currentSubmission?.products?.[index] || {}), // Keep existing product data
|
||||||
|
id: product.id, // Update with new product ID from response
|
||||||
|
product_id: product.product_id // Ensure product_id is also updated
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
localStorage.setItem('currentSubmission', JSON.stringify(updatedSubmission));
|
||||||
|
}
|
||||||
|
|
||||||
|
setShowSuccessPopup(true);
|
||||||
|
setHasSubmitted(true);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error submitting survey:', error);
|
||||||
|
setError(error.message || 'Failed to submit survey. Please try again.');
|
||||||
|
} finally {
|
||||||
|
setIsSubmitting(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
const StepBadge = ({ state, number }) => {
|
const StepBadge = ({ state, number }) => {
|
||||||
if (state === 'active') {
|
if (state === 'active') {
|
||||||
return (
|
return (
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user