From 0a3d8e6b5b0ba437da77afb3d2873ff82ba62dd6 Mon Sep 17 00:00:00 2001 From: Malini Date: Wed, 4 Feb 2026 21:18:04 +0530 Subject: [PATCH] Reapply "Added save as draft flow" This reverts commit 73fe26edc4294a95b6f0f83b87ff9df1844f01eb. --- .../dashboard/SubmissionHistory.jsx | 85 ++++- .../EstablishmentInfo/EstablishmentInfo.jsx | 61 +++- .../survey/ProductData/ProductData.jsx | 304 +++++++++++++++++- .../survey/ReviewSubmit/ReviewSubmit.jsx | 234 ++++++++++++-- .../src/pages/Overview/Overview.jsx | 62 +++- .../src/pages/Survey/Survey.jsx | 130 ++++++-- 6 files changed, 802 insertions(+), 74 deletions(-) diff --git a/ipi-survey-platform/src/components/dashboard/SubmissionHistory.jsx b/ipi-survey-platform/src/components/dashboard/SubmissionHistory.jsx index f290daf..4199104 100644 --- a/ipi-survey-platform/src/components/dashboard/SubmissionHistory.jsx +++ b/ipi-survey-platform/src/components/dashboard/SubmissionHistory.jsx @@ -6,7 +6,10 @@ 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); @@ -138,13 +141,45 @@ 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) => { @@ -186,6 +221,7 @@ export const SubmissionHistory = ({ history = [], loading = false, error = '' }) const tableRows = filtered.map((item) => { const productsLabel = `${item.products}`; + const isDraft = item.status.toLowerCase() === 'draft'; return [ item.survey_name, item.year, @@ -193,12 +229,47 @@ export const SubmissionHistory = ({ history = [], loading = false, error = '' }) getStatusBadge(item.status), {item.submission_on}, productsLabel, - , +
+ {isDraft ? ( + <> + + + + ) : ( + + )} +
+ ]; }); diff --git a/ipi-survey-platform/src/components/survey/EstablishmentInfo/EstablishmentInfo.jsx b/ipi-survey-platform/src/components/survey/EstablishmentInfo/EstablishmentInfo.jsx index a87b52b..820c9c9 100644 --- a/ipi-survey-platform/src/components/survey/EstablishmentInfo/EstablishmentInfo.jsx +++ b/ipi-survey-platform/src/components/survey/EstablishmentInfo/EstablishmentInfo.jsx @@ -175,6 +175,65 @@ 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 = { @@ -183,7 +242,7 @@ const EstablishmentInfo = ({ endDate: data.end_date || '' }; localStorage.setItem('currentSurveyPeriod', JSON.stringify(surveyPeriod)); - localStorage.setItem('currentQuarter', data.quarter); // Store quarter separately + localStorage.setItem('currentQuarter', data.quarter); localStorage.setItem('currentYear', data.year); return surveyPeriod; } diff --git a/ipi-survey-platform/src/components/survey/ProductData/ProductData.jsx b/ipi-survey-platform/src/components/survey/ProductData/ProductData.jsx index eab289a..c0f5315 100644 --- a/ipi-survey-platform/src/components/survey/ProductData/ProductData.jsx +++ b/ipi-survey-platform/src/components/survey/ProductData/ProductData.jsx @@ -1,6 +1,8 @@ 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 } from '@/services/submissions/submissionService'; +import { getQuarterPeriods, getPreviousForecastData, getEstablishmentProducts , submitSurvey,resubmitSurvey} from '@/services/submissions/submissionService'; import { fetchVariationReasons, fetchZeroTargetReasons, @@ -302,6 +304,17 @@ const Select = ({ open )} + ); }; @@ -378,6 +391,10 @@ 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(() => { @@ -454,6 +471,256 @@ 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(); @@ -616,7 +883,7 @@ const ProductData = ({ endDate: '' })); -const location = useLocation(); +// const location = useLocation(); // Update surveyData when props or location changes React.useEffect(() => { @@ -1195,7 +1462,7 @@ const location = useLocation();
Due: - + {surveyData.endDate ? ( new Date(surveyData.endDate).toLocaleDateString('en-US', { year: 'numeric', @@ -1905,21 +2172,26 @@ const location = useLocation();
- -
+
+
+
+ -
- - -
+
+ +
+
); diff --git a/ipi-survey-platform/src/pages/Overview/Overview.jsx b/ipi-survey-platform/src/pages/Overview/Overview.jsx index a9d9bb3..bb96046 100644 --- a/ipi-survey-platform/src/pages/Overview/Overview.jsx +++ b/ipi-survey-platform/src/pages/Overview/Overview.jsx @@ -1,13 +1,18 @@ // src/pages/Overview/Overview.jsx import React, { useState, useEffect, useMemo } from 'react'; 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 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]', @@ -46,6 +51,18 @@ 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 () => { @@ -113,9 +130,48 @@ const Overview = () => { ) : '-', record.status, formatDate(record.created_at), - 'View Details', + record.status === 'Draft' ? ( +
+ + +
+ ) : 'View Details', ]); - }, [filteredRows]); + }, [filteredRows, uae_currency]); const handlePageChange = (newPage) => { setPagination(prev => ({ diff --git a/ipi-survey-platform/src/pages/Survey/Survey.jsx b/ipi-survey-platform/src/pages/Survey/Survey.jsx index 7fcb870..04eac13 100644 --- a/ipi-survey-platform/src/pages/Survey/Survey.jsx +++ b/ipi-survey-platform/src/pages/Survey/Survey.jsx @@ -799,36 +799,114 @@ 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); + // 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; } - }; + 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 (