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 */}
@@ -33,9 +33,9 @@ const Footer = () => {
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
>
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
- Manage Submissions
+ Manage Submissions
@@ -741,14 +809,24 @@ const ManageSubmissions = () => {
diff --git a/ipi-survey-platform/src/services/admin/submission.js b/ipi-survey-platform/src/services/admin/submission.js
index de77f46..9efdca1 100644
--- a/ipi-survey-platform/src/services/admin/submission.js
+++ b/ipi-survey-platform/src/services/admin/submission.js
@@ -1,9 +1,65 @@
import apiClient from '@/services/api/apiClient';
-/** Fetch all submissions for the listing page */
-export const getSubmissions = async () => {
- const response = await apiClient.get('/submissions');
- return response.data;
+/**
+ * Fetch submissions with pagination and filtering
+ * @param {Object} params - Query parameters
+ * @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} - 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 */