233 lines
8.3 KiB
TypeScript
233 lines
8.3 KiB
TypeScript
import { useEffect, useRef, useState } from "react";
|
|
|
|
import { useConfirm } from "@frontend-opensource/use-react-hooks";
|
|
import type { RealGridReact } from "realgrid-react";
|
|
|
|
import { useAlertStore, useGlobalProgressStore } from "@/shared/lib";
|
|
import { statisticsSewerageAggregateSummaryApi } from "@/entities/statistics/api/statistics-sewerage-aggregate-summary-service";
|
|
import type { SewerageAggregateSummaryDownloadRequest } from "@/entities/statistics/model/statistics-total/statistics-sewerage-aggregate-summary-interface";
|
|
import { LoadingWithAnimation } from "@common/components";
|
|
|
|
import DataGrid from "@/components/module/DataGrid";
|
|
import SearchForm from "@/components/module/SearchForm";
|
|
import type { SearchFormValues } from "@/components/type/SearchForm.type";
|
|
import { toGridRows } from "@/components/util/grid/gridFunction";
|
|
|
|
const columnInfo = {
|
|
columns: [
|
|
{
|
|
name: "statsCd",
|
|
fieldName: "statsCd",
|
|
header: { text: "통계항목코드" },
|
|
editable: false,
|
|
visible: false,
|
|
},
|
|
{ name: "tabNm", fieldName: "tabNm", header: { text: "통계항목" }, editable: false },
|
|
],
|
|
columnLayout: ["statsCd", "tabNm"],
|
|
};
|
|
|
|
type GridViewWithCheckApi = {
|
|
getCheckedRows?: (refresh?: boolean) => number[];
|
|
getCheckedItems?: (refresh?: boolean) => number[];
|
|
};
|
|
|
|
const EXCEL_DOWNLOAD_PROGRESS_MAX = 98;
|
|
const EXCEL_DOWNLOAD_PROGRESS_STEP = 1;
|
|
const EXCEL_DOWNLOAD_PROGRESS_INTERVAL_MS = 500;
|
|
const EXCEL_DOWNLOAD_PROGRESS_LABEL =
|
|
"엑셀 파일을 생성 중입니다.\n창을 닫거나 이동하지 말고 잠시만 기다려주세요.";
|
|
|
|
const SewerageAggregateSummary = () => {
|
|
const { confirm } = useConfirm();
|
|
const { setMessage: alert } = useAlertStore((state) => state);
|
|
const showGlobalProgress = useGlobalProgressStore((state) => state.show);
|
|
const hideGlobalProgress = useGlobalProgressStore((state) => state.hide);
|
|
const setGlobalProgress = useGlobalProgressStore((state) => state.setProgress);
|
|
const [rowData, setRowData] = useState<Record<string, string | number>[]>([]);
|
|
const [isLoading, setIsLoading] = useState(false);
|
|
|
|
const getGridDataRef = useRef<(() => Record<string, string | number>[]) | null>(null);
|
|
const getGridRef = useRef<RealGridReact | null>(null);
|
|
const downloadProgressTimerRef = useRef<ReturnType<typeof setInterval> | null>(null);
|
|
|
|
useEffect(() => {
|
|
let cancelled = false;
|
|
|
|
void (async () => {
|
|
try {
|
|
setIsLoading(true);
|
|
const response =
|
|
await statisticsSewerageAggregateSummaryApi.getAggregateSummaryTabList();
|
|
|
|
if (cancelled) return;
|
|
|
|
if (response.status === "success") {
|
|
setRowData(toGridRows(response.data ?? []));
|
|
} else {
|
|
alert(response.message || "데이터 조회 중 오류가 발생했습니다.");
|
|
}
|
|
} catch {
|
|
if (!cancelled) {
|
|
alert("데이터 조회 중 오류가 발생했습니다.");
|
|
}
|
|
} finally {
|
|
if (!cancelled) {
|
|
setIsLoading(false);
|
|
}
|
|
}
|
|
})();
|
|
|
|
return () => {
|
|
cancelled = true;
|
|
};
|
|
}, [alert]);
|
|
|
|
useEffect(() => {
|
|
return () => {
|
|
if (downloadProgressTimerRef.current !== null) {
|
|
clearInterval(downloadProgressTimerRef.current);
|
|
}
|
|
};
|
|
}, []);
|
|
|
|
const stopDownloadProgressTimer = () => {
|
|
if (downloadProgressTimerRef.current !== null) {
|
|
clearInterval(downloadProgressTimerRef.current);
|
|
downloadProgressTimerRef.current = null;
|
|
}
|
|
};
|
|
|
|
const startDownloadProgress = () => {
|
|
stopDownloadProgressTimer();
|
|
let currentProgress = 0;
|
|
|
|
showGlobalProgress({
|
|
label: EXCEL_DOWNLOAD_PROGRESS_LABEL,
|
|
progress: 0,
|
|
});
|
|
|
|
downloadProgressTimerRef.current = setInterval(() => {
|
|
currentProgress = Math.min(
|
|
currentProgress + EXCEL_DOWNLOAD_PROGRESS_STEP,
|
|
EXCEL_DOWNLOAD_PROGRESS_MAX,
|
|
);
|
|
setGlobalProgress(currentProgress);
|
|
|
|
if (currentProgress >= EXCEL_DOWNLOAD_PROGRESS_MAX) {
|
|
stopDownloadProgressTimer();
|
|
}
|
|
}, EXCEL_DOWNLOAD_PROGRESS_INTERVAL_MS);
|
|
};
|
|
|
|
const handleDownload = async (values: SearchFormValues) => {
|
|
const gridView = getGridRef.current?.gridView as GridViewWithCheckApi | undefined;
|
|
const checkedIndexes =
|
|
gridView?.getCheckedRows?.(false) ?? gridView?.getCheckedItems?.(false) ?? [];
|
|
|
|
const allRows = getGridDataRef.current?.() ?? rowData;
|
|
const statsCdList = checkedIndexes
|
|
.map((idx) => normalizeGridString(allRows[idx]?.statsCd))
|
|
.filter((cd) => cd.length > 0);
|
|
|
|
if (statsCdList.length === 0) {
|
|
alert("내려받을 통계항목을 선택해주세요.");
|
|
return;
|
|
}
|
|
|
|
if (!values.year) {
|
|
alert("통계연도를 선택해주세요.");
|
|
return;
|
|
}
|
|
|
|
if (
|
|
!(await confirm(
|
|
`선택한 데이터 및 사용자 네트워크 환경에 따라 최대 1시간까지 시간이 소요될 수 있습니다.
|
|
진행하시겠습니까?`
|
|
))
|
|
) {
|
|
return;
|
|
}
|
|
|
|
const summaryYearCnt =
|
|
values.isSummary && values.isSummary !== "ALL"
|
|
? Number(values.isSummary)
|
|
: 0;
|
|
|
|
const request: SewerageAggregateSummaryDownloadRequest = {
|
|
statsCdList,
|
|
statsYr: values.year,
|
|
ctpvNm: values.sido === "ALL" ? "" : (values.sido ?? ""),
|
|
sggNm: values.gugun === "ALL" ? "" : (values.gugun ?? ""),
|
|
sewerBzmnCd: values.bsnmCd ?? "",
|
|
summaryYearCnt: Number.isFinite(summaryYearCnt) ? summaryYearCnt : 0,
|
|
};
|
|
|
|
try {
|
|
startDownloadProgress();
|
|
const { blob, fileName } =
|
|
await statisticsSewerageAggregateSummaryApi.downloadAggregateSummary(request);
|
|
|
|
stopDownloadProgressTimer();
|
|
setGlobalProgress(100);
|
|
|
|
const url = window.URL.createObjectURL(blob);
|
|
const a = document.createElement("a");
|
|
a.href = url;
|
|
a.download = fileName || `하수도통계 집계 요약표_${values.year}.xlsx`;
|
|
document.body.appendChild(a);
|
|
a.click();
|
|
window.URL.revokeObjectURL(url);
|
|
document.body.removeChild(a);
|
|
|
|
await new Promise((resolve) => setTimeout(resolve, 400));
|
|
alert("저장되었습니다.");
|
|
} catch (error) {
|
|
const message =
|
|
error instanceof Error && error.message
|
|
? error.message
|
|
: "엑셀 내려받기 중 오류가 발생했습니다.";
|
|
alert(message);
|
|
} finally {
|
|
stopDownloadProgressTimer();
|
|
hideGlobalProgress();
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="relative">
|
|
<SearchForm
|
|
isSummary={true}
|
|
sidoSelectText="전체"
|
|
searchButtonText="내려받기"
|
|
onSubmit={(values) => void handleDownload(values)}
|
|
isReset={false}
|
|
/>
|
|
{isLoading && (
|
|
<div className="absolute inset-0 z-50 [data-theme='black']:bg-transparent rounded-lg p-10 flex justify-center items-center">
|
|
<div className="relative size-[5rem]">
|
|
<LoadingWithAnimation color="primary" />
|
|
</div>
|
|
</div>
|
|
)}
|
|
<DataGrid
|
|
columnInfo={columnInfo}
|
|
rowStartNum={1}
|
|
isExcelImport={false}
|
|
isExcelExport={false}
|
|
showHeaderCheckbox
|
|
rowData={rowData}
|
|
setRowData={setRowData}
|
|
getGridDataRef={getGridDataRef}
|
|
getGridRef={getGridRef}
|
|
/>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
function normalizeGridString(value: unknown): string {
|
|
if (value === null || value === undefined) return "";
|
|
return typeof value === "string" ? value : String(value);
|
|
}
|
|
|
|
export default SewerageAggregateSummary; |