diff --git a/ipi-survey-platform/src/components/overview/DetailedOverview.jsx b/ipi-survey-platform/src/components/overview/DetailedOverview.jsx
index ef5ecbf..3f7e863 100644
--- a/ipi-survey-platform/src/components/overview/DetailedOverview.jsx
+++ b/ipi-survey-platform/src/components/overview/DetailedOverview.jsx
@@ -1,80 +1,133 @@
-import React, { useState, useCallback, useMemo } from 'react';
+import React, { useState, useCallback, useMemo, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
+import { fetchSubmissionDetail } from '@/services/submissions/submissionService';
import Table from '@/components/common/Table';
import ProductDetails from './ProductDetails';
const searchIconSrc = '/assets/images/material-symbols_search-rounded.svg';
const caretDownSrc = '/assets/images/CaretDown.svg';
const downloadIconSrc = '/assets/images/DownloadSimple.svg';
+
export const DetailedOverview = ({ submission, onBack }) => {
const navigate = useNavigate();
const [searchTerm, setSearchTerm] = useState('');
const [selectedStatus, setSelectedStatus] = useState('');
const [selectedProduct, setSelectedProduct] = useState(null);
+ const [loading, setLoading] = useState(true);
+ const [submissionData, setSubmissionData] = useState(null);
+ const [quarterPeriods, setQuarterPeriods] = useState(null);
+ const [loadingProduct, setLoadingProduct] = useState(false);
- // Mock data - replace with actual data from props
- const productData = [
- {
- id: 'PRD-2023-001',
- hsCode: '1234.56.78',
- name: 'Product 1',
- unit: 'PCS',
- quantity: 1,
- cost: '1,000.00',
- status: 'Active',
- submissionDate: '30/10/2023 14:30',
- },
- {
- id: 'PRD-2023-002',
- hsCode: '8765.43.21',
- name: 'Product 2',
- unit: 'KG',
- quantity: 5,
- cost: '2,000.00',
- status: 'Pending',
- submissionDate: '29/10/2023 10:15',
- },
- ];
+ // Add state for tracking loading and error states
+ const [isLoading, setIsLoading] = useState(true);
+ const [error, setError] = useState(null);
+
+ useEffect(() => {
+ const fetchSubmissionDetails = async () => {
+ try {
+ setIsLoading(true);
+ setError(null);
+ console.log('Fetching submission details for ID:', submission.id);
+ const response = await fetchSubmissionDetail(submission.id);
+
+ console.log('API Response:', response);
+
+ if (response && response.data) {
+ console.log('Setting submission data:', response.data);
+ setSubmissionData(response.data);
+ setQuarterPeriods(response.quarter_periods);
+
+ // Log the products array to verify it's being set correctly
+ if (response.data.products) {
+ console.log('Products in response:', response.data.products);
+ console.log('Number of products:', response.data.products.length);
+ }
+ } else {
+ console.warn('No data received in response');
+ }
+ } catch (error) {
+ console.error('Error fetching submission details:', error);
+ setError('Failed to load submission details. Please try again.');
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ if (submission?.id) {
+ fetchSubmissionDetails();
+ } else {
+ console.error('No submission ID provided');
+ setError('No submission ID provided');
+ setIsLoading(false);
+ }
+ }, [submission]);
const filteredProducts = useMemo(() => {
+ console.log('Filtering products...');
+ console.log('Raw products data:', submissionData?.products);
+
+ if (!submissionData?.products || !Array.isArray(submissionData.products)) {
+ console.log('No products array found in submissionData');
+ return [];
+ }
+
const normalizedTerm = searchTerm.trim().toLowerCase();
- return productData.filter((product) => {
+ const filtered = submissionData.products.filter((product) => {
+ // Add null checks for product and product.product
+ if (!product || !product.product) return false;
+
+ const productName = product.product?.product_name || '';
+ const hsCode = product.product?.hs_code || '';
+ const status = product.status || '';
+
const matchesSearch =
!normalizedTerm ||
- product.name.toLowerCase().includes(normalizedTerm) ||
- product.hsCode.toLowerCase().includes(normalizedTerm) ||
- product.status.toLowerCase().includes(normalizedTerm);
- const matchesStatus =
- selectedStatus === '' ||
- product.status.toLowerCase() === selectedStatus.toLowerCase();
+ productName.toString().toLowerCase().includes(normalizedTerm) ||
+ hsCode.toString().toLowerCase().includes(normalizedTerm) ||
+ status.toLowerCase().includes(ormalizedTerm) ||
+ 'pending'.includes(normalizedTerm);
+
+ const matchesStatus = selectedStatus === '' ||
+ (selectedStatus === 'pending' && !product.is_active) ||
+ (selectedStatus === 'approved' && product.is_active);
+
return matchesSearch && matchesStatus;
});
- }, [searchTerm, selectedStatus, productData]);
+
+ console.log('Filtered products:', filtered);
+ return filtered;
+ }, [searchTerm, selectedStatus, submissionData]);
const downloadCsv = useCallback(() => {
- if (!filteredProducts.length) return;
+ if (!filteredProducts.length || !submissionData) return;
const headers = [
'HS Code',
'Product',
'Unit',
- 'Quantity',
- 'Cost (AED)',
- 'Status',
- 'Submission Date & Time'
+ 'Previous Quarter Quantity',
+ 'Previous Quarter Cost (AED)',
+ 'Current Quarter Quantity',
+ 'Current Quarter Cost (AED)',
+ 'Forecast Quarter Quantity',
+ 'Forecast Quarter Cost (AED)',
+ 'Status'
];
const csvRows = [headers.join(',')];
filteredProducts.forEach((product) => {
csvRows.push([
- product.hsCode,
- product.name,
- product.unit,
- product.quantity,
- product.cost,
- product.status,
- product.submissionDate
+ product.product.hs_code,
+ product.product.product_name,
+ product.unit?.uom || 'N/A',
+ product.previous_quantity || '0',
+ product.previous_cost || '0',
+ product.current_quantity || '0',
+ product.current_cost || '0',
+ product.forecast_quantity || '0',
+ product.forecast_cost || '0',
+ product.is_active ? 'Approved' : 'Pending'
].map((value) => `"${String(value).replace(/"/g, '""')}"`).join(','));
});
@@ -89,26 +142,90 @@ export const DetailedOverview = ({ submission, onBack }) => {
URL.revokeObjectURL(url);
}, [filteredProducts]);
- const rows = filteredProducts.map(product => [
- product.hsCode,
- product.name,
- product.unit,
- product.quantity,
- `AED ${product.cost}`,
-
- {product.status}
- ,
- product.submissionDate,
-
- ]);
+ const handleViewDetails = (product) => {
+ // Use the product data we already have from the submission
+ setSelectedProduct(product);
+ };
+
+ const rows = useMemo(() => {
+ if (!filteredProducts || !Array.isArray(filteredProducts)) {
+ console.log('No filtered products available');
+ return [];
+ }
+
+ console.log('Generating rows for filtered products:', filteredProducts);
+
+ return filteredProducts.map((product, index) => {
+ // Add null checks for product and product properties
+ const productData = product.product || {};
+ const unit = product.unit || {};
+
+ return [
+ productData.hs_code || '—',
+ productData.product_name || '—',
+ unit.uom || 'N/A',
+ product.previous_quantity || '0',
+ `AED ${product.previous_cost || '0'}`,
+ product.current_quantity || '0',
+ `AED ${product.current_cost || '0'}`,
+ product.forecast_quantity || '0',
+ `AED ${product.forecast_cost || '0'}`,
+
+ {product.is_active ? 'Approved' : 'Pending'}
+ ,
+
+ ];
+ });
+ }, [filteredProducts]);
+
+ // Show loading state for initial data load
+ if (isLoading) {
+ return (
+
+
+
Loading submission details...
+
+ );
+ }
+
+
+ // Show error state
+ if (error) {
+ return (
+
+
Error
+
{error}
+
+
+ );
+ }
+
+ // Show no data state
+ if (!submissionData) {
+ return (
+
+
No submission data available
+
+ );
+ }
const toolbar = (
@@ -165,18 +282,29 @@ export const DetailedOverview = ({ submission, onBack }) => {
'HS Code',
'Product',
'Unit',
- 'Quantity',
- 'Cost (AED)',
+ 'Previous Qty',
+ 'Previous Cost (AED)',
+ 'Current Qty',
+ 'Current Cost (AED)',
+ 'Forecast Qty',
+ 'Forecast Cost (AED)',
'Status',
- 'Submission Date & Time',
'Action'
];
+ // Calculate total products and average cost
+ const totalProducts = filteredProducts.length;
+ const totalCost = submissionData.products?.reduce((sum, product) => {
+ return sum + parseFloat(product.current_cost || 0);
+ }, 0);
+ const averageCost = totalProducts > 0 ? (totalCost / totalProducts).toFixed(2) : 0;
+
// If a product is selected, show the product details view
if (selectedProduct) {
return (
setSelectedProduct(null)}
/>
);
@@ -198,43 +326,38 @@ export const DetailedOverview = ({ submission, onBack }) => {
-
Establishment ID:
-
Al Khazna Investment
+
Establishment:
+
{submissionData.establishment?.factory_name || 'N/A'}
Total Submitted Products:
-
678
+
{totalProducts}
-
Average Cost:
-
7889
+
Average Cost (AED):
+
{averageCost}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
@@ -60,7 +89,7 @@ const ProductDetails = ({ product, onBack }) => {
- {product.name}
+ {formattedProduct.name}
@@ -68,7 +97,7 @@ const ProductDetails = ({ product, onBack }) => {
- {product.hsCode || '—'}
+ {formattedProduct.hsCode}
@@ -76,7 +105,7 @@ const ProductDetails = ({ product, onBack }) => {
- {product.unit || 'PCS'}
+ {formattedProduct.unit}
@@ -84,11 +113,11 @@ const ProductDetails = ({ product, onBack }) => {
- {product.status || '—'}
+ {formattedProduct.status}
@@ -132,17 +161,17 @@ const ProductDetails = ({ product, onBack }) => {
{/* Quantity Row */}
|
- Quantity ({product.unit || 'units'})
+ Quantity ({formattedProduct.unit})
|
- {product.q4_oct_quantity || '—'} |
- {product.q4_nov_quantity || '—'} |
- {product.q4_dec_quantity || '—'} |
- {product.q1_jan_quantity || '—'} |
- {product.q1_feb_quantity || '—'} |
- {product.q1_mar_quantity || '—'} |
- {product.q2_apr_quantity || '—'} |
- {product.q2_may_quantity || '—'} |
- {product.q2_jun_quantity || '—'} |
+ {formattedProduct.q4_oct_quantity} |
+ {formattedProduct.q4_nov_quantity} |
+ {formattedProduct.q4_dec_quantity} |
+ {formattedProduct.q1_jan_quantity} |
+ {formattedProduct.q1_feb_quantity} |
+ {formattedProduct.q1_mar_quantity} |
+ {formattedProduct.q2_apr_quantity} |
+ {formattedProduct.q2_may_quantity} |
+ {formattedProduct.q2_jun_quantity} |
{/* Cost Row */}
@@ -150,15 +179,15 @@ const ProductDetails = ({ product, onBack }) => {
Cost (AED)
|
- {product.q4_oct_cost || '—'} |
- {product.q4_nov_cost || '—'} |
- {product.q4_dec_cost || '—'} |
- {product.q1_jan_cost || '—'} |
- {product.q1_feb_cost || '—'} |
- {product.q1_mar_cost || '—'} |
- {product.q2_apr_cost || '—'} |
- {product.q2_may_cost || '—'} |
- {product.q2_jun_cost || '—'} |
+ {formattedProduct.q4_oct_cost} |
+ {formattedProduct.q4_nov_cost} |
+ {formattedProduct.q4_dec_cost} |
+ {formattedProduct.q1_jan_cost} |
+ {formattedProduct.q1_feb_cost} |
+ {formattedProduct.q1_mar_cost} |
+ {formattedProduct.q2_apr_cost} |
+ {formattedProduct.q2_may_cost} |
+ {formattedProduct.q2_jun_cost} |
{/* Reason Current */}
@@ -170,7 +199,7 @@ const ProductDetails = ({ product, onBack }) => {
-- --
- {product.variationReason || '—'}
+ {formattedProduct.variationReason}
|
-- --
@@ -186,7 +215,7 @@ const ProductDetails = ({ product, onBack }) => {
-- --
|
- {product.zeroTargetReason || '—'}
+ {formattedProduct.zeroTargetReason}
|
@@ -197,7 +226,7 @@ const ProductDetails = ({ product, onBack }) => {
Remarks
- {product.remarks || 'No remarks provided for this product.'}
+ {formattedProduct.remarks}
@@ -205,16 +234,22 @@ const ProductDetails = ({ product, onBack }) => {
-
Submitted by: User Name
-
Submission Date: {product.submissionDate || '—'}
+
Submitted by:
+ {product.created_by || 'N/A'}
+
+
Submission Date:
+ {new Date(product.created_at).toLocaleDateString() || '—'}
+
-
Last Updated: {product.lastUpdated || product.submissionDate || '—'}
+
Last Updated:
+ {product.updated_at ? new Date(product.updated_at).toLocaleDateString() : '—'}
+
Status:
- {product.status}
+ {formattedProduct.status}
diff --git a/ipi-survey-platform/src/components/survey/ProductData/ProductData.jsx b/ipi-survey-platform/src/components/survey/ProductData/ProductData.jsx
index 58f9b3e..cf33a55 100644
--- a/ipi-survey-platform/src/components/survey/ProductData/ProductData.jsx
+++ b/ipi-survey-platform/src/components/survey/ProductData/ProductData.jsx
@@ -290,11 +290,57 @@ const ProductData = ({
onProductsChange(products.filter((p) => p.id !== id));
};
- const updateProductField = (id, field, value) => {
+ const updateProductField = (id, field, value, options = []) => {
onProductsChange(
- products.map((product) =>
- product.id === id ? { ...product, [field]: value } : product
- )
+ products.map((product) => {
+ if (product.id !== id) return product;
+
+ // When product is selected, store both ID and name
+ if (field === 'product') {
+ const selectedProduct = options.find(p => p.value === value || p.id === value);
+ return {
+ ...product,
+ product: value,
+ productName: selectedProduct?.label || selectedProduct?.name || '',
+ [field]: value
+ };
+ }
+
+ // When unit is selected, store both ID and name
+ if (field === 'unit') {
+ const selectedUnit = options.find(u => u.value === value || u.id === value);
+ return {
+ ...product,
+ unit: value,
+ unitName: selectedUnit?.label || selectedUnit?.name || '',
+ [field]: value
+ };
+ }
+
+ // When variation reason is selected, store both ID and name
+ if (field === 'variationReason') {
+ const selectedReason = options.find(r => r.value === value || r.id === value);
+ return {
+ ...product,
+ variationReason: value,
+ variationReasonName: selectedReason?.label || selectedReason?.name || '',
+ [field]: value
+ };
+ }
+
+ // When zero target reason is selected, store both ID and name
+ if (field === 'zeroTargetReason') {
+ const selectedReason = options.find(r => r.value === value || r.id === value);
+ return {
+ ...product,
+ zeroTargetReason: value,
+ zeroTargetReasonName: selectedReason?.label || selectedReason?.name || '',
+ [field]: value
+ };
+ }
+
+ return { ...product, [field]: value };
+ })
);
};
@@ -467,7 +513,7 @@ const ProductData = ({
placeholder={isLoadingProducts ? 'Loading products...' : 'Select product HS code'}
options={productOptions}
value={p.product || ''}
- onChange={(e) => updateProductField(p.id, 'product', e.target.value)}
+ onChange={(e) => updateProductField(p.id, 'product', e.target.value, productOptions)}
/>
{productsError && productOptions.length === 0 && (
{productsError}
@@ -479,7 +525,7 @@ const ProductData = ({
placeholder={isLoadingUnits ? 'Loading units...' : 'Select unit'}
options={unitOptions}
value={p.unit || ''}
- onChange={(e) => updateProductField(p.id, 'unit', e.target.value)}
+ onChange={(e) => updateProductField(p.id, 'unit', e.target.value, unitOptions)}
/>
{unitsError && unitOptions.length === 0 && (
{unitsError}
@@ -646,7 +692,7 @@ const ProductData = ({
placeholder={isLoadingReasons ? 'Loading reasons...' : 'Select reason'}
options={variationReasons}
value={p.variationReason || ''}
- onChange={(e) => updateProductField(p.id, 'variationReason', e.target.value)}
+ onChange={(e) => updateProductField(p.id, 'variationReason', e.target.value, variationReasons)}
/>
{reasonsError && variationReasons.length === 0 && (
{reasonsError}
@@ -676,6 +722,7 @@ const ProductData = ({
type="number"
required
placeholder="Enter"
+ value={p.mayQuantity || ''}
onChange={(e) => updateProductField(p.id, 'mayQuantity', e.target.value)}
/>
@@ -686,6 +733,7 @@ const ProductData = ({
type="number"
required
placeholder="Enter"
+ value={p.junQuantity || ''}
onChange={(e) => updateProductField(p.id, 'junQuantity', e.target.value)}
/>
@@ -731,7 +779,7 @@ const ProductData = ({
placeholder={isLoadingZeroReasons ? 'Loading reasons...' : 'Select reason'}
options={zeroTargetReasons}
value={p.zeroTargetReason || ''}
- onChange={(e) => updateProductField(p.id, 'zeroTargetReason', e.target.value)}
+ onChange={(e) => updateProductField(p.id, 'zeroTargetReason', e.target.value, zeroTargetReasons)}
/>
{zeroReasonsError && zeroTargetReasons.length === 0 && (
{zeroReasonsError}
diff --git a/ipi-survey-platform/src/components/survey/ReviewSubmit/ReviewSubmit.jsx b/ipi-survey-platform/src/components/survey/ReviewSubmit/ReviewSubmit.jsx
index d86cf2b..1ce99a5 100644
--- a/ipi-survey-platform/src/components/survey/ReviewSubmit/ReviewSubmit.jsx
+++ b/ipi-survey-platform/src/components/survey/ReviewSubmit/ReviewSubmit.jsx
@@ -179,15 +179,16 @@ const buildProductRows = (products = [], options = {}) => {
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)
+ // Use stored unitName if available, otherwise try to find it in unitOptions
+ let unitName = product.unitName;
+ if (!unitName && product.unit) {
+ 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);
+ // Use stored reason names if available, otherwise try to find them in the options
+ const variationReasonName = product.variationReasonName || findDisplayName(variationReasons, product.variationReason);
+ const zeroTargetReasonName = product.zeroTargetReasonName || findDisplayName(zeroTargetReasons, product.zeroTargetReason);
const capacity = product.capacity !== undefined && product.capacity !== null ? formatNumber(product.capacity) : '—';
@@ -226,7 +227,7 @@ const buildProductRows = (products = [], options = {}) => {
// Reasons and remarks - use display names
variationReason: variationReasonName,
zeroTargetReason: zeroTargetReasonName,
- remarks: product.remarks || '—',
+ remarks: product.remarks || '',
// Keep the raw product data for debugging
_raw: { ...product }
@@ -574,11 +575,11 @@ const ReviewSubmit = ({
{/* Header */}
-
- Product:{' '}
+
+ Product:{' '}
{product.product}
{product.productCode && product.productCode !== product.product && (
- (ID: {product.productCode})
+
)}
@@ -668,7 +669,7 @@ const ReviewSubmit = ({
-- --
- {product.variationReason || '—'}
+ {product.variationReasonName || product.variationReason || '—'}
|
-- --
@@ -684,20 +685,25 @@ const ReviewSubmit = ({
-- --
|
- {product.zeroTargetReason || '—'}
+ {product.zeroTargetReasonName || product.zeroTargetReason || '—'}
|
- {/* Remarks */}
-
-
Remarks:
-
- {product.remarks || 'No remarks provided'}
-
-
+ {/* Remarks */}
+
+
Remarks
+
+ {product.remarks?.trim() ? (
+ product.remarks
+ ) : (
+ Add Short Note..
+ )}
+
+
+
))}
diff --git a/ipi-survey-platform/src/pages/History/History.jsx b/ipi-survey-platform/src/pages/History/History.jsx
index 80fec69..b71db68 100644
--- a/ipi-survey-platform/src/pages/History/History.jsx
+++ b/ipi-survey-platform/src/pages/History/History.jsx
@@ -58,6 +58,20 @@ const History = () => {
return date.toLocaleDateString('en-GB');
}, []);
+ const formatDateTime = React.useCallback((value) => {
+ if (!value) return '—';
+ const date = new Date(value);
+ if (Number.isNaN(date.getTime())) return '—';
+ return date.toLocaleString('en-GB', {
+ day: '2-digit',
+ month: '2-digit',
+ year: 'numeric',
+ hour: '2-digit',
+ minute: '2-digit',
+ hour12: true
+ });
+ }, []);
+
React.useEffect(() => {
let cancelled = false;
const loadHistory = async () => {
@@ -86,15 +100,14 @@ const History = () => {
}, []);
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' }
+ { name: 'Submission Date & Time', className: 'text-left whitespace-nowrap w-[140px]' },
+ { name: 'Year', className: 'text-left w-[60px]' },
+ { name: 'Quarter', className: 'text-left w-[70px]' },
+ { name: 'HS Code', className: 'text-left w-[80px]' },
+ { name: 'Product', className: 'text-left w-[120px]' },
+ { name: 'Status', className: 'text-center w-[100px]' },
+ { name: 'Actors', className: 'text-left w-[150px]' },
+ { name: 'Details', className: 'text-left flex-1 min-w-[200px]' }
];
const statusOptions = React.useMemo(
@@ -107,6 +120,7 @@ const History = () => {
],
[]
);
+
const handleViewDetails = React.useCallback((submission) => {
if (!submission) return;
navigate('/survey', { state: { submission, initialStep: 3 } });
@@ -135,22 +149,83 @@ const History = () => {
});
}, [rows, searchTerm, statusFilter, toVariant, formatStatus, formatDate]);
- const tableRows = filteredRows.map((entry) => [
- entry.year || '—',
- entry.quarter || '—',
- entry.submission_window || '—',
- Number.isFinite(Number(entry.total_products)) ? entry.total_products : '—',
- Number.isFinite(Number(entry.products_submitted)) ? entry.products_submitted : '—',
- entry.total_cost ? Intl.NumberFormat('en-US').format(entry.total_cost) : '—',
- formatStatus(entry.status),
- formatDate(entry.created_at),
- 'View Details',
- ]);
+ const formatNumber = (num) => {
+ if (num === null || num === undefined) return '—';
+ return Number(num).toLocaleString('en-IN');
+ };
+
+ // 🟨 Added for expand/collapse logic
+ const [expandedRow, setExpandedRow] = React.useState(null);
+ const handleToggleDetails = (rowIndex) => {
+ setExpandedRow(expandedRow === rowIndex ? null : rowIndex);
+ };
+
+ // 🟩 Updated tableRows with expandable details
+ const tableRows = filteredRows.map((entry, index) => {
+ const isExpanded = expandedRow === index;
+ return [
+ formatDateTime(entry.created_at),
+ entry.year || '—',
+ entry.quarter || '—',
+ entry.hs_code || '—',
+ entry.product_name || '—',
+ formatStatus(entry.status),
+ entry.actors ? entry.actors.join(', ') : '—',
+
+
+
+ Quantity updated 1,450 to 1,500; Cost updated 87,000 to 90,000
+
+
+
+
+
+ {isExpanded && (
+
+
+
+
Quantity Updated:
+
1,450 → 1,500
+
+
+
Cost Updated:
+
87,000 → 90,000
+
+
+
+
Reason for variations:
+
Seasonal Production Surge
+
+
+ )}
+
+ ];
+ });
const toolbar = (
-
Quarter Overview
+
Submission History
{loading && Loading…}
{error && {error}}
@@ -218,20 +293,121 @@ const History = () => {
);
+ // Calculate total products and average cost for the card
+ const totalProducts = rows.length;
+ const averageCost = rows.length > 0
+ ? Math.round(rows.reduce((sum, row) => sum + (parseFloat(row.total_cost) || 0), 0) / rows.length)
+ : 0;
+
return (
+ {/* Card Section */}
+
+
+
+
+
Establishment ID:
+
{totalProducts}
+
+ {/*
+
Average Cost (AED):
+
{averageCost.toLocaleString('en-IN')}
+
*/}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{
- if (ci === 6) {
+ if (ci === 3) {
return {value};
}
- if (ci === headers.length - 1) {
+ if (ci === 5) {
const submission = filteredRows[ri];
const disabled = !submission;
return (
diff --git a/ipi-survey-platform/src/pages/Overview/Overview.jsx b/ipi-survey-platform/src/pages/Overview/Overview.jsx
index 021de32..86bf97a 100644
--- a/ipi-survey-platform/src/pages/Overview/Overview.jsx
+++ b/ipi-survey-platform/src/pages/Overview/Overview.jsx
@@ -1,119 +1,112 @@
-import React, { useState } from 'react';
+// src/pages/Overview/Overview.jsx
+import React, { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
+import { getSubmissions } from '@/services/submissions/submissionService';
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] ',
+ approved: 'text-[#2F663C] bg-[#F3FAF4]',
submitted: 'text-[#003CFF] bg-[#E7F5FF]',
- pending: 'text-[#B45309] bg-[#FEF2F2]',
+ pending: 'text-[#7C5E24] bg-[#F9F7ED]',
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 formatDate = (dateString) => {
+ if (!dateString) return '-';
+ const date = new Date(dateString);
+ return date.toLocaleDateString('en-GB');
};
const Overview = () => {
const [searchTerm, setSearchTerm] = useState('');
- const [selectedYear, setSelectedYear] = useState('');
- const [selectedQuarter, setSelectedQuarter] = useState('');
const [selectedStatus, setSelectedStatus] = useState('');
const [selectedSubmission, setSelectedSubmission] = useState(null);
+ const [submissions, setSubmissions] = useState([]);
+ const [loading, setLoading] = useState(true);
+ const [error, setError] = useState(null);
+ const [pagination, setPagination] = useState({
+ page: 1,
+ limit: 100,
+ total: 0,
+ totalPages: 1
+ });
const navigate = useNavigate();
+ useEffect(() => {
+ const fetchSubmissions = async () => {
+ try {
+ setLoading(true);
+ const response = await getSubmissions(pagination.page, pagination.limit);
+ setSubmissions(response.data);
+ setPagination(response.pagination);
+ } catch (err) {
+ setError('Failed to load submissions');
+ console.error('Error:', err);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ fetchSubmissions();
+ }, [pagination.page, pagination.limit]);
+
const filteredRows = React.useMemo(() => {
const normalizedTerm = searchTerm.trim().toLowerCase();
- return mockOverview.filter((record) => {
+ return submissions.filter((record) => {
const matchesSearch =
!normalizedTerm ||
- record.year.toLowerCase().includes(normalizedTerm) ||
+ String(record.year).toLowerCase().includes(normalizedTerm) ||
record.quarter.toLowerCase().includes(normalizedTerm) ||
- record.window.toLowerCase().includes(normalizedTerm) ||
- record.status.toLowerCase().includes(normalizedTerm);
+ `${record.quarter} ${record.year}`.toLowerCase().includes(normalizedTerm) ||
+ record.status.toLowerCase().includes(normalizedTerm) ||
+ record.establishment?.factory_name?.toLowerCase().includes(normalizedTerm);
+
const matchesStatus =
- selectedStatus === '' || record.status.toLowerCase() === selectedStatus;
+ !selectedStatus ||
+ (selectedStatus === 'approved' && record.status === 'Approved') ||
+ (selectedStatus === 'submitted' && record.status === 'Submitted') ||
+ (selectedStatus === 'rejected' && record.status === 'Rejected');
+
return matchesSearch && matchesStatus;
});
- }, [searchTerm, selectedStatus]);
+ }, [searchTerm, selectedStatus, submissions]);
const rows = filteredRows.map((record) => [
record.year,
record.quarter,
- record.window,
- record.totalProducts,
- record.submittedProducts,
- `AED ${formatCurrency(record.totalCost)}`,
+ `${record.quarter} ${record.year}`,
+ 10, // Always show 10 for Total Products
+ record.product_count || 0, // Show actual submitted products count
+ `-`, // Total cost not in API
record.status,
- record.submittedOn,
+ formatDate(record.created_at),
'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.quarter} ${record.year}`,
+ 10, // Always show 10 for Total Products in CSV
+ record.product_count || 0, // Show actual submitted products count
+ '-', // Total cost not in API
record.status,
- record.submittedOn,
+ formatDate(record.created_at)
].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');
@@ -136,7 +129,7 @@ const Overview = () => {
type="text"
value={searchTerm}
onChange={(event) => setSearchTerm(event.target.value)}
- placeholder="Search establishment"
+ placeholder="Search submissions..."
className="w-full h-10 rounded-md border border-[#E2E8F0] pl-10 pr-3 text-sm text-[#232528] focus:outline-none"
/>
{
type="button"
onClick={downloadCsv}
disabled={!filteredRows.length}
- className={`inline-flex items-center gap-2 h-10 rounded-md border px-4 text-sm font-medium ${filteredRows.length ? 'border-[#92722A] text-[#92722A] hover:bg-[#F2ECCF]' : 'border-gray-200 text-gray-400 cursor-not-allowed'}`}
+ className={`inline-flex items-center gap-2 h-10 rounded-md border px-4 text-sm font-medium ${
+ filteredRows.length
+ ? 'border-[#92722A] text-[#92722A] hover:bg-[#F2ECCF]'
+ : 'border-gray-200 text-gray-400 cursor-not-allowed'
+ }`}
>
Export CSV
@@ -179,6 +176,41 @@ const Overview = () => {
setSelectedSubmission(filteredRows[rowIndex]);
};
+ if (loading) {
+ return (
+
+ );
+ }
+
+ if (error) {
+ return (
+
+ );
+ }
+
if (selectedSubmission) {
return (
@@ -209,16 +241,16 @@ const Overview = () => {
rowGapClass="border-spacing-y-2"
className="w-full min-w-[1200px] [&_td]:whitespace-nowrap [&_th]:whitespace-nowrap"
renderCell={(value, rowIndex, columnIndex) => {
- if (columnIndex === 6) {
+ if (columnIndex === 6) { // Status column
const normalized = String(value).toLowerCase();
- const style = statusStyles[normalized] || 'text-[#232528] bg-[#F8FAFC] ';
+ const style = statusStyles[normalized] || 'text-[#232528] bg-[#F8FAFC]';
return (
{value}
);
}
- if (columnIndex === 8) {
+ if (columnIndex === 8) { // Action column
return (
);
}
- return {value};
+ return value;
}}
/>
@@ -237,4 +269,4 @@ const Overview = () => {
);
};
-export default Overview;
+export default Overview;
\ No newline at end of file
diff --git a/ipi-survey-platform/src/pages/Survey/Survey.jsx b/ipi-survey-platform/src/pages/Survey/Survey.jsx
index d0b38f2..d90d2cf 100644
--- a/ipi-survey-platform/src/pages/Survey/Survey.jsx
+++ b/ipi-survey-platform/src/pages/Survey/Survey.jsx
@@ -398,6 +398,9 @@ const Survey = () => {
try {
const payload = buildSubmissionPayload();
const response = await submitSurvey(payload);
+
+ // Navigate to overview page after successful submission
+ navigate('/overview');
console.log('Survey submission response:', response);
showToast('success', response?.message || 'You have successfully submitted the survey.');
setHasSubmitted(true);
diff --git a/ipi-survey-platform/src/services/submissions/submissionService.js b/ipi-survey-platform/src/services/submissions/submissionService.js
index f480e3b..f394920 100644
--- a/ipi-survey-platform/src/services/submissions/submissionService.js
+++ b/ipi-survey-platform/src/services/submissions/submissionService.js
@@ -3,6 +3,24 @@ import resolveEstablishmentId from '@/services/utils/establishment';
const endpoint = '/submissions';
+// Add this new function to fetch submissions with pagination
+export const getSubmissions = async (page = 1, limit = 100, config = {}) => {
+ try {
+ const response = await getRequest(endpoint, {
+ ...config,
+ params: {
+ page,
+ limit,
+ ...(config.params || {}) // Preserve any existing params
+ }
+ });
+ return response.data;
+ } catch (error) {
+ console.error('Error fetching submissions:', error);
+ throw error;
+ }
+};
+
export const submitSurvey = async (payload = {}, config = {}) => {
const establishmentId = resolveEstablishmentId(payload.establishment_id);
const requestBody = {
@@ -32,8 +50,10 @@ export const fetchSubmissionDetail = async (submissionId, config = {}) => {
return response.data;
};
+// Add the new function to the default export
export default {
+ getSubmissions, // Add this line
submitSurvey,
fetchSubmissionHistory,
fetchSubmissionDetail,
-};
+};
\ No newline at end of file