-
+
-
+
updateProductField(p.id, 'mayQuantity', e.target.value)}
/>
-
-
+
+
updateProductField(p.id, 'junQuantity', e.target.value)}
/>
-
+
-
+
-
+
-
+
+
+ {(hasError || isInitialLoad) && (
+
+ {isInitialLoad && !hasError && (
+
Loading product data...
+ )}
+ {hasError && (
+
+ {error &&
{error}
}
+ {reasonsError &&
{reasonsError}
}
+ {zeroReasonsError &&
{zeroReasonsError}
}
+ {productsError &&
{productsError}
}
+ {unitsError &&
{unitsError}
}
+
+ )}
+
+ )}
);
};
diff --git a/ipi-survey-platform/src/pages/History/History.jsx b/ipi-survey-platform/src/pages/History/History.jsx
index 3091769..80fec69 100644
--- a/ipi-survey-platform/src/pages/History/History.jsx
+++ b/ipi-survey-platform/src/pages/History/History.jsx
@@ -85,7 +85,17 @@ const History = () => {
};
}, []);
- const headers = ['Year', 'Quarter', 'Submission Window', 'Total Products', 'Products Submitted', 'Total Cost (AED)', 'Status', 'Date Submitted', 'Actions'];
+ const headers = [
+ { name: 'Year', className: 'text-left' },
+ { name: 'Quarter', className: 'text-left' },
+ { name: 'Submission Window', className: 'text-left' },
+ { name: 'Total Products', className: 'text-center' },
+ { name: 'Products Submitted', className: 'text-center' },
+ { name: 'Total Cost (AED)', className: 'text-right' },
+ { name: 'Status', className: 'text-center' },
+ { name: 'Date Submitted', className: 'text-left' },
+ { name: 'Actions', className: 'text-center' }
+ ];
const statusOptions = React.useMemo(
() => [
diff --git a/ipi-survey-platform/src/pages/Overview/Overview.jsx b/ipi-survey-platform/src/pages/Overview/Overview.jsx
new file mode 100644
index 0000000..021de32
--- /dev/null
+++ b/ipi-survey-platform/src/pages/Overview/Overview.jsx
@@ -0,0 +1,240 @@
+import React, { useState } from 'react';
+import { useNavigate } from 'react-router-dom';
+import HeaderBar from '@/components/layout/HeaderBar';
+import Table from '@/components/common/Table';
+import DetailedOverview from '@/components/overview/DetailedOverview';
+
+const searchIconSrc = '/assets/images/material-symbols_search-rounded.svg';
+const caretDownSrc = '/assets/images/CaretDown.svg';
+const downloadIconSrc = '/assets/images/DownloadSimple.svg';
+
+const statusStyles = {
+ approved: 'text-[#2F663C] bg-[#F3FAF4] ',
+ submitted: 'text-[#003CFF] bg-[#E7F5FF]',
+ pending: 'text-[#B45309] bg-[#FEF2F2]',
+ rejected: 'text-[#B52520] bg-[#FEF2F2]',
+};
+
+const mockOverview = [
+ {
+ year: '2025',
+ quarter: 'Q4',
+ window: 'Oct-Dec 2025',
+ totalProducts: 10,
+ submittedProducts: 4,
+ totalCost: 195000,
+ status: 'Approved',
+ submittedOn: '18/10/2025',
+ },
+ {
+ year: '2025',
+ quarter: 'Q3',
+ window: 'Jul-Sep 2025',
+ totalProducts: 10,
+ submittedProducts: 6,
+ totalCost: 295000,
+ status: 'Submitted',
+ submittedOn: '28/08/2025',
+ },
+ {
+ year: '2025',
+ quarter: 'Q2',
+ window: 'Apr-Jun 2025',
+ totalProducts: 10,
+ submittedProducts: 6,
+ totalCost: 395000,
+ status: 'Rejected',
+ submittedOn: '24/05/2025',
+ },
+ {
+ year: '2025',
+ quarter: 'Q1',
+ window: 'Jan-Mar 2025',
+ totalProducts: 10,
+ submittedProducts: 6,
+ totalCost: 495000,
+ status: 'Rejected',
+ submittedOn: '21/03/2025',
+ },
+];
+
+const formatCurrency = (amount) => {
+ if (amount === null || amount === undefined) return '—';
+ return new Intl.NumberFormat('en-AE').format(amount);
+};
+
+const Overview = () => {
+ const [searchTerm, setSearchTerm] = useState('');
+ const [selectedYear, setSelectedYear] = useState('');
+ const [selectedQuarter, setSelectedQuarter] = useState('');
+ const [selectedStatus, setSelectedStatus] = useState('');
+ const [selectedSubmission, setSelectedSubmission] = useState(null);
+ const navigate = useNavigate();
+
+ const filteredRows = React.useMemo(() => {
+ const normalizedTerm = searchTerm.trim().toLowerCase();
+ return mockOverview.filter((record) => {
+ const matchesSearch =
+ !normalizedTerm ||
+ record.year.toLowerCase().includes(normalizedTerm) ||
+ record.quarter.toLowerCase().includes(normalizedTerm) ||
+ record.window.toLowerCase().includes(normalizedTerm) ||
+ record.status.toLowerCase().includes(normalizedTerm);
+ const matchesStatus =
+ selectedStatus === '' || record.status.toLowerCase() === selectedStatus;
+ return matchesSearch && matchesStatus;
+ });
+ }, [searchTerm, selectedStatus]);
+
+ const rows = filteredRows.map((record) => [
+ record.year,
+ record.quarter,
+ record.window,
+ record.totalProducts,
+ record.submittedProducts,
+ `AED ${formatCurrency(record.totalCost)}`,
+ record.status,
+ record.submittedOn,
+ 'View Details',
+ ]);
+
+ const downloadCsv = React.useCallback(() => {
+ if (!filteredRows.length) return;
+ const headers = ['Year', 'Quarter', 'Submission Window', 'Total Products', 'Products Submitted', 'Total Cost (AED)', 'Status', 'Date Submitted'];
+ const csvRows = [headers.join(',')];
+ filteredRows.forEach((record) => {
+ csvRows.push([
+ record.year,
+ record.quarter,
+ record.window,
+ record.totalProducts,
+ record.submittedProducts,
+ record.totalCost,
+ record.status,
+ record.submittedOn,
+ ].map((value) => `"${String(value).replace(/"/g, '""')}"`).join(','));
+ });
+ const blob = new Blob([csvRows.join('\n')], { type: 'text/csv;charset=utf-8;' });
+ const url = URL.createObjectURL(blob);
+ const link = document.createElement('a');
+ link.href = url;
+ link.setAttribute('download', 'quarter-overview.csv');
+ document.body.appendChild(link);
+ link.click();
+ document.body.removeChild(link);
+ URL.revokeObjectURL(url);
+ }, [filteredRows]);
+
+ const toolbar = (
+
+
+
Quarter Overview
+
+
+
+
setSearchTerm(event.target.value)}
+ placeholder="Search establishment"
+ className="w-full h-10 rounded-md border border-[#E2E8F0] pl-10 pr-3 text-sm text-[#232528] focus:outline-none"
+ />
+

+
+
+
+

+
+
+
+
+ );
+
+ const handleViewDetails = (rowIndex) => {
+ setSelectedSubmission(filteredRows[rowIndex]);
+ };
+
+ if (selectedSubmission) {
+ return (
+
+
+
+ setSelectedSubmission(null)}
+ />
+
+
+ );
+ }
+
+ return (
+
+
+
+
+
Overview
+
+
+ {
+ if (columnIndex === 6) {
+ const normalized = String(value).toLowerCase();
+ const style = statusStyles[normalized] || 'text-[#232528] bg-[#F8FAFC] ';
+ return (
+
+ {value}
+
+ );
+ }
+ if (columnIndex === 8) {
+ return (
+
+ );
+ }
+ return {value};
+ }}
+ />
+
+
+
+ );
+};
+
+export default Overview;
diff --git a/ipi-survey-platform/src/pages/Overview/Overviewreview.jsx b/ipi-survey-platform/src/pages/Overview/Overviewreview.jsx
new file mode 100644
index 0000000..021de32
--- /dev/null
+++ b/ipi-survey-platform/src/pages/Overview/Overviewreview.jsx
@@ -0,0 +1,240 @@
+import React, { useState } from 'react';
+import { useNavigate } from 'react-router-dom';
+import HeaderBar from '@/components/layout/HeaderBar';
+import Table from '@/components/common/Table';
+import DetailedOverview from '@/components/overview/DetailedOverview';
+
+const searchIconSrc = '/assets/images/material-symbols_search-rounded.svg';
+const caretDownSrc = '/assets/images/CaretDown.svg';
+const downloadIconSrc = '/assets/images/DownloadSimple.svg';
+
+const statusStyles = {
+ approved: 'text-[#2F663C] bg-[#F3FAF4] ',
+ submitted: 'text-[#003CFF] bg-[#E7F5FF]',
+ pending: 'text-[#B45309] bg-[#FEF2F2]',
+ rejected: 'text-[#B52520] bg-[#FEF2F2]',
+};
+
+const mockOverview = [
+ {
+ year: '2025',
+ quarter: 'Q4',
+ window: 'Oct-Dec 2025',
+ totalProducts: 10,
+ submittedProducts: 4,
+ totalCost: 195000,
+ status: 'Approved',
+ submittedOn: '18/10/2025',
+ },
+ {
+ year: '2025',
+ quarter: 'Q3',
+ window: 'Jul-Sep 2025',
+ totalProducts: 10,
+ submittedProducts: 6,
+ totalCost: 295000,
+ status: 'Submitted',
+ submittedOn: '28/08/2025',
+ },
+ {
+ year: '2025',
+ quarter: 'Q2',
+ window: 'Apr-Jun 2025',
+ totalProducts: 10,
+ submittedProducts: 6,
+ totalCost: 395000,
+ status: 'Rejected',
+ submittedOn: '24/05/2025',
+ },
+ {
+ year: '2025',
+ quarter: 'Q1',
+ window: 'Jan-Mar 2025',
+ totalProducts: 10,
+ submittedProducts: 6,
+ totalCost: 495000,
+ status: 'Rejected',
+ submittedOn: '21/03/2025',
+ },
+];
+
+const formatCurrency = (amount) => {
+ if (amount === null || amount === undefined) return '—';
+ return new Intl.NumberFormat('en-AE').format(amount);
+};
+
+const Overview = () => {
+ const [searchTerm, setSearchTerm] = useState('');
+ const [selectedYear, setSelectedYear] = useState('');
+ const [selectedQuarter, setSelectedQuarter] = useState('');
+ const [selectedStatus, setSelectedStatus] = useState('');
+ const [selectedSubmission, setSelectedSubmission] = useState(null);
+ const navigate = useNavigate();
+
+ const filteredRows = React.useMemo(() => {
+ const normalizedTerm = searchTerm.trim().toLowerCase();
+ return mockOverview.filter((record) => {
+ const matchesSearch =
+ !normalizedTerm ||
+ record.year.toLowerCase().includes(normalizedTerm) ||
+ record.quarter.toLowerCase().includes(normalizedTerm) ||
+ record.window.toLowerCase().includes(normalizedTerm) ||
+ record.status.toLowerCase().includes(normalizedTerm);
+ const matchesStatus =
+ selectedStatus === '' || record.status.toLowerCase() === selectedStatus;
+ return matchesSearch && matchesStatus;
+ });
+ }, [searchTerm, selectedStatus]);
+
+ const rows = filteredRows.map((record) => [
+ record.year,
+ record.quarter,
+ record.window,
+ record.totalProducts,
+ record.submittedProducts,
+ `AED ${formatCurrency(record.totalCost)}`,
+ record.status,
+ record.submittedOn,
+ 'View Details',
+ ]);
+
+ const downloadCsv = React.useCallback(() => {
+ if (!filteredRows.length) return;
+ const headers = ['Year', 'Quarter', 'Submission Window', 'Total Products', 'Products Submitted', 'Total Cost (AED)', 'Status', 'Date Submitted'];
+ const csvRows = [headers.join(',')];
+ filteredRows.forEach((record) => {
+ csvRows.push([
+ record.year,
+ record.quarter,
+ record.window,
+ record.totalProducts,
+ record.submittedProducts,
+ record.totalCost,
+ record.status,
+ record.submittedOn,
+ ].map((value) => `"${String(value).replace(/"/g, '""')}"`).join(','));
+ });
+ const blob = new Blob([csvRows.join('\n')], { type: 'text/csv;charset=utf-8;' });
+ const url = URL.createObjectURL(blob);
+ const link = document.createElement('a');
+ link.href = url;
+ link.setAttribute('download', 'quarter-overview.csv');
+ document.body.appendChild(link);
+ link.click();
+ document.body.removeChild(link);
+ URL.revokeObjectURL(url);
+ }, [filteredRows]);
+
+ const toolbar = (
+
+
+
Quarter Overview
+
+
+
+
setSearchTerm(event.target.value)}
+ placeholder="Search establishment"
+ className="w-full h-10 rounded-md border border-[#E2E8F0] pl-10 pr-3 text-sm text-[#232528] focus:outline-none"
+ />
+

+
+
+
+

+
+
+
+
+ );
+
+ const handleViewDetails = (rowIndex) => {
+ setSelectedSubmission(filteredRows[rowIndex]);
+ };
+
+ if (selectedSubmission) {
+ return (
+
+
+
+ setSelectedSubmission(null)}
+ />
+
+
+ );
+ }
+
+ return (
+
+
+
+
+
Overview
+
+
+ {
+ if (columnIndex === 6) {
+ const normalized = String(value).toLowerCase();
+ const style = statusStyles[normalized] || 'text-[#232528] bg-[#F8FAFC] ';
+ return (
+
+ {value}
+
+ );
+ }
+ if (columnIndex === 8) {
+ return (
+
+ );
+ }
+ return {value};
+ }}
+ />
+
+
+
+ );
+};
+
+export default Overview;
diff --git a/ipi-survey-platform/src/pages/Survey/Survey.jsx b/ipi-survey-platform/src/pages/Survey/Survey.jsx
index 0b39fb0..d0b38f2 100644
--- a/ipi-survey-platform/src/pages/Survey/Survey.jsx
+++ b/ipi-survey-platform/src/pages/Survey/Survey.jsx
@@ -7,12 +7,13 @@ import HeaderBar from '@/components/layout/HeaderBar';
import ProductData from '@/components/survey/ProductData/ProductData';
import ReviewSubmit from '@/components/survey/ReviewSubmit/ReviewSubmit';
-const checkCircleActiveSrc = '/assets/images/circleframe.svg';
+const checkCircleActiveSrc = '/assets/images/CircleFrame.svg';
const createEmptyProduct = (id) => ({
id,
product: '',
unit: '',
+ mand: '',
capacity: '',
previousQuantity: '',
previousCost: '',
@@ -318,26 +319,80 @@ const Survey = () => {
products: productsData.map((product) => {
const variationId = parseNumber(product.variationReason, null);
const zeroTargetId = parseNumber(product.zeroTargetReason, null);
+
+ // Map monthly data to period-specific fields
return {
- product_id: parseNumber(product.product),
+ product_id: parseNumber(product.productId) || parseNumber(product.product),
unit_id: parseNumber(product.unit),
annual_installed_capacity: stringify(product.capacity),
- previous_quantity: stringify(product.previousQuantity),
- previous_cost: stringify(product.previousCost),
- current_quantity: stringify(product.currentQuantity),
- current_cost: stringify(product.currentCost),
- forecast_quantity: stringify(product.forecastQuantity),
- forecast_cost: stringify(product.forecastCost),
+
+ // Previous Quarter (Q4) - October, November, December
+ previous_quantity_period_one: stringify(product.octQuantity || '0'),
+ previous_quantity_period_two: stringify(product.novQuantity || '0'),
+ previous_quantity_period_three: stringify(product.decQuantity || '0'),
+ previous_cost_period_one: stringify(product.octCost || '0'),
+ previous_cost_period_two: stringify(product.novCost || '0'),
+ previous_cost_period_three: stringify(product.decCost || '0'),
+
+ // Current Quarter (Q1) - January, February, March
+ current_quantity_period_one: stringify(product.janQuantity || '0'),
+ current_quantity_period_two: stringify(product.febQuantity || '0'),
+ current_quantity_period_three: stringify(product.marQuantity || '0'),
+ current_cost_period_one: stringify(product.janCost || '0'),
+ current_cost_period_two: stringify(product.febCost || '0'),
+ current_cost_period_three: stringify(product.marCost || '0'),
+
+ // Forecast Quarter (Q2) - April, May, June
+ forecast_quantity_period_one: stringify(product.aprQuantity || '0'),
+ forecast_quantity_period_two: stringify(product.mayQuantity || '0'),
+ forecast_quantity_period_three: stringify(product.junQuantity || '0'),
+ forecast_cost_period_one: stringify(product.aprCost || '0'),
+ forecast_cost_period_two: stringify(product.mayCost || '0'),
+ forecast_cost_period_three: stringify(product.junCost || '0'),
+
+ // Totals (kept for backward compatibility)
+ previous_quantity: stringify(
+ (parseNumber(product.octQuantity) || 0) +
+ (parseNumber(product.novQuantity) || 0) +
+ (parseNumber(product.decQuantity) || 0)
+ ),
+ previous_cost: stringify(
+ (parseNumber(product.octCost) || 0) +
+ (parseNumber(product.novCost) || 0) +
+ (parseNumber(product.decCost) || 0)
+ ),
+ current_quantity: stringify(
+ (parseNumber(product.janQuantity) || 0) +
+ (parseNumber(product.febQuantity) || 0) +
+ (parseNumber(product.marQuantity) || 0)
+ ),
+ current_cost: stringify(
+ (parseNumber(product.janCost) || 0) +
+ (parseNumber(product.febCost) || 0) +
+ (parseNumber(product.marCost) || 0)
+ ),
+ forecast_quantity: stringify(
+ (parseNumber(product.aprQuantity) || 0) +
+ (parseNumber(product.mayQuantity) || 0) +
+ (parseNumber(product.junQuantity) || 0)
+ ),
+ forecast_cost: stringify(
+ (parseNumber(product.aprCost) || 0) +
+ (parseNumber(product.mayCost) || 0) +
+ (parseNumber(product.junCost) || 0)
+ ),
+
+ // Other fields
variation_reason_master_id: Number.isFinite(variationId) ? variationId : null,
- other_variation_reason: stringify(product.otherVariationReason),
+ other_variation_reason: stringify(product.otherVariationReason || ''),
zero_target_reason_master_id: Number.isFinite(zeroTargetId) ? zeroTargetId : null,
- other_zero_target_reason: stringify(product.otherZeroTargetReason),
- remarks: stringify(product.remarks),
+ other_zero_target_reason: stringify(product.otherZeroTargetReason || ''),
+ remarks: stringify(product.remarks || '')
};
}),
};
}, [establishmentData, parseNumber, productsData, stringify]);
-
+ console.log("productsData",productsData)
const handleSubmit = React.useCallback(async () => {
setIsSubmitting(true);
try {