parent
8f3234167a
commit
73fe26edc4
@ -6,10 +6,7 @@ import HeaderBar from '@/components/layout/HeaderBar';
|
||||
import DetailedOverview from '@/components/overview/DetailedOverview';
|
||||
|
||||
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) => {
|
||||
if (!value) return '—';
|
||||
const date = new Date(value);
|
||||
@ -141,45 +138,13 @@ export const SubmissionHistory = ({ history = [], loading = false, error = '' })
|
||||
const [searchTerm, setSearchTerm] = React.useState('');
|
||||
const [selectedSubmission, setSelectedSubmission] = React.useState(null);
|
||||
const [currentPage, setCurrentPage] = React.useState(1);
|
||||
const [hoveredDelete, setHoveredDelete] = React.useState(null);
|
||||
|
||||
const pageSize = 10;
|
||||
const navigate = useNavigate();
|
||||
|
||||
|
||||
|
||||
const handleViewDetails = (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(() => {
|
||||
if (!Array.isArray(history)) return [];
|
||||
return history.map((entry, index) => {
|
||||
@ -221,7 +186,6 @@ const handleDeleteDraft = async (e, submission) => {
|
||||
|
||||
const tableRows = filtered.map((item) => {
|
||||
const productsLabel = `${item.products}`;
|
||||
const isDraft = item.status.toLowerCase() === 'draft';
|
||||
return [
|
||||
item.survey_name,
|
||||
item.year,
|
||||
@ -229,47 +193,12 @@ const handleDeleteDraft = async (e, submission) => {
|
||||
getStatusBadge(item.status),
|
||||
<span className="whitespace-nowrap">{item.submission_on}</span>,
|
||||
productsLabel,
|
||||
<div className="flex items-center gap-2">
|
||||
{isDraft ? (
|
||||
<>
|
||||
<button
|
||||
onClick={(e) => {
|
||||
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>
|
||||
|
||||
<button
|
||||
onClick={() => handleViewDetails(item)}
|
||||
className="text-[#9E792B] hover:text-[#7b5f22] font-medium"
|
||||
>
|
||||
View Details
|
||||
</button>,
|
||||
];
|
||||
});
|
||||
|
||||
|
||||
@ -175,65 +175,6 @@ const EstablishmentInfo = ({
|
||||
localStorage.setItem('surveyRemarks', remarksresubmit)
|
||||
|
||||
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
|
||||
if (location.state?.fromResubmit && data?.quarter && data?.year) {
|
||||
const surveyPeriod = {
|
||||
@ -242,7 +183,7 @@ const EstablishmentInfo = ({
|
||||
endDate: data.end_date || ''
|
||||
};
|
||||
localStorage.setItem('currentSurveyPeriod', JSON.stringify(surveyPeriod));
|
||||
localStorage.setItem('currentQuarter', data.quarter);
|
||||
localStorage.setItem('currentQuarter', data.quarter); // Store quarter separately
|
||||
localStorage.setItem('currentYear', data.year);
|
||||
return surveyPeriod;
|
||||
}
|
||||
|
||||
@ -1,8 +1,6 @@
|
||||
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 { getQuarterPeriods, getPreviousForecastData, getEstablishmentProducts , submitSurvey,resubmitSurvey} from '@/services/submissions/submissionService';
|
||||
import { getQuarterPeriods, getPreviousForecastData, getEstablishmentProducts } from '@/services/submissions/submissionService';
|
||||
import {
|
||||
fetchVariationReasons,
|
||||
fetchZeroTargetReasons,
|
||||
@ -304,17 +302,6 @@ const Select = ({
|
||||
<img src={caretDownSrc} alt="open" className="h-4 w-4" />
|
||||
)}
|
||||
</div>
|
||||
<ToastContainer
|
||||
position="top-right"
|
||||
autoClose={3000}
|
||||
hideProgressBar={false}
|
||||
newestOnTop={false}
|
||||
closeOnClick
|
||||
rtl={false}
|
||||
pauseOnFocusLoss
|
||||
draggable
|
||||
pauseOnHover
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@ -391,10 +378,6 @@ const ProductData = ({
|
||||
const [unitOptions, setUnitOptions] = React.useState([]);
|
||||
const [isLoadingUnits, setIsLoadingUnits] = React.useState(false);
|
||||
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
|
||||
useEffect(() => {
|
||||
@ -471,256 +454,6 @@ const ProductData = ({
|
||||
fetchQuarterPeriods();
|
||||
}, [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(() => {
|
||||
const variationController = new AbortController();
|
||||
const zeroController = new AbortController();
|
||||
@ -883,7 +616,7 @@ useEffect(() => {
|
||||
endDate: ''
|
||||
}));
|
||||
|
||||
// const location = useLocation();
|
||||
const location = useLocation();
|
||||
|
||||
// Update surveyData when props or location changes
|
||||
React.useEffect(() => {
|
||||
@ -1462,7 +1195,7 @@ useEffect(() => {
|
||||
<div className="flex items-center space-x-1">
|
||||
<img src={calendarIcon} alt="" className="h-4 w-4" />
|
||||
<span className="font-medium">Due:</span>
|
||||
<span className="text-sm text-[#6C4527] whitespace-nowrap">
|
||||
<span className="text-sm text-[#6C4527] whitespace-nowrap">
|
||||
{surveyData.endDate ? (
|
||||
new Date(surveyData.endDate).toLocaleDateString('en-US', {
|
||||
year: 'numeric',
|
||||
@ -2172,37 +1905,24 @@ useEffect(() => {
|
||||
|
||||
|
||||
<div className="flex flex-col sm:flex-row justify-between gap-5 mt-6">
|
||||
<div className="flex-1">
|
||||
<button
|
||||
onClick={onBack}
|
||||
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"
|
||||
>
|
||||
<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
|
||||
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>
|
||||
<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="M9 5l7 7-7 7" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
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>
|
||||
<button
|
||||
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>
|
||||
<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="M9 5l7 7-7 7" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{(hasError || isInitialLoad) && (
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import Table from '@/components/common/Table';
|
||||
import { getQuarterPeriods , resubmitSurvey } from '@/services/submissions/submissionService';
|
||||
import { getQuarterPeriods } from '@/services/submissions/submissionService';
|
||||
import { getEmirates, fetchVariationReasons, fetchZeroTargetReasons } from '@/services/masters/masterService';
|
||||
const caretUpSrc = '/assets/images/caret-up.svg';
|
||||
const caretDownSrc = '/assets/images/CaretDown-black.svg';
|
||||
@ -360,9 +360,7 @@ const ReviewSubmit = ({
|
||||
const [emirateName, setEmirateName] = useState('');
|
||||
const [variationReasons, setVariationReasons] = useState([]);
|
||||
const [zeroTargetReasons, setZeroTargetReasons] = useState([]);
|
||||
const [isSavingDraft, setIsSavingDraft] = React.useState(false);
|
||||
const [productOptions, setProductOptions] = React.useState([]);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
const fetchEmiratesData = async () => {
|
||||
try {
|
||||
@ -445,193 +443,6 @@ const ReviewSubmit = ({
|
||||
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 = () => {
|
||||
try {
|
||||
const submissionData = formatSubmissionData(
|
||||
@ -1231,16 +1042,6 @@ const ReviewSubmit = ({
|
||||
<img src={caretDownSrc} alt="" className="h-4 w-4 rotate-90" />
|
||||
<span>Back</span>
|
||||
</button>
|
||||
<div className="flex gap-4">
|
||||
<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
|
||||
disabled={!confirm || isSubmitting}
|
||||
onClick={onSubmit}
|
||||
@ -1254,7 +1055,6 @@ const ReviewSubmit = ({
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -1,18 +1,13 @@
|
||||
// src/pages/Overview/Overview.jsx
|
||||
import React, { useState, useEffect, useMemo } from 'react';
|
||||
import { useNavigate, useLocation } from 'react-router-dom';
|
||||
import { getSubmissions, getSubmissionHistoryByEstablishment, fetchSubmissionDetail } from '@/services/submissions/submissionService';
|
||||
import { getSubmissions, getSubmissionHistoryByEstablishment } from '@/services/submissions/submissionService';
|
||||
import HeaderBar from '@/components/layout/HeaderBar';
|
||||
import Table from '@/components/common/Table';
|
||||
import DetailedOverview from '@/components/overview/DetailedOverview';
|
||||
const searchIconSrc = '/assets/images/material-symbols_search-rounded.svg';
|
||||
const caretDownSrc = '/assets/images/CaretDown.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 = {
|
||||
approved: 'text-[#2F663C] bg-[#F3FAF4]',
|
||||
@ -51,18 +46,6 @@ const Overview = () => {
|
||||
setSelectedSubmission(location.state.selectedSubmission);
|
||||
}
|
||||
}, [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(() => {
|
||||
const fetchSubmissions = async () => {
|
||||
@ -130,48 +113,9 @@ const Overview = () => {
|
||||
) : '-',
|
||||
record.status,
|
||||
formatDate(record.created_at),
|
||||
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',
|
||||
'View Details',
|
||||
]);
|
||||
}, [filteredRows, uae_currency]);
|
||||
}, [filteredRows]);
|
||||
|
||||
const handlePageChange = (newPage) => {
|
||||
setPagination(prev => ({
|
||||
|
||||
@ -799,114 +799,36 @@ const Survey = () => {
|
||||
}
|
||||
};
|
||||
|
||||
// const handleFinalSubmit = async () => {
|
||||
// if (isResubmit && submissionId) {
|
||||
// await handleResubmit();
|
||||
// return;
|
||||
// }
|
||||
const handleFinalSubmit = async () => {
|
||||
if (isResubmit && submissionId) {
|
||||
await handleResubmit();
|
||||
return;
|
||||
}
|
||||
|
||||
// setIsSubmitting(true);
|
||||
// setError('');
|
||||
setIsSubmitting(true);
|
||||
setError('');
|
||||
|
||||
// try {
|
||||
// const payload = buildSubmissionPayload();
|
||||
// await submitSurvey(payload);
|
||||
// setShowSuccessPopup(true);
|
||||
// setHasSubmitted(true);
|
||||
// showToast('success', 'Survey submitted successfully!');
|
||||
// localStorage.removeItem('surveyRemarks');
|
||||
// // Clear the stored survey data after successful submission
|
||||
// localStorage.removeItem('currentSurvey');
|
||||
// // Remove currentSurveyPeriod from localStorage
|
||||
// localStorage.removeItem('currentSurveyPeriod');
|
||||
// } catch (error) {
|
||||
// const message = error?.response?.data?.message || error?.message || 'Failed to submit survey. Please try again.';
|
||||
// console.error('Submission Error:', error);
|
||||
// setError(message);
|
||||
// showToast('error', message);
|
||||
// } finally {
|
||||
// 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;
|
||||
try {
|
||||
const payload = buildSubmissionPayload();
|
||||
await submitSurvey(payload);
|
||||
setShowSuccessPopup(true);
|
||||
setHasSubmitted(true);
|
||||
showToast('success', 'Survey submitted successfully!');
|
||||
localStorage.removeItem('surveyRemarks');
|
||||
// Clear the stored survey data after successful submission
|
||||
localStorage.removeItem('currentSurvey');
|
||||
// Remove currentSurveyPeriod from localStorage
|
||||
localStorage.removeItem('currentSurveyPeriod');
|
||||
} catch (error) {
|
||||
const message = error?.response?.data?.message || error?.message || 'Failed to submit survey. Please try again.';
|
||||
console.error('Submission Error:', error);
|
||||
setError(message);
|
||||
showToast('error', message);
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
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 }) => {
|
||||
if (state === 'active') {
|
||||
return (
|
||||
|
||||
Loading…
Reference in New Issue
Block a user