From a136e2415fe2f983bbb07f53e43285a9c4112997 Mon Sep 17 00:00:00 2001 From: Malini Date: Mon, 10 Nov 2025 19:21:36 +0530 Subject: [PATCH] bug fixed --- .../src/components/admin/SubmissionTable.jsx | 276 +++++++++++++++++- .../src/pages/Admin/AdminDashboard.jsx | 21 +- .../src/pages/Admin/ValidationReview.jsx | 66 ++++- .../src/pages/Admin/Validations.jsx | 19 +- .../src/services/admin/submission.js | 4 +- 5 files changed, 337 insertions(+), 49 deletions(-) diff --git a/ipi-survey-platform/src/components/admin/SubmissionTable.jsx b/ipi-survey-platform/src/components/admin/SubmissionTable.jsx index a11e90e..5d4d5e3 100644 --- a/ipi-survey-platform/src/components/admin/SubmissionTable.jsx +++ b/ipi-survey-platform/src/components/admin/SubmissionTable.jsx @@ -2,12 +2,20 @@ import React, { useState, useEffect, useMemo } from 'react'; import { useNavigate } from 'react-router-dom'; import Table from '@/components/common/Table'; import apiClient from '../../services/api/apiClient.js'; -import { CheckCircle2, XCircle, Eye } from 'lucide-react'; +import { CheckCircle2, XCircle, Eye, X } from 'lucide-react'; +import { toast } from 'react-toastify'; const SubmissionTable = ({ selectedQuarter, selectedYear }) => { const [data, setData] = useState([]); const [loading, setLoading] = useState(true); const [currentPage, setCurrentPage] = React.useState(1); + const [showApproveModal, setShowApproveModal] = useState(false); + const [showRejectModal, setShowRejectModal] = useState(false); + const [selectedSubmission, setSelectedSubmission] = useState(null); + const [actionType, setActionType] = useState(''); + const [rejectionReason, setRejectionReason] = useState(''); + const [showRejectError, setShowRejectError] = useState(false); + const [isProcessing, setIsProcessing] = useState(false); const pageSize = 10; const navigate = useNavigate(); @@ -118,6 +126,123 @@ const SubmissionTable = ({ selectedQuarter, selectedYear }) => { const userProfile = JSON.parse(sessionStorage.getItem("user_profile")); + const handleActionClick = (submission, type) => { + setSelectedSubmission(submission); + setActionType(type); + if (type === 'approve') { + setShowApproveModal(true); + } else { + setRejectionReason(''); + setShowRejectError(false); + setShowRejectModal(true); + } + }; + + const handleCloseApproveModal = () => { + setShowApproveModal(false); + setSelectedSubmission(null); + }; + + const handleCloseRejectModal = () => { + setShowRejectModal(false); + setSelectedSubmission(null); + setRejectionReason(''); + setShowRejectError(false); + }; + + const handleApproveConfirm = async () => { + if (!selectedSubmission || isProcessing) return; + + setIsProcessing(true); + try { + const response = await apiClient.put(`/approveOrRejectSubmission/${selectedSubmission.id}`, { + approve_reject_status: 1, + remarks: '' + }); + + const ok = (response && response.data) || response?.status === 200 || response?.status === 'success'; + + if (ok) { + toast.success('Submission approved successfully'); + + // Update the status of the current item in the data array + setData(prevData => + prevData.map(item => + item.id === selectedSubmission.id + ? { + ...item, + status: 'Approved', + reviewer: JSON.parse(sessionStorage.getItem("user_profile"))?.name || 'Admin', + reviewerOn: new Date().toISOString() + } + : item + ) + ); + } else { + throw new Error('Failed to approve submission'); + } + } catch (error) { + console.error('Error approving submission:', error); + toast.error(error.message || 'Failed to approve submission'); + } finally { + setIsProcessing(false); + setShowApproveModal(false); + setSelectedSubmission(null); + } + }; + + const handleRejectConfirm = async () => { + if (!selectedSubmission || isProcessing) { + return; + } + + // Validate the rejection reason + if (!rejectionReason.trim()) { + setShowRejectError(true); + toast.error('Please provide a reason for rejection'); + return; + } + + setIsProcessing(true); + try { + const response = await apiClient.put(`/approveOrRejectSubmission/${selectedSubmission.id}`, { + approve_reject_status: 0, + remarks: rejectionReason.trim() + }); + + const ok = (response && response.data) || response?.status === 200 || response?.status === 'success'; + + if (ok) { + toast.error('Submission has been rejected'); + + // Update the status of the current item in the data array + setData(prevData => + prevData.map(item => + item.id === selectedSubmission.id + ? { + ...item, + status: 'Rejected', + reviewer: JSON.parse(sessionStorage.getItem("user_profile"))?.name || 'Admin', + reviewerOn: new Date().toISOString() + } + : item + ) + ); + + setRejectionReason(''); + } else { + throw new Error('Failed to reject submission'); + } + } catch (error) { + console.error('Error rejecting submission:', error); + toast.error(error.message || 'Failed to reject submission'); + } finally { + setIsProcessing(false); + setShowRejectModal(false); + setSelectedSubmission(null); + } + }; + const headers = [ 'Establishment', 'Emirates', @@ -195,30 +320,22 @@ const SubmissionTable = ({ selectedQuarter, selectedYear }) => {
- navigate(`/admin/validations/${item.id}`, { - state: { actionType: 'approve' }, - }) - } + className={`${item.status?.toLowerCase() === 'approved' ? 'text-gray-400 cursor-not-allowed' : 'text-green-600 cursor-pointer hover:scale-110'} transition-transform`} + onClick={item.status?.toLowerCase() !== 'approved' ? () => handleActionClick(item, 'approve') : undefined} /> - Approve + {item.status?.toLowerCase() === 'approved' ? 'Already Approved' : 'Approve'}
- navigate(`/admin/validations/${item.id}`, { - state: { actionType: 'reject' }, - }) - } + className={`${item.status?.toLowerCase() === 'rejected' ? 'text-gray-400 cursor-not-allowed' : 'text-red-600 cursor-pointer hover:scale-110'} transition-transform`} + onClick={item.status?.toLowerCase() !== 'rejected' ? () => handleActionClick(item, 'reject') : undefined} /> - Reject + {item.status?.toLowerCase() === 'rejected' ? 'Already Rejected' : 'Reject'}
@@ -259,6 +376,135 @@ const SubmissionTable = ({ selectedQuarter, selectedYear }) => { View All Submissions + + {/* Approve Confirmation Modal */} + {showApproveModal && selectedSubmission && ( +
+
+
+

Approve this submission?

+ +
+
+

+ You're about to approve {selectedSubmission.establishment?.factory_name} for {selectedSubmission.quarter} {selectedSubmission.year}. +

+

+ This will lock the record for the establishment. +

+
+ + +
+
+
+
+ )} + + {/* Reject Confirmation Modal */} + {showRejectModal && selectedSubmission && ( +
+
+
+

Reject this submission?

+ +
+
+

+ You're about to return {selectedSubmission.establishment?.factory_name}'s {selectedSubmission.quarter} {selectedSubmission.year} submission for correction. +

+

+ The status will change to Rejected and the establishment can resubmit within the survey window. +

+
+ + +
+ {/*

+ Please refer to the screenshot +

*/} +
@@ -682,6 +680,50 @@ const ValidationReview = () => {
)} + + {/* Fixed bottom action bar */} +
+
+ + +
+
+ + {/* Fixed bottom action bar */} +
+
+ + +
+
); }; diff --git a/ipi-survey-platform/src/pages/Admin/Validations.jsx b/ipi-survey-platform/src/pages/Admin/Validations.jsx index f35883d..8f8b3c4 100644 --- a/ipi-survey-platform/src/pages/Admin/Validations.jsx +++ b/ipi-survey-platform/src/pages/Admin/Validations.jsx @@ -15,16 +15,16 @@ const STATUS_VARIANTS = { Pending: { background: '#E7F5FF', text: '#286CFF', border: '#E7F5FF' }, Rejected: { background: '#FEF2F2', text: '#B52520', border: '#FEF2F2' }, Resubmitted: { background: '#F9F7ED', text: '#7C5E24', border: '#FEF2F2' }, - Submitted: { background: '#E7F5FF', text: '#003CFF', border: '#FEF2F2' }, + Submitted: { background: '#E7F5FF', text: '#003CFF', border: '#E7F5FF' }, }; const SUMMARY_VARIANTS = { total: { background: '#F9F7ED', label: '#7C5E24', value: '#232528' }, approved: { background: '#F3FAF4', label: '#2F663C', value: '#232528' }, - pending: { background: '#E7F5FF', label: '#003CFF', value: '#232528' }, + // pending: { background: '#E7F5FF', label: '#003CFF', value: '#232528' }, + submitted: { background: '#E7F5FF', text: '#003CFF', value: '#232528',border: '#FEF2F2' }, rejected: { background: '#FEF2F2', label: '#B52520', value: '#232528' }, resubmitted: { background: '#F9F7ED', text: '#7C5E24', border: '#FEF2F2' }, - submitted: { background: '#E7F5FF', text: '#003CFF', border: '#FEF2F2' }, }; @@ -281,7 +281,7 @@ React.useEffect(() => { } }, [location.state, showToast]); - const fetchDashboardData = async (quarter, year, page = 1, limit = 10) => { + const fetchDashboardData = async (quarter, year, page = 1, limit = 10000) => { try { const params = { quarter: quarter && quarter !== 'All' ? quarter : undefined, @@ -290,12 +290,9 @@ React.useEffect(() => { limit }; - console.log('Fetching with params:', params); const result = await getSubmissions(params); - console.log('API Response:', result); // Check if result has a data property or is the data array itself const data = Array.isArray(result) ? result : (result?.data || []); - console.log('Processed data:', data); return data; } catch (error) { console.error('Error fetching dashboard data:', error); @@ -402,7 +399,6 @@ React.useEffect(() => { return matches; }); - console.log('Filtered items count:', filteredItems.length); return filteredItems; }, [submissions, search, year, quarter, emirate, status]); @@ -410,8 +406,9 @@ React.useEffect(() => { () => ({ total: submissions.length, approved: submissions.filter((i) => i.status === 'Approved').length, - pending: submissions.filter((i) => i.status === 'Pending').length, + submitted: submissions.filter((i) => i.status === 'Submitted').length, rejected: submissions.filter((i) => i.status === 'Rejected').length, + resubmitted: submissions.filter((i) => i.status === 'Resubmitted').length, }), [submissions] ); @@ -829,6 +826,10 @@ React.useEffect(() => { {showRejectError && !rejectReason.trim() && (

Reason for rejection is required

)} +

+ Please refer to the screenshot +

+