changes in phase 2

This commit is contained in:
Malini 2026-01-09 18:25:23 +05:30
parent 093eed99b2
commit 5e61c94f23
2 changed files with 285 additions and 261 deletions

View File

@ -146,6 +146,12 @@ const AdminHeader = () => {
iconActive={configActiveSrc} iconActive={configActiveSrc}
iconInactive={configInactiveSrc} iconInactive={configInactiveSrc}
/> />
<NavItem
to="/manufacturing-index"
label="Manufacturing Index"
iconActive={configActiveSrc}
iconInactive={configInactiveSrc}
/>
</nav> </nav>
<div className="hidden lg:flex items-center gap-3 ml-auto"> <div className="hidden lg:flex items-center gap-3 ml-auto">
@ -240,6 +246,12 @@ const AdminHeader = () => {
iconActive={configActiveSrc} iconActive={configActiveSrc}
iconInactive={configInactiveSrc} iconInactive={configInactiveSrc}
/> />
<NavItem
to="/manufacturing-index"
label="Manufacturing"
iconActive={configActiveSrc}
iconInactive={configInactiveSrc}
/>
<NavItem <NavItem
to="/admin/users" to="/admin/users"
label="Users" label="Users"

View File

@ -1,4 +1,5 @@
import React, { useState, useMemo, useEffect } from 'react'; import React, { useState, useMemo, useEffect } from 'react';
import AdminHeader from '@/components/admin/AdminHeader';
import { import {
IconButton, IconButton,
Dialog, Dialog,
@ -10,8 +11,10 @@ import Table from '@/components/common/Table';
import { import {
FiberManualRecord as FiberManualRecordIcon, FiberManualRecord as FiberManualRecordIcon,
KeyboardArrowRight as KeyboardArrowRightIcon, KeyboardArrowRight as KeyboardArrowRightIcon,
InfoOutlined as InfoOutlinedIcon InfoOutlined as InfoOutlinedIcon,
NavigateNext as NavigateNextIcon
} from '@mui/icons-material'; } from '@mui/icons-material';
import { Breadcrumbs, Link } from '@mui/material';
import Card from '@/components/common/Card'; import Card from '@/components/common/Card';
import { getManufacturingIndex, getManufacturingMonthlyOverview } from '@/services/manufacturingIndex/ManufacturingIndex'; import { getManufacturingIndex, getManufacturingMonthlyOverview } from '@/services/manufacturingIndex/ManufacturingIndex';
@ -59,12 +62,6 @@ const ManufacturingIndex = () => {
totalItems: 0 totalItems: 0
}); });
// Month name to number mapping
const monthMap = {
'Jan': 1, 'Feb': 2, 'Mar': 3, 'Apr': 4,
'May': 5, 'Jun': 6, 'Jul': 7, 'Aug': 8,
'Sep': 9, 'Oct': 10, 'Nov': 11, 'Dec': 12
};
// Fetch data from API // Fetch data from API
useEffect(() => { useEffect(() => {
@ -348,277 +345,292 @@ const ManufacturingIndex = () => {
} }
return ( return (
<div className="min-h-screen bg-gray-100"> <div className="min-h-screen w-full bg-gray-50">
{/* Popup Dialog */} <AdminHeader />
<Dialog <div className="max-w-[1280px] mx-auto px-4 py-6">
open={isPopupOpen} {/* Popup Dialog */}
onClose={handleClosePopup} <Dialog
maxWidth="lg" open={isPopupOpen}
fullWidth={true} onClose={handleClosePopup}
className="relative" maxWidth="lg"
> fullWidth={true}
<DialogTitle className="bg-[#F9F5E8] text-[#92722A] flex justify-between items-center pr-2"> className="relative"
<span>Monthly ISIC breakdown >
<DialogTitle className="bg-[#F9F5E8] text-[#92722A] flex justify-between items-center pr-2">
<span>Monthly ISIC breakdown
- {selectedMonth?.month}</span> - {selectedMonth?.month}</span>
<IconButton <IconButton
onClick={handleClosePopup} onClick={handleClosePopup}
size="small" size="small"
className="absolute right-2 top-2 text-gray-500 hover:text-gray-700" className="absolute right-2 top-2 text-gray-500 hover:text-gray-700"
sx={{ sx={{
'&:hover': { '&:hover': {
backgroundColor: 'transparent' backgroundColor: 'transparent'
} }
}} }}
> >
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" viewBox="0 0 20 20" fill="currentColor"> <svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
<path fillRule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clipRule="evenodd" /> <path fillRule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clipRule="evenodd" />
</svg> </svg>
</IconButton> </IconButton>
</DialogTitle> </DialogTitle>
<DialogContent className="p-0"> <DialogContent className="p-0">
<div className="p-6"> <div className="p-6">
{apiLoading ? ( {apiLoading ? (
<div className="flex justify-center items-center h-64"> <div className="flex justify-center items-center h-64">
<div className="animate-spin rounded-full h-10 w-10 border-b-2 border-[#92722A]"></div> <div className="animate-spin rounded-full h-10 w-10 border-b-2 border-[#92722A]"></div>
<span className="ml-3">Loading detailed data...</span> <span className="ml-3">Loading detailed data...</span>
</div> </div>
) : ( ) : (
<div className="space-y-0 w-[1000px] max-w-[95vw] min-w-[800px]"> <div className="space-y-0 w-[1000px] max-w-[95vw] min-w-[800px]">
<p className="text-sm text-gray-600 mb-4"> <p className="text-sm text-gray-600 mb-4">
Start from the manufacturing total, then drill down into ISIC 2-digit and 3 & 4-digit groups for the selected month. Start from the manufacturing total, then drill down into ISIC 2-digit and 3 & 4-digit groups for the selected month.
</p> </p>
{/* Tabs Header */} {/* Tabs Header */}
<div className="sticky top-0 z-10 bg-white pt-2"> <div className="sticky top-0 z-10 bg-white pt-2">
<div className="flex justify-between items-center border-b border-gray-200 pb-2"> <div className="flex justify-between items-center border-b border-gray-200 pb-2">
<div className="flex space-x-1"> <div className="flex space-x-1">
{['manufacturing', 'isic2', 'isic3', 'isic4'].map((tab) => ( {['manufacturing', 'isic2', 'isic3', 'isic4'].map((tab) => (
<button <button
key={tab} key={tab}
onClick={() => setActiveTab(tab)} onClick={() => setActiveTab(tab)}
className={`px-4 py-2 text-sm font-medium ${ className={`px-4 py-2 text-sm font-medium ${
activeTab === tab activeTab === tab
? 'text-[#92722A] border-b-2 border-[#92722A]' ? 'text-[#92722A] border-b-2 border-[#92722A]'
: 'text-medium-500 hover:text-gray-700' : 'text-medium-500 hover:text-gray-700'
}`} }`}
> >
{tab === 'manufacturing' ? 'Manufacturing total' : {tab === 'manufacturing' ? 'Manufacturing total' :
tab === 'isic2' ? 'ISIC 2-digit' : tab === 'isic2' ? 'ISIC 2-digit' :
tab === 'isic3' ? 'ISIC 3-digit' : 'ISIC 4-digit'} tab === 'isic3' ? 'ISIC 3-digit' : 'ISIC 4-digit'}
</button> </button>
))} ))}
</div>
<div className="w-5"></div>
</div> </div>
<div className="w-5"></div> </div>
{/* Tab Content */}
<div className="bg-white mt-2 rounded-lg shadow p-6 w-full">
{activeTab === 'manufacturing' && (
<ManufacturingTotal
selectedMonthData={selectedMonthData}
setActiveTab={setActiveTab}
apiData={monthlyOverviewData?.manufacturing}
/>
)}
{activeTab === 'isic2' && (
<ISIC2Digit
setActiveTab={setActiveTab}
apiData={monthlyOverviewData?.isic2}
/>
)}
{activeTab === 'isic3' && (
<ISIC3Digit
setActiveTab={setActiveTab}
apiData={monthlyOverviewData?.isic3}
/>
)}
{activeTab === 'isic4' && (
<ISIC4Digit
setActiveTab={setActiveTab}
apiData={monthlyOverviewData?.isic4}
/>
)}
</div> </div>
</div> </div>
)}
{/* Tab Content */}
<div className="bg-white mt-2 rounded-lg shadow p-6 w-full">
{activeTab === 'manufacturing' && (
<ManufacturingTotal
selectedMonthData={selectedMonthData}
setActiveTab={setActiveTab}
apiData={monthlyOverviewData?.manufacturing}
/>
)}
{activeTab === 'isic2' && (
<ISIC2Digit
setActiveTab={setActiveTab}
apiData={monthlyOverviewData?.isic2}
/>
)}
{activeTab === 'isic3' && (
<ISIC3Digit
setActiveTab={setActiveTab}
apiData={monthlyOverviewData?.isic3}
/>
)}
{activeTab === 'isic4' && (
<ISIC4Digit
setActiveTab={setActiveTab}
apiData={monthlyOverviewData?.isic4}
/>
)}
</div>
</div>
)}
</div>
</DialogContent>
</Dialog>
{/* Main UI */}
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
<div className="mb-6">
<Card className="p-6 bg-white rounded-lg shadow">
<div className="flex flex-col md:flex-row justify-between items-start md:items-start gap-4">
<div>
<h2 className="text-xl font-semibold text-gray-900 mb-2">
Manufacturing Index - Monthly overview
</h2>
<p className="text-sm text-gray-600 max-w-3xl">
Monthly manufacturing indices are automatically generated on the 11th of each month using the finalized Laspeyres item indices. This view enables users to monitor trends and drill down into ISIC grouplevel details.
</p>
</div>
<div className="text-right">
<div className="mt-5 flex flex-col items-end gap-2">
<div className="flex items-center text-sm text-green-600">
<FiberManualRecordIcon className="text-xs mr-1" />
Next scheduled run: 11 Nov 2025, 02:00 GST
</div>
<div className="flex text-sm text-gray-600">
<InfoOutlinedIcon className="text-gray-400 mr-1 mt-0.5 flex-shrink-0" style={{ fontSize: '1rem' }} />
<span className="whitespace-nowrap">Formula: Manufacturing index<sub>t</sub> = Σ (sector weight<sub>i</sub> × group index<sub>i,t</sub>)</span>
</div>
</div>
</div>
</div> </div>
</Card> </DialogContent>
</div> </Dialog>
{/* Main Table */} {/* Main UI */}
<div className="space-y-6"> <div className="w-full py-6">
<Card className="p-6 bg-white rounded-lg shadow"> {/* Breadcrumbs */}
<div className="space-y-4"> <nav className="mb-6 text-sm text-[#8F9299] flex items-center gap-2">
<div className="border border-gray-200 rounded-lg overflow-hidden"> <Link
{/* Filters */} to="/admin/dashboard"
<div className="bg-white px-6 py-4 border-b border-gray-200"> className="font-bold flex items-center gap-1 !text-black no-underline hover:underline hover:!text-black"
<div className="flex flex-col sm:flex-row justify-between items-start sm:items-center mb-4"> >
<div className="mb-4 sm:mb-0"> Dashboard
<h3 className="text-lg font-semibold text-gray-900 mb-1"> <img src="/assets/images/Breadcrumb-vector.svg" alt="" className="ml-1 w-3 h-3" />
Manufacturing index by reference month ({filteredMonths.length}) </Link>
</h3> <span className="text-[#92722A] font-medium">Manufacturing Index</span>
<p className="text-sm text-gray-600"> </nav>
Filter by year and quickly jump to a month to see its ISIC breakdown.
</p> <div className="mb-6">
<div className="flex items-center gap-4 mt-2"> <Card className="p-6 bg-white rounded-lg shadow">
<div className="text-sm text-gray-600 bg-gray-100 px-3 py-1 rounded-full"> <div className="flex flex-col md:flex-row justify-between items-start md:items-start gap-4">
Base year: 2022 = 100 <div>
</div> <h2 className="text-xl font-semibold text-gray-900 mb-2">
<div className="text-sm text-gray-600 bg-gray-100 px-3 py-1 rounded-full"> Manufacturing Index - Monthly overview
Coverage: Manufacturing (ISIC C) </h2>
<p className="text-sm text-gray-600 max-w-3xl">
Monthly manufacturing indices are automatically generated on the 11th of each month using the finalized Laspeyres item indices. This view enables users to monitor trends and drill down into ISIC grouplevel details.
</p>
</div>
<div className="text-right">
<div className="mt-5 flex flex-col items-end gap-2">
<div className="flex items-center text-sm text-green-600">
<FiberManualRecordIcon className="text-xs mr-1" />
Next scheduled run: 11 Nov 2025, 02:00 GST
</div>
<div className="flex text-sm text-gray-600">
<InfoOutlinedIcon className="text-gray-400 mr-1 mt-0.5 flex-shrink-0" style={{ fontSize: '1rem' }} />
<span className="whitespace-nowrap">Formula: Manufacturing index<sub>t</sub> = Σ (sector weight<sub>i</sub> × group index<sub>i,t</sub>)</span>
</div>
</div>
</div>
</div>
</Card>
</div>
{/* Main Table */}
<div className="space-y-6">
<Card className="p-6 bg-white rounded-lg shadow">
<div className="space-y-4">
<div className="border border-gray-200 rounded-lg overflow-hidden">
{/* Filters */}
<div className="bg-white px-6 py-4 border-b border-gray-200">
<div className="flex flex-col sm:flex-row justify-between items-start sm:items-center mb-4">
<div className="mb-4 sm:mb-0">
<h3 className="text-lg font-semibold text-gray-900 mb-1">
Manufacturing index by reference month ({filteredMonths.length})
</h3>
<p className="text-sm text-gray-600">
Filter by year and quickly jump to a month to see its ISIC breakdown.
</p>
<div className="flex items-center gap-4 mt-2">
<div className="text-sm text-gray-600 bg-gray-100 px-3 py-1 rounded-full">
Base year: 2022 = 100
</div>
<div className="text-sm text-gray-600 bg-gray-100 px-3 py-1 rounded-full">
Coverage: Manufacturing (ISIC C)
</div>
</div> </div>
</div> </div>
</div> <div className="flex items-center gap-3 w-full sm:w-auto">
<div className="flex items-center gap-3 w-full sm:w-auto"> <div className="flex-1 sm:flex-none w-40">
<div className="flex-1 sm:flex-none w-40"> <div className="relative">
<div className="relative"> <div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none"> <img src="/assets/images/material-symbols_search-rounded.svg" alt="Search" className="h-5 w-5" />
<img src="/assets/images/material-symbols_search-rounded.svg" alt="Search" className="h-5 w-5" /> </div>
<input
type="text"
placeholder="Search month"
className="w-full pl-10 pr-3 py-2 border border-gray-300 rounded-md h-[40px]"
value={searchMonth}
onChange={handleSearchMonth}
/>
</div> </div>
<input </div>
type="text" <div className="flex items-center w-32">
placeholder="Search month" <SelectField
className="w-full pl-10 pr-3 py-2 border border-gray-300 rounded-md h-[40px]" value={year}
value={searchMonth} onChange={handleYearChange}
onChange={handleSearchMonth} className="w-full h-[40px]"
size="small"
options={years.map(y => ({ value: y, label: y }))}
/> />
</div> </div>
<button
className="h-10 px-3 xl:px-4 rounded-[6px] bg-[#F7F7F7] border border-[#C3C6CB] text-sm inline-flex items-center gap-2 hover:bg-gray-50 whitespace-nowrap"
onClick={handleExportCSV}
>
<img src={downloadIconSrc} alt="Download" className="h-5 w-5" />
<span className="hidden xl:inline font-medium text-[#232528]">Export CSV</span>
<span className="xl:hidden font-medium text-[#232528]">Export</span>
</button>
</div> </div>
<div className="flex items-center w-32"> </div>
<SelectField </div>
value={year}
onChange={handleYearChange} {/* Table */}
className="w-full h-[40px]" <div className="relative">
size="small" <div className="w-full">
options={years.map(y => ({ value: y, label: y }))} <Table
/> headers={[
</div> 'Reference Month',
<button 'Manufacturing Index',
className="h-10 px-3 xl:px-4 rounded-[6px] bg-[#F7F7F7] border border-[#C3C6CB] text-sm inline-flex items-center gap-2 hover:bg-gray-50 whitespace-nowrap" 'MOM Change',
onClick={handleExportCSV} 'YOY Change',
> 'Generated On',
<img src={downloadIconSrc} alt="Download" className="h-5 w-5" /> 'Status',
<span className="hidden xl:inline font-medium text-[#232528]">Export CSV</span> 'Actions'
<span className="xl:hidden font-medium text-[#232528]">Export</span> ]}
</button> rows={filteredMonths.map((row) => {
const isSelected = selectedMonth?.month === row.month;
return [
<div key="month" className={`${isSelected ? 'font-medium' : ''}`}>
{row.month}
</div>,
<span className={isSelected ? 'font-medium' : ''} key="index">
{row.index} (2022=100)
</span>,
<span className={`${parseFloat(row.mom) >= 0 ? 'text-green-600' : 'text-red-600'} ${isSelected ? 'font-medium' : ''}`} key="mom">
{row.mom}%
</span>,
<span className={`${parseFloat(row.yoy) >= 0 ? 'text-green-600' : 'text-red-600'} ${isSelected ? 'font-medium' : ''}`} key="yoy">
{row.yoy}%
</span>,
<span className={isSelected ? 'font-medium' : ''} key="generated">
{row.generated}
</span>,
<span className={isSelected ? 'font-medium' : ''} key="status">
<span className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${
row.status === 'Completed'
? 'bg-green-100 text-green-800'
: 'bg-yellow-100 text-yellow-800'
}`}>
{row.status}
</span>
</span>,
<div
key="arrow"
className={`cursor-pointer flex items-center justify-end pr-4 ${
isSelected ? 'text-gray-900' : 'text-gray-400'
}`}
onClick={() => handleMonthSelect(row)}
>
<KeyboardArrowRightIcon />
</div>
];
})}
columnWidths={['180px', '190px', '130px', '130px', '200px', '120px', '80px']}
getRowClass={(index) => {
const isSelected = selectedMonth?.month === filteredMonths[index]?.month;
return isSelected ? 'bg-[#F5F0E1]' : 'hover:bg-gray-50';
}}
pagination={{
currentPage: pagination.currentPage,
onPageChange: (page) => setPagination(prev => ({ ...prev, currentPage: page })),
pageSize: pagination.pageSize,
totalItems: filteredMonths.length,
pageSizeOptions: [10, 20, 50, 100],
onPageSizeChange: (size) => {
setPagination(prev => ({
...prev,
pageSize: size,
currentPage: 1
}));
}
}}
className="w-full"
/>
</div> </div>
</div> </div>
</div> </div>
{/* Table */} <p className="text-xs text-gray-500 mt-2">
<div className="relative"> Note: Indices shown here are headline manufacturing totals. Use the drill-down actions to move from manufacturing total to ISIC 2-digit, then to ISIC 3 & 4-digit groups.
<div className="w-full"> </p>
<Table
headers={[
'Reference Month',
'Manufacturing Index',
'MOM Change',
'YOY Change',
'Generated On',
'Status',
'Actions'
]}
rows={filteredMonths.map((row) => {
const isSelected = selectedMonth?.month === row.month;
return [
<div key="month" className={`${isSelected ? 'font-medium' : ''}`}>
{row.month}
</div>,
<span className={isSelected ? 'font-medium' : ''} key="index">
{row.index} (2022=100)
</span>,
<span className={`${parseFloat(row.mom) >= 0 ? 'text-green-600' : 'text-red-600'} ${isSelected ? 'font-medium' : ''}`} key="mom">
{row.mom}%
</span>,
<span className={`${parseFloat(row.yoy) >= 0 ? 'text-green-600' : 'text-red-600'} ${isSelected ? 'font-medium' : ''}`} key="yoy">
{row.yoy}%
</span>,
<span className={isSelected ? 'font-medium' : ''} key="generated">
{row.generated}
</span>,
<span className={isSelected ? 'font-medium' : ''} key="status">
<span className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium ${
row.status === 'Completed'
? 'bg-green-100 text-green-800'
: 'bg-yellow-100 text-yellow-800'
}`}>
{row.status}
</span>
</span>,
<div
key="arrow"
className={`cursor-pointer flex items-center justify-end pr-4 ${
isSelected ? 'text-gray-900' : 'text-gray-400'
}`}
onClick={() => handleMonthSelect(row)}
>
<KeyboardArrowRightIcon />
</div>
];
})}
columnWidths={['180px', '190px', '130px', '130px', '200px', '120px', '80px']}
getRowClass={(index) => {
const isSelected = selectedMonth?.month === filteredMonths[index]?.month;
return isSelected ? 'bg-[#F5F0E1]' : 'hover:bg-gray-50';
}}
pagination={{
currentPage: pagination.currentPage,
onPageChange: (page) => setPagination(prev => ({ ...prev, currentPage: page })),
pageSize: pagination.pageSize,
totalItems: filteredMonths.length,
pageSizeOptions: [10, 20, 50, 100],
onPageSizeChange: (size) => {
setPagination(prev => ({
...prev,
pageSize: size,
currentPage: 1
}));
}
}}
className="w-full"
/>
</div>
</div>
</div> </div>
</Card>
<p className="text-xs text-gray-500 mt-2"> </div>
Note: Indices shown here are headline manufacturing totals. Use the drill-down actions to move from manufacturing total to ISIC 2-digit, then to ISIC 3 & 4-digit groups.
</p>
</div>
</Card>
</div> </div>
</div> </div>
</div> </div>