-
-
- {rows.map(([label, value], idx) => (
-
- |
- {label}
- |
- {value} |
-
- ))}
-
-
+const InfoCard = ({ icon: Icon, label, value }) => (
+
+
+

+
+
+
{label}
+
{value || '—'}
+
+
+);
+
+const DetailSection = ({ title, children, className = '' }) => (
+
);
@@ -46,42 +55,193 @@ const formatNumber = (value) => {
return new Intl.NumberFormat('en-US').format(number);
};
-const buildEstablishmentRows = (establishment) => {
+const buildEstablishmentDetails = (establishment) => {
if (!establishment) return [];
return [
- ['Establishment Name', establishment.establishmentName || '—'],
- ['Establishment ID', establishment.permanentFactoryCode || '—'],
- ['Industry Code', establishment.industryCode || '—'],
- ['License Number', establishment.licenseNumber || '—'],
- ['ISIC Code', establishment.isicCode || '—'],
- ['Emirate', establishment.emirate || '—'],
- ['Quarter', establishment.quarter || '—'],
- ['Year', establishment.year || '—'],
+ { icon: establishmentIconSrc, label: 'Establishment Name', value: establishment.establishmentName },
+ { icon: establishmentIdIconSrc, label: 'Establishment ID', value: establishment.licenseNumber },
+ { icon: emailIconSrc, label: 'Email', value: establishment.email },
+ { icon: emiratesIconSrc, label: 'Emirate', value: establishment.emirate },
];
};
-const buildEmploymentRows = (establishment) => {
- const info = establishment?.employeeInfo || {};
- return [
- ['Total Employees', formatNumber(info.totalEmployees) || '—'],
- ['Emirati Male', formatNumber(info.emiratiMale) || '—'],
- ['Emirati Female', formatNumber(info.emiratiFemale) || '—'],
- ['Non-Emirati Male', formatNumber(info.nonEmiratiMale) || '—'],
- ['Non-Emirati Female', formatNumber(info.nonEmiratiFemale) || '—'],
- ['Total Emirati', formatNumber(info.totalEmirati) || '—'],
- ];
+
+const employmentIcons = {
+ 'Total Employees': totalEmployeesIconSrc,
+ 'Male Employees': maleEmployeesIconSrc,
+ 'Female Employees': femaleEmployeesIconSrc,
+ 'UAE Nationals': uaeIconSrc,
+ 'Non-UAE Nationals': nonUaeIconSrc,
+};
+const buildEmploymentDetails = (establishment) => {
+ if (!establishment?.employeeInfo) return [];
+
+ const employeeInfo = establishment.employeeInfo;
+ const result = [];
+
+ // Only add fields that exist in the employeeInfo object
+ if (employeeInfo.emiratiMale !== undefined || employeeInfo.nonEmiratiMale !== undefined) {
+ const totalMale = employeeInfo.emiratiMale;
+ result.push({
+ icon: maleEmployeesIconSrc,
+ label: 'Male Employees',
+ value: formatNumber(totalMale)
+ });
+ }
+
+ if (employeeInfo.emiratiFemale !== undefined || employeeInfo.nonEmiratiFemale !== undefined) {
+ const totalFemale = employeeInfo.emiratiFemale;
+ result.push({
+ icon: femaleEmployeesIconSrc,
+ label: 'Female Employees',
+ value: formatNumber(totalFemale)
+ });
+ }
+
+ if (employeeInfo.emiratiMale !== undefined || employeeInfo.emiratiFemale !== undefined) {
+ const totalUAE = (Number(employeeInfo.emiratiMale) || 0) + (Number(employeeInfo.emiratiFemale) || 0);
+ result.push({
+ icon: uaeIconSrc,
+ label: 'UAE Nationals',
+ value: formatNumber(totalUAE)
+ });
+ }
+
+ if (employeeInfo.nonEmiratiMale !== undefined || employeeInfo.nonEmiratiFemale !== undefined) {
+ const totalNonUAE = (Number(employeeInfo.nonEmiratiMale) || 0) + (Number(employeeInfo.nonEmiratiFemale) || 0);
+ result.push({
+ icon: nonUaeIconSrc,
+ label: 'Non-UAE Nationals',
+ value: formatNumber(totalNonUAE)
+ });
+ }
+
+ // Add total employees if any of the fields exist
+ if (result.length > 0) {
+ const total = result.reduce((sum, item) => {
+ if (item.label === 'Male Employees' || item.label === 'Female Employees') {
+ return sum + parseInt(item.value.replace(/,/g, '') || 0);
+ }
+ return sum;
+ }, 0);
+
+ result.unshift({
+ icon: totalEmployeesIconSrc,
+ label: 'Total Employees',
+ value: formatNumber(employeeInfo.totalEmployees || total)
+ });
+ }
+
+ return result;
};
-const buildProductRows = (products = []) => {
- if (!Array.isArray(products)) return [];
- return products.map((product) => [
- product.product || '—',
- product.unit || '—',
- formatNumber(product.currentQuantity) || '—',
- formatNumber(product.currentCost) || '—',
- formatNumber(product.forecastQuantity) || '—',
- formatNumber(product.forecastCost) || '—',
- ]);
+// Helper function to find display name by ID in an array of options
+const findDisplayName = (options, value) => {
+ if (!options || !Array.isArray(options)) return value || '—';
+ const option = options.find(opt => opt.value === value || opt.id === value);
+ return option ? option.label || option.name || value : value || '—';
+};
+
+const buildProductRows = (products = [], options = {}) => {
+ console.log('Raw products data:', products); // Debug log
+ console.log('Available options:', options); // Debug log
+
+ if (!Array.isArray(products)) {
+ console.warn('buildProductRows: Expected an array but got', typeof products);
+ return [];
+ }
+
+ if (products.length === 0) {
+ console.warn('buildProductRows: Empty products array received');
+ return [];
+ }
+
+ const {
+ variationReasons = [],
+ zeroTargetReasons = [],
+ unitOptions = [],
+ productOptions = []
+ } = options;
+
+ const rows = products.map((product, index) => {
+ if (!product) {
+ console.warn(`buildProductRows: Product at index ${index} is null or undefined`);
+ return null;
+ }
+
+ try {
+ // Find the full product details from productOptions if available
+ const fullProduct = productOptions.find(p => p.id === product.productId || p.value === product.productId);
+ console.log("fullProduct",fullProduct)
+ // Extract product details with better fallbacks
+ const productId = product.id || `product-${Math.random().toString(36).substr(2, 9)}`;
+ const productName = fullProduct?.label || fullProduct?.name || product.productName || product.product?.name || product.product;
+ console.log("productName",productName)
+ const productCode = fullProduct?.code || product.productCode || product.code || product.product?.code || product.id || `CODE-${index + 1}`;
+ console.log("productCode",productCode)
+ // Find unit display name
+ const unitnames = findDisplayName(unitOptions)
+ console.log("unitnames",unitnames)
+ const unitName = findDisplayName(unitOptions, product.unit);
+ console.log("unitName",unitName)
+
+ // Find reason display names
+ const variationReasonName = findDisplayName(variationReasons, product.variationReason);
+ const zeroTargetReasonName = findDisplayName(zeroTargetReasons, product.zeroTargetReason);
+
+ const capacity = product.capacity !== undefined && product.capacity !== null ? formatNumber(product.capacity) : '—';
+
+ // Map the quantity and cost data from the product form
+ const row = {
+ id: productId,
+ product: productName,
+ productCode: productCode,
+ unit: unitName, // Use display name instead of ID
+ capacity: capacity,
+
+ // Current Quarter (Q1) - January to March
+ q1_jan_quantity: formatNumber(product.janQuantity) || '—',
+ q1_feb_quantity: formatNumber(product.febQuantity) || '—',
+ q1_mar_quantity: formatNumber(product.marQuantity) || '—',
+ q1_jan_cost: formatNumber(product.janCost) || '—',
+ q1_feb_cost: formatNumber(product.febCost) || '—',
+ q1_mar_cost: formatNumber(product.marCost) || '—',
+
+ // Next Quarter (Q2) - April to June
+ q2_apr_quantity: formatNumber(product.aprQuantity) || '—',
+ q2_may_quantity: formatNumber(product.mayQuantity) || '—',
+ q2_jun_quantity: formatNumber(product.junQuantity) || '—',
+ q2_apr_cost: formatNumber(product.aprCost) || '—',
+ q2_may_cost: formatNumber(product.mayCost) || '—',
+ q2_jun_cost: formatNumber(product.junCost) || '—',
+
+ // Previous Quarter (Q4) - October to December (if available)
+ q4_oct_quantity: formatNumber(product.previousQuantity) || '—',
+ q4_nov_quantity: formatNumber(product.novQuantity) || '—',
+ q4_dec_quantity: formatNumber(product.decQuantity) || '—',
+ q4_oct_cost: formatNumber(product.octCost) || '—',
+ q4_nov_cost: formatNumber(product.novCost) || '—',
+ q4_dec_cost: formatNumber(product.decCost) || '—',
+
+ // Reasons and remarks - use display names
+ variationReason: variationReasonName,
+ zeroTargetReason: zeroTargetReasonName,
+ remarks: product.remarks || '—',
+
+ // Keep the raw product data for debugging
+ _raw: { ...product }
+ };
+
+ console.log(`Processed product ${index + 1}:`, row); // Debug log
+ return row;
+ } catch (error) {
+ console.error(`Error processing product at index ${index}:`, error, product);
+ return null;
+ }
+ });
+
+ // Filter out any null/undefined rows that might have been created due to errors
+ return rows.filter(Boolean);
};
const ReviewSubmit = ({
@@ -95,9 +255,11 @@ const ReviewSubmit = ({
hasSubmitted = false,
}) => {
const [confirm, setConfirm] = React.useState(false);
- const establishmentRows = React.useMemo(() => buildEstablishmentRows(establishment), [establishment]);
- const employmentRows = React.useMemo(() => buildEmploymentRows(establishment), [establishment]);
+ const establishmentDetails = React.useMemo(() => buildEstablishmentDetails(establishment), [establishment]);
+ const employmentDetails = React.useMemo(() => buildEmploymentDetails(establishment), [establishment]);
const productRows = React.useMemo(() => buildProductRows(products), [products]);
+
+ const infoBadgeClass = 'inline-flex items-center rounded-[10px] border border-[#E5D7AA] bg-[#FDF9EB] px-3 py-1 text-sm font-medium text-[#92722A]';
const submitLabel = React.useMemo(() => {
if (hasSubmitted) return 'Submitted';
@@ -152,36 +314,119 @@ const ReviewSubmit = ({
-
- {/* Info note */}
- {!hasSubmitted && (
+ {/* {!hasSubmitted && (
Review your data carefully before submission. You can go back to previous steps to edit any details. Once submitted, changes will require admin approval.
);
diff --git a/ipi-survey-platform/src/pages/Admin/AdminDashboard.jsx b/ipi-survey-platform/src/pages/Admin/AdminDashboard.jsx
index 46feedd..18a1339 100644
--- a/ipi-survey-platform/src/pages/Admin/AdminDashboard.jsx
+++ b/ipi-survey-platform/src/pages/Admin/AdminDashboard.jsx
@@ -2,14 +2,15 @@ import React, { useState, useEffect } from 'react';
import AdminHeader from '@/components/admin/AdminHeader';
import StatCard from '@/components/admin/StatCard';
import SubmissionTable from '@/components/admin/SubmissionTable';
-import WpfSurveyIcon from '@/assets/images/wpf_survey.svg';
-import SubmittedIcon from '@/assets/images/check-circle-fill 1.svg';
-import ApprovedIcon from '@/assets/images/check-circle-fill 1-approved.svg';
-import RejectedIcon from '@/assets/images/warning-circle-fill 1.svg';
-import OverdueIcon from '@/assets/images/warning-fill 1.svg';
-import PendingIcon from '@/assets/images/info-fill 1.svg';
import apiClient from '@/services/api/apiClient';
+const surveyIconSrc = '/assets/images/wpf_survey.svg';
+const submittedIconSrc = '/assets/images/check-circle-fill 1.svg';
+const approvedIconSrc = '/assets/images/check-circle-fill 1-approved.svg';
+const rejectedIconSrc = '/assets/images/warning-circle-fill 1.svg';
+const overdueIconSrc = '/assets/images/warning-fill 1.svg';
+const pendingIconSrc = '/assets/images/info-fill 1.svg';
+
const AdminDashboard = () => {
const [summary, setSummary] = useState({
total_establishments: 0,
@@ -96,37 +97,37 @@ const AdminDashboard = () => {
label="Total Establishments"
value={loading ? '--' : summary.total_establishments}
width={290}
- icon={