Files
frontend/apps/fo/app/pages/dashboard/page.tsx
T
2025-07-15 20:54:38 +09:00

307 lines
11 KiB
TypeScript

"use client"
import { useFoStore } from "@/app/store";
import { ContentsDiv, downloadExcel, downloadMultipleExcel, filePickedBlobArray, MainDiv, SelectBox, UITab, type SelectDataType } from "@common/business_components";
import { Button, TabsContent } from "@common/components/ui";
import { useEffect, useState } from "react";
import DashBoardFclty from "../dashboardMain/page";
/* import DrainageEquipment from "./drainageEquipment/page"; */
import ProcessFacilitySearch from "./facilityInfo/page";
import DashBoardChart from "./maingraph/page";
import RainManageTab from "./rainManage/page";
import DrainageEquipment from "./drainageEquipment/page";
const tabList = [
{ value: 'facilityInfo', label: '시설정보 및 운영현황' },
{ value: 'mainGraph', label: '주요그래프' },
{ value: 'rainManage', label: '강우일 운영현황' },
{ value: 'drainageEquipment', label: '배수설비 정비현황' }
];
export default function DashBoard() {
// 입력값 상태 관리
const { Common, initializeCommonData,searchMainGraph, searchRainManage, searchOperationStatus, OperationStatus, RainManage, MainGraph, PlanFclty } = useFoStore((state) => state);
const [activeTab, setActiveTab] = useState<string>('facilityInfo');
const [filteredSigunData, setFilteredSigunData] = useState<SelectDataType[]>([{ text: "전체", val: '0' }]);
const [sidoValue, setSidoValue] = useState<string>('0');
const [sigunValue, setSigunValue] = useState<string>('0');
const {
isInit,
selectSearchYear,
btnClass,
selectSidoData,
selectSigunData,
} = Common;
const {
planFcltyList
} = PlanFclty;
const {
operationalStatusList
} = OperationStatus;
const {
excelList
} = RainManage;
const {
chart_inflowRain,
chart_bodRain,
chart_inflTp,
chart_chongInTp,
chart_efflTp,
chart_inflTn,
chart_efflTn,
} = MainGraph;
/******************선택된 데이터******************/
// 연도
const [searchYear, setSearchYear] = useState<string>(selectSearchYear[0]?.val || '0');
// 시설명
const [fcltyList, setFcltyList] = useState<{bscFctCd: string, bscFctNm: string, fcltyCpc: number | '', trtFrfNm: string | '', efflWqltStndrdNm: string | ''}>({
bscFctCd: '',
bscFctNm: '',
fcltyCpc: '',
trtFrfNm: '',
efflWqltStndrdNm: ''
});
useEffect(() => {
if (!isInit) {
initializeCommonData();
setFcltyList({
bscFctCd: '11000SW001R',
bscFctNm: '난지',
fcltyCpc: 860000,
trtFrfNm: '표준활성슬러지법, MLE, A2O',
efflWqltStndrdNm: '500톤 이상(Ⅲ지역)'
});
}
}, [isInit, initializeCommonData]);
const handleFacilityChange = (facility: { bscFctCd: string, bscFctNm: string, fcltyCpc: number | '', trtFrfNm: string | '', efflWqltStndrdNm: string | '' }) => {
setFcltyList(facility);
};
const onSelectValue = (val: string, type: string) => {
switch(type) {
case 'sido':
setSidoValue(val);
initializeSigunData(val);
onSelectValue('0', 'sigun');
break;
case 'sigun':
setSigunValue(val);
break;
}
}
// 시군구 세팅
const initializeSigunData = (sidoCd: string) => {
if (sidoCd === '0') {
setSigunValue('0');
setFilteredSigunData([{ text: "전체", val: '0' }]);
} else {
const newFilteredData = [
{ text: "전체", val: '0' },
...selectSigunData.filter((item: SelectDataType) => item.val.substring(0, 2) === sidoCd)
];
setSigunValue('0');
setFilteredSigunData(newFilteredData);
}
}
const excelDownload = () => {
if (!fcltyList.bscFctCd) return;
switch (activeTab) {
case 'facilityInfo':
searchOperationStatus(false, searchYear, fcltyList.bscFctCd);
fetch(`/mainDashBoard.xlsx`, {}).then(async (response) => {
if (!response.ok) {
throw Error(response.statusText);
}
// 받은 파일 바인너리 처리
const blobRet = await response.arrayBuffer();
const excelData1 = planFcltyList.map((item) => {
return [
item.item,
item.provisionalApproval,
item.now,
item.stepOne,
item.stepTwo,
item.stepThree,
item.stepFour,
];
});
// operationalStatusList를 2차원 배열로 변환
const excelData2 = operationalStatusList.map((item, index) => {
const baseData = [
item.yearAvg,
item.yearMax,
item.yearMin,
item.summerAll,
item.summerThree,
item.summerRain,
item.winterAll,
item.winterSunnyAvg,
item.winterSunnyMax,
item.winterSunnyMin,
item.groundWaterAvg,
item.groundWaterSummer,
item.groundWaterWinter
];
// index가 0일 때만 rdi와 rdiRain 추가
return index === 0 ? [...baseData, item.rdi, item.rdiRain] : baseData;
});
const sheetParameter = [
{
data: excelData1,
headerNum: 3,
headerRange: 'A1:G2',
sheetName: '운영실태',
startCol: 'A'
},
{
data: excelData2,
headerNum: 4,
headerRange: 'A1:Q3',
sheetName: '운영실태',
startCol: 'C'
}
];
// 버퍼에 있는 엑셀데이터 전달 후 json으로 변환
await filePickedBlobArray(blobRet, '시설정보및운영현황황', sheetParameter);
});
break;
case 'mainGraph':
searchMainGraph(searchYear, fcltyList.bscFctCd);
const sheetParameter = [
{
data: [['날짜', '강수량'],...chart_inflowRain.map((item) => [item.label, item.barVal0])],
headerNum: 1,
headerRange: 'A1:B1',
sheetName: '강수량'
},
{
data: [['날짜', '유입하수량'],...chart_inflowRain.map((item) => [item.label, item.lineVal1])],
headerNum: 1,
headerRange: 'A1:B1',
sheetName: '유입하수량'
},
{
data: [['날짜', '시설용량'],...chart_inflowRain.map((item) => [item.label, item.lineVal0])],
headerNum: 1,
headerRange: 'A1:B1',
sheetName: '시설용량'
},
{
data: [['날짜', '유입BOD'],...chart_bodRain.map((item) => [item.label, item.lineVal0])],
headerNum: 1,
headerRange: 'A1:B1',
sheetName: '유입BOD'
},
{
data: [['날짜', '유입T-P'],...chart_inflTp.map((item) => [item.label, item.val])],
headerNum: 1,
headerRange: 'A1:B1',
sheetName: '유입T-P'
},
{
data: [['날짜', '총인유입T-P'],...chart_chongInTp.map((item) => [item.label, item.val])],
headerNum: 1,
headerRange: 'A1:B1',
sheetName: '총인유입T-P'
},
{
data: [['날짜', '방류T-P'],...chart_efflTp.map((item) => [item.label, item.val])],
headerNum: 1,
headerRange: 'A1:B1',
sheetName: '방류T-P'
},
{
data: [['날짜', '유입T-N'],...chart_inflTn.map((item) => [item.label, item.val])],
headerNum: 1,
headerRange: 'A1:B1',
sheetName: '유입T-N'
},
{
data: [['날짜', '방류T-N'],...chart_efflTn.map((item) => [item.label, item.val])],
headerNum: 1,
headerRange: 'A1:B1',
sheetName: '방류T-N'
}
];
downloadMultipleExcel('주요그래프', sheetParameter);
break;
case 'rainManage':
searchRainManage(searchYear, fcltyList.bscFctCd, true);
if (excelList && excelList.length > 0) {
downloadExcel({ data: excelList as unknown as JSON[], fileName: '강우일 운영현황', sheetNameArray: ['강우일 운영현황'], headerRange: 'A1:T1' });
}
break;
// Add other cases for different tabs as needed
default:
break;
}
}
const initVal = () => {
setFcltyList({
bscFctCd: '11000SW001R',
bscFctNm: '난지',
fcltyCpc: 860000,
trtFrfNm: '표준활성슬러지법, MLE, A2O',
efflWqltStndrdNm: '500톤 이상(Ⅲ지역)'
});
}
return (
<MainDiv>
<ContentsDiv>
<div className="flex card card-border !mt-6">
<div className="grid grid-cols-12 gap-5 w-8/12 m-4">
<SelectBox className="col-span-2" label={'연도'} selectArray={selectSearchYear} onSelectValue={ (val) => setSearchYear(val.toString()) } value={searchYear} selectClass={undefined}/>
<SelectBox className="col-span-3" label={'시도'} selectArray={selectSidoData} onSelectValue={(val) => onSelectValue(val.toString(), 'sido')} value={sidoValue} selectClass={undefined}/>
<SelectBox className="col-span-3" label={'시군구'} selectArray={filteredSigunData} onSelectValue={(val) => onSelectValue(val.toString(), 'sigun')} value={sigunValue} selectClass={undefined}/>
<div className="col-span-4 flex">
<DashBoardFclty onFacilityChange={handleFacilityChange} sidoSigun={sigunValue!='0'?sigunValue:sidoValue!='0'?sidoValue:''} facilityNm={fcltyList.bscFctNm}></DashBoardFclty>
</div>
</div>
<div className="w-4/12 mx-5 flex items-center justify-end space-x-2">
<Button className={btnClass} onClick={initVal}>
초기화
</Button>
<Button className={btnClass} color="green" onClick={excelDownload}>
엑셀다운로드
</Button>
</div>
</div>
<UITab tabList={tabList} defaultValue="facilityInfo" onValueChange={setActiveTab}>
<TabsContent value="facilityInfo">
<ProcessFacilitySearch searchYear={searchYear} fcltyList={[fcltyList]} onFacilityChange={handleFacilityChange} />
</TabsContent>
<TabsContent value="mainGraph">
<DashBoardChart searchYear={searchYear} fcltyList={[fcltyList]}/>
</TabsContent>
<TabsContent value="rainManage">
<RainManageTab searchYear={searchYear} fcltyList={[fcltyList]}/>
</TabsContent>
<TabsContent value="drainageEquipment">
<DrainageEquipment searchYear={searchYear} fcltyList={[fcltyList]}/>
</TabsContent>
</UITab>
</ContentsDiv>
</MainDiv>
);
}