diff --git a/ipi-survey-platform/src/components/common/Footer.jsx b/ipi-survey-platform/src/components/common/Footer.jsx index 0e7677b..c09df4d 100644 --- a/ipi-survey-platform/src/components/common/Footer.jsx +++ b/ipi-survey-platform/src/components/common/Footer.jsx @@ -14,18 +14,18 @@ const Footer = () => { {/* About FCSC */}

About FCSC

- +

+ The Federal Competitiveness and Statistics Centre (FCSC) provides reliable data and insights to enhance the UAE's competitiveness and support sustainable national development. +

{/* Our Policies */}

Our Policies

@@ -33,9 +33,9 @@ const Footer = () => {

Information and Support

diff --git a/ipi-survey-platform/src/components/layout/HeaderBar.jsx b/ipi-survey-platform/src/components/layout/HeaderBar.jsx index 3ad912b..a3c9f3a 100644 --- a/ipi-survey-platform/src/components/layout/HeaderBar.jsx +++ b/ipi-survey-platform/src/components/layout/HeaderBar.jsx @@ -84,12 +84,12 @@ const HeaderBar = () => { className={({ isActive, isPending }) => `inline-flex items-center gap-2 pb-1 border-b-2 ${ isActive ? '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-[#232528] hover:text-gray-900 border-transparent' }`} onClick={(e) => { - if (window.location.pathname === '/dashboard') { + if (window.location.pathname === '/dashboard' || window.location.pathname === '/overview') { e.preventDefault(); } }} @@ -99,7 +99,7 @@ const HeaderBar = () => { Survey Survey diff --git a/ipi-survey-platform/src/components/survey/ProductData/ProductData.jsx b/ipi-survey-platform/src/components/survey/ProductData/ProductData.jsx index 11bda13..c6f51d8 100644 --- a/ipi-survey-platform/src/components/survey/ProductData/ProductData.jsx +++ b/ipi-survey-platform/src/components/survey/ProductData/ProductData.jsx @@ -8,6 +8,7 @@ import { fetchProducts, fetchUnits, } from '@/services/masters/masterService.js'; +import { fetchEstablishmentDetail } from '@/services/establishments/establishmentService'; import { useLocation } from 'react-router-dom'; 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(() => { const maxId = products.reduce((max, item) => (item && typeof item.id === 'number' ? Math.max(max, item.id) : max), 0); if (maxId >= nextId.current) { @@ -996,7 +1045,8 @@ const location = useLocation(); ...p, product: selectedProduct?.value || selectedValue, 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 const updatedProducts = products.map(prod => diff --git a/ipi-survey-platform/src/pages/Admin/Validations.jsx b/ipi-survey-platform/src/pages/Admin/Validations.jsx index effa708..f35883d 100644 --- a/ipi-survey-platform/src/pages/Admin/Validations.jsx +++ b/ipi-survey-platform/src/pages/Admin/Validations.jsx @@ -79,6 +79,8 @@ const ManageSubmissions = () => { const [rejectReason, setRejectReason] = React.useState(''); const [showRejectError, setShowRejectError] = React.useState(false); const pageSize = 10; + const [isInitialLoad, setIsInitialLoad] = React.useState(true); + const toastTimeoutRef = React.useRef(null); @@ -115,6 +117,8 @@ const ManageSubmissions = () => { const handleRejectClick = (submission) => { setSelectedSubmission(submission); + setRejectReason(''); + setShowRejectError(false); setShowRejectModal(true); }; @@ -122,6 +126,7 @@ const ManageSubmissions = () => { setShowRejectModal(false); setSelectedSubmission(null); setRejectReason(''); + setShowRejectError(false); }; const handleApproveConfirm = async () => { @@ -169,10 +174,14 @@ const ManageSubmissions = () => { }; const handleRejectConfirm = async () => { - if (!selectedSubmission || isRejecting || !rejectReason.trim()) { - if (!rejectReason.trim()) { - showToast('error', 'Please provide a reason for rejection'); - } + if (!selectedSubmission || isRejecting) { + return; + } + + // Validate the rejection reason + if (!rejectReason.trim()) { + setShowRejectError(true); + showToast('error', 'Please provide a reason for rejection'); return; } @@ -186,7 +195,7 @@ const ManageSubmissions = () => { const ok = (response && response.data) || response?.status === 200 || response?.status === 'success'; if (ok) { - showToast('success', 'Submission has been rejected'); + showToast('error', 'Submission has been rejected'); // Refresh the submissions list const data = await fetchDashboardData(selectedQuarter, selectedYear); const userProfile = JSON.parse(sessionStorage.getItem("user_profile")); @@ -201,7 +210,7 @@ const ManageSubmissions = () => { submittedAt: formatDateTime(item.created_at), status: item.status ?? '-', reviewer: userProfile?.name || '-', - reviewerOn: formatDateTime(item.updated_at || item.created_at), + reviewerOn: formatDateTime(item.created_at), })); setSubmissions(mapped); 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(() => { if (location.state?.showSuccessToast && location.state?.successMessage) { showToast('success', location.state.successMessage); @@ -233,47 +281,49 @@ const ManageSubmissions = () => { } }, [location.state, showToast]); - const fetchDashboardData = async (quarter, year) => { + const fetchDashboardData = async (quarter, year, page = 1, limit = 10) => { try { - // Add quarter and year to the API call to filter submissions - const params = new URLSearchParams(); - if (quarter && quarter !== 'All') params.append('quarter', quarter); - if (year && year !== 'All') params.append('year', year); + const params = { + quarter: quarter && quarter !== 'All' ? quarter : undefined, + year: year && year !== 'All' ? year : undefined, + page, + limit + }; - const result = await getSubmissions(params.toString()); - return result?.status === 'success' ? result.data : []; + 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); return []; } }; - // Fetch current quarter and year from admin_dashboard endpoint + // Fetch current quarter and year from admin_dashboard endpoint and initial data React.useEffect(() => { - const fetchCurrentQuarterAndYear = async () => { + const fetchInitialData = async () => { try { + setLoading(true); + + // First, get the current quarter and year 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(); - console.log("currentQuarter",currentQuarter) - console.log("currentYear",currentYear) + + // Set the filter states setSelectedQuarter(currentQuarter); setSelectedYear(currentYear); setQuarter(currentQuarter); setYear(currentYear); - } catch (error) { - console.error('Error fetching current quarter and year:', error); - } - }; - - fetchCurrentQuarterAndYear(); - }, []); - - React.useEffect(() => { - const fetchSubmissions = async () => { - try { - const data = await fetchDashboardData(selectedQuarter, selectedYear); - const userProfile = JSON.parse(sessionStorage.getItem("user_profile")); + + // Then fetch the submissions with the current quarter and year + const data = await fetchDashboardData(currentQuarter, currentYear); + const userProfile = JSON.parse(sessionStorage.getItem("user_profile") || '{}'); + const mapped = (data || []).map((item) => ({ id: item.id, establishment: item?.establishment?.factory_name || '-', @@ -281,25 +331,26 @@ const ManageSubmissions = () => { year: String(item.year ?? '-'), quarter: item.quarter ?? '-', products: item?.product_count || '-', - // submittedBy: item.created_by ?? '-', submittedBy: userProfile?.name || '-', submittedAt: formatDateTime(item.created_at), - status: item.status ?? '-', - // reviewer: item?.updated_by || '-', + status: item.status || 'Pending', reviewer: userProfile?.name || '-', - // reviewerOn: formatDateTime(item?.updated_at), - reviewerOn: formatDateTime(item.created_at), + reviewerOn: formatDateTime(item.updated_at || item.created_at), })); setSubmissions(mapped); + setError(null); } 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.'); + setSubmissions([]); } finally { setLoading(false); + setIsInitialLoad(false); } }; - fetchSubmissions(); + + fetchInitialData(); }, []); const yearOptions = React.useMemo(() => { @@ -324,21 +375,35 @@ const ManageSubmissions = () => { }, [submissions]); const filtered = React.useMemo(() => { + console.log('Current submissions:', submissions); const term = search.trim().toLowerCase(); - - return submissions.filter((item) => { + + const filteredItems = submissions.filter((item) => { + if (!item) return false; + const allValues = Object.values(item) .map((v) => String(v ?? '').toLowerCase()) .join(' '); 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 matchEmirate = !emirate || emirate === 'All' || item.emirate === emirate; 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]); const summaryCounts = React.useMemo( @@ -426,7 +491,7 @@ const ManageSubmissions = () => { ); - if (loading) + if (loading && isInitialLoad){ return (
@@ -436,6 +501,7 @@ const ManageSubmissions = () => { Loading submissions...
); + } if (error) return
{error}
; 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 ${ toast.type === 'success' ? 'bg-[#F3FAF4] border-[#C6E7D8] text-[#2F663C]' + : toast.type === 'reject' + ? 'bg-[#FEF2F2] border-[#FECACA] text-[#B91C1C]' : 'bg-[#FEF2F2] border-[#FECACA] text-[#B91C1C]' }`} > @@ -528,7 +596,7 @@ const ManageSubmissions = () => { Dashboard Dashboard - Manage Submissions + Manage Submissions
@@ -741,14 +809,24 @@ const ManageSubmissions = () => {