added validations percentage

This commit is contained in:
Malini 2025-12-16 11:40:31 +05:30
parent 61ddf150e1
commit c014fcf4bc
3 changed files with 1448 additions and 533 deletions

File diff suppressed because it is too large Load Diff

View File

@ -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 (
<div className="min-h-screen bg-gray-50">
{/* Header with logo only */}
<header className="bg-white shadow-sm">
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div className="h-16 flex items-center">
<img
className="h-10 w-auto"
src="/assets/images/FCSCLogo.svg"
alt="FCSC IPI"
/>
</div>
</div>
</header>
{/* Main content */}
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
{/* Header */}
<div className="mb-6">
<div className="flex flex-col md:flex-row md:justify-between md:items-center mb-4 gap-4">
<h1 className="text-2xl font-semibold text-gray-900">IIP Manufacturing Index - Monthly Overview</h1>
{/* <Button
variant="contained"
className="bg-primary-600 hover:bg-primary-700 text-white"
>
Edit with ChatGPT
</Button> */}
</div>
<Card className="p-6 mb-6">
<div className="flex flex-col md:flex-row justify-between items-start md:items-center gap-4">
<div>
<div className="flex items-center mb-2">
{/* <Chip
label="IPI ADMIN"
size="small"
className="bg-primary-50 text-primary-700 mr-2 font-medium"
icon={<FiberManualRecordIcon className="text-primary-500 text-xs" />}
/> */}
<h2 className="text-lg font-semibold text-gray-900">
Manufacturing Index Monthly overview
</h2>
</div>
<p className="text-gray-600 text-sm">
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.
</p>
</div>
<div className="text-right">
<div className="text-green-600 flex items-center justify-end text-sm">
<FiberManualRecordIcon className="text-xs mr-1" />
Next scheduled run: 11 Nov 2025, 02:00 GST
</div>
<p className="text-gray-500 text-xs mt-1">
Formula: Manufacturing index = Σ (sector weightᵢ × group indexᵢ)
</p>
</div>
</div>
</Card>
</div>
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
{/* Left Column */}
<div className="lg:col-span-2">
<Card className="p-6 mb-6">
<h3 className="text-lg font-semibold text-gray-900 mb-4">Manufacturing index by reference month</h3>
<p className="text-gray-600 mb-6">
Filter by year and quickly jump to a month to see its ISIC breakdown.
</p>
<div className="flex flex-wrap gap-2 mb-6">
<Chip
label="Base year: 2022 = 100"
variant="outlined"
size="small"
className="text-gray-600 border-gray-300"
/>
<Chip
label="Coverage: Manufacturing (ISIC C)"
variant="outlined"
size="small"
className="text-gray-600 border-gray-300"
/>
</div>
<div className="flex flex-col sm:flex-row gap-3 mb-6">
<TextField
select
label="Year"
value={year}
onChange={(e) => setYear(e.target.value)}
size="small"
className="min-w-[120px]"
InputLabelProps={{ className: 'text-gray-700' }}
>
{years.map((y) => (
<MenuItem key={y} value={y}>
{y}
</MenuItem>
))}
</TextField>
<TextField
placeholder="Search month"
size="small"
className="flex-grow"
InputProps={{
endAdornment: (
<IconButton size="small" className="text-gray-500">
<FileDownloadIcon />
</IconButton>
),
}}
/>
</div>
<div className="flex flex-col sm:flex-row justify-between items-start sm:items-center mb-4">
<h4 className="text-sm font-medium text-gray-900 mb-2 sm:mb-0">Showing 5 months for 2025</h4>
<p className="text-sm text-gray-500">Click a row to open its ISIC breakdown on the right.</p>
</div>
<div className="overflow-x-auto">
<table className="min-w-full divide-y divide-gray-200">
<thead className="bg-gray-50">
<tr>
<th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">REFERENCE MONTH</th>
<th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">MANUFACTURING INDEX</th>
<th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">MOM CHANGE</th>
<th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">YOY CHANGE</th>
<th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">GENERATED ON</th>
<th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">STATUS</th>
<th scope="col" className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">ACTIONS</th>
</tr>
</thead>
<tbody className="bg-white divide-y divide-gray-200">
{months.map((row) => (
<tr
key={row.month}
onClick={() => setSelectedMonth(row.month)}
className={`cursor-pointer ${selectedMonth === row.month ? 'bg-primary-50' : 'hover:bg-gray-50'}`}
>
<td className="px-6 py-4 whitespace-nowrap">
<div className="flex items-center">
<span className="text-sm font-medium text-gray-900">{row.month}</span>
{selectedMonth === row.month && (
<span className="ml-2 inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-primary-100 text-primary-800">
Selected
</span>
)}
</div>
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{row.index} (2022=100)</td>
<td className="px-6 py-4 whitespace-nowrap">
<span className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${
row.mom.startsWith('+')
? 'bg-green-100 text-green-800'
: 'bg-red-100 text-red-800'
}`}>
{row.mom}%
</span>
</td>
<td className="px-6 py-4 whitespace-nowrap">
<span className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${
row.yoy.startsWith('+')
? 'bg-green-100 text-green-800'
: 'bg-red-100 text-red-800'
}`}>
{row.yoy}%
</span>
</td>
<td className="px-6 py-4 whitespace-nowrap text-sm text-gray-500">{row.generated}</td>
<td className="px-6 py-4 whitespace-nowrap">
<span className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800">
{row.status}
</span>
</td>
<td className="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
<IconButton size="small" className="text-gray-400 hover:text-gray-600">
<ArrowRightAltIcon />
</IconButton>
</td>
</tr>
))}
</tbody>
</table>
</div>
</Card>
</div>
{/* Right Column */}
<div>
<Card className="p-6">
<div className="flex justify-between items-center mb-4">
<h3 className="text-lg font-semibold text-gray-900">Monthly ISIC breakdown</h3>
<Chip
label={selectedMonth}
size="small"
className="bg-gray-900 text-white font-medium"
/>
</div>
<p className="text-gray-600 text-sm mb-6">
Start from the manufacturing total, then drill down into ISIC 2-digit and 3 & 4-digit groups for the selected month.
</p>
<ToggleButtonGroup
color="primary"
value={view}
exclusive
onChange={handleViewChange}
aria-label="View"
fullWidth
className="mb-6"
>
<ToggleButton
value="manufacturing"
className={`flex-1 ${view === 'manufacturing' ? 'bg-primary-50 text-primary-700' : 'text-gray-600 hover:bg-gray-50'}`}
>
Manufacturing total
</ToggleButton>
<ToggleButton
value="isic2"
className={`flex-1 ${view === 'isic2' ? 'bg-primary-50 text-primary-700' : 'text-gray-600 hover:bg-gray-50'}`}
>
ISIC 2-digit
</ToggleButton>
<ToggleButton
value="isic34"
className={`flex-1 ${view === 'isic34' ? 'bg-primary-50 text-primary-700' : 'text-gray-600 hover:bg-gray-50'}`}
>
ISIC 3 & 4-digit
</ToggleButton>
</ToggleButtonGroup>
<div className="bg-gray-800 p-4 rounded-lg mb-6">
<h4 className="text-gray-400 text-xs font-medium uppercase tracking-wider mb-2">MANUFACTURING INDEX (HEADLINE)</h4>
<h3 className="text-2xl font-bold text-white mb-2">114.8 (2022=100)</h3>
<div className="flex gap-4 mb-3">
<p className="text-gray-400">MoM: <span className="text-green-500 font-semibold">+0.9%</span></p>
<p className="text-gray-400">YoY: <span className="text-green-500 font-semibold">+3.2%</span></p>
</div>
<p className="text-gray-300 text-sm">
The manufacturing index increased by 0.9% compared to the previous month and 3.2% compared to the same month last year.
</p>
</div>
<Divider className="border-[#424242] my-6" />
<div>
<h4 className="text-gray-500 text-sm font-medium uppercase tracking-wider mb-4">KEY CONTRIBUTORS</h4>
<div className="mb-5">
<div className="flex justify-between mb-1">
<p className="text-sm text-gray-800">Food products (ISIC 10-12)</p>
<p className="font-semibold text-gray-900">+0.24</p>
</div>
<div className="w-full h-1.5 bg-gray-100 rounded-full overflow-hidden">
<div className="h-full bg-green-500 w-3/5"></div>
</div>
</div>
<div className="mb-5">
<div className="flex justify-between mb-1">
<p className="text-sm text-gray-800">Chemicals (ISIC 20-21)</p>
<p className="font-semibold text-gray-900">+0.18</p>
</div>
<div className="w-full h-1.5 bg-gray-100 rounded-full overflow-hidden">
<div className="h-full bg-green-500 w-[45%]"></div>
</div>
</div>
<div className="mb-2">
<div className="flex justify-between mb-1">
<p className="text-sm text-gray-800">Basic metals (ISIC 24)</p>
<p className="text-red-500 font-semibold">-0.12</p>
</div>
<div className="w-full h-1.5 bg-gray-100 rounded-full overflow-hidden">
<div className="h-full bg-red-500 w-[30%]"></div>
</div>
</div>
</div>
</Card>
</div>
</div>
</div>
</div>
);
};
export default ManufacturingIndex;

View File

@ -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
};
getProductSubmissionHistory,
getBeforePreviousData
};