diff --git a/ipi-survey-platform/src/pages/Admin/ValidationReview.jsx b/ipi-survey-platform/src/pages/Admin/ValidationReview.jsx index 4ed0e43..7af4932 100644 --- a/ipi-survey-platform/src/pages/Admin/ValidationReview.jsx +++ b/ipi-survey-platform/src/pages/Admin/ValidationReview.jsx @@ -1,108 +1,28 @@ -import React from 'react'; +import React, { useState } from 'react'; import { Link, useNavigate, useParams, useLocation } from 'react-router-dom'; import AdminHeader from '@/components/admin/AdminHeader'; import { getSubmissionById } from '@/services/admin/submission'; -import { getProductSubmissionHistory } from '@/services/submissions/submissionService'; import apiClient from '@/services/api/apiClient'; +import { getBeforePreviousData, getQuarterPeriods } from '@/services/submissions/submissionService'; +import { getProductSubmissionHistory } from '@/services/submissions/submissionService'; import { Chart as ChartJS, CategoryScale, LinearScale, BarElement, LineElement, PointElement, Title, Tooltip, Legend } from 'chart.js'; -import { Line, Bar } from 'react-chartjs-2'; +import { Line } from 'react-chartjs-2'; // Register ChartJS components ChartJS.register(CategoryScale, LinearScale, BarElement, LineElement, PointElement, Title, Tooltip, Legend); -// Static Quarterly Data Component -const StaticQuarterlyChart = () => { - const staticChartData = { - labels: ['Q1 2023', 'Q2 2023', 'Q3 2023', 'Q4 2023', 'Q1 2024', 'Q2 2024', 'Q3 2024', 'Q4 2024', 'Q1 2025', 'Q2 2025', 'Q3 2025', 'Q4 2025'], - datasets: [ - { - label: 'Details', - data: [1200, 1300, 1250, 1400, 1350, 1450, 1500, 1550, 1600, 1650, 1700, 1750], - backgroundColor: 'rgba(75, 192, 192, 0.6)', - borderColor: 'rgba(75, 192, 192, 1)', - borderWidth: 1 - }, - { - label: 'Capacity', - data: [1500, 1500, 1500, 1500, 1600, 1600, 1600, 1600, 1700, 1700, 1700, 1700], - backgroundColor: 'rgba(54, 162, 235, 0.6)', - borderColor: 'rgba(54, 162, 235, 1)', - borderWidth: 1 - }, - { - label: 'Difference', - data: [300, 200, 250, 100, 250, 150, 100, 50, 100, 50, 0, -50], - backgroundColor: 'rgba(255, 99, 132, 0.6)', - borderColor: 'rgba(255, 99, 132, 1)', - borderWidth: 1 - } - ] +// Helper function to get months for quarter +const getMonthsForQuarter = (quarter, year) => { + const monthMap = { + 'Q1': ['Jan', 'Feb', 'Mar'], + 'Q2': ['Apr', 'May', 'Jun'], + 'Q3': ['Jul', 'Aug', 'Sep'], + 'Q4': ['Oct', 'Nov', 'Dec'] }; - - const options = { - responsive: true, - maintainAspectRatio: false, - scales: { - y: { - beginAtZero: true, - title: { - display: true, - text: 'Quantity', - font: { - weight: 'bold' - } - } - } - }, - plugins: { - legend: { - position: 'top', - }, - tooltip: { - callbacks: { - label: function(context) { - return `${context.dataset.label}: ${context.parsed.y.toLocaleString()}`; - } - } - } - } - }; - - return ( -
-

Quarterly Overview (2023-2025)

-
- -
-
- ); -}; - -// Wrapper component to render multiple charts for products -const CombinedProductsChart = ({ products, establishmentId }) => { - if (!products?.length) return null; - - return ( -
- {products.map((product, index) => ( -
-

- {product.product_name || `Product ${index + 1}`} Metrics -

- -
- ))} -
- ); + return monthMap[quarter] || ['Jan', 'Feb', 'Mar']; }; +// Product Chart Component const ProductChart = ({ product, establishmentId }) => { const [productHistory, setProductHistory] = React.useState(null); const [loading, setLoading] = React.useState(true); @@ -110,15 +30,15 @@ const ProductChart = ({ product, establishmentId }) => { React.useEffect(() => { const fetchProductHistory = async () => { - if (!establishmentId || !product?.product_id) return; + if (!establishmentId || !product?.product?.id) return; try { setLoading(true); - const result = await getProductSubmissionHistory(establishmentId, product.product_id); + const result = await getProductSubmissionHistory(establishmentId, product.product.id); setProductHistory(result); } catch (err) { - console.error(`Error fetching history for product ${product.product_id}:`, err); - setError(`Failed to load history for product ${product.product_name || product.product_id}`); + console.error(`Error fetching history for product ${product.product.id}:`, err); + setError(`Failed to load history for product ${product.product?.product_name || product.product?.id}`); } finally { setLoading(false); } @@ -318,9 +238,73 @@ const ProductChart = ({ product, establishmentId }) => { } }, plugins: { - // Hide the default legend since we're using a custom ColorLegend component legend: { - display: false + // position: 'top', + // align: 'center', + // labels: { + // usePointStyle: true, + // boxWidth: 12, + // boxHeight: 12, + // padding: 12, + // font: { + // size: 12, + // lineHeight: '16px' + // }, + // generateLabels: function(chart) { + // const data = chart.data; + // if (data.labels.length && data.datasets.length) { + // return chart.data.datasets.map((dataset, i) => { + // const meta = chart.getDatasetMeta(i); + // let label = dataset.label || ''; + // label = ' ' + label.trim(); + + // // For Annual Capacity (dashed line) + // if (label.includes('Annual Capacity')) { + // return { + // text: label, + // fillStyle: 'transparent', + // hidden: !meta.visible, + // lineDash: [3, 3], + // lineWidth: 2, + // strokeStyle: dataset.borderColor, + // pointStyle: 'line', + // rotation: 0, + // datasetIndex: i + // }; + // } + + // // For Quantity (solid line) + // if (label.includes('Quantity')) { + // return { + // text: label, + // fillStyle: 'transparent', + // hidden: !meta.visible, + // lineDash: [], + // lineWidth: 2, + // strokeStyle: dataset.borderColor, + // pointStyle: 'line', + // rotation: 0, + // datasetIndex: i + // }; + // } + + // // For bar items (Previous, Current, Forecast) - square boxes + // return { + // text: label, + // fillStyle: dataset.backgroundColor, + // hidden: !meta.visible, + // lineWidth: 0, + // strokeStyle: 'transparent', + // pointStyle: 'rect', + // rotation: 0, + // datasetIndex: i + // }; + // }); + // } + // return []; + // } + // } + display:false, }, tooltip: { callbacks: { @@ -375,7 +359,7 @@ const ProductChart = ({ product, establishmentId }) => { ); } - // Color legend component - This is the only legend we want to show + // Color legend component const ColorLegend = () => (
@@ -404,7 +388,7 @@ const ProductChart = ({ product, establishmentId }) => { return (
-

Products Overview

+

Product Performance Overview

@@ -422,174 +406,8 @@ const ProductChart = ({ product, establishmentId }) => { ); }; -const BarChart = ({ product, quarterPeriods }) => { - if (!product || !quarterPeriods) return null; - - // Get month names for x-axis labels - const getMonthName = (monthIndex) => { - const date = new Date(2000, monthIndex - 1, 1); - return date.toLocaleString('default', { month: 'short' }); - }; - - // Generate x-axis labels based on the quarters - const getMonthLabels = () => { - const months = []; - if (quarterPeriods?.previous_month) { - months.push( - getMonthName(quarterPeriods.previous_month.previous_period_one), - getMonthName(quarterPeriods.previous_month.previous_period_two), - getMonthName(quarterPeriods.previous_month.previous_period_three) - ); - } - if (quarterPeriods?.current_month) { - months.push( - getMonthName(quarterPeriods.current_month.current_period_one), - getMonthName(quarterPeriods.current_month.current_period_two), - getMonthName(quarterPeriods.current_month.current_period_three) - ); - } - if (quarterPeriods?.forecast_month) { - months.push( - getMonthName(quarterPeriods.forecast_month.forecast_period_one), - getMonthName(quarterPeriods.forecast_month.forecast_period_two), - getMonthName(quarterPeriods.forecast_month.forecast_period_three) - ); - } - return months; - }; - - // Prepare data for the chart - const chartData = { - labels: getMonthLabels(), - datasets: [ - // Previous Quarter - { - label: `Previous (${quarterPeriods.previous_quarter} ${quarterPeriods.previous_year})`, - data: [ - product.previous_quantity_period_one, - product.previous_quantity_period_two, - product.previous_quantity_period_three, - null, null, null, // Empty for current quarter - null, null, null // Empty for forecast quarter - ], - borderColor: '#92722A', // Gray color for previous - backgroundColor: 'transparent', - borderWidth: 2, - tension: 0.3, - pointRadius: 3, - pointHoverRadius: 5 - }, - // Current Quarter - { - label: `Current (${quarterPeriods.current_quarter} ${quarterPeriods.current_year})`, - data: [ - null, null, null, // Empty for previous quarter - product.current_quantity_period_one, - product.current_quantity_period_two, - product.current_quantity_period_three, - null, null, null // Empty for forecast quarter - ], - borderColor: '#10B981', // Green color for current - backgroundColor: 'transparent', - borderWidth: 2, - tension: 0.3, - pointRadius: 3, - pointHoverRadius: 5 - }, - // Forecast Quarter - { - label: `Forecast (${quarterPeriods.forecast_quarter} ${quarterPeriods.forecast_year})`, - data: [ - null, null, null, // Empty for previous quarter - null, null, null, // Empty for current quarter - product.forecast_quantity_period_one, - product.forecast_quantity_period_two, - product.forecast_quantity_period_three - ], - borderColor: '#F59E0B', // Yellow color for forecast - backgroundColor: 'transparent', - borderWidth: 2, - tension: 0.3, - pointRadius: 3, - pointHoverRadius: 5 - }, - // Annual Capacity Line - { - label: 'Annual Capacity', - data: new Array(9).fill(product.annual_installed_capacity), - borderColor: '#3B82F6', // Blue color for capacity line - backgroundColor: 'transparent', - borderWidth: 1, - borderDash: [5, 5], - pointRadius: 0, - borderCapStyle: 'round' - } - ] - }; - - const options = { - responsive: true, - maintainAspectRatio: false, - interaction: { - mode: 'index', - intersect: false, - }, - scales: { - y: { - beginAtZero: true, - title: { - display: true, - text: 'Quantity' - }, - grid: { - drawBorder: false - } - }, - x: { - grid: { - display: false - } - } - }, - plugins: { - legend: { - position: 'top', - align: 'end', - labels: { - usePointStyle: true, - boxWidth: 10, - padding: 20 - } - }, - tooltip: { - callbacks: { - label: function(context) { - let label = context.dataset.label || ''; - if (label) { - label += ': '; - } - if (context.parsed.y !== null) { - label += context.parsed.y; - } - return label; - } - } - } - } - }; - - return ( -
- -
- ); -}; - const caretUpSrc = '/assets/images/caret-up.svg'; +const caretDownSrc = '/assets/images/caret-down.svg'; const establishmentIconSrc = '/assets/images/Establishment.svg'; const establishmentIdIconSrc = '/assets/images/Establishmentid.svg'; const emailIconSrc = '/assets/images/emailid.svg'; @@ -609,6 +427,7 @@ const employmentIcons = { }; const ValidationReview = () => { + const [threshold, setThreshold] = React.useState(10); const navigate = useNavigate(); const location = useLocation(); const actionType = location.state?.actionType || 'view'; @@ -622,8 +441,15 @@ const ValidationReview = () => { const [submissionData, setSubmissionData] = React.useState(null); const [quarterPeriods, setQuarterPeriods] = React.useState(null); const [isApproveOpen, setIsApproveOpen] = React.useState(false); + const [varianceHighlighting, setVarianceHighlighting] = React.useState(false); + const [showPreviousNext, setShowPreviousNext] = React.useState(false); + const [nextQuarterData, setNextQuarterData] = React.useState({}); + const [fetchingNextQuarter, setFetchingNextQuarter] = React.useState(false); + const [yearLabels, setYearLabels] = React.useState(null); const [establishmentId, setEstablishmentId] = React.useState(null); - const [products, setProducts] = React.useState([]); + const [additionalForecastQuarter, setAdditionalForecastQuarter] = React.useState(''); + const [additionalForecastYear, setAdditionalForecastYear] = React.useState(''); + const [additionalForecastMonths, setAdditionalForecastMonths] = React.useState(['', '', '']); React.useEffect(() => { const fetchSubmission = async () => { @@ -644,11 +470,23 @@ const ValidationReview = () => { data = result.data; if (result.quarter_periods) { setQuarterPeriods(result.quarter_periods); + + // Generate year labels based on quarter periods + if (result.quarter_periods) { + const labels = generateYearLabels(result.quarter_periods); + setYearLabels(labels); + } } } else if (result?.data) { data = result.data; if (result.quarter_periods) { setQuarterPeriods(result.quarter_periods); + + // Generate year labels based on quarter periods + if (result.quarter_periods) { + const labels = generateYearLabels(result.quarter_periods); + setYearLabels(labels); + } } } else { data = result; @@ -662,9 +500,6 @@ const ValidationReview = () => { if (data.establishment?.id) { setEstablishmentId(data.establishment.id); } - if (data.products) { - setProducts(data.products); - } } catch (err) { console.error('Failed to load submission details:', err); setError(err.message || 'Unable to load submission details. Please try again later.'); @@ -676,6 +511,214 @@ const ValidationReview = () => { fetchSubmission(); }, [id]); + // Function to generate year labels for each quarter + const generateYearLabels = (quarterPeriods) => { + if (!quarterPeriods) return null; + + const { + previous_quarter, + previous_year, + current_quarter, + current_year, + forecast_quarter, + forecast_year + } = quarterPeriods; + + // Determine the quarter-year combinations + const quarters = [ + { quarter: previous_quarter, year: previous_year, type: 'previous' }, + { quarter: current_quarter, year: current_year, type: 'current' }, + { quarter: forecast_quarter, year: forecast_year, type: 'forecast' } + ]; + + // Sort quarters chronologically + quarters.sort((a, b) => { + const aValue = a.year * 4 + getQuarterOrder(a.quarter); + const bValue = b.year * 4 + getQuarterOrder(b.quarter); + return aValue - bValue; + }); + + // Generate labels for each quarter with unique year display + const labels = {}; + + quarters.forEach((q, index) => { + // Show year for first quarter or when year changes + if (index === 0 || q.year !== quarters[index - 1].year) { + labels[q.type] = `${q.quarter} ${q.year}`; + } else { + labels[q.type] = q.quarter; + } + }); + + return labels; + }; + + const getQuarterOrder = (quarter) => { + const order = { 'Q1': 1, 'Q2': 2, 'Q3': 3, 'Q4': 4 }; + return order[quarter] || 0; + }; + + const getNextQuarterInfo = (currentQuarter, currentYear) => { + const quarterInfo = { + Q1: { next: "Q2", startMonth: 4 }, + Q2: { next: "Q3", startMonth: 7 }, + Q3: { next: "Q4", startMonth: 10 }, + Q4: { next: "Q1", startMonth: 1 }, + }; + + const current = quarterInfo[currentQuarter] || quarterInfo["Q1"]; + let year = currentQuarter === "Q4" ? currentYear + 1 : currentYear; + + const monthNames = [ + "Jan","Feb","Mar","Apr","May","Jun", + "Jul","Aug","Sep","Oct","Nov","Dec", + ]; + + const months = []; + for (let i = 0; i < 3; i++) { + const monthIndex = (current.startMonth - 1 + i) % 12; + months.push(monthNames[monthIndex]); + } + + return { + next: current.next, + months, + year, + }; +}; + + const handleShowPreviousNextChange = async (e) => { + const checked = e.target.checked; + setShowPreviousNext(checked); + + if (checked) { + try { + setFetchingNextQuarter(true); + + const currentQuarter = quarterPeriods?.current_quarter || "Q4"; + const currentYear = quarterPeriods?.current_year || 2025; + + const establishmentId = submissionData?.establishment?.id; + const productId = submissionData?.products?.[0]?.product?.id; + + if (!establishmentId || !currentQuarter || !currentYear || !productId) { + console.error("Missing required parameters:", { + establishmentId, + currentQuarter, + currentYear, + productId, + }); + return; + } + + // API call + const response = await getBeforePreviousData( + establishmentId, + currentQuarter, + currentYear, + productId + ); + + console.log("API Response:", response); + + let mappedDataMap = {}; + + if (response?.data) { + submissionData.products.forEach((product) => { + const pid = product?.product?.id; + if (pid) { + mappedDataMap[pid] = { + forecast_quantity_period_one: + response.data.current_quantity_period_one ?? 0, + forecast_quantity_period_two: + response.data.current_quantity_period_two ?? 0, + forecast_quantity_period_three: + response.data.current_quantity_period_three ?? 0, + + forecast_cost_period_one: + response.data.current_cost_period_one ?? 0, + forecast_cost_period_two: + response.data.current_cost_period_two ?? 0, + forecast_cost_period_three: + response.data.current_cost_period_three ?? 0, + }; + } + }); + } else { + // if no backend data + submissionData.products.forEach((product) => { + const pid = product?.product?.id; + if (pid) { + mappedDataMap[pid] = { + forecast_quantity_period_one: 0, + forecast_quantity_period_two: 0, + forecast_quantity_period_three: 0, + forecast_cost_period_one: 0, + forecast_cost_period_two: 0, + forecast_cost_period_three: 0, + }; + } + }); + } + + setNextQuarterData(mappedDataMap); + + // Calculate previous-previous quarter UI labels + const q = Number(currentQuarter.replace("Q", "")); + let historicalQuarter = `Q${q - 2}`; + let historicalYear = currentYear; + + if (q - 2 <= 0) { + historicalQuarter = `Q${4 + (q - 2)}`; + historicalYear = currentYear - 1; + } + + setAdditionalForecastQuarter(historicalQuarter); + setAdditionalForecastYear(historicalYear); + setAdditionalForecastMonths( + getMonthsForQuarter(historicalQuarter, historicalYear) + ); + + } catch (error) { + console.error("Error fetching before-previous data:", error); + } finally { + setFetchingNextQuarter(false); + } + + } else { + // On uncheck, clear values + setNextQuarterData({}); + setAdditionalForecastQuarter(""); + setAdditionalForecastYear(""); + setAdditionalForecastMonths(["", "", ""]); + } +}; + + + // Helper to get forecast data for a specific product + const getCurrentYearForecastData = (productId) => { + if (!showPreviousNext || !productId) { + return { + forecast_quantity_period_one: 0, + forecast_quantity_period_two: 0, + forecast_quantity_period_three: 0, + forecast_cost_period_one: 0, + forecast_cost_period_two: 0, + forecast_cost_period_three: 0 + }; + } + + // Return the forecast data or empty values if not available + return nextQuarterData[productId] || { + forecast_quantity_period_one: 0, + forecast_quantity_period_two: 0, + forecast_quantity_period_three: 0, + forecast_cost_period_one: 0, + forecast_cost_period_two: 0, + forecast_cost_period_three: 0 + }; + }; + const handleApprove = async () => { setIsApproveOpen(false); if (actionLoading) return; @@ -749,6 +792,88 @@ const ValidationReview = () => { } }; + const calculateVariance = (previous, current) => { + const previousNum = parseFloat(previous) || 0; + const currentNum = parseFloat(current) || 0; + + if (previousNum === 0) { + return currentNum === 0 ? 0 : 100; + } + + return ((currentNum - previousNum) / Math.abs(previousNum)) * 100; + }; + + const formatPercentage = (variance) => { + const absVariance = Math.abs(variance); + + if (absVariance === 0) return '0%'; + + if (absVariance >= 1000) { + return `${variance > 0 ? '+' : ''}${Math.round(variance).toLocaleString()}%`; + } + + if (absVariance >= 100) { + return `${variance > 0 ? '+' : ''}${Math.round(variance)}%`; + } + + return `${variance > 0 ? '+' : ''}${variance.toFixed(1)}%`; + }; + + const getVarianceDisplay = (previousValue, currentValue, isFirstMonth = false) => { + if (isFirstMonth) return null; + + const variance = calculateVariance(previousValue, currentValue); + const absVariance = Math.abs(variance); + + if (absVariance === 0) { + return ( +
+ 0% +
+ ); + } + + const isPositive = variance > 0; + const arrow = isPositive ? '▲' : '▼'; + + let colorClass = isPositive ? 'text-[#1E7C34]' : 'text-[#B52520]'; + + const percentage = formatPercentage(variance); + + return ( +
+ {arrow} {percentage} +
+ ); + }; + + const getCellClass = (previousValue, currentValue, isFirstMonth = false) => { + if (isFirstMonth || !varianceHighlighting) return ''; + + const variance = calculateVariance(previousValue, currentValue); + const absVariance = Math.abs(variance); + + if (absVariance > threshold) return ''; + + return variance > 0 ? 'bg-[#F0FDF4]' : 'bg-[#FEF2F2]'; + }; + + const getBorderClass = (previousValue, currentValue, isFirstMonth = false) => { + if (isFirstMonth || !varianceHighlighting) return ''; + + const variance = calculateVariance(previousValue, currentValue); + const absVariance = Math.abs(variance); + + if (absVariance > threshold) return ''; + + return variance > 0 ? 'border-2 border-[#1E7C34]' : 'border-2 border-[#B52520]'; + }; + +const getQuarterYearLabel = (quarter, year) => { + return `${quarter} ${year}`; +}; + + if (loading) { return (
@@ -792,6 +917,55 @@ const ValidationReview = () => { } const establishment = submissionData?.establishment || {}; + + // Get dynamic quarter information from API response + const previousQuarter = quarterPeriods?.previous_quarter || 'Q3'; + const previousYear = quarterPeriods?.previous_year || 2025; + const currentQuarter = quarterPeriods?.current_quarter || 'Q4'; + const currentYear = quarterPeriods?.current_year || 2025; + const forecastQuarter = quarterPeriods?.forecast_quarter || 'Q1'; + const forecastYear = quarterPeriods?.forecast_year || 2026; + + // Get dynamic month information from API response + const previousMonths = { + previous_period_one: quarterPeriods?.previous_month?.previous_period_one || getMonthsForQuarter(previousQuarter, previousYear)[0], + previous_period_two: quarterPeriods?.previous_month?.previous_period_two || getMonthsForQuarter(previousQuarter, previousYear)[1], + previous_period_three: quarterPeriods?.previous_month?.previous_period_three || getMonthsForQuarter(previousQuarter, previousYear)[2] + }; + + const currentMonths = { + current_period_one: quarterPeriods?.current_month?.current_period_one || getMonthsForQuarter(currentQuarter, currentYear)[0], + current_period_two: quarterPeriods?.current_month?.current_period_two || getMonthsForQuarter(currentQuarter, currentYear)[1], + current_period_three: quarterPeriods?.current_month?.current_period_three || getMonthsForQuarter(currentQuarter, currentYear)[2] + }; + + const forecastMonths = { + forecast_period_one: quarterPeriods?.forecast_month?.forecast_period_one || getMonthsForQuarter(forecastQuarter, forecastYear)[0], + forecast_period_two: quarterPeriods?.forecast_month?.forecast_period_two || getMonthsForQuarter(forecastQuarter, forecastYear)[1], + forecast_period_three: quarterPeriods?.forecast_month?.forecast_period_three || getMonthsForQuarter(forecastQuarter, forecastYear)[2] + }; + + const previousLabel = getQuarterYearLabel(previousQuarter, previousYear); +const currentLabel = getQuarterYearLabel(currentQuarter, currentYear); + +// Next Year Forecast +const forecastLabel = getQuarterYearLabel(forecastQuarter, currentYear + 1); + +// Optional: Previous Forecast (same year usually) +// const additionalForecastLabel = getQuarterYearLabel(forecastQuarter, currentYear); + + // Use generated year labels or fallback to full year display + // const previousLabel = yearLabels?.previous || `${previousQuarter} ${previousYear}`; + // const currentLabel = yearLabels?.current || `${currentQuarter} ${currentYear}`; + // const forecastLabel = yearLabels?.forecast || `${forecastQuarter} ${forecastYear}`; + + // Calculate additional forecast label dynamically +const additionalForecastLabel = + showPreviousNext && additionalForecastQuarter && additionalForecastYear + ? `${additionalForecastQuarter} ${additionalForecastYear}` + : ''; + + const establishmentDetails = [ { @@ -844,73 +1018,10 @@ const ValidationReview = () => { }, ]; - const previousQuarter = quarterPeriods?.previous_quarter; - const currentQuarter = quarterPeriods?.current_quarter; - const forecastQuarter = quarterPeriods?.forecast_quarter; - const year = submissionData?.year || new Date().getFullYear(); - - const submissionProducts = submissionData?.products || []; - const productDetails = Array.isArray(submissionProducts) ? submissionProducts.map((product) => { - const quarters = [ - { label: `${previousQuarter}-${year}`, color: '#0C64F6', cellColor: '#F4F8FF', type: 'previous' }, - { label: `${currentQuarter}-${year}`, color: '#1E7C34', cellColor: '#F3FAF4', type: 'current' }, - { label: `${forecastQuarter}-${year}`, color: '#D97706', cellColor: '#FFF6EC', type: 'forecast' }, - ]; - - const unitValue = product.unit?.uom || 'units'; - const unit = String(unitValue); - - const previousQuantity = product.previous_quantity || '—'; - const previousCost = product.previous_cost || '—'; - const currentQuantity = product.current_quantity || '—'; - const currentCost = product.current_cost || '—'; - const forecastQuantity = product.forecast_quantity || '—'; - const forecastCost = product.forecast_cost || '—'; - - const variationReason = product.variation_reason?.reason || product.other_variation_reason || '—'; - const zeroTargetReason = product.zero_target_reason?.reason || product.other_zero_target_reason || '—'; - - return { - code: product.product?.hs_code || '—', - name: product.product?.product_name || '—', - submittedBy: submissionData?.created_by || 'System', - submittedOn: submissionData?.created_at ? new Date(submissionData.created_at).toLocaleDateString('en-GB') : '—', - unit: unit, - capacity: product.annual_installed_capacity?.toString() || '—', - quarters, - rows: [ - { - label: `Quantity (${unit.toLowerCase()})`, - values: [ - previousQuantity.toString(), - currentQuantity.toString(), - forecastQuantity.toString() - ] - }, - { - label: 'Cost (AED)', - values: [ - previousCost !== '—' ? Number(previousCost).toLocaleString() : '—', - currentCost !== '—' ? Number(currentCost).toLocaleString() : '—', - forecastCost !== '—' ? Number(forecastCost).toLocaleString() : '—' - ] - }, - { - label: 'Reason', - values: [ - '—', - variationReason, - zeroTargetReason - ] - }, - ], - remarks: product.remarks || 'NA', - }; - }) : []; - return (
+
- {/* Products will be rendered below with their respective charts */} - {submissionData?.products?.length > 0 ? ( -
+
{submissionData.products.map((product, idx) => ( -
- {/* Header Section */} -
+
+
Product:{' '} {product?.product?.hs_code} - {product?.product?.product_name}
-
- - Submitted By: System - - - Unit: {product?.unit?.uom || 'units'} - - - Capacity: {product?.annual_installed_capacity} - +
+
+ Submitted By: + System +
+
+ Unit: + {product?.unit?.uom || 'units'} +
+
+ Capacity: + {product?.annual_installed_capacity} +
- {/* Product Metrics with Chart */} -
-

Product Metrics

-
- -
-
+ {/* Product Chart Section */} + - {/* Table Section */}
+
+
+ setVarianceHighlighting(e.target.checked)} + className="h-4 w-4 rounded border-gray-300 text-[#92722A] focus:ring-[#92722A]" + /> + + {varianceHighlighting && ( +
+ Threshold + +
+ )} +
+
+ + +
+
+ {varianceHighlighting && ( +
+ Cells with variance > {threshold}% will be highlighted +
+ )} +
- - - - - - + + + + {showPreviousNext ? ( + <> + {/* 12 columns when showPreviousNext is true */} + + + + + + ) : ( + <> + {/* 9 columns when showPreviousNext is false */} + + + + + )} + - - - - + + {showPreviousNext ? ( + <> + {/* Previous Quarter Months */} + + + - - - + {/* Current Quarter Months */} + + + - - - - - + {/* Previous Forecast Months */} + + + + + {/* Next Year Forecast Months */} + + + + + ) : ( + <> + {/* Previous Quarter Months */} + + + + + {/* Current Quarter Months */} + + + + + {/* Next Year Forecast Months */} + + + + + )} + + - {/* Quantity Row */} - - - - + + {showPreviousNext ? ( + <> + {/* Previous Quarter Data */} + + + - - - + {/* Current Quarter Data */} + + + - - - + {/* HISTORICAL Data (from API) */} + {(() => { + const historicalData = getCurrentYearForecastData(product?.product?.id); + return ( + <> + + + + + ); + })()} + + {/* NEXT YEAR FORECAST Data (original from submission) */} + + + + + ) : ( + <> + + + + + + + + + + + + + )} - {/* Cost Row */} - - - - + + {showPreviousNext ? ( + <> + {/* Previous Quarter Cost */} + + + - - - + {/* Current Quarter Cost */} + + + - - - + {/* HISTORICAL Cost (from API) */} + {(() => { + const historicalData = getCurrentYearForecastData(product?.product?.id); + return ( + <> + + + + + ); + })()} + + {/* NEXT YEAR FORECAST Cost (original from submission) */} + + + + + ) : ( + <> + + + + + + + + + + + + + )} - - {/* Reason (Current) */} + + + {showPreviousNext ? ( + <> + + + {/* Previous Forecast (colSpan 3) */} + + {/* Next Year Forecast (colSpan 3) */} + + + ) : ( + <> + {/* Previous Quarter (colSpan 3) */} + + {/* Current Quarter (colSpan 3) - இங்குதான் Reason (Current) இருக்க வேண்டும் */} + + {/* Next Year Forecast (colSpan 3) */} + + + )} + - - - - - - - {/* Reason (Forecast) */} - - - - - + {showPreviousNext ? ( + <> + + + + + + ) : ( + <> + + + + + )}
- Metric - - {quarterPeriods?.previous_quarter}-{quarterPeriods?.previous_year}{' '} - (Previous Quarter) - - {quarterPeriods?.current_quarter}-{quarterPeriods?.current_year}{' '} - (Current Quarter) - - {quarterPeriods?.forecast_quarter}-{quarterPeriods?.forecast_year}{' '} - (Next Quarter) -
+ Metric + + {previousLabel || 'Q1 2024'} (PREVIOUS QUARTER) + + {currentLabel || 'Q2 2024'} (CURRENT QUARTER) + + {additionalForecastLabel || 'Q3 2023'} (PREVIOUS FORECAST) + + {forecastLabel || 'Q3 2024'} (NEXT YEAR FORECAST) + + {previousLabel || 'Q1 2024'} (PREVIOUS QUARTER) + + {currentLabel || 'Q2 2024'} (CURRENT QUARTER) + + {forecastLabel || 'Q3 2024'} (NEXT YEAR FORECAST) +
- {quarterPeriods?.previous_month?.previous_period_one} - - {quarterPeriods?.previous_month?.previous_period_two} - - {quarterPeriods?.previous_month?.previous_period_three} -
+ {previousMonths?.previous_period_one || 'Jan'} + + {previousMonths?.previous_period_two || 'Feb'} + + {previousMonths?.previous_period_three || 'Mar'} + - {quarterPeriods?.current_month?.current_period_one} - - {quarterPeriods?.current_month?.current_period_two} - - {quarterPeriods?.current_month?.current_period_three} - + {currentMonths?.current_period_one || 'Apr'} + + {currentMonths?.current_period_two || 'May'} + + {currentMonths?.current_period_three || 'Jun'} + - {quarterPeriods?.forecast_month?.forecast_period_one} - - {quarterPeriods?.forecast_month?.forecast_period_two} - - {quarterPeriods?.forecast_month?.forecast_period_three} -
+ {additionalForecastMonths?.[0] || 'Jul'} + + {additionalForecastMonths?.[1] || 'Aug'} + + {additionalForecastMonths?.[2] || 'Sep'} + + {forecastMonths?.forecast_period_one || 'Oct'} + + {forecastMonths?.forecast_period_two || 'Nov'} + + {forecastMonths?.forecast_period_three || 'Dec'} + + {previousMonths?.previous_period_one || 'Jan'} + + {previousMonths?.previous_period_two || 'Feb'} + + {previousMonths?.previous_period_three || 'Mar'} + + {currentMonths?.current_period_one || 'Apr'} + + {currentMonths?.current_period_two || 'May'} + + {currentMonths?.current_period_three || 'Jun'} + + {forecastMonths?.forecast_period_one || 'Jul'} + + {forecastMonths?.forecast_period_two || 'Aug'} + + {forecastMonths?.forecast_period_three || 'Sep'} +
+ Quantity ({product?.unit?.uom || 'units'}) {product.previous_quantity_period_one || '0'}{product.previous_quantity_period_two || '0'}{product.previous_quantity_period_three || '0'} +
+ {product.previous_quantity_period_one || '0'} +
+
+
+ {product.previous_quantity_period_two || '0'} + {getVarianceDisplay(product.previous_quantity_period_one, product.previous_quantity_period_two, true)} +
+
+
+ {product.previous_quantity_period_three || '0'} + {getVarianceDisplay(product.previous_quantity_period_two, product.previous_quantity_period_three)} +
+
{product.current_quantity_period_one || '0'}{product.current_quantity_period_two || '0'}{product.current_quantity_period_three || '0'} +
+ {product.current_quantity_period_one || '0'} + {getVarianceDisplay(product.previous_quantity_period_three, product.current_quantity_period_one)} +
+
+
+ {product.current_quantity_period_two || '0'} + {getVarianceDisplay(product.current_quantity_period_one, product.current_quantity_period_two)} +
+
+
+ {product.current_quantity_period_three || '0'} + {getVarianceDisplay(product.current_quantity_period_two, product.current_quantity_period_three)} +
+
{product.forecast_quantity_period_one || '0'}{product.forecast_quantity_period_two || '0'}{product.forecast_quantity_period_three || '0'} +
+ {historicalData.forecast_quantity_period_one || '0'} + {getVarianceDisplay(product.current_quantity_period_three, historicalData.forecast_quantity_period_one)} +
+
+
+ {historicalData.forecast_quantity_period_two || '0'} + {getVarianceDisplay(historicalData.forecast_quantity_period_one, historicalData.forecast_quantity_period_two)} +
+
+
+ {historicalData.forecast_quantity_period_three || '0'} + {getVarianceDisplay(historicalData.forecast_quantity_period_two, historicalData.forecast_quantity_period_three)} +
+
+
+ {product.forecast_quantity_period_one || '0'} + {getVarianceDisplay(product.current_quantity_period_three, product.forecast_quantity_period_one)} +
+
+
+ {product.forecast_quantity_period_two || '0'} + {getVarianceDisplay(product.forecast_quantity_period_one, product.forecast_quantity_period_two)} +
+
+
+ {product.forecast_quantity_period_three || '0'} + {getVarianceDisplay(product.forecast_quantity_period_two, product.forecast_quantity_period_three)} +
+
+
+ {product.previous_quantity_period_one || '0'} +
+
+
+ {product.previous_quantity_period_two || '0'} + {getVarianceDisplay(product.previous_quantity_period_one, product.previous_quantity_period_two, true)} +
+
+
+ {product.previous_quantity_period_three || '0'} + {getVarianceDisplay(product.previous_quantity_period_two, product.previous_quantity_period_three)} +
+
+
+ {product.current_quantity_period_one || '0'} + {getVarianceDisplay(product.previous_quantity_period_three, product.current_quantity_period_one)} +
+
+
+ {product.current_quantity_period_two || '0'} + {getVarianceDisplay(product.current_quantity_period_one, product.current_quantity_period_two)} +
+
+
+ {product.current_quantity_period_three || '0'} + {getVarianceDisplay(product.current_quantity_period_two, product.current_quantity_period_three)} +
+
+
+ {product.forecast_quantity_period_one || '0'} + {getVarianceDisplay(product.current_quantity_period_three, product.forecast_quantity_period_one)} +
+
+
+ {product.forecast_quantity_period_two || '0'} + {getVarianceDisplay(product.forecast_quantity_period_one, product.forecast_quantity_period_two)} +
+
+
+ {product.forecast_quantity_period_three || '0'} + {getVarianceDisplay(product.forecast_quantity_period_two, product.forecast_quantity_period_three)} +
+
+ Cost (AED) {product.previous_cost_period_one || '0'}{product.previous_cost_period_two || '0'}{product.previous_cost_period_three || '0'} +
+ {product.previous_cost_period_one || '0'} +
+
+
+ {product.previous_cost_period_two || '0'} + {getVarianceDisplay(product.previous_cost_period_one, product.previous_cost_period_two, true)} +
+
+
+ {product.previous_cost_period_three || '0'} + {getVarianceDisplay(product.previous_cost_period_two, product.previous_cost_period_three)} +
+
{product.current_cost_period_one || '0'}{product.current_cost_period_two || '0'}{product.current_cost_period_three || '0'} +
+ {product.current_cost_period_one || '0'} + {getVarianceDisplay(product.previous_cost_period_three, product.current_cost_period_one)} +
+
+
+ {product.current_cost_period_two || '0'} + {getVarianceDisplay(product.current_cost_period_one, product.current_cost_period_two)} +
+
+
+ {product.current_cost_period_three || '0'} + {getVarianceDisplay(product.current_cost_period_two, product.current_cost_period_three)} +
+
{product.forecast_cost_period_one || '0'}{product.forecast_cost_period_two || '0'}{product.forecast_cost_period_three || '0'} +
+ {historicalData.forecast_cost_period_one || '0'} + {getVarianceDisplay(product.current_cost_period_three, historicalData.forecast_cost_period_one)} +
+
+
+ {historicalData.forecast_cost_period_two || '0'} + {getVarianceDisplay(historicalData.forecast_cost_period_one, historicalData.forecast_cost_period_two)} +
+
+
+ {historicalData.forecast_cost_period_three || '0'} + {getVarianceDisplay(historicalData.forecast_cost_period_two, historicalData.forecast_cost_period_three)} +
+
+
+ {product.forecast_cost_period_one || '0'} + {getVarianceDisplay(product.current_cost_period_three, product.forecast_cost_period_one)} +
+
+
+ {product.forecast_cost_period_two || '0'} + {getVarianceDisplay(product.forecast_cost_period_one, product.forecast_cost_period_two)} +
+
+
+ {product.forecast_cost_period_three || '0'} + {getVarianceDisplay(product.forecast_cost_period_two, product.forecast_cost_period_three)} +
+
+
+ {product.previous_cost_period_one || '0'} +
+
+
+ {product.previous_cost_period_two || '0'} + {getVarianceDisplay(product.previous_cost_period_one, product.previous_cost_period_two, true)} +
+
+
+ {product.previous_cost_period_three || '0'} + {getVarianceDisplay(product.previous_cost_period_two, product.previous_cost_period_three)} +
+
+
+ {product.current_cost_period_one || '0'} + {getVarianceDisplay(product.previous_cost_period_three, product.current_cost_period_one)} +
+
+
+ {product.current_cost_period_two || '0'} + {getVarianceDisplay(product.current_cost_period_one, product.current_cost_period_two)} +
+
+
+ {product.current_cost_period_three || '0'} + {getVarianceDisplay(product.current_cost_period_two, product.current_cost_period_three)} +
+
+
+ {product.forecast_cost_period_one || '0'} + {getVarianceDisplay(product.current_cost_period_three, product.forecast_cost_period_one)} +
+
+
+ {product.forecast_cost_period_two || '0'} + {getVarianceDisplay(product.forecast_cost_period_one, product.forecast_cost_period_two)} +
+
+
+ {product.forecast_cost_period_three || '0'} + {getVarianceDisplay(product.forecast_cost_period_two, product.forecast_cost_period_three)} +
+
+ Reason (Current) + + – + + {product.other_variation_reason || + (product?.variation_reason?.reason ? + (product.variation_reason.reason.startsWith('Other (specify)') ? '' : product.variation_reason.reason) + : 'No reason provided') + } + + – + + – + + – + + {product.other_variation_reason || + (product?.variation_reason?.reason ? + (product.variation_reason.reason.startsWith('Other (specify)') ? '' : product.variation_reason.reason) + : 'No reason provided') + } + + – +
- Reason (Current) - - -- - - {product.other_variation_reason || - (product?.variation_reason?.reason ? - (product.variation_reason.reason.startsWith('Other (specify)') ? '' : product.variation_reason.reason) - : 'No reason provided') -} - - -- -
+ Reason (Forecast) - -- - - -- - - {product.other_zero_target_reason || - (product?.zero_target_reason?.reason ? - (product.zero_target_reason.reason.startsWith('Other (specify)') ? '' : product.zero_target_reason.reason) - : 'No reason provided') - } - + – + + – + + – + + {product.other_zero_target_reason || + (product?.zero_target_reason?.reason ? + (product.zero_target_reason.reason.startsWith('Other (specify)') ? '' : product.zero_target_reason.reason) + : 'No reason provided') + } + + – + + – + + {product.other_zero_target_reason || + (product?.zero_target_reason?.reason ? + (product.zero_target_reason.reason.startsWith('Other (specify)') ? '' : product.zero_target_reason.reason) + : 'No reason provided') + } +
@@ -1293,7 +1827,6 @@ const ValidationReview = () => {
)} - {/* Action buttons moved to the main content area */}
- {/* Approval Confirmation Dialog */} {isApproveOpen && (
@@ -1323,29 +1855,29 @@ const ValidationReview = () => {

Approve this submission?

-

- You're about to approve {submissionData?.establishment?.factory_name|| 'this establishment'} for {submissionData?.quarter} {submissionData?.year}. -

- This will lock the record for the establishment. -

-
- - -
+

+ You're about to approve {submissionData?.establishment?.factory_name|| 'this establishment'} for {submissionData?.quarter} {submissionData?.year}. +

+ This will lock the record for the establishment. +

+
+ + +
diff --git a/ipi-survey-platform/src/pages/ManufacturingIndex/ManufacturingIndex.jsx b/ipi-survey-platform/src/pages/ManufacturingIndex/ManufacturingIndex.jsx new file mode 100644 index 0000000..f107005 --- /dev/null +++ b/ipi-survey-platform/src/pages/ManufacturingIndex/ManufacturingIndex.jsx @@ -0,0 +1,340 @@ +import React, { useState } from 'react'; +import { + TextField, + MenuItem, + Button, + IconButton, + Chip, + Divider, + ToggleButtonGroup, + ToggleButton, + Avatar, + Menu, + MenuItem as MuiMenuItem, + ListItemIcon, + Typography +} from '@mui/material'; +import { + ArrowRightAlt as ArrowRightAltIcon, + FileDownload as FileDownloadIcon, + FiberManualRecord as FiberManualRecordIcon, + NotificationsNone as NotificationsIcon, + HelpOutline as HelpIcon, + Settings as SettingsIcon, + Logout as LogoutIcon, + Person as PersonIcon, + KeyboardArrowDown as KeyboardArrowDownIcon +} from '@mui/icons-material'; +import Card from '@/components/common/Card'; + +const ManufacturingIndex = () => { + const [year, setYear] = useState('2025'); + const [selectedMonth, setSelectedMonth] = useState('Oct 2025'); + const [view, setView] = useState('manufacturing'); + + const years = ['2025', '2024', '2023']; + const months = [ + { month: 'Oct 2025', index: '114.8', mom: '+0.9', yoy: '+3.2', generated: '12 Nov 2025, 09:15', status: 'Completed' }, + { month: 'Sep 2025', index: '113.9', mom: '-0.2', yoy: '+2.8', generated: '11 Oct 2025, 09:15', status: 'Completed' }, + { month: 'Aug 2025', index: '114.1', mom: '+0.5', yoy: '+3.0', generated: '11 Sep 2025, 09:15', status: 'Completed' }, + { month: 'Jul 2025', index: '113.6', mom: '+0.3', yoy: '+2.9', generated: '11 Aug 2025, 09:15', status: 'Completed' }, + { month: 'Jun 2025', index: '113.3', mom: '+0.7', yoy: '+2.8', generated: '11 Jul 2025, 09:15', status: 'Completed' }, + ]; + + const handleViewChange = (event, newView) => { + if (newView !== null) { + setView(newView); + } + }; + + return ( +
+ {/* Header with logo only */} +
+
+
+ FCSC IPI +
+
+
+ + {/* Main content */} +
+ {/* Header */} +
+
+

IIP Manufacturing Index - Monthly Overview

+ {/* */} +
+ + +
+
+
+ {/* } + /> */} +

+ Manufacturing Index — Monthly overview +

+
+

+ Monthly manufacturing indices are generated automatically on the 11th of each month using the finalized Laspeyres item indices. + Use this view to monitor trends and drill down into ISIC groups. +

+
+ +
+
+ + Next scheduled run: 11 Nov 2025, 02:00 GST +
+

+ Formula: Manufacturing index = Σ (sector weightᵢ × group indexᵢ) +

+
+
+
+
+ +
+ {/* Left Column */} +
+ +

Manufacturing index — by reference month

+

+ Filter by year and quickly jump to a month to see its ISIC breakdown. +

+ +
+ + +
+ +
+ setYear(e.target.value)} + size="small" + className="min-w-[120px]" + InputLabelProps={{ className: 'text-gray-700' }} + > + {years.map((y) => ( + + {y} + + ))} + + + + + + ), + }} + /> +
+ +
+

Showing 5 months for 2025

+

Click a row to open its ISIC breakdown on the right.

+
+ +
+ + + + + + + + + + + + + + {months.map((row) => ( + setSelectedMonth(row.month)} + className={`cursor-pointer ${selectedMonth === row.month ? 'bg-primary-50' : 'hover:bg-gray-50'}`} + > + + + + + + + + + ))} + +
REFERENCE MONTHMANUFACTURING INDEXMOM CHANGEYOY CHANGEGENERATED ONSTATUSACTIONS
+
+ {row.month} + {selectedMonth === row.month && ( + + Selected + + )} +
+
{row.index} (2022=100) + + {row.mom}% + + + + {row.yoy}% + + {row.generated} + + {row.status} + + + + + +
+
+
+
+ + {/* Right Column */} +
+ +
+

Monthly ISIC breakdown

+ +
+ +

+ Start from the manufacturing total, then drill down into ISIC 2-digit and 3 & 4-digit groups for the selected month. +

+ + + + Manufacturing total + + + ISIC 2-digit + + + ISIC 3 & 4-digit + + + +
+

MANUFACTURING INDEX (HEADLINE)

+

114.8 (2022=100)

+
+

MoM: +0.9%

+

YoY: +3.2%

+
+

+ The manufacturing index increased by 0.9% compared to the previous month and 3.2% compared to the same month last year. +

+
+ + + +
+

KEY CONTRIBUTORS

+ +
+
+

Food products (ISIC 10-12)

+

+0.24

+
+
+
+
+
+ +
+
+

Chemicals (ISIC 20-21)

+

+0.18

+
+
+
+
+
+ +
+
+

Basic metals (ISIC 24)

+

-0.12

+
+
+
+
+
+
+
+
+
+
+
+ ); +}; + +export default ManufacturingIndex; diff --git a/ipi-survey-platform/src/services/submissions/submissionService.js b/ipi-survey-platform/src/services/submissions/submissionService.js index 022741c..3038d47 100644 --- a/ipi-survey-platform/src/services/submissions/submissionService.js +++ b/ipi-survey-platform/src/services/submissions/submissionService.js @@ -1,11 +1,11 @@ import { getRequest, postRequest } from '@/services/api/CommonService'; import resolveEstablishmentId from '@/services/utils/establishment'; import { putRequest } from '../api/CommonService'; - + const endpoint = '/submissions'; const ESTABLISHMENT_PRODUCTS_ENDPOINT = '/establishment-products'; const QUARTER_PERIODS_ENDPOINT = '/getQuarterPeriods'; - + // Add this new function to fetch submissions with pagination export const getSubmissions = async (page = 1, limit = 100, config = {}) => { try { @@ -23,7 +23,7 @@ export const getSubmissions = async (page = 1, limit = 100, config = {}) => { throw error; } }; - + export const submitSurvey = async (payload = {}, config = {}) => { const establishmentId = resolveEstablishmentId(payload.establishment_id); const requestBody = { @@ -33,24 +33,24 @@ export const submitSurvey = async (payload = {}, config = {}) => { const response = await postRequest(endpoint, requestBody, config); return response.data; }; - + export const resubmitSurvey = async (submissionId, payload = {}, config = {}) => { if (!submissionId) { throw new Error('Submission ID is required for resubmission'); } - + const establishmentId = resolveEstablishmentId(payload.establishment_id); const requestBody = { ...payload, ...(establishmentId !== undefined ? { establishment_id: establishmentId } : {}), }; - + const url = `${endpoint}/${submissionId}`; // Use putRequest instead of postRequest with method override const response = await putRequest(url, requestBody, config); return response.data; }; - + export const fetchSubmissionHistory = async (establishmentId, config = {}) => { const resolvedId = resolveEstablishmentId(establishmentId); if (resolvedId === undefined) { @@ -60,7 +60,7 @@ export const fetchSubmissionHistory = async (establishmentId, config = {}) => { const response = await getRequest(url, config); return response.data; }; - + export const fetchSubmissionDetail = async (submissionId, config = {}) => { if (!submissionId) { throw new Error('Submission ID is required to fetch submission detail.'); @@ -69,7 +69,7 @@ export const fetchSubmissionDetail = async (submissionId, config = {}) => { const response = await getRequest(url, config); return response.data; }; - + // Add the new function to the default export export const getQuarterPeriods = async (currentYear, currentQuarter) => { try { @@ -83,7 +83,7 @@ export const getQuarterPeriods = async (currentYear, currentQuarter) => { throw error; } }; - + export const getPreviousForecastData = async (establishmentId, quarter, year, productId) => { try { const response = await getRequest( @@ -105,7 +105,48 @@ export const getPreviousForecastData = async (establishmentId, quarter, year, pr return null; } }; - + +export const getBeforePreviousData = async ( + establishmentId, + currentQuarter, + currentYear, + productId +) => { + try { + if (!establishmentId || !currentQuarter || !currentYear || !productId) { + throw new Error("Missing required parameters"); + } + + const q = Number(currentQuarter.replace("Q", "")); + let beforePrevQuarter = `Q${q - 2}`; + let beforePrevYear = currentYear; + + if (q - 2 <= 0) { + beforePrevQuarter = `Q${4 + (q - 2)}`; + beforePrevYear = currentYear - 1; + } + + const response = await getRequest( + "/submissions/getBeforePreviousData", + { + params: { + establishment_id: establishmentId, + current_quarter: beforePrevQuarter, + current_year: beforePrevYear, + product_id: productId + } + } + ); + + return response?.data ?? response; + } catch (error) { + console.error("Error fetching before-previous data:", error); + return null; + } +}; + + + export const getSubmissionHistoryByEstablishment = async (establishmentId, config = {}) => { try { if (!establishmentId) { @@ -121,27 +162,27 @@ export const getSubmissionHistoryByEstablishment = async (establishmentId, confi throw error; } }; - + export const getSubmissionAuditHistory = async (establishmentId, params = {}) => { try { if (!establishmentId) { throw new Error('Establishment ID is required'); } - + const response = await getRequest('/submission-audit-history', { params: { establishment_id: establishmentId, ...params // Spread any additional parameters (year, quarter, product_id) } }); - + return response.data || response; } catch (error) { console.error('Error fetching submission audit history:', error); throw error; } }; - + export const getEstablishmentProducts = async (establishmentId, config = {}) => { try { if (!establishmentId) { @@ -160,7 +201,7 @@ export const getEstablishmentProducts = async (establishmentId, config = {}) => throw error; } }; - + export const getProductSubmissionHistory = async (establishmentId, productId, config = {}) => { try { if (!establishmentId || !productId) { @@ -180,7 +221,7 @@ export const getProductSubmissionHistory = async (establishmentId, productId, co throw error; } }; - + export default { getSubmissions, submitSurvey, @@ -192,5 +233,7 @@ export default { getSubmissionHistoryByEstablishment, getSubmissionAuditHistory, getEstablishmentProducts, - getProductSubmissionHistory -}; \ No newline at end of file + getProductSubmissionHistory, + getBeforePreviousData +}; + \ No newline at end of file