Files
frontend/apps/wr/app/stats/rainStat/page.tsx
T

288 lines
10 KiB
TypeScript

"use client";
import { useWrStore } from "@/app/store";
import type { RainStatProps } from "@/app/store/stats/rainStat";
import { excelDownload, tableData, yearOptions } from "@/components/lib/func/commom";
import { downloadExcel } from "@common/business_components";
import {
ContentsDiv,
ScrollArea,
SearchDiv,
SelectBox,
Title,
UiTable
} from "@common/business_components/ui";
import ListDiv from "@common/business_components/ui/list_div";
import { Button } from "@common/components/ui";
import { useEffect, useState } from "react";
import toast from "react-hot-toast";
import { useTranslation } from "react-i18next";
export default function RainStat() {
const { code, rainStat, searchRainStatList } = useWrStore((state) => state);
const { rainStatList, rainDetailList } = rainStat;
const [searchYear, setSearchYear] = useState<string>(
String(new Date().getFullYear())
);
//const [exData, setExData] = useState<Array<Record<string, string | number>>>();
const [bdstTable, setBdstTable] = useState<RainStatProps[]>();
const { t } = useTranslation('base');
useEffect(() => {
searchRainStatList(searchYear ,code.cityCd);
}, [code.cityCd, searchRainStatList])
const excelDetailDownLoad=() =>{
const sidoList = new Set<string>();
const sidoDataMap = new Map<string, (string | number)[]>(); // 시도별 데이터를 저장할 Map
const sidoDetailMap = new Map<string, (string | number)[][]>(); // 시도별 상세 데이터를 저장할 Map
// 복사된 배열을 사용
const transformedRainDetailList = rainDetailList.map((originItem: RainStatProps[]) => {
const item = [...originItem]; // 원본 복사
const sidoVal = item.at(0)?.toString().trim();
const sigunVal = item.at(1)?.toString().trim();
const sido = code.cityCd.find((city) => city.val === sidoVal);
const sigun = code.sggCd.find((sgg) => sgg.val === sigunVal);
const sidoText = sido?.text || '';
const sigunText = sigun?.text || '';
if (item.length > 0) item[0] = sidoText as unknown as RainStatProps;
if (item.length > 1) item[1] = sigunText as unknown as RainStatProps;
if (item.length > 2) item[2] = `${sidoText} ${sigunText}` as unknown as RainStatProps;
// 시도별 상세 데이터 수집
if (sidoText) {
if (!sidoDetailMap.has(sidoText)) {
sidoDetailMap.set(sidoText, []);
}
sidoDetailMap.get(sidoText)!.push(item as unknown as (string | number)[]);
}
// 시도별 합계 데이터 수집
if (sidoText && !sidoList.has(sidoText)) {
sidoList.add(sidoText);
const initialData: (string | number)[] = [sidoText];
for (let i = 1; i < item.length; i++) {
initialData.push(0);
}
sidoDataMap.set(sidoText, initialData);
}
return item;
});
// 이후 합계 계산에서도 transformedRainDetailList 사용
transformedRainDetailList.forEach((item: RainStatProps[]) => {
const sidoText = item[0]?.toString();
if (sidoText && sidoDataMap.has(sidoText)) {
const sidoData = sidoDataMap.get(sidoText)!;
for (let i = 1; i < item.length && i < sidoData.length; i++) {
if(i == 2){
sidoData[i] = '합계';
} else if (i == 3) {
const sidoDataCount = transformedRainDetailList.filter((data: unknown[]) =>
Array.isArray(data) && data.length > 0 && String(data[0]) === sidoText
).length;
sidoData[i] = sidoDataCount;
} else if( i == 4){
sidoData[i] = '';
} else if (i == 6) {
const bumCnt = transformedRainDetailList.filter((data: unknown[]) =>
Array.isArray(data) && data.length > 6 && String(data[0]) === sidoText
).filter((data: unknown[]) => String(data[6]) === '대상').length;
sidoData[i] = bumCnt;
} else if ([8, 11, 12, 14].includes(i)) {
const value = Number(item[i]) || 0;
sidoData[i] = (Number(sidoData[i]) + value) as string | number;
} else {
sidoData[i] = '';
}
}
}
});
// 시도별로 합계와 상세 데이터를 순서대로 배치
const summaryExcelData: (string | number)[][] = [];
// 시도별로 순회하면서 합계와 상세 데이터를 추가
Array.from(sidoList).forEach((sidoText) => {
const sidoSummary = sidoDataMap.get(sidoText);
const sidoDetails = sidoDetailMap.get(sidoText) || [];
if (sidoSummary) {
// 시도 합계 데이터 추가
summaryExcelData.push(sidoSummary);
// 해당 시도의 상세 데이터들 추가
sidoDetails.forEach(detail => {
summaryExcelData.push(detail);
});
}
});
const sidoSummaryList = Array.from(sidoDataMap.values());
const sidoTotalSummary = [['전국', '', '합계', 0, '', '', 0, '', '', 0, 0, 0, 0, 0, 0, '']];
sidoSummaryList.forEach((item: (string | number)[]) => {
item.forEach((value: string | number, index: number) => {
if(typeof value === 'number') {
if (sidoTotalSummary[0] && index < sidoTotalSummary[0].length) {
sidoTotalSummary[0][index] = Number(sidoTotalSummary[0][index]) + Number(value);
}
}
});
});
// sidoSummaryList와 rainDetailList를 합쳐서 엑셀 데이터 생성
const excelData = [...sidoTotalSummary, ...sidoSummaryList, ...summaryExcelData];
console.log(' excelData ', excelData)
excelDownload(
excelData as unknown as [][],
"빗물이용시설 통계",
"sample_rainStats.xlsx",
"A1:O3",
4
);
}
const excelClick = () => {
if(bdstTable){
const tbData = bdstTable .map((item: RainStatProps) => {
return {
"시도": item.cityText,
"개소": item.tmp,
"설치비(백만원)": item.instlCst,
"저류조용량(m³/년)": item.dtfcltCpct,
"연간사용량(m³/년)": item.wtrUsqty,
"조경용수": item.lndwtr,
"청소화장실용수": item.cleanToilUswtr,
"세척살수용수": item.resn,
"농업용수": item.farmUswtr,
"공업용수": item.indstUswtr,
"친수용수": item.hydUswtr,
"기타": item.ithlEtcUswtr,
"연간운영비(백만원/년)": item.anlOperCst,
};
})
downloadExcel({
data: (tbData as unknown as JSON[]) ?? [],
fileName: "재이용통계",
sheetNameArray: ["Sheet1"],
headerRange: "A1:M1"
})
}else{
toast.error(t('excelDownErrMsg'));
}
}
// useEffect(() => {
// if(excelList && excelList.length>0){
// console.log('excelList ', excelList)
// const transExcel = Array.isArray(excelList) ? excelList : [excelList];
// const excelData = tableData(transExcel, { code });
// const tbData = Array.isArray(excelData)
// ? excelData
// .filter((item: RainStatProps) => item.cityText !== '없음')
// .map((item: RainStatProps) => {
// return {
// "시도": item.cityText,
// "개소": item.tmp,
// "설치비(백만원)": item.instlCst,
// "저류조용량(m³/년)": item.dtfcltCpct,
// "연간사용량(m³/년)": item.wtrUsqty,
// "조경용수": item.lndwtr,
// "청소화장실용수": item.cleanToilUswtr,
// "세척살수용수": item.resn,
// "농업용수": item.farmUswtr,
// "공업용수": item.indstUswtr,
// "친수용수": item.hydUswtr,
// "기타": item.ithlEtcUswtr,
// "연간운영비(백만원/년)": item.anlOperCst,
// };
// })
// : [];
// setExData(tbData);
// }
// }, [code, excelList])
useEffect(() => {
const transData = Array.isArray(rainStatList) ? rainStatList : [rainStatList];
const data = tableData(transData, { code });
const tbData = Array.isArray(data)
? data.map((item: RainStatProps) => {
return {
...item,
ctpvCd: item.cityText==='전체'?'전국': item.cityText,
cityText: item.cityText==='전체'?'전국': item.cityText,
};
}).filter((item: RainStatProps) => item.cityText !== '없음')
: [];
setBdstTable(tbData);
}, [code, rainStatList]);
return (
<>
<ContentsDiv>
<div className="grid grid-cols-4 gap-4">
<div className="col-span-4">
<Title title={"빗물이용시설 통계"} />
<SearchDiv>
<div className="grid grid-cols-12 gap-5 m-4">
<div className="col-span-2">
<SelectBox
className="w-full h-full text-2xl"
label="통계년도"
selectClass={""}
selectArray={yearOptions}
value={searchYear}
onSelectValue={(val: string | number) =>
setSearchYear(String(val))
}
/>
</div>
<div className="flex flex-row space-x-2 items-center">
<Button className="" size={"sm"} onClick={() => { searchRainStatList(searchYear ,code.cityCd); }}>
조회
</Button>
<Button className="" size={"sm"} onClick={() => excelClick()}>
다운로드
</Button>
<Button className="" size={"sm"} onClick={() => excelDetailDownLoad()}>
전체통계다운로드
</Button>
</div>
</div>
</SearchDiv>
{/* <SearchFormRight>
</SearchFormRight> */}
</div>
</div>
<div className="grid grid-cols-6 gap-4">
<div className="col-span-6 h-full min-h-0">
<ListDiv className="">
<ScrollArea className="h-[67vh]">
<UiTable
tableData={bdstTable || []}
headlist={rainStat.rainStatHeadList}
pageSize={10}
total={0}
headName={""}
cellClick={undefined}
onPageChange={() => {}}
></UiTable>
</ScrollArea>
</ListDiv>
</div>
</div>
</ContentsDiv>
</>
);
}