added delete api in draft flow

This commit is contained in:
Malini 2026-02-05 08:42:40 +05:30
parent 0a3d8e6b5b
commit b75950b0d6
7 changed files with 287 additions and 232 deletions

View File

@ -4,6 +4,7 @@ import Table from '@/components/common/Table';
import Footer from '../common/Footer';
import HeaderBar from '@/components/layout/HeaderBar';
import DetailedOverview from '@/components/overview/DetailedOverview';
import { deleteDraftSubmission } from '@/services/submissions/submissionService';
const searchIconSrc = '/assets/images/material-symbols_search-rounded.svg';
const pencilIconSrc = '/assets/images/PencilSimple.svg';
@ -142,6 +143,14 @@ export const SubmissionHistory = ({ history = [], loading = false, error = '' })
const [selectedSubmission, setSelectedSubmission] = React.useState(null);
const [currentPage, setCurrentPage] = React.useState(1);
const [hoveredDelete, setHoveredDelete] = React.useState(null);
const [toast, setToast] = React.useState({ show: false, message: '', type: 'success' });
const showToast = (message, type = 'success') => {
setToast({ show: true, message, type });
setTimeout(() => {
setToast({ ...toast, show: false });
}, 1000);
};
const pageSize = 10;
const navigate = useNavigate();
@ -171,14 +180,28 @@ export const SubmissionHistory = ({ history = [], loading = false, error = '' })
};
const handleDeleteDraft = async (e, submission) => {
e.preventDefault();
e.stopPropagation();
try {
} catch (error) {
console.error('Error deleting submission:', error);
e.preventDefault();
e.stopPropagation();
try {
if (!submission?.id) {
throw new Error('No submission ID provided');
}
};
if (!window.confirm('Are you sure you want to delete this draft?')) {
return;
}
await deleteDraftSubmission(submission.id);
showToast('Draft deleted successfully', 'success');
// Optionally reload the page or refresh data
window.location.reload();
} catch (error) {
console.error('Error deleting submission:', error);
alert(error.message || 'Failed to delete draft');
}
};
const normalizedHistory = React.useMemo(() => {
if (!Array.isArray(history)) return [];
@ -276,6 +299,14 @@ const handleDeleteDraft = async (e, submission) => {
return (
<>
<div className="min-h-[4rem] mt-[-30px]">
{/* Add this toast component */}
{toast.show && (
<CustomToast
message={toast.message}
type={toast.type}
onClose={() => setToast({ ...toast, show: false })}
/>
)}
<div className="max-w-[1280px] mx-auto mb-5 bg-white border border-[#E5E7EB] shadow-[0_16px_32px_rgba(15,23,42,0.06)] rounded-[8px] mt-8 w-full overflow-hidden">
<div className="flex items-center justify-between px-6 pt-6 pb-4">
<h2 className="text-[18px] leading-[28px] font-medium text-[#232528]">

View File

@ -63,6 +63,7 @@ const SurveyCarousel = ({
<button
onClick={() => {
localStorage.removeItem('currentSubmission');
onStartSurvey?.(current);
navigate('/survey', { state: { survey: current } });
}}

View File

@ -840,7 +840,7 @@ const EstablishmentInfo = ({
<img src={backVectorSrc} alt="" className="h-4 w-4" />
<span>Back</span>
</button>
<button
{/* <button
onClick={onNext}
className="inline-flex items-center gap-2 h-9 px-5 rounded-md bg-[#92722A] text-white hover:bg-[#7b5c1f] cursor-pointer"
disabled={loading}
@ -849,7 +849,68 @@ const EstablishmentInfo = ({
<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>
</button> */}
<button
onClick={(e) => {
e.preventDefault();
// Save form data to localStorage
const currentSurvey = JSON.parse(localStorage.getItem('currentSurvey') || '{}');
const currentSurveyPeriod = JSON.parse(localStorage.getItem('currentSurveyPeriod') || '{}');
const formData = {
quarter: currentSurvey.quarter || currentSurveyPeriod.quarter || localStorage.getItem('currentQuarter') || '',
year: currentSurvey.year || currentSurveyPeriod.year || localStorage.getItem('currentYear') || '',
establishmentName: data.establishmentName || '',
permanentFactoryCode: data.permanentFactoryCode || '',
industryCode: data.industryCode || '',
licenseNumber: data.licenseNumber || '',
isicCode: data.isicCode || '',
emirate: emirateName || '',
establishment_contact_email: data.establishment_contact_email || '',
employeeInfo: {
...defaultEmployeeInfo,
...(data.employeeInfo || {}),
},
...data
};
// Save to localStorage
localStorage.setItem('establishmentFormData', JSON.stringify(formData));
// Also update currentSubmission in localStorage
// const currentSubmission = {
// ...formData,
// establishment: {
// id: data.establishmentId,
// factory_name: data.establishmentName,
// permanent_factory_code: data.permanentFactoryCode,
// industry_code: data.industryCode,
// license_number: data.licenseNumber,
// isic_code: data.isicCode,
// establishment_emirate_id: data.emirate,
// establishment_contact_email: data.establishment_contact_email,
// // Employee info
// emirati_male: formData.employeeInfo?.emiratiMale || 0,
// emirati_female: formData.employeeInfo?.emiratiFemale || 0,
// non_emirati_male: formData.employeeInfo?.nonEmiratiMale || 0,
// non_emirati_female: formData.employeeInfo?.nonEmiratiFemale || 0,
// total_emirati: formData.employeeInfo?.totalEmirati || 0,
// total_employees: formData.employeeInfo?.totalEmployees || 0
// }
// };
// localStorage.setItem('currentSubmission', JSON.stringify(currentSubmission));
// Call the original onNext handler
onNext();
}}
className="inline-flex items-center gap-2 h-9 px-5 rounded-md bg-[#92722A] text-white hover:bg-[#7b5c1f] cursor-pointer"
disabled={loading}
>
<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>
{(loading || error) && (

View File

@ -476,17 +476,28 @@ const handleSaveDraft = async () => {
setIsSavingDraft(true);
// Get current submission from localStorage
let currentSubmission;
const currentSubmissionStr = localStorage.getItem('currentSubmission');
const establishmentFormDataStr = localStorage.getItem('establishmentFormData');
if (!currentSubmissionStr) {
throw new Error('Current submission data not found');
// If no current submission, try to use establishmentFormData
if (!establishmentFormDataStr) {
throw new Error('No submission data found. Please complete the establishment information first.');
}
currentSubmission = JSON.parse(establishmentFormDataStr);
} else {
currentSubmission = JSON.parse(currentSubmissionStr);
}
const currentSubmission = JSON.parse(currentSubmissionStr);
// 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;
const establishmentId = currentSubmission.establishment_id ||
(currentSubmission.establishment && currentSubmission.establishment.id) ||
localStorage.getItem('establishment_id');
if (!establishmentId) {
throw new Error('Establishment ID not found in submission');
}
@ -510,10 +521,29 @@ const handleSaveDraft = async () => {
}
// Get establishment data
const establishment = currentSubmission.establishment;
if (!establishment) {
throw new Error('Establishment data not found in submission');
}
// Get establishment data
let establishment;
if (currentSubmission.establishment) {
establishment = currentSubmission.establishment;
} else {
// Try to get establishment data from establishmentFormData in localStorage
const establishmentFormDataStr = localStorage.getItem('establishmentFormData');
if (establishmentFormDataStr) {
const establishmentFormData = JSON.parse(establishmentFormDataStr);
establishment = {
emirati_male: establishmentFormData.employeeInfo?.emiratiMale || 0,
emirati_female: establishmentFormData.employeeInfo?.emiratiFemale || 0,
non_emirati_male: establishmentFormData.employeeInfo?.nonEmiratiMale || 0,
non_emirati_female: establishmentFormData.employeeInfo?.nonEmiratiFemale || 0,
total_emirati: establishmentFormData.employeeInfo?.totalEmirati || 0,
total_employees: establishmentFormData.employeeInfo?.totalEmployees || 0
};
}
}
if (!establishment) {
throw new Error('Establishment data not found in submission or localStorage');
}
// Prepare products data in the required format
const formattedProducts = products.map(product => {
@ -585,14 +615,7 @@ const handleSaveDraft = async () => {
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
});
alert('Draft updated successfully')
} else {
// Create new submission
response = await submitSurvey(payload);
@ -603,19 +626,12 @@ const handleSaveDraft = async () => {
localStorage.setItem('currentSubmission', JSON.stringify(updatedSubmission));
}
toast.success('Draft saved successfully', {
position: "top-right",
autoClose: 3000,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true,
draggable: true
});
alert('Draft saved successfully')
}
} catch (error) {
console.error('Error saving draft:', error);
toast.error(error.message || 'Failed to save draft');
alert(error.message || 'Failed to save draft');
} finally {
setIsSavingDraft(false);
}

View File

@ -445,23 +445,33 @@ const ReviewSubmit = ({
return '';
});
const handleSaveDraft = async () => {
const handleSaveDraft = async () => {
try {
setIsSavingDraft(true);
// Get current submission from localStorage
let currentSubmission;
const currentSubmissionStr = localStorage.getItem('currentSubmission');
const establishmentFormDataStr = localStorage.getItem('establishmentFormData');
if (!currentSubmissionStr) {
throw new Error('Current submission data not found');
// If no current submission, try to use establishmentFormData
if (!establishmentFormDataStr) {
throw new Error('No submission data found. Please complete the establishment information first.');
}
currentSubmission = JSON.parse(establishmentFormDataStr);
} else {
currentSubmission = JSON.parse(currentSubmissionStr);
}
const currentSubmission = JSON.parse(currentSubmissionStr);
// const currentSubmission = JSON.parse(currentSubmissionStr);
const currentQuarter = localStorage.getItem('currentQuarter');
const currentYear = localStorage.getItem('currentYear');
const currentYear = localStorage.getItem('currentYear')
// Get establishment ID from current submission
const submissionId = currentSubmission.id;
const establishmentId = currentSubmission.establishment_id;
const submissionId = currentSubmission.id; // Changed this line to get ID from currentSubmission
const establishmentId = currentSubmission.establishment_id ||
(currentSubmission.establishment && currentSubmission.establishment.id) ||
localStorage.getItem('establishment_id');
if (!establishmentId) {
throw new Error('Establishment ID not found in submission');
}
@ -471,10 +481,8 @@ const ReviewSubmit = ({
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');
}
@ -482,49 +490,85 @@ const ReviewSubmit = ({
// 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');
}
// Get establishment data
let establishment;
if (currentSubmission.establishment) {
establishment = currentSubmission.establishment;
} else {
// Try to get establishment data from establishmentFormData in localStorage
const establishmentFormDataStr = localStorage.getItem('establishmentFormData');
if (establishmentFormDataStr) {
const establishmentFormData = JSON.parse(establishmentFormDataStr);
establishment = {
emirati_male: establishmentFormData.employeeInfo?.emiratiMale || 0,
emirati_female: establishmentFormData.employeeInfo?.emiratiFemale || 0,
non_emirati_male: establishmentFormData.employeeInfo?.nonEmiratiMale || 0,
non_emirati_female: establishmentFormData.employeeInfo?.nonEmiratiFemale || 0,
total_emirati: establishmentFormData.employeeInfo?.totalEmirati || 0,
total_employees: establishmentFormData.employeeInfo?.totalEmployees || 0
};
}
}
// 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 || ''
}));
if (!establishment) {
throw new Error('Establishment data not found in submission or localStorage');
}
// Prepare the complete payload
// 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,
@ -539,95 +583,30 @@ const ReviewSubmit = ({
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
}
// Example: const response = await api.saveDraft(payload);
let response;
if (submissionId) {
// Update existing submission
response = await resubmitSurvey(submissionId, payload);
alert('Draft updated successfully');
} 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));
}
// 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;
alert('Draft saved successfully')
}
} 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
});
} catch (error) {
console.error('Error saving draft:', error);
alert(error.message || 'Failed to save draft');
} finally {
setIsSavingDraft(false);
}

View File

@ -13,7 +13,7 @@ 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';
import { deleteDraftSubmission } from '@/services/submissions/submissionService';
const statusStyles = {
approved: 'text-[#2F663C] bg-[#F3FAF4]',
submitted: 'text-[#003CFF] bg-[#E7F5FF]',
@ -51,16 +51,29 @@ const Overview = () => {
setSelectedSubmission(location.state.selectedSubmission);
}
}, [location.state]);
const handleDeleteDraft = async (e, record) => {
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
if (!record?.id) {
throw new Error('No submission ID provided');
}
if (!window.confirm('Are you sure you want to delete this draft?')) {
return;
}
// Call the delete API
await deleteDraftSubmission(record.id);
// Show success message
alert('Draft deleted successfully');
// Navigate back or refresh the page
window.location.reload();
} catch (error) {
console.error('Error deleting submission:', error);
// Optionally show an error message to the user
alert(error.message || 'Failed to delete draft');
}
};

View File

@ -84,67 +84,6 @@ export const getQuarterPeriods = async (currentYear, currentQuarter) => {
}
};
// export const getPreviousForecastData = async (
// establishmentId,
// quarter,
// year,
// productId
// ) => {
// try {
// const response = await getRequest(
// "/submissions/getPreviousForecastData",
// {
// params: {
// establishment_id: establishmentId,
// quarter,
// year,
// product_id: productId,
// },
// }
// );
// // Always return only backend data
// return response?.data || null;
// } catch (error) {
// console.error("Error fetching previous forecast data:", error);
// return null;
// }
// };
// export const getBeforePreviousData = async (
// establishmentId,
// quarter,
// year,
// productId
// ) => {
// try {
// if (!establishmentId || !quarter || !year || !productId) {
// throw new Error("Missing required parameters");
// }
// const response = await getRequest(
// "/submissions/getBeforePreviousData",
// {
// params: {
// establishment_id: establishmentId,
// current_quarter: quarter,
// current_year: year,
// product_id: productId,
// },
// }
// );
// return response?.data ?? null;
// } catch (error) {
// console.error("Error fetching data:", error);
// return null;
// }
// };
export const getPreviousForecastData = async (
establishmentId,
quarter,
@ -280,6 +219,20 @@ export const getProductSubmissionHistory = async (establishmentId, productId, co
throw error;
}
};
export const deleteDraftSubmission = async (submissionId, config = {}) => {
if (!submissionId) {
throw new Error('Submission ID is required to delete draft submission');
}
const response = await getRequest(`${endpoint}/deleteDraftSubmissionData`, {
...config,
params: {
id: submissionId,
...(config.params || {})
}
});
return response.data;
};
export default {
getSubmissions,
@ -293,7 +246,8 @@ export default {
getSubmissionAuditHistory,
getEstablishmentProducts,
getProductSubmissionHistory,
getBeforePreviousData
getBeforePreviousData,
deleteDraftSubmission
};