bug fixed
This commit is contained in:
parent
ebd0e4c4f5
commit
541e5a12a0
@ -14,18 +14,18 @@ const Footer = () => {
|
|||||||
{/* About FCSC */}
|
{/* About FCSC */}
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-[#92722A] font-semibold mb-3 text-lg">About FCSC</h3>
|
<h3 className="text-[#92722A] font-semibold mb-3 text-lg">About FCSC</h3>
|
||||||
<ul className="space-y-1 text-sm">
|
<p className="text-sm">
|
||||||
<li><a href="#" className="hover:underline">The Federal Competitiveness and Statistics Centre (FCSC) provides reliable data and insights to enhance the UAE’s competitiveness and support sustainable national development.</a></li>
|
The Federal Competitiveness and Statistics Centre (FCSC) provides reliable data and insights to enhance the UAE's competitiveness and support sustainable national development.
|
||||||
</ul>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Our Policies */}
|
{/* Our Policies */}
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-[#92722A] font-semibold mb-3 ml-16 text-lg">Our Policies</h3>
|
<h3 className="text-[#92722A] font-semibold mb-3 ml-16 text-lg">Our Policies</h3>
|
||||||
<ul className="space-y-1 ml-16 text-sm">
|
<ul className="space-y-1 ml-16 text-sm">
|
||||||
<li><a href="#" className="hover:underline">Disclaimer</a></li>
|
<li>Disclaimer</li>
|
||||||
<li><a href="#" className="hover:underline">Privacy policy</a></li>
|
<li>Privacy policy</li>
|
||||||
<li><a href="#" className="hover:underline">Terms and conditions</a></li>
|
<li>Terms and conditions</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -33,9 +33,9 @@ const Footer = () => {
|
|||||||
<div>
|
<div>
|
||||||
<h3 className="text-[#92722A] font-semibold mb-3 ml-1 text-lg">Information and Support</h3>
|
<h3 className="text-[#92722A] font-semibold mb-3 ml-1 text-lg">Information and Support</h3>
|
||||||
<ul className="space-y-1 ml-1 text-sm">
|
<ul className="space-y-1 ml-1 text-sm">
|
||||||
<li><a href="#" className="hover:underline">Contact us</a></li>
|
<li>Contact us</li>
|
||||||
<li><a href="#" className="hover:underline">FAQ’s</a></li>
|
<li>FAQ’s</li>
|
||||||
<li><a href="#" className="hover:underline">Feedback and complaints</a></li>
|
<li>Feedback and complaints</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -84,12 +84,12 @@ const HeaderBar = () => {
|
|||||||
className={({ isActive, isPending }) => `inline-flex items-center gap-2 pb-1 border-b-2 ${
|
className={({ isActive, isPending }) => `inline-flex items-center gap-2 pb-1 border-b-2 ${
|
||||||
isActive
|
isActive
|
||||||
? 'text-[#92722A] font-medium border-[#92722A]'
|
? 'text-[#92722A] font-medium border-[#92722A]'
|
||||||
: window.location.pathname === '/dashboard'
|
: (window.location.pathname === '/dashboard' || window.location.pathname === '/overview')
|
||||||
? 'text-gray-400 cursor-not-allowed'
|
? 'text-gray-400 cursor-not-allowed'
|
||||||
: 'text-[#232528] hover:text-gray-900 border-transparent'
|
: 'text-[#232528] hover:text-gray-900 border-transparent'
|
||||||
}`}
|
}`}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
if (window.location.pathname === '/dashboard') {
|
if (window.location.pathname === '/dashboard' || window.location.pathname === '/overview') {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
@ -99,7 +99,7 @@ const HeaderBar = () => {
|
|||||||
<img
|
<img
|
||||||
src={isActive ? surveyIconActiveSrc : surveyIconInactiveSrc}
|
src={isActive ? surveyIconActiveSrc : surveyIconInactiveSrc}
|
||||||
alt="Survey"
|
alt="Survey"
|
||||||
className={`h-[18px] w-[18px] ${window.location.pathname === '/dashboard' ? 'opacity-50' : ''}`}
|
className={`h-[18px] w-[18px] ${(window.location.pathname === '/dashboard' || window.location.pathname === '/overview') ? 'opacity-50' : ''}`}
|
||||||
/>
|
/>
|
||||||
<span>Survey</span>
|
<span>Survey</span>
|
||||||
</>
|
</>
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import {
|
|||||||
fetchProducts,
|
fetchProducts,
|
||||||
fetchUnits,
|
fetchUnits,
|
||||||
} from '@/services/masters/masterService.js';
|
} from '@/services/masters/masterService.js';
|
||||||
|
import { fetchEstablishmentDetail } from '@/services/establishments/establishmentService';
|
||||||
import { useLocation } from 'react-router-dom';
|
import { useLocation } from 'react-router-dom';
|
||||||
|
|
||||||
const caretDownSrc = '/assets/images/CaretDown-black.svg';
|
const caretDownSrc = '/assets/images/CaretDown-black.svg';
|
||||||
@ -446,6 +447,54 @@ const ProductData = ({
|
|||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|
||||||
|
// Fetch establishment details and products on component mount
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchEstablishmentProducts = async () => {
|
||||||
|
try {
|
||||||
|
const establishmentId = sessionStorage.getItem('establishment_id');
|
||||||
|
if (!establishmentId) {
|
||||||
|
console.error('Establishment ID not found in session storage');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = await fetchEstablishmentDetail(establishmentId);
|
||||||
|
if (response && response.data && Array.isArray(response.data.establishment_products)) {
|
||||||
|
// Transform products data to match the expected format
|
||||||
|
const formattedProducts = response.data.establishment_products.map(item => {
|
||||||
|
const product = item.product || {};
|
||||||
|
return {
|
||||||
|
value: product.hs_code || '',
|
||||||
|
label: product.product_name ?
|
||||||
|
`${product.product_name} (${product.hs_code || 'N/A'})` :
|
||||||
|
'Unknown Product',
|
||||||
|
productId: item.product_id, // Include the actual product ID
|
||||||
|
productData: product // Include the full product data
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update the product options state
|
||||||
|
setProductOptions(formattedProducts);
|
||||||
|
|
||||||
|
// If you need to update the form with initial products, uncomment and modify:
|
||||||
|
// if (formattedProducts.length > 0) {
|
||||||
|
// const initialProducts = formattedProducts.map((product, index) => ({
|
||||||
|
// id: index + 1,
|
||||||
|
// product: product.value,
|
||||||
|
// unit: '',
|
||||||
|
// // ... other default values
|
||||||
|
// }));
|
||||||
|
// onProductsChange(initialProducts);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Error fetching establishment products:', err);
|
||||||
|
setError('Failed to load establishment products');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fetchEstablishmentProducts();
|
||||||
|
}, []);
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const maxId = products.reduce((max, item) => (item && typeof item.id === 'number' ? Math.max(max, item.id) : max), 0);
|
const maxId = products.reduce((max, item) => (item && typeof item.id === 'number' ? Math.max(max, item.id) : max), 0);
|
||||||
if (maxId >= nextId.current) {
|
if (maxId >= nextId.current) {
|
||||||
@ -996,7 +1045,8 @@ const location = useLocation();
|
|||||||
...p,
|
...p,
|
||||||
product: selectedProduct?.value || selectedValue,
|
product: selectedProduct?.value || selectedValue,
|
||||||
productName: selectedProduct?.label || '',
|
productName: selectedProduct?.label || '',
|
||||||
productId: selectedProduct?.value || selectedValue
|
productId: selectedProduct?.productId || selectedValue, // Use the actual product ID
|
||||||
|
product_id: selectedProduct?.productId || selectedValue // Add product_id for submission
|
||||||
};
|
};
|
||||||
// Update the products array with the updated product
|
// Update the products array with the updated product
|
||||||
const updatedProducts = products.map(prod =>
|
const updatedProducts = products.map(prod =>
|
||||||
|
|||||||
@ -79,6 +79,8 @@ const ManageSubmissions = () => {
|
|||||||
const [rejectReason, setRejectReason] = React.useState('');
|
const [rejectReason, setRejectReason] = React.useState('');
|
||||||
const [showRejectError, setShowRejectError] = React.useState(false);
|
const [showRejectError, setShowRejectError] = React.useState(false);
|
||||||
const pageSize = 10;
|
const pageSize = 10;
|
||||||
|
const [isInitialLoad, setIsInitialLoad] = React.useState(true);
|
||||||
|
|
||||||
|
|
||||||
const toastTimeoutRef = React.useRef(null);
|
const toastTimeoutRef = React.useRef(null);
|
||||||
|
|
||||||
@ -115,6 +117,8 @@ const ManageSubmissions = () => {
|
|||||||
|
|
||||||
const handleRejectClick = (submission) => {
|
const handleRejectClick = (submission) => {
|
||||||
setSelectedSubmission(submission);
|
setSelectedSubmission(submission);
|
||||||
|
setRejectReason('');
|
||||||
|
setShowRejectError(false);
|
||||||
setShowRejectModal(true);
|
setShowRejectModal(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -122,6 +126,7 @@ const ManageSubmissions = () => {
|
|||||||
setShowRejectModal(false);
|
setShowRejectModal(false);
|
||||||
setSelectedSubmission(null);
|
setSelectedSubmission(null);
|
||||||
setRejectReason('');
|
setRejectReason('');
|
||||||
|
setShowRejectError(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleApproveConfirm = async () => {
|
const handleApproveConfirm = async () => {
|
||||||
@ -169,10 +174,14 @@ const ManageSubmissions = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleRejectConfirm = async () => {
|
const handleRejectConfirm = async () => {
|
||||||
if (!selectedSubmission || isRejecting || !rejectReason.trim()) {
|
if (!selectedSubmission || isRejecting) {
|
||||||
if (!rejectReason.trim()) {
|
return;
|
||||||
showToast('error', 'Please provide a reason for rejection');
|
}
|
||||||
}
|
|
||||||
|
// Validate the rejection reason
|
||||||
|
if (!rejectReason.trim()) {
|
||||||
|
setShowRejectError(true);
|
||||||
|
showToast('error', 'Please provide a reason for rejection');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -186,7 +195,7 @@ const ManageSubmissions = () => {
|
|||||||
const ok = (response && response.data) || response?.status === 200 || response?.status === 'success';
|
const ok = (response && response.data) || response?.status === 200 || response?.status === 'success';
|
||||||
|
|
||||||
if (ok) {
|
if (ok) {
|
||||||
showToast('success', 'Submission has been rejected');
|
showToast('error', 'Submission has been rejected');
|
||||||
// Refresh the submissions list
|
// Refresh the submissions list
|
||||||
const data = await fetchDashboardData(selectedQuarter, selectedYear);
|
const data = await fetchDashboardData(selectedQuarter, selectedYear);
|
||||||
const userProfile = JSON.parse(sessionStorage.getItem("user_profile"));
|
const userProfile = JSON.parse(sessionStorage.getItem("user_profile"));
|
||||||
@ -201,7 +210,7 @@ const ManageSubmissions = () => {
|
|||||||
submittedAt: formatDateTime(item.created_at),
|
submittedAt: formatDateTime(item.created_at),
|
||||||
status: item.status ?? '-',
|
status: item.status ?? '-',
|
||||||
reviewer: userProfile?.name || '-',
|
reviewer: userProfile?.name || '-',
|
||||||
reviewerOn: formatDateTime(item.updated_at || item.created_at),
|
reviewerOn: formatDateTime(item.created_at),
|
||||||
}));
|
}));
|
||||||
setSubmissions(mapped);
|
setSubmissions(mapped);
|
||||||
setRejectReason('');
|
setRejectReason('');
|
||||||
@ -226,6 +235,45 @@ const ManageSubmissions = () => {
|
|||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
// Fetch data when component mounts or when quarter/year changes
|
||||||
|
React.useEffect(() => {
|
||||||
|
const loadData = async () => {
|
||||||
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
const data = await fetchDashboardData(selectedQuarter, selectedYear);
|
||||||
|
const userProfile = JSON.parse(sessionStorage.getItem("user_profile"));
|
||||||
|
const mapped = (data || []).map((item) => ({
|
||||||
|
id: item.id,
|
||||||
|
establishment: item?.establishment?.factory_name || '-',
|
||||||
|
emirate: item?.establishment?.establishment_emirate?.name || '-',
|
||||||
|
year: String(item.year ?? '-'),
|
||||||
|
quarter: item.quarter ?? '-',
|
||||||
|
products: item?.product_count || '-',
|
||||||
|
submittedBy: userProfile?.name || '-',
|
||||||
|
submittedAt: formatDateTime(item.created_at),
|
||||||
|
status: item.status ?? '-',
|
||||||
|
reviewer: userProfile?.name || '-',
|
||||||
|
reviewerOn: formatDateTime(item.updated_at || item.created_at),
|
||||||
|
}));
|
||||||
|
|
||||||
|
setSubmissions(mapped);
|
||||||
|
setError(null);
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Failed to load submissions', err);
|
||||||
|
setError('Unable to load submissions. Please try again later.');
|
||||||
|
setSubmissions([]);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
setIsInitialLoad(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Only load data if we have valid quarter and year
|
||||||
|
if (selectedQuarter && selectedYear) {
|
||||||
|
loadData();
|
||||||
|
}
|
||||||
|
}, [selectedQuarter, selectedYear]);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (location.state?.showSuccessToast && location.state?.successMessage) {
|
if (location.state?.showSuccessToast && location.state?.successMessage) {
|
||||||
showToast('success', location.state.successMessage);
|
showToast('success', location.state.successMessage);
|
||||||
@ -233,47 +281,49 @@ const ManageSubmissions = () => {
|
|||||||
}
|
}
|
||||||
}, [location.state, showToast]);
|
}, [location.state, showToast]);
|
||||||
|
|
||||||
const fetchDashboardData = async (quarter, year) => {
|
const fetchDashboardData = async (quarter, year, page = 1, limit = 10) => {
|
||||||
try {
|
try {
|
||||||
// Add quarter and year to the API call to filter submissions
|
const params = {
|
||||||
const params = new URLSearchParams();
|
quarter: quarter && quarter !== 'All' ? quarter : undefined,
|
||||||
if (quarter && quarter !== 'All') params.append('quarter', quarter);
|
year: year && year !== 'All' ? year : undefined,
|
||||||
if (year && year !== 'All') params.append('year', year);
|
page,
|
||||||
|
limit
|
||||||
|
};
|
||||||
|
|
||||||
const result = await getSubmissions(params.toString());
|
console.log('Fetching with params:', params);
|
||||||
return result?.status === 'success' ? result.data : [];
|
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) {
|
} catch (error) {
|
||||||
console.error('Error fetching dashboard data:', error);
|
console.error('Error fetching dashboard data:', error);
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Fetch current quarter and year from admin_dashboard endpoint
|
// Fetch current quarter and year from admin_dashboard endpoint and initial data
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
const fetchCurrentQuarterAndYear = async () => {
|
const fetchInitialData = async () => {
|
||||||
try {
|
try {
|
||||||
|
setLoading(true);
|
||||||
|
|
||||||
|
// First, get the current quarter and year
|
||||||
const response = await apiClient.get('/admin_dashboard');
|
const response = await apiClient.get('/admin_dashboard');
|
||||||
const currentQuarter = response?.data?.selected_quarter;
|
const currentQuarter = response?.data?.selected_quarter || 'Q1';
|
||||||
const currentYear = response?.data?.selected_year || new Date().getFullYear().toString();
|
const currentYear = response?.data?.selected_year || new Date().getFullYear().toString();
|
||||||
console.log("currentQuarter",currentQuarter)
|
|
||||||
console.log("currentYear",currentYear)
|
// Set the filter states
|
||||||
setSelectedQuarter(currentQuarter);
|
setSelectedQuarter(currentQuarter);
|
||||||
setSelectedYear(currentYear);
|
setSelectedYear(currentYear);
|
||||||
setQuarter(currentQuarter);
|
setQuarter(currentQuarter);
|
||||||
setYear(currentYear);
|
setYear(currentYear);
|
||||||
} catch (error) {
|
|
||||||
console.error('Error fetching current quarter and year:', error);
|
// Then fetch the submissions with the current quarter and year
|
||||||
}
|
const data = await fetchDashboardData(currentQuarter, currentYear);
|
||||||
};
|
const userProfile = JSON.parse(sessionStorage.getItem("user_profile") || '{}');
|
||||||
|
|
||||||
fetchCurrentQuarterAndYear();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
const fetchSubmissions = async () => {
|
|
||||||
try {
|
|
||||||
const data = await fetchDashboardData(selectedQuarter, selectedYear);
|
|
||||||
const userProfile = JSON.parse(sessionStorage.getItem("user_profile"));
|
|
||||||
const mapped = (data || []).map((item) => ({
|
const mapped = (data || []).map((item) => ({
|
||||||
id: item.id,
|
id: item.id,
|
||||||
establishment: item?.establishment?.factory_name || '-',
|
establishment: item?.establishment?.factory_name || '-',
|
||||||
@ -281,25 +331,26 @@ const ManageSubmissions = () => {
|
|||||||
year: String(item.year ?? '-'),
|
year: String(item.year ?? '-'),
|
||||||
quarter: item.quarter ?? '-',
|
quarter: item.quarter ?? '-',
|
||||||
products: item?.product_count || '-',
|
products: item?.product_count || '-',
|
||||||
// submittedBy: item.created_by ?? '-',
|
|
||||||
submittedBy: userProfile?.name || '-',
|
submittedBy: userProfile?.name || '-',
|
||||||
submittedAt: formatDateTime(item.created_at),
|
submittedAt: formatDateTime(item.created_at),
|
||||||
status: item.status ?? '-',
|
status: item.status || 'Pending',
|
||||||
// reviewer: item?.updated_by || '-',
|
|
||||||
reviewer: userProfile?.name || '-',
|
reviewer: userProfile?.name || '-',
|
||||||
// reviewerOn: formatDateTime(item?.updated_at),
|
reviewerOn: formatDateTime(item.updated_at || item.created_at),
|
||||||
reviewerOn: formatDateTime(item.created_at),
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
setSubmissions(mapped);
|
setSubmissions(mapped);
|
||||||
|
setError(null);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Failed to load submissions', err);
|
console.error('Failed to load initial data:', err);
|
||||||
setError('Unable to load submissions. Please try again later.');
|
setError('Unable to load submissions. Please try again later.');
|
||||||
|
setSubmissions([]);
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
|
setIsInitialLoad(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
fetchSubmissions();
|
|
||||||
|
fetchInitialData();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const yearOptions = React.useMemo(() => {
|
const yearOptions = React.useMemo(() => {
|
||||||
@ -324,21 +375,35 @@ const ManageSubmissions = () => {
|
|||||||
}, [submissions]);
|
}, [submissions]);
|
||||||
|
|
||||||
const filtered = React.useMemo(() => {
|
const filtered = React.useMemo(() => {
|
||||||
|
console.log('Current submissions:', submissions);
|
||||||
const term = search.trim().toLowerCase();
|
const term = search.trim().toLowerCase();
|
||||||
|
|
||||||
return submissions.filter((item) => {
|
const filteredItems = submissions.filter((item) => {
|
||||||
|
if (!item) return false;
|
||||||
|
|
||||||
const allValues = Object.values(item)
|
const allValues = Object.values(item)
|
||||||
.map((v) => String(v ?? '').toLowerCase())
|
.map((v) => String(v ?? '').toLowerCase())
|
||||||
.join(' ');
|
.join(' ');
|
||||||
|
|
||||||
const matchSearch = !term || allValues.includes(term);
|
const matchSearch = !term || allValues.includes(term);
|
||||||
const matchYear = !year || year === 'All' || item.year === year;
|
const matchYear = !year || year === 'All' || String(item.year) === String(year);
|
||||||
const matchQuarter = !quarter || quarter === 'All' || item.quarter === quarter;
|
const matchQuarter = !quarter || quarter === 'All' || item.quarter === quarter;
|
||||||
const matchEmirate = !emirate || emirate === 'All' || item.emirate === emirate;
|
const matchEmirate = !emirate || emirate === 'All' || item.emirate === emirate;
|
||||||
const matchStatus = !status || status === 'All' || item.status === status;
|
const matchStatus = !status || status === 'All' || item.status === status;
|
||||||
|
|
||||||
return matchSearch && matchYear && matchQuarter && matchEmirate && matchStatus;
|
const matches = matchSearch && matchYear && matchQuarter && matchEmirate && matchStatus;
|
||||||
|
|
||||||
|
if (matches) {
|
||||||
|
console.log('Matching item:', item);
|
||||||
|
} else {
|
||||||
|
console.log('Filtered out item:', item, { matchSearch, matchYear, matchQuarter, matchEmirate, matchStatus });
|
||||||
|
}
|
||||||
|
|
||||||
|
return matches;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
console.log('Filtered items count:', filteredItems.length);
|
||||||
|
return filteredItems;
|
||||||
}, [submissions, search, year, quarter, emirate, status]);
|
}, [submissions, search, year, quarter, emirate, status]);
|
||||||
|
|
||||||
const summaryCounts = React.useMemo(
|
const summaryCounts = React.useMemo(
|
||||||
@ -426,7 +491,7 @@ const ManageSubmissions = () => {
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
if (loading)
|
if (loading && isInitialLoad){
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center justify-center h-screen text-[#92722A]">
|
<div className="flex items-center justify-center h-screen text-[#92722A]">
|
||||||
<svg className="animate-spin h-6 w-6 mr-3 text-[#92722A]" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
<svg className="animate-spin h-6 w-6 mr-3 text-[#92722A]" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
||||||
@ -436,6 +501,7 @@ const ManageSubmissions = () => {
|
|||||||
Loading submissions...
|
Loading submissions...
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (error) return <div className="flex items-center justify-center h-screen text-red-600">{error}</div>;
|
if (error) return <div className="flex items-center justify-center h-screen text-red-600">{error}</div>;
|
||||||
const downloadCsv = (data) => {
|
const downloadCsv = (data) => {
|
||||||
@ -506,6 +572,8 @@ const ManageSubmissions = () => {
|
|||||||
className={`text-sm rounded px-3 py-2 border flex items-start justify-between gap-3 shadow ${
|
className={`text-sm rounded px-3 py-2 border flex items-start justify-between gap-3 shadow ${
|
||||||
toast.type === 'success'
|
toast.type === 'success'
|
||||||
? 'bg-[#F3FAF4] border-[#C6E7D8] text-[#2F663C]'
|
? 'bg-[#F3FAF4] border-[#C6E7D8] text-[#2F663C]'
|
||||||
|
: toast.type === 'reject'
|
||||||
|
? 'bg-[#FEF2F2] border-[#FECACA] text-[#B91C1C]'
|
||||||
: 'bg-[#FEF2F2] border-[#FECACA] text-[#B91C1C]'
|
: 'bg-[#FEF2F2] border-[#FECACA] text-[#B91C1C]'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
@ -528,7 +596,7 @@ const ManageSubmissions = () => {
|
|||||||
<Link to="/admin/dashboard" className="flex items-center gap-2 text-[#232528] hover:underline">
|
<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" />
|
Dashboard <img src={caretUpSrc} alt="Dashboard" className="h-3 w-3" />
|
||||||
</Link>
|
</Link>
|
||||||
<span className="text-[#92722A] font-medium">Manage Submissions</span>
|
<span className="text-[#7A7F87] font-medium">Manage Submissions</span>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div className="flex flex-wrap gap-[6px] rounded-[12px] bg-white px-5 py-3 shadow items-center justify-between">
|
<div className="flex flex-wrap gap-[6px] rounded-[12px] bg-white px-5 py-3 shadow items-center justify-between">
|
||||||
@ -741,14 +809,24 @@ const ManageSubmissions = () => {
|
|||||||
<textarea
|
<textarea
|
||||||
id="rejectReason"
|
id="rejectReason"
|
||||||
rows={4}
|
rows={4}
|
||||||
className={`w-full px-3 py-2 border ${!rejectReason.trim() && showRejectError ? 'border-red-500' : 'border-gray-300'} rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-[#92722A] focus:border-[#92722A] text-sm`}
|
className={`w-full px-3 py-2 border ${showRejectError && !rejectReason.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"
|
placeholder="Please provide a reason for rejection"
|
||||||
value={rejectReason}
|
value={rejectReason}
|
||||||
onChange={(e) => setRejectReason(e.target.value)}
|
onChange={(e) => {
|
||||||
onBlur={() => setShowRejectError(!rejectReason.trim())}
|
setRejectReason(e.target.value);
|
||||||
|
// Clear error when user starts typing
|
||||||
|
if (e.target.value.trim() && showRejectError) {
|
||||||
|
setShowRejectError(false);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onBlur={() => {
|
||||||
|
if (!rejectReason.trim()) {
|
||||||
|
setShowRejectError(true);
|
||||||
|
}
|
||||||
|
}}
|
||||||
disabled={isRejecting}
|
disabled={isRejecting}
|
||||||
/>
|
/>
|
||||||
{!rejectReason.trim() && showRejectError && (
|
{showRejectError && !rejectReason.trim() && (
|
||||||
<p className="mt-1 text-sm text-red-600">Reason for rejection is required</p>
|
<p className="mt-1 text-sm text-red-600">Reason for rejection is required</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,9 +1,65 @@
|
|||||||
import apiClient from '@/services/api/apiClient';
|
import apiClient from '@/services/api/apiClient';
|
||||||
|
|
||||||
/** Fetch all submissions for the listing page */
|
/**
|
||||||
export const getSubmissions = async () => {
|
* Fetch submissions with pagination and filtering
|
||||||
const response = await apiClient.get('/submissions');
|
* @param {Object} params - Query parameters
|
||||||
return response.data;
|
* @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
|
||||||
|
* @returns {Promise<Object>} - Response data
|
||||||
|
*/
|
||||||
|
export const getSubmissions = async (params = {}) => {
|
||||||
|
try {
|
||||||
|
// Set default values
|
||||||
|
const {
|
||||||
|
quarter = '',
|
||||||
|
year = '',
|
||||||
|
page = 1,
|
||||||
|
limit = 100,
|
||||||
|
...restParams
|
||||||
|
} = params;
|
||||||
|
|
||||||
|
console.log('getSubmissions called with params:', { quarter, year, page, limit, ...restParams });
|
||||||
|
|
||||||
|
// Build query string
|
||||||
|
const queryParams = new URLSearchParams();
|
||||||
|
|
||||||
|
// Add pagination
|
||||||
|
queryParams.append('page', page);
|
||||||
|
queryParams.append('limit', limit);
|
||||||
|
|
||||||
|
// Add filters if provided
|
||||||
|
if (quarter) queryParams.append('quarter', quarter);
|
||||||
|
if (year) queryParams.append('year', year);
|
||||||
|
|
||||||
|
// Add any additional parameters
|
||||||
|
Object.entries(restParams).forEach(([key, value]) => {
|
||||||
|
if (value !== undefined && value !== null && value !== '') {
|
||||||
|
queryParams.append(key, value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const url = `/submissions?${queryParams.toString()}`;
|
||||||
|
console.log('Making API request to:', url);
|
||||||
|
|
||||||
|
const response = await apiClient.get(url);
|
||||||
|
console.log('API response received:', response);
|
||||||
|
|
||||||
|
// Check if response.data is an array or has a data property
|
||||||
|
const responseData = Array.isArray(response.data) ? response.data : response.data?.data;
|
||||||
|
console.log('Processed response data:', responseData);
|
||||||
|
|
||||||
|
return responseData || [];
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error in getSubmissions:', error);
|
||||||
|
console.error('Error details:', {
|
||||||
|
message: error.message,
|
||||||
|
response: error.response?.data,
|
||||||
|
status: error.response?.status
|
||||||
|
});
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/** 🔹 Fetch a single submission by ID */
|
/** 🔹 Fetch a single submission by ID */
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user