bug fixed
This commit is contained in:
parent
f5ebecdfbb
commit
a136e2415f
@ -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 }) => {
|
||||
<div className="relative group">
|
||||
<CheckCircle2
|
||||
size={22}
|
||||
className="text-green-600 cursor-pointer hover:scale-110 transition-transform"
|
||||
onClick={() =>
|
||||
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}
|
||||
/>
|
||||
<span className="absolute bottom-full mb-1 left-1/2 -translate-x-1/2 bg-black text-white text-xs px-2 py-[2px] rounded opacity-0 group-hover:opacity-100 transition">
|
||||
Approve
|
||||
{item.status?.toLowerCase() === 'approved' ? 'Already Approved' : 'Approve'}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="relative group">
|
||||
<XCircle
|
||||
size={22}
|
||||
className="text-red-600 cursor-pointer hover:scale-110 transition-transform"
|
||||
onClick={() =>
|
||||
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}
|
||||
/>
|
||||
<span className="absolute bottom-full mb-1 left-1/2 -translate-x-1/2 bg-black text-white text-xs px-2 py-[2px] rounded opacity-0 group-hover:opacity-100 transition">
|
||||
Reject
|
||||
{item.status?.toLowerCase() === 'rejected' ? 'Already Rejected' : 'Reject'}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@ -259,6 +376,135 @@ const SubmissionTable = ({ selectedQuarter, selectedYear }) => {
|
||||
View All Submissions
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Approve Confirmation Modal */}
|
||||
{showApproveModal && selectedSubmission && (
|
||||
<div className="fixed inset-0 flex items-center justify-center z-50 p-4 bg-black/30">
|
||||
<div className="bg-white rounded-lg w-full max-w-lg border border-gray-200 shadow-lg">
|
||||
<div className="flex items-center justify-between h-12 p-4 border-b border-gray-200">
|
||||
<h3 className="text-base font-medium text-gray-900">Approve this submission?</h3>
|
||||
<button
|
||||
onClick={handleCloseApproveModal}
|
||||
className="text-gray-400 hover:text-gray-500"
|
||||
>
|
||||
<X size={18} />
|
||||
</button>
|
||||
</div>
|
||||
<div className="p-6">
|
||||
<p className="text-sm text-gray-600 mb-4">
|
||||
You're about to approve <span className="font-medium text-gray-900">{selectedSubmission.establishment?.factory_name}</span> for <span className="font-medium text-gray-900">{selectedSubmission.quarter} {selectedSubmission.year}</span>.
|
||||
</p>
|
||||
<p className="text-sm text-gray-600 mb-6">
|
||||
This will lock the record for the establishment.
|
||||
</p>
|
||||
<div className="flex justify-end gap-3">
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleCloseApproveModal}
|
||||
className="px-4 py-2 text-sm font-medium text-[#92722A] bg-white border border-[#92722A] rounded-md shadow-sm hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-[#92722A]"
|
||||
disabled={isProcessing}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleApproveConfirm}
|
||||
className="px-4 py-2 text-sm font-medium text-white bg-[#92722A] border border-transparent rounded-md shadow-sm hover:bg-[#7c5e24] focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-[#92722A] flex items-center gap-2"
|
||||
disabled={isProcessing}
|
||||
>
|
||||
{isProcessing ? (
|
||||
<>
|
||||
<svg className="animate-spin h-4 w-4 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||||
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
|
||||
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8v8z"></path>
|
||||
</svg>
|
||||
Approving...
|
||||
</>
|
||||
) : 'Approve'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Reject Confirmation Modal */}
|
||||
{showRejectModal && selectedSubmission && (
|
||||
<div className="fixed inset-0 flex items-center justify-center z-50 p-4 bg-black/30">
|
||||
<div className="bg-white rounded-lg w-full max-w-lg border border-gray-200 shadow-lg">
|
||||
<div className="flex items-center justify-between h-12 p-4 border-b border-gray-200">
|
||||
<h3 className="text-base font-medium text-gray-900">Reject this submission?</h3>
|
||||
<button
|
||||
onClick={handleCloseRejectModal}
|
||||
className="text-gray-400 hover:text-gray-500"
|
||||
>
|
||||
<X size={18} />
|
||||
</button>
|
||||
</div>
|
||||
<div className="p-6">
|
||||
<p className="text-sm text-gray-600 leading-6 mb-2">
|
||||
You're about to return <span className="font-medium text-gray-900">{selectedSubmission.establishment?.factory_name}</span>'s <span className="font-medium text-gray-900">{selectedSubmission.quarter} {selectedSubmission.year}</span> submission for correction.
|
||||
</p>
|
||||
<p className="text-sm text-gray-600 leading-6 mb-3">
|
||||
The status will change to Rejected and the establishment can resubmit within the survey window.
|
||||
</p>
|
||||
<div className="mb-0">
|
||||
<label htmlFor="rejectReason" className="block text-sm font-medium text-gray-700 mb-2">
|
||||
Reason for rejection: <span className="text-red-500">*</span>
|
||||
</label>
|
||||
<textarea
|
||||
id="rejectReason"
|
||||
rows={4}
|
||||
className={`w-full px-3 py-2 border ${showRejectError && !rejectionReason.trim() ? 'border-red-500' : 'border-gray-300'} rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-[#92722A] focus:border-[#92722A] text-sm`}
|
||||
placeholder="Please provide a reason for rejection"
|
||||
value={rejectionReason}
|
||||
onChange={(e) => {
|
||||
setRejectionReason(e.target.value);
|
||||
if (e.target.value.trim() && showRejectError) {
|
||||
setShowRejectError(false);
|
||||
}
|
||||
}}
|
||||
onBlur={() => {
|
||||
if (!rejectionReason.trim()) {
|
||||
setShowRejectError(true);
|
||||
}
|
||||
}}
|
||||
disabled={isProcessing}
|
||||
/>
|
||||
{showRejectError && !rejectionReason.trim() && (
|
||||
<p className="mt-1 text-sm text-red-600">Reason for rejection is required</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="mt-4 flex justify-end gap-3">
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleCloseRejectModal}
|
||||
className="px-4 py-2 text-sm font-medium text-[#92722A] bg-white border border-[#92722A] rounded-md shadow-sm hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-[#92722A]"
|
||||
disabled={isProcessing}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleRejectConfirm}
|
||||
className="px-4 py-2 text-sm font-medium text-white bg-[#92722A] border border-transparent rounded-md shadow-sm hover:bg-[#7c5e24] focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-[#92722A] flex items-center gap-2"
|
||||
disabled={isProcessing || !rejectionReason.trim()}
|
||||
>
|
||||
{isProcessing ? (
|
||||
<>
|
||||
<svg className="animate-spin h-4 w-4 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||||
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
|
||||
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8v8z"></path>
|
||||
</svg>
|
||||
Rejecting...
|
||||
</>
|
||||
) : 'Reject'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@ -32,7 +32,7 @@ const AdminDashboard = () => {
|
||||
const isMounted = useRef(false);
|
||||
const prevParams = useRef({ quarter: null, year: null });
|
||||
|
||||
const fetchDashboardData = React.useCallback(async (quarter, year) => {
|
||||
const fetchDashboardData = React.useCallback(async (quarter = 'All', year = 'All') => {
|
||||
// Skip if params haven't changed
|
||||
if (prevParams.current.quarter === quarter && prevParams.current.year === year) {
|
||||
return;
|
||||
@ -45,6 +45,7 @@ const AdminDashboard = () => {
|
||||
setLoading(true);
|
||||
const params = new URLSearchParams();
|
||||
|
||||
// Only add quarter and year to params if they are not 'All'
|
||||
if (quarter && quarter !== 'All') {
|
||||
params.append('quarter', quarter);
|
||||
}
|
||||
@ -58,8 +59,6 @@ const AdminDashboard = () => {
|
||||
if (!isMounted.current) return;
|
||||
|
||||
const data = response?.data?.summary || {};
|
||||
const selectedQuarterFromApi = response?.data?.selected_quarter || quarter || 'All';
|
||||
const selectedYearFromApi = response?.data?.selected_year || year || 'All';
|
||||
const quarterlyWindowsData = response?.data?.quarterly_windows || {};
|
||||
|
||||
console.log("Fetching data for:", { quarter, year });
|
||||
@ -80,9 +79,8 @@ const AdminDashboard = () => {
|
||||
grace_periods_days: quarterlyWindowsData.grace_periods_days || 0
|
||||
}));
|
||||
|
||||
// Only update these if they're different to prevent unnecessary re-renders
|
||||
setSelectedQuarter(prev => prev !== selectedQuarterFromApi ? selectedQuarterFromApi : prev);
|
||||
setSelectedYear(prev => prev !== selectedYearFromApi ? selectedYearFromApi : prev);
|
||||
// Keep the selected values as they are
|
||||
// The dropdowns will maintain their current selection
|
||||
} catch (error) {
|
||||
console.error('Error fetching dashboard summary:', error);
|
||||
} finally {
|
||||
@ -121,11 +119,11 @@ const AdminDashboard = () => {
|
||||
onChange={(e) => setSelectedQuarter(e.target.value)}
|
||||
className="border border-[#D0D5DD] rounded-md h-8 px-2 text-sm focus:outline-none focus:ring-1 focus:ring-[#92722A]"
|
||||
>
|
||||
{/* <option>All</option> */}
|
||||
<option>Q1</option>
|
||||
<option>Q2</option>
|
||||
<option>Q3</option>
|
||||
<option>Q4</option>
|
||||
<option value="All">All</option>
|
||||
<option value="Q1">Q1</option>
|
||||
<option value="Q2">Q2</option>
|
||||
<option value="Q3">Q3</option>
|
||||
<option value="Q4">Q4</option>
|
||||
</select>
|
||||
|
||||
<select
|
||||
@ -133,6 +131,7 @@ const AdminDashboard = () => {
|
||||
onChange={(e) => setSelectedYear(e.target.value)}
|
||||
className="border border-[#D0D5DD] rounded-md h-8 px-2 text-sm focus:outline-none focus:ring-1 focus:ring-[#92722A]"
|
||||
>
|
||||
<option value="All">All</option>
|
||||
{Array.from(
|
||||
{ length: new Date().getFullYear() - 2021 },
|
||||
(_, i) => new Date().getFullYear() - i
|
||||
|
||||
@ -311,21 +311,14 @@ const ValidationReview = () => {
|
||||
}) : [];
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-[#F6F5F1]">
|
||||
<div className="min-h-screen bg-[#F6F5F1] pb-24">
|
||||
<AdminHeader />
|
||||
<div className="max-w-[1280px] mx-auto px-4 py-6 space-y-6">
|
||||
<nav className="flex items-center gap-2 text-sm text-[#8F9299]">
|
||||
<Link to="/admin/dashboard" className="flex items-center gap-2 text-[#232528] hover:underline">
|
||||
Dashboard
|
||||
<img src={caretUpSrc} alt="Dashboard" className="h-3 w-3" />
|
||||
</Link>
|
||||
<nav className="flex items-center gap-2 text-sm text-[#8F9299] mb-6">
|
||||
<Link to="/admin/validations" className="flex items-center gap-2 text-[#232528] hover:underline">
|
||||
Manage Submissions
|
||||
<img src={caretUpSrc} alt="Manage Submissions" className="h-3 w-3" />
|
||||
Manage Submissions <img src={caretUpSrc} alt="" className="h-3 w-3 rotate-90" />
|
||||
</Link>
|
||||
<span className="flex items-center gap-2 text-[#92722A] font-medium">
|
||||
Submission Details
|
||||
</span>
|
||||
<span className="text-[#7A7F87] font-medium">View Submission</span>
|
||||
</nav>
|
||||
|
||||
<section className="min-h-[219px] rounded-[16px] border border-[#E6EAF5] bg-white px-0 py-6 shadow-[0_16px_32px_rgba(15,23,42,0.08)]">
|
||||
@ -587,8 +580,13 @@ const ValidationReview = () => {
|
||||
<textarea
|
||||
rows="2"
|
||||
className="w-full rounded-[10px] border border-[#D1D5DB] bg-[#FFFCF2] px-4 py-2 text-sm text-[#232528] focus:ring-2 focus:ring-[#92722A] focus:outline-none"
|
||||
placeholder="Add short note..."
|
||||
placeholder="Please enter remarks here"
|
||||
></textarea>
|
||||
<div className="mt-1 flex justify-end">
|
||||
{/* <p className="text-xs text-[#6B7280] italic inline-flex items-center gap-1">
|
||||
<span>Please refer to the screenshot</span>
|
||||
</p> */}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@ -682,6 +680,50 @@ const ValidationReview = () => {
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Fixed bottom action bar */}
|
||||
<div className="fixed bottom-0 left-0 right-0 border-t border-[#E5E7EB] bg-white py-4 px-6 shadow-[0_-4px_12px_rgba(0,0,0,0.05)]">
|
||||
<div className="mx-auto flex max-w-[1280px] justify-end gap-4">
|
||||
<button
|
||||
type="button"
|
||||
className="inline-flex h-11 items-center justify-center rounded-[10px] border border-[#B52520] px-3 text-sm font-semibold text-[#B52520] transition-colors hover:bg-[#FEE2E2] disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
onClick={() => setIsRejectOpen(true)}
|
||||
disabled={actionLoading || submissionData?.status === 'Rejected'}
|
||||
>
|
||||
{submissionData?.status === 'Rejected' ? 'Already Rejected' : 'Reject'}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="inline-flex h-11 items-center justify-center rounded-[10px] bg-[#92722A] px-6 text-sm font-semibold text-white transition-colors hover:bg-[#B68A35] disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
onClick={handleApprove}
|
||||
disabled={actionLoading || submissionData?.status === 'Approved'}
|
||||
>
|
||||
{actionLoading ? 'Processing...' : submissionData?.status === 'Approved' ? 'Already Approved' : 'Approve'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Fixed bottom action bar */}
|
||||
<div className="fixed bottom-0 left-0 right-0 border-t border-[#E5E7EB] bg-white py-4 px-6 shadow-[0_-4px_12px_rgba(0,0,0,0.05)]">
|
||||
<div className="mx-auto flex max-w-[1280px] justify-end gap-4">
|
||||
<button
|
||||
type="button"
|
||||
className="inline-flex h-11 items-center justify-center rounded-[10px] border border-[#B52520] px-6 text-sm font-semibold text-[#B52520] transition-colors hover:bg-[#FEE2E2] disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
onClick={() => setIsRejectOpen(true)}
|
||||
disabled={actionLoading || submissionData?.status === 'Rejected'}
|
||||
>
|
||||
{submissionData?.status === 'Rejected' ? 'Already Rejected' : 'Reject'}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="inline-flex h-11 items-center justify-center rounded-[10px] bg-[#92722A] px-6 text-sm font-semibold text-white transition-colors hover:bg-[#B68A35] disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
onClick={handleApprove}
|
||||
disabled={actionLoading || submissionData?.status === 'Approved'}
|
||||
>
|
||||
{actionLoading ? 'Processing...' : submissionData?.status === 'Approved' ? 'Already Approved' : 'Approve'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@ -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() && (
|
||||
<p className="mt-1 text-sm text-red-600">Reason for rejection is required</p>
|
||||
)}
|
||||
<p className="mt-1 text-xs text-right text-[#6B7280] italic">
|
||||
Please refer to the screenshot
|
||||
</p>
|
||||
|
||||
</div>
|
||||
<div className="mt-4 flex justify-end gap-3">
|
||||
<button
|
||||
|
||||
@ -6,7 +6,7 @@ import apiClient from '@/services/api/apiClient';
|
||||
* @param {string} [params.quarter] - Filter by quarter (Q1, Q2, Q3, Q4)
|
||||
* @param {string} [params.year] - Filter by year
|
||||
* @param {number} [params.page=1] - Page number for pagination
|
||||
* @param {number} [params.limit=10] - Number of items per page
|
||||
* @param {number} [params.limit=10000] - Number of items per page
|
||||
* @returns {Promise<Object>} - Response data
|
||||
*/
|
||||
export const getSubmissions = async (params = {}) => {
|
||||
@ -16,7 +16,7 @@ export const getSubmissions = async (params = {}) => {
|
||||
quarter = '',
|
||||
year = '',
|
||||
page = 1,
|
||||
limit = 100,
|
||||
limit = 10000,
|
||||
...restParams
|
||||
} = params;
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user