Files
frontend/apps/st/src/components/statistics/total/SewageExcretaSlurgeTot.tsx
T

212 lines
9.7 KiB
TypeScript

import { useRef, useState } from "react";
import { useAlertStore } from "@/shared/lib";
import { LoadingWithAnimation, Tabs, TabsContent, TabsList, TabsTrigger } from "@common/components";
import SearchForm from "@/components/module/SearchForm";
import DataGrid from "@/components/module/DataGrid";
import type { SearchFormValues } from "@/components/type/SearchForm.type";
import { toGridRows } from "@/components/util/grid/gridFunction";
// 4개 탭 컬럼 설정 임포트
import { SludgeProcessingTab1ColumnConfig } from "@/components/util/column/total/sludgeProcessing/SludgeProcessingTab1Column";
import { DigestionTankTab2ColumnConfig } from "@/components/util/column/total/sludgeProcessing/DigestionTankTab2Column";
import { SewageSludgeOperTab3ColumnConfig } from "@/components/util/column/total/sludgeProcessing/SewageSludgeOperTab3Column";
import { HumanWasteSludgeTab4ColumnConfig } from "@/components/util/column/total/sludgeProcessing/HumanWasteSludgeTab4Column";
// API 인스턴스 및 인터페이스 임포트 (타입 안전성 확보)
import { statisticsTotalStatusApi } from "@/entities/statistics/api/statistics-total-service";
import type {
SludgeSummaryRequest,
SludgeProcessingTab1Response,
DigestionTankOperTab2Response,
SewageSludgeOperTab3Response,
HumanWasteSludgeTab4Response
} from "@/entities/statistics/model/statistics-total/statistics-sludge-total-interface";
import CellColorInfo from "./CellColorInfo";
// 탭 순회 설정
const SludgeColumnListTot = [
SludgeProcessingTab1ColumnConfig,
DigestionTankTab2ColumnConfig,
SewageSludgeOperTab3ColumnConfig,
HumanWasteSludgeTab4ColumnConfig
].filter(Boolean);
const firstTabKey = SludgeColumnListTot[0]?.tabKey ?? "ST00110001";
const SewageExcretaSlurgeTot = () => {
const { setMessage: alert } = useAlertStore((state) => state);
const [rowDataMap, setRowDataMap] = useState<Map<string, Record<string, string | number>[]>>(new Map());
const [isLoading, setIsLoading] = useState(false);
const [activeTab, setActiveTab] = useState(firstTabKey);
const lastSearchedValuesRef = useRef<SearchFormValues | null>(null);
const columnInfo = SludgeColumnListTot;
const handleReset = () => {
setRowDataMap(new Map());
setActiveTab(firstTabKey);
lastSearchedValuesRef.current = null;
};
const loadGridData = async (tabKey: string, values: SearchFormValues): Promise<boolean> => {
setIsLoading(true);
const param: SludgeSummaryRequest = {
ctpvNm: values.sido === "ALL" ? "" : values.sido,
sggNm: values.gugun === "ALL" ? "" : values.gugun,
sewerBzmnCd: values.bsnmCd,
statsYr: values.year,
rowDataYn: values.isDetail,
summaryYn: values.isSummary !== 'ALL',
summaryYearCnt: values.isSummary !== 'ALL' ? Number(values.isSummary) : undefined
};
try {
// [TAB1] 하수및분뇨찌꺼기 > 찌꺼기처리시설현황
if (tabKey === "ST00110001") {
const response: SludgeProcessingTab1Response = await statisticsTotalStatusApi.getSludgeProcessingTab1List(param);
if (response?.status === "success") {
setRowDataMap((prev) => new Map(prev).set(tabKey, toGridRows(response.data as object[])));
return true;
}
}
// [TAB2] 소화조 운영현황
else if (tabKey === "ST00110002") {
const response: DigestionTankOperTab2Response = await statisticsTotalStatusApi.getSludgeTab2List(param);
if (response?.status === "success") {
setRowDataMap((prev) => new Map(prev).set(tabKey, toGridRows(response.data as object[])));
return true;
}
}
// [TAB3] 하수찌꺼기 처리시설 운영현황
else if (tabKey === "ST00110003") {
const response: SewageSludgeOperTab3Response = await statisticsTotalStatusApi.getSludgeTab3List(param);
if (response?.status === "success") {
setRowDataMap((prev) => new Map(prev).set(tabKey, toGridRows(response.data as object[])));
return true;
}
}
// [TAB4] 분뇨찌꺼기 처리현황
else if (tabKey === "ST00110004") {
const response: HumanWasteSludgeTab4Response = await statisticsTotalStatusApi.getSludgeTab4List(param);
if (response?.status === "success") {
setRowDataMap((prev) => new Map(prev).set(tabKey, toGridRows(response.data as object[])));
return true;
}
}
alert("데이터 조회 중 오류가 발생했습니다.");
return false;
} catch {
alert("서버 연결 중 오류가 발생했습니다.");
return false;
} finally {
setIsLoading(false);
}
};
/** 검색 버튼 이벤트: SearchForm의 Values 타입을 기반으로 호출 */
const handleSearch = async (values: SearchFormValues) => {
try {
setIsLoading(true);
// 탭 변경 없이 현재 탭(activeTab)에 대해서만 명시적 타입 호출
const ok = await loadGridData(activeTab, values);
if (ok) {
lastSearchedValuesRef.current = values;
} else {
lastSearchedValuesRef.current = null;
}
} catch {
alert("데이터 조회 중 오류가 발생했습니다.");
lastSearchedValuesRef.current = null;
} finally {
setIsLoading(false);
}
};
/** 탭 변경 이벤트: 현재 탭 상태와 SearchForm의 마지막 값을 동기화 */
const handleActiveTabChange = async (tabKey: string) => {
setActiveTab(tabKey);
// 마지막 검색 값이 있으면 해당 탭만 새로 로드
const values = lastSearchedValuesRef.current;
if (!values || rowDataMap.has(tabKey)) return;
try {
setIsLoading(true);
await loadGridData(tabKey, values);
} catch {
alert("데이터 조회 중 오류가 발생했습니다.");
} finally {
setIsLoading(false);
}
};
return (
<div className="relative">
<SearchForm isDetail={true} isSummary={true} onSubmit={handleSearch} onReset={handleReset} sidoSelectText="전체" defaultRegionAll />
{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>
)}
<CellColorInfo />
<div className="flex flex-col gap-[0.6rem] mx-[0.6rem]">
<Tabs value={activeTab} onValueChange={(v) => void handleActiveTabChange(v)} className="w-full" variant="default">
<TabsList>
{columnInfo.map((info) => (
<TabsTrigger key={info.tabKey} value={info.tabKey}>{info.tabLabel}</TabsTrigger>
))}
</TabsList>
{columnInfo.map((info) => {
const tabKey = info.tabKey;
const rowData = rowDataMap.get(tabKey) ?? [];
return (
<TabsContent key={tabKey} value={tabKey}>
<DataGrid
columnInfo={{ columns: info.columns, columnLayout: info.columnLayout }}
rowStartNum={info.rowStartNum}
rowData={rowData}
excelExportFileSuffix={info.tabLabel}
setRowData={(updater) => {
setRowDataMap((prev) => {
const next = new Map(prev);
const cur = next.get(tabKey) ?? [];
const n = typeof updater === "function" ? updater(cur) : updater;
next.set(tabKey, n);
return next;
});
}}
isExcelImport={false}
isExcelExport={true}
excelExportApplyStyles
showHeaderCheckbox={false}
fixedColumns={info.fixedColumns}
enableSort={false}
cellStyleCallback={({ row }) => {
const rowType = String(row.rowType ?? '');
if (rowType === 'DETAIL') return undefined;
if (rowType === 'BIZ_SUM') return 'rg-cell-rowtype-biz-sum';
if (rowType === 'SUMMARY') return 'rg-cell-rowtype-summary';
return 'rg-cell-rowtype-other';
}}
/>
</TabsContent>
);
})}
</Tabs>
</div>
</div>
);
};
export default SewageExcretaSlurgeTot;