Files
frontend/apps/wr/app/(with-layout)/ruseFcltMng/fcltSttn/rainSttn/page.tsx
T
2026-09-22 10:05:49 +09:00

415 lines
13 KiB
TypeScript

/**
* 재이용시설 현황 > 빗물이용시설 현황
*/
"use client";
import { useWrStore } from "@/app/store";
import { btnClass } from "@/app/store/common/code";
import type {
customRainBdst,
} from "@/app/store/ruseFcltMng/fcltReg/rainReg";
import { rainSttnSize } from "@/app/store/ruseFcltMng/fcltSttn/rainSttn";
import {
excelDownloadNumStyle,
getCity,
tableData,
} from "@/components/lib/func/common";
import { type SheetParameter } from "@common/business_components/ui";
import type { objSearchType } from "@/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal";
import HeadChModal from "@/components/ui/modal/sttn/headChModal/headChModal";
import SttnDetailModal from "@/components/ui/modal/sttn/sttnDetailModal/sttnDetailModal";
import {
ContentsDiv,
SearchDiv,
SearchFormLeft,
SearchFormRight,
SearchInput,
SelectBox,
UiModal,
UiTable,
type SelectDataType,
} from "@common/business_components/ui";
import ListDiv from "@common/business_components/ui/list_div";
import { HeadMakeColSpan } from "@common/business_components/ui/table/HeadMakeColSpan";
import { Button, Label } from "@common/components/ui";
import { useCallback, useEffect, useRef, useState } from "react";
import { useAlertStore } from "@/src/shared/lib";
import { useTranslation } from "react-i18next";
const defaultSigunOptions: SelectDataType[] = [{ text: "전체", val: "0" }];
export default function RainSttn() {
const { auth, code, rainSttn, rainSttnSearch, getLaeSeCdCheck } = useWrStore(
(state) => state
);
const { rainSttnRes, rainSttnResCount } = rainSttn;
const { laeSeCd } = auth;
const [pageNation, setPageNation] = useState(1);
const [isChHead, setIsChHead] = useState(false);
const [bdstTable, setBdstTable] = useState<customRainBdst[]>();
const [selectSgg, setSelectSgg] = useState<SelectDataType[] | []>(defaultSigunOptions);
const [resetPage, isResetPage] = useState(false);
const [objSearch, setObjSearch] = useState<objSearchType>({
sgg: "",
city: "",
});
const [detailModal, setDetailModal] = useState<{
isOpen: boolean;
data: customRainBdst | undefined;
}>({ data: undefined, isOpen: false });
const [fcltNm, setFcltNm] = useState<string>("");
const [appliedSearch, setAppliedSearch] = useState<{
conValue: string;
fcltNm: string;
} | null>(null);
const { t } = useTranslation("base");
const alert = useAlertStore((state) => state.setMessage);
const initializedSearchKeyRef = useRef<string | null>(null);
const isAllAreaUser = laeSeCd === "0";
const isLaeSeCdReady =
laeSeCd === "0" || laeSeCd.length === 2 || laeSeCd.length === 5;
const fixedSidoValue =
!isAllAreaUser && laeSeCd?.length ? laeSeCd.slice(0, 2) : "";
const fixedSigunValue =
!isAllAreaUser && laeSeCd?.length === 5 ? laeSeCd : "0";
const cellClick = (index: number | undefined) => {
if (index !== undefined && bdstTable !== undefined) {
setDetailModal({ isOpen: true, data: bdstTable[index] });
}
};
const getSelCity = (val: string | number) => {
const cityValue = String(val);
if (cityValue === "0") {
setSelectSgg(defaultSigunOptions);
setObjSearch({ city: cityValue, sgg: "" });
} else {
const data = getCity(cityValue, code.sggCd);
setSelectSgg(data.length > 0 ? data : defaultSigunOptions);
const firstSggVal =
data.find((item) => item.val === "0")?.val || (data[0]?.val ?? "");
setObjSearch({ city: cityValue, sgg: firstSggVal });
}
};
const getSgg = (val: string | number) => {
setObjSearch({ ...objSearch, sgg: String(val) });
};
const getConValue = useCallback((): string => {
if (!objSearch.city || objSearch.city === "0") {
return "0";
}
if (objSearch.sgg && objSearch.sgg !== "0") {
return String(objSearch.sgg);
}
return String(objSearch.city);
}, [objSearch]);
const getFixedConValue = useCallback((): string => {
if (laeSeCd === undefined || laeSeCd === "0") {
return getConValue();
}
return laeSeCd.length === 5 ? laeSeCd : laeSeCd.slice(0, 2);
}, [laeSeCd, getConValue]);
useEffect(() => {
getLaeSeCdCheck();
}, [getLaeSeCdCheck]);
useEffect(() => {
if (!isLaeSeCdReady) {
return;
}
if (laeSeCd === "0") {
setSelectSgg(defaultSigunOptions);
setObjSearch({ city: "0", sgg: "" });
return;
}
const cityValue = laeSeCd.slice(0, 2);
const sigunOptions = getCity(cityValue, code.sggCd);
setSelectSgg(sigunOptions.length > 0 ? sigunOptions : defaultSigunOptions);
setObjSearch({
city: cityValue,
sgg: laeSeCd.length === 5 ? laeSeCd : "0",
});
}, [laeSeCd, code.sggCd, isLaeSeCdReady]);
useEffect(() => {
if (!isLaeSeCdReady) {
return;
}
const initialSearchKey = laeSeCd ?? "0";
if (initializedSearchKeyRef.current === initialSearchKey) {
return;
}
const initialConValue =
laeSeCd === undefined || laeSeCd === "0"
? "0"
: laeSeCd.length === 5
? laeSeCd
: laeSeCd.slice(0, 2);
initializedSearchKeyRef.current = initialSearchKey;
setAppliedSearch({
conValue: initialConValue,
fcltNm: "",
});
setPageNation(1);
}, [isLaeSeCdReady, laeSeCd]);
useEffect(() => {
if (!isLaeSeCdReady || !appliedSearch) {
return;
}
rainSttnSearch(
false,
pageNation,
rainSttnSize,
appliedSearch.conValue,
appliedSearch.fcltNm,
code as unknown as Record<string, SelectDataType[]>
);
isResetPage(false);
}, [
pageNation,
rainSttnSearch,
code,
appliedSearch,
isLaeSeCdReady,
]);
const excelClick = useCallback(async () => {
if (!isLaeSeCdReady || !appliedSearch) {
return;
}
const excelData = await rainSttnSearch(
true,
1,
rainSttnResCount,
appliedSearch.conValue,
appliedSearch.fcltNm,
code as unknown as Record<string, SelectDataType[]>
);
if (excelData && excelData.length > 0) {
const sheetArray: SheetParameter[] = [{
data: excelData,
headerNum: 3,
headerRange: "A1:W3",
sheetName: "sheet1",
startCol: "A",
decimalArray0: [6, 10, 12, 14, 16],
}];
excelDownloadNumStyle(
"빗물이용시설 현황",
"sample_rainSttn.xlsx",
sheetArray
);
return;
}
alert(t("excelDownErrMsg"));
}, [
rainSttnSearch,
rainSttnResCount,
code,
appliedSearch,
isLaeSeCdReady,
alert,
t,
]);
const handleSearch = useCallback(() => {
if (!isLaeSeCdReady) {
return;
}
const nextSearch = {
conValue: String(getFixedConValue()),
fcltNm,
};
setAppliedSearch(nextSearch);
isResetPage(true);
setPageNation(1);
}, [fcltNm, getFixedConValue, isLaeSeCdReady]);
useEffect(() => {
const data = tableData(rainSttnRes, { code });
const chData = data
.map((item: any) => {
return {
...item,
...item.reuseBuilding[0],
...item.reuseBuilding[0].rainyObj,
dtfcltCpct:
item.reuseBuilding[0].rainyObj.dtfcltCpct === -1
? "확인불가"
: item.reuseBuilding[0].rainyObj.dtfcltCpct,
instlCst:
item.reuseBuilding[0].instlCst === -1
? "확인불가"
: item.reuseBuilding[0].instlCst,
instlCstSprtAmt:
item.reuseBuilding[0].instlCstSprtAmt === -1
? "확인불가"
: item.reuseBuilding[0].instlCstSprtAmt,
chBuildingYn: item?.chgBuilding[0]?.bdstChgNm !== null && item?.chgBuilding.length > 0 ? 'Y' : 'N',
pntfColArea:
item.reuseBuilding[0].rainyObj.pntfColArea === -1
? "확인불가"
: item.reuseBuilding[0].rainyObj.pntfColArea,
filtrTrtmfctCpct:
item.reuseBuilding[0].rainyObj.filtrTrtmfctCpct === -1
? "확인불가"
: item.reuseBuilding[0].rainyObj.filtrTrtmfctCpct,
// archArea:
// Math.floor(item.archArea) === -1 ? "확인불가" : Math.trunc(item.archArea) ? item.archArea,
archArea: isNaN(Number(item.archArea))
? item.archArea
: Math.floor(Number(item.archArea)) === -1
? '확인불가'
: Math.trunc(Number(item.archArea)),
pntfColKnd: code.rainFcltCd.find((pnt) => pnt.val === item.reuseBuilding[0].rainyObj?.pntfColKnd)?.text ??''
};
})
.filter((item: any) => item.rainyObj !== undefined);
setBdstTable(chData);
}, [code, rainSttnRes]);
return (
<>
<ContentsDiv>
<div className="grid grid-cols-4 gap-4 ">
<div className="col-span-4">
<SearchDiv>
<SearchFormLeft>
<div className="col-span-10 flex flex-row">
<SelectBox
className="col-span-2 w-1/6 h-full text-2xl"
label="시도"
selectClass={""}
selectArray={
!isAllAreaUser && fixedSidoValue
? code.cityCd.filter((item) => item.val === fixedSidoValue)
: code.cityCd
}
value={objSearch.city || "0"}
onSelectValue={getSelCity}
/>
<SelectBox
className="col-span-2 w-1/6 h-full text-2xl ml-4"
label="시군구"
selectClass={""}
selectArray={
!isAllAreaUser && laeSeCd?.length === 5
? (selectSgg || defaultSigunOptions).filter((item) => item.val === "0" || item.val === fixedSigunValue)
: selectSgg || defaultSigunOptions
}
value={objSearch.sgg || "0"}
onSelectValue={getSgg}
/>
<div className="col-span-2 flex flex-row items-center space-x-2 ml-3">
<Label className="w-28 text-xl">
{"시설명"}
</Label>
<SearchInput
className={"w-full text-2xl"}
label={""}
textValue={fcltNm || ""}
setText={setFcltNm}
/>
</div>
</div>
</SearchFormLeft>
<SearchFormRight className="w-2/12 mx-5 flex flex-col items-center justify-center space-x-2 ">
<div className="flex flex-row space-x-2 items-center">
<Button
className={btnClass}
onClick={handleSearch}
>
조회
</Button>
<Button
className={`${btnClass} green`}
onClick={excelClick}
>
엑셀다운로드
</Button>
<Button
className={btnClass}
onClick={() => {
setIsChHead(true);
}}
>
조회항목설정
</Button>
</div>
</SearchFormRight>
</SearchDiv>
</div>
</div>
<div className="grid grid-cols-6 gap-4 ">
<div className="col-span-6">
<ListDiv>
<div className="h-full w-full overflow-x-scroll min-[1900px]:overflow-x-hidden">
<div className="w-[160%] min-[1900px]:w-[100%]">
<UiTable
headName={undefined}
tableData={bdstTable || []}
headlist={rainSttn.rainSttHeadList}
pageSize={rainSttnSize}
total={rainSttnResCount || 0}
cellClick={(index: number | undefined) => {
cellClick(index ?? undefined);
}}
onPageChange={setPageNation}
resetPageNo={resetPage}
widthFixed={true}
>
<HeadMakeColSpan upHeadList={rainSttn.rainSttNodeList} />
</UiTable>
</div>
</div>
</ListDiv>
</div>
</div>
<UiModal
open={isChHead}
onOpenChange={() => setIsChHead(!isChHead)}
bodyClass={""}
body={
<HeadChModal
type={"RNB"}
headList={rainSttn.rainSttHeadList.map((item) => ({
...item,
visible: item.visible ?? false,
}))}
onSaved={() => setIsChHead(false)}
/>
}
/>
<UiModal
open={detailModal.isOpen}
onOpenChange={() => {
setDetailModal({ ...detailModal, isOpen: !detailModal.isOpen });
}}
bodyClass={"w-[40vw] max-w-none max-h-[calc(100%-70px)] overflow-y-auto"}
body={<SttnDetailModal type={"RNB"} formObj={detailModal.data} />}
/>
</ContentsDiv>
</>
);
}