From ac518ea5f307cabb58f05d973bd66c66d5a4e523 Mon Sep 17 00:00:00 2001 From: leedj312 Date: Tue, 13 May 2025 09:32:48 +0900 Subject: [PATCH] =?UTF-8?q?=EC=98=A8=EB=B0=B0=EC=88=98=20=ED=98=84?= =?UTF-8?q?=ED=99=A9=20=EB=B0=8F=20=EC=A4=91=EC=88=98=EB=8F=84=20=EC=A7=80?= =?UTF-8?q?=EB=8F=84=EC=A0=90=EA=B2=80=20=EC=9E=91=EC=97=85=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wr/app/page.tsx | 7 + .../ruseFcltMng/fcltSttn/htdflSttn/page.tsx | 76 +++++ .../ruseFcltMng/fcltSttn/rainSttn/page.tsx | 2 +- .../ruseFcltMng/fcltSttn/rswtSttn/page.tsx | 81 ++++- .../prfmncReg/chkgdnReg/TableHead.tsx | 2 +- .../ruseFcltMng/prfmncReg/chkgdnReg/page.tsx | 168 +++++++--- .../prfmncReg/chkgdnReg/rainChkgdn/excel.tsx | 98 ++++-- .../chkgdnReg/rainChkgdn/headList.tsx | 282 +++++++++++++++++ .../prfmncReg/chkgdnReg/rainChkgdn/index.tsx | 295 +----------------- .../prfmncReg/chkgdnReg/rswrtChkgdn/excel.tsx | 225 +++++++++++++ .../chkgdnReg/rswrtChkgdn/headList.tsx | 109 +++++++ .../prfmncReg/chkgdnReg/rswrtChkgdn/index.tsx | 170 ++++++---- apps/wr/components/lib/func/TableData.tsx | 112 ++++++- apps/wr/components/lib/func/commom.tsx | 26 +- .../ui/modal/chkgd/chkgdExcel/chkgdExcel.tsx | 263 +++++++++++----- .../ui/modal/chkgd/chkgdModal/chkgdModal.tsx | 2 +- .../sttn/sttnDetailModal/sttnDetailModal.tsx | 6 +- .../chkgdTable/rainChkgd/rainChkgdTable.tsx | 10 +- .../chkgdTable/rswtChkgd/rswtChkgdTable.tsx | 93 ++++-- apps/wr/public/sample_rs_chk.xlsx | Bin 12511 -> 12474 bytes 20 files changed, 1460 insertions(+), 567 deletions(-) create mode 100644 apps/wr/app/ruseFcltMng/fcltSttn/htdflSttn/page.tsx create mode 100644 apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList.tsx create mode 100644 apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/excel.tsx create mode 100644 apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/headList.tsx diff --git a/apps/wr/app/page.tsx b/apps/wr/app/page.tsx index 63c97952b..c173173ff 100644 --- a/apps/wr/app/page.tsx +++ b/apps/wr/app/page.tsx @@ -76,6 +76,13 @@ export default function Home() { +
+ +
diff --git a/apps/wr/app/ruseFcltMng/fcltSttn/htdflSttn/page.tsx b/apps/wr/app/ruseFcltMng/fcltSttn/htdflSttn/page.tsx new file mode 100644 index 000000000..960edd227 --- /dev/null +++ b/apps/wr/app/ruseFcltMng/fcltSttn/htdflSttn/page.tsx @@ -0,0 +1,76 @@ +'use client' + +import { useWrStore } from "@/app/store"; +import { btnClass } from "@/app/store/common/code"; +import type { chgBuilding, customRainBdst } from "@/app/store/ruseFcltMng/fcltReg/rainReg"; +import { getCityOrSgg } from "@/components/lib/func/commom"; +import { ContentsDiv, SearchBaseForm, Title ,type ReturnType, } from "@common/business_components/ui" +import { Button } from "@common/components/ui"; +import { useCallback, useRef, useState } from "react"; + +export default function HtdflSttn(){ +const { code } = useWrStore( + (state) => state + ); +const ref = useRef({} as ReturnType); + + const [pageNation, setPageNation] = useState(1); + const [isChHead, setIsChHead] = useState(false); + const [bdstTable, setBdstTable] = useState(); + const [detailModal, setDetailModal] = useState<{isOpen:boolean,data:customRainBdst|undefined}>({data:undefined,isOpen:false }) + const [bdstChgProps, setBdstChgProps] = useState<{ + isOpen: boolean; + chBuilding: chgBuilding[]; + }>({ isOpen: false, chBuilding: [] }); + + const codeData = getCityOrSgg(ref); + + const excelClick = useCallback(() => { + // if (codeData) rainSttnSearch(1, "10000", codeData); + }, []); + + return( + <> + +
+
+ + <SearchBaseForm + selectSidoData={code.cityCd} + selectSigunData={code.sggCd} + selectRegionData={code.rvbsnCd} + selectUnitWaterShedData={code.lgbsnCd} + selectMiddleRegionData={code.mdmbsnCd} + selectEnvPartData={code.lclgvrnmntCd} + rightChildren={ + <Button + className={btnClass} + onClick={() => { + setIsChHead(true); + }} + > + 조회항목설정 + </Button> + } + leftChildren={undefined} + queryClick={function (returnType: ReturnType): void { + // '{partValue: 'A', sidoValue: '0', sigunValue: '0', regionValue: '0', unitWaterShedValue: '0', …}' + let con; + if (returnType.sigunValue != "0") { + con = returnType.sigunValue; + } else { + con = returnType.sidoValue; + } + // rainSttnSearch(1, "20", con); + }} + excelClick={excelClick} + initClick={function (): void {}} + btnClass={btnClass} + ref={ref} + /> + </div> + </div> + </ContentsDiv> + </> + ) +} \ No newline at end of file diff --git a/apps/wr/app/ruseFcltMng/fcltSttn/rainSttn/page.tsx b/apps/wr/app/ruseFcltMng/fcltSttn/rainSttn/page.tsx index 8ed57f120..66dd6ba3d 100644 --- a/apps/wr/app/ruseFcltMng/fcltSttn/rainSttn/page.tsx +++ b/apps/wr/app/ruseFcltMng/fcltSttn/rainSttn/page.tsx @@ -73,7 +73,7 @@ export default function RainSttn() { ...item, ...item.reuseBuilding[0], ...item.reuseBuilding[0].rainyObj, - chBuildingYn:item.chgBuilding[0].bdstChgNm!==null?'Y':'N' + chBuildingYn:item.chgBuilding[0]?.bdstChgNm!==null?'Y':'N' })) .filter((item: any) => item.rainyObj !== undefined); diff --git a/apps/wr/app/ruseFcltMng/fcltSttn/rswtSttn/page.tsx b/apps/wr/app/ruseFcltMng/fcltSttn/rswtSttn/page.tsx index d13cf8a12..3608cddab 100644 --- a/apps/wr/app/ruseFcltMng/fcltSttn/rswtSttn/page.tsx +++ b/apps/wr/app/ruseFcltMng/fcltSttn/rswtSttn/page.tsx @@ -1,9 +1,16 @@ "use client"; import { useWrStore } from "@/app/store"; import { btnClass } from "@/app/store/common/code"; +import type { chgBuilding } from "@/app/store/ruseFcltMng/fcltReg/rainReg"; import type { customRswtBdst } from "@/app/store/ruseFcltMng/fcltReg/rswtReg"; -import { excelDownload, getCityOrSgg, tableData } from "@/components/lib/func/commom"; +import { + excelDownload, + getCityOrSgg, + tableData, +} from "@/components/lib/func/commom"; +import BdstChgModal from "@/components/ui/modal/fcltReg/bdstChgModal/bdstChgModal"; import HeadChModal from "@/components/ui/modal/sttn/headChModal/headChModal"; +import SttnDetailModal from "@/components/ui/modal/sttn/sttnDetailModal/sttnDetailModal"; import { ContentsDiv, SearchBaseForm, @@ -18,21 +25,43 @@ import { Button } from "@common/components/ui"; import { useCallback, useEffect, useRef, useState } from "react"; export default function RswtSttn() { - const { code, rswtSttn, rswtSttnSearch ,rswtSttnInit} = useWrStore((state) => state); + const { code, rswtSttn, rswtSttnSearch, rswtSttnInit } = useWrStore( + (state) => state + ); const [pageNation, setPageNation] = useState(1); const { rswtSttnRes } = rswtSttn; const [isChHead, setIsChHead] = useState(false); const ref = useRef<ReturnType>({} as ReturnType); const [bdstTable, setBdstTable] = useState<customRswtBdst[]>(); + const [detailModal, setDetailModal] = useState<{isOpen:boolean,data:customRswtBdst|undefined}>({data:undefined,isOpen:false }) + const [bdstChgProps, setBdstChgProps] = useState<{ + isOpen: boolean; + chBuilding: chgBuilding[]; + }>({ isOpen: false, chBuilding: [] }); const codeData = getCityOrSgg(ref); +const cellClick=(index:number|undefined, headId: string)=>{ + + if(headId!=='chBuildingYn' && index!==undefined && bdstTable!==undefined){ + setDetailModal({isOpen:true ,data:bdstTable[index] }) + }else if( headId==='chBuildingYn' && index!==undefined && bdstTable!==undefined){ + if ( + bdstTable[index] && + bdstTable[index]?.chgBuilding[0]?.bdstChgNm!==null + + ) { + setBdstChgProps({ isOpen:true , chBuilding: bdstTable[index].chgBuilding}); + } + } + + } + useEffect(() => { if (!rswtSttn.isInit) { - - rswtSttnInit('0') + rswtSttnInit("0"); } }, [rswtSttn.isInit, rswtSttnInit]); @@ -40,20 +69,21 @@ export default function RswtSttn() { if (rswtSttnRes.length > 0 && code.cityCd.length > 0) { const data = tableData(rswtSttnRes, { code }); const chData = data - .map((item: customRswtBdst) => ({ + .map((item: customRswtBdst) => ({ ...item, ...item.reuseBuilding[0], ...item.reuseBuilding[0]?.rswtrObj, - chBuildingYn:item.chgBuilding.length>0?'Y':'N' + chBuildingYn:item?.chgBuilding[0]?.bdstChgNm!==null?'Y':'N' })) - .filter((item: customRswtBdst) => item.reuseBuilding[0]?.rswtrObj !== undefined); + .filter( + (item: customRswtBdst) => + item.reuseBuilding[0]?.rswtrObj !== undefined + ); - console.log("chData ", chData) setBdstTable(chData); } }, [code, rswtSttnRes]); - useEffect(() => { if (rswtSttn.excelList.length > 0) { if (rswtSttn.excelList.length === 0) return; @@ -82,7 +112,6 @@ export default function RswtSttn() { if (codeData != null) rswtSttnSearch(pageNation, "20", codeData); }, [pageNation]); - return ( <> <ContentsDiv> @@ -136,9 +165,8 @@ export default function RswtSttn() { headlist={rswtSttn.rswtSttHeadList} pageSize={20} total={10000} - cellClick={undefined} + cellClick={(index: number | undefined,headId:string)=>{cellClick(index ?? undefined,headId)} } onPageChange={setPageNation} - ></UiTable> </ListDiv> </div> @@ -149,7 +177,34 @@ export default function RswtSttn() { setIsChHead(!isChHead); }} bodyClass={""} - body={<HeadChModal headList={(rswtSttn.rswtSttHeadList||[]).map(item => ({ ...item, visible: item.visible ?? false }))} type={"RST"} />} + body={ + <HeadChModal + headList={(rswtSttn.rswtSttHeadList || []).map((item) => ({ + ...item, + visible: item.visible ?? false, + }))} + type={"RST"} + /> + } + /> + <UiModal + open={detailModal.isOpen} + onOpenChange={() => { + setDetailModal({ ...detailModal, isOpen: !detailModal.isOpen }); + }} + bodyClass={"w-[40vw] max-w-none max-h-[calc(100%-70px)]"} + body={<SttnDetailModal type={"RST"} formObj={detailModal.data} />} + /> + + <UiModal + bodyClass={""} + open={bdstChgProps.isOpen} + body={ + <BdstChgModal chgBuilding={bdstChgProps.chBuilding}></BdstChgModal> + } + onOpenChange={(open) => { + setBdstChgProps({ ...bdstChgProps, isOpen: open }); + }} /> </ContentsDiv> </> diff --git a/apps/wr/app/ruseFcltMng/prfmncReg/chkgdnReg/TableHead.tsx b/apps/wr/app/ruseFcltMng/prfmncReg/chkgdnReg/TableHead.tsx index 0d21b08e8..978805d9d 100644 --- a/apps/wr/app/ruseFcltMng/prfmncReg/chkgdnReg/TableHead.tsx +++ b/apps/wr/app/ruseFcltMng/prfmncReg/chkgdnReg/TableHead.tsx @@ -1,4 +1,4 @@ -import type { chHeadType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn"; +import type { chHeadType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList"; import type { TableUpperProps } from "@/app/store/ruseFcltMng/fcltReg/rainReg"; import { TableHead, TableRow } from "@common/components/ui"; diff --git a/apps/wr/app/ruseFcltMng/prfmncReg/chkgdnReg/page.tsx b/apps/wr/app/ruseFcltMng/prfmncReg/chkgdnReg/page.tsx index abf3dcc8f..b83284c32 100644 --- a/apps/wr/app/ruseFcltMng/prfmncReg/chkgdnReg/page.tsx +++ b/apps/wr/app/ruseFcltMng/prfmncReg/chkgdnReg/page.tsx @@ -1,11 +1,12 @@ "use client"; import { useWrStore } from "@/app/store"; +import { type rainChkZodType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn"; +import type { rswtChkZodType } from "@/app/store/prfmncReg/chkgdnReg/rswrtChkgdn"; import { - fcltDiv, - type rainChkZodType -} from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn"; -import { excelDownload } from "@/components/lib/func/commom"; + excelArrayDownload, + excelDownload, +} from "@/components/lib/func/commom"; import ChkgdExcel from "@/components/ui/modal/chkgd/chkgdExcel/chkgdExcel"; import ChkgdModal from "@/components/ui/modal/chkgd/chkgdModal/chkgdModal"; import RainChkgdTable from "@/components/ui/table/chkgdTable/rainChkgd/rainChkgdTable"; @@ -18,7 +19,7 @@ import { SearchInput, SelectBox, Title, - UiModal + UiModal, } from "@common/business_components/ui"; import ListDiv from "@common/business_components/ui/list_div"; import { Button } from "@common/components/ui"; @@ -27,59 +28,97 @@ import { Toaster } from "react-hot-toast"; //----------------------- export default function Page() { - - const { code, rainChkgdn, rainChkInit,rainChkDetilUpdate, rainChkDelete , rainChkExcelDownload, rswtrChkExcelDownload } = useWrStore((state) => { + const { + code, + rainChkgdn, + rswtChkgdn, + rainChkInit, + rainChkDetilUpdate, + rainChkDelete, + rainChkExcelDownload, + rswtrChkExcelDownload, + + rswtChkgdnInit + } = useWrStore((state) => { return state; }); const [isRegExcel, setIsRegExcel] = useState(false); const [searchTxt, setSearchTxt] = useState<string>(); - const [updateRainDetail, setUpdateRainDetail] = useState<rainChkZodType[]>([]); - const [deleteIdx, setDeleteIdx ] = useState<number[]> ([]); + const [updateRainDetail, setUpdateRainDetail] = useState<rainChkZodType[]>( + [] + ); + const [deleteIdx, setDeleteIdx] = useState<number[]>([]); const [selFcltDiv, setSelFcltDiv] = useState("RNB"); const [isModal, setIsModal] = useState({ open: false, data: {}, }); const cellClick = () => { - if(searchTxt) - rainChkInit(searchTxt,'0') - + if (searchTxt) rainChkInit(searchTxt, "0"); }; - useEffect(() => { + useEffect(() => { if (!rainChkgdn.isInit) { - rainChkInit('2024','0'); - setIsModal({data:{}, open:rainChkgdn.isModalClose }) - setIsRegExcel(false) + rainChkInit("2024", "0"); + setIsModal({ data: {}, open: rainChkgdn.isModalClose }); + setIsRegExcel(false); + } + + if (!rswtChkgdn.isInit) { + rswtChkgdnInit("2024", "0"); + setIsModal({ data: {}, open: rainChkgdn.isModalClose }); + setIsRegExcel(false); } }, [rainChkgdn.isInit, rainChkInit]); + useEffect(() => { + console.log("rswtChkgdn.excelList ", rswtChkgdn.excelList); + + if (rswtChkgdn.excelList[0]?.data.length > 1) + excelArrayDownload( + "중수도_점검결과_이용량현황", + "sample_rs_chk.xlsx", + rswtChkgdn.excelList + ); + }, [rswtChkgdn.excelList]); useEffect(() => { - excelDownload(rainChkgdn.excelList ,'빗물이용시설 지도점검','sample_rainy.xlsx', 'A1:AN1'); + excelDownload( + rainChkgdn.excelList, + "빗물이용시설 지도점검", + "sample_rainy.xlsx", + "A1:AN1" + ); }, [rainChkgdn.excelList]); - const excelClick= useCallback(()=>{ - if(selFcltDiv === "RNB") { + const excelClick = useCallback(() => { + console.log("selFcltDiv ", selFcltDiv); + if (selFcltDiv === "RNB") { rainChkExcelDownload(code.cityCd, code.sggCd); } else { rswtrChkExcelDownload(code.cityCd, code.sggCd); } - }, [rainChkExcelDownload, rswtrChkExcelDownload, code.cityCd, code.sggCd]); + }, [ + rainChkExcelDownload, + rswtrChkExcelDownload, + code.cityCd, + code.sggCd, + selFcltDiv, + ]); + const handleClick = useCallback( + (data: any) => { + if (data) setIsModal({ open: true, data: data }); + }, + [isModal] + ); - - const handleClick = useCallback((data:any) => { - if(data) setIsModal({ open: true, data: data }); - }, [isModal]); - - const clickDelete=(data:number[])=>{ - if(data.length>0){ - rainChkDelete( data ) + const clickDelete = (data: number[]) => { + if (data.length > 0) { + rainChkDelete(data); } - } + }; - return ( <ContentsDiv> <div className="grid grid-cols-4 gap-4 "> @@ -90,16 +129,20 @@ export default function Page() { <SearchInput className="col-span-3 my-7 !text-xl" label=" 지도점검년도" - textValue={searchTxt??''} - setText={(e) => {setSearchTxt(e);}} + textValue={searchTxt ?? ""} + setText={(e) => { + setSearchTxt(e); + }} /> <SelectBox className={"col-span-3 w-full h-full text-2xl"} label={"시설구분"} selectClass={""} - selectArray={fcltDiv} - onSelectValue={setSelFcltDiv} - value={selFcltDiv ?? fcltDiv[0]?.val} + selectArray={rainChkgdn.fcltDiv} + onSelectValue={(val: string | number) => + setSelFcltDiv(String(val)) + } + value={selFcltDiv ?? rainChkgdn.fcltDiv[0]?.val} /> </SearchFormLeft> <SearchFormRight> @@ -113,22 +156,33 @@ export default function Page() { > 조회 </Button> - <Button size={"sm"} onClick={() => { clickDelete(deleteIdx) }}> + <Button + size={"sm"} + onClick={() => { + clickDelete(deleteIdx); + }} + > 삭제 </Button> - <Button size={"sm"} onClick={() => { rainChkDetilUpdate( updateRainDetail as rainChkZodType[]) }}> + <Button + size={"sm"} + onClick={() => { + rainChkDetilUpdate(updateRainDetail as rainChkZodType[]); + }} + > 저장 </Button> </div> <div className="flex flex-row space-x-2 pt-3"> - <Button size={"sm"} onClick={() => {setIsRegExcel(!isRegExcel)}}> + <Button + size={"sm"} + onClick={() => { + setIsRegExcel(!isRegExcel); + }} + > 결과등록 </Button> - <Button - color="green" - size={"sm"} - onClick={excelClick} - > + <Button color="green" size={"sm"} onClick={excelClick}> 양식받기 </Button> </div> @@ -140,18 +194,30 @@ export default function Page() { <div className="grid grid-cols-6 gap-4 "> <div className="col-span-6"> <ListDiv> - {selFcltDiv === "RNB" ? <RainChkgdTable deleteData={setDeleteIdx} detailUp={setUpdateRainDetail} handleClick={handleClick} /> : <RswtChkgdTable />} + {selFcltDiv === "RNB" ? ( + <RainChkgdTable + deleteData={setDeleteIdx} + detailUp={setUpdateRainDetail} + handleClick={handleClick} + /> + ) : ( + <RswtChkgdTable detaiRswtlUpdate={function (data: rswtChkZodType[]): void { + throw new Error("Function not implemented."); + } } deleteRswtData={function (selectedIds: number[]): void { + throw new Error("Function not implemented."); + } } /> + )} </ListDiv> </div> </div> <UiModal - open={isModal.open} - onOpenChange={() => { - setIsModal({ open: !open, data: {} }); - }} - bodyClass={"w-[80vw] max-w-none"} - body={<ChkgdModal type={"RNB"} obj={isModal.data} />} - ></UiModal> + open={isModal.open} + onOpenChange={() => { + setIsModal({ open: !open, data: {} }); + }} + bodyClass={"w-[80vw] max-w-none"} + body={<ChkgdModal type={"RNB"} obj={isModal.data} />} + ></UiModal> <UiModal open={isRegExcel} onOpenChange={setIsRegExcel} diff --git a/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/excel.tsx b/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/excel.tsx index c34d0b9fc..c39456095 100644 --- a/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/excel.tsx +++ b/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/excel.tsx @@ -1,8 +1,8 @@ import type { SelectDataType } from "@common/business_components"; import type { rainChkZodType } from "."; import { TEST_UUID } from "@/app/store/ruseFcltMng/fcltReg/rainReg"; -import { buildingType } from '../../../common/code/index'; -const useTypes = [ +import { buildingType } from "../../../common/code/index"; +export const useTypes = [ "조경용수", "청소화장실용수", "세척살수용수", @@ -30,31 +30,61 @@ export const buildingToExcelType = ( buildings: Buildingtype[], sidoCode: SelectDataType[], sggCdoe: SelectDataType[], - bdType: string, ) => { const excelData: string[][] = []; - if(bdType===buildingType.RNB){ buildings.forEach((building) => { useTypes.forEach((useType) => { excelData.push([ building.bdstInfoPk.toString(), sidoCode.find((sido) => sido.val === building.ctpvCd.toString()) ?.text || "", - sggCdoe.find((sido) => sido.val === building.sggCd.toString())?.text || "",building.daddr,building.bdstNm,"","","","",useType,"","","","","","","","","","","","","","","","","","","","","","","","","","","","","","", + sggCdoe.find((sido) => sido.val === building.sggCd.toString()) + ?.text || "", + building.daddr, + building.bdstNm, + "", + "", + "", + "", + useType, + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", ]); }); }); return excelData; - }else if(bdType===buildingType.RST){ - - } + }; -export const excelToRainChkData = ( - excelData: any, - year: string, -) => { - +export const excelToRainChkData = (excelData: any, year: string) => { const rainChkgdData: rainChkZodType[] = excelData.map((item: any) => { const makeGuidDetail = Array.from({ length: 12 }, (_, i) => { let month = `${i + 1}`; @@ -64,33 +94,33 @@ export const excelToRainChkData = ( return { utztnUsgCd: item["이용용도"], ym: year + month, - chckYmd: item["점검일"] , + chckYmd: item["점검일"], splyUswtrUtztnQnt: splyData !== "" ? splyData : 0, - ruseFcltUtztnQnt: ruseData !== "" ? ruseData : 0, + ruseFcltUtztnQnt: ruseData !== "" ? ruseData : 0, flg: "RNB", }; }); return { - bdstRuseFcltFk: Number(item["NO"]), - chckYmd: item["점검일"] , - spwtPositn: item["유량측정방법"], - rgtr: TEST_UUID, - oilqMeasMthd: item["운영관리형태"], - anlOperCst: Number(item["연간운영비(원/년)"])??0, - operMngCd: item["유량측정방법"], - guideDetails: makeGuidDetail, - rainyDetails: [ - { - utztnUsgCd: item["이용용도"], - ph: Number(item["PH"])??0, - trbt: Number(item["탁도"])??0, - grssClfg: Number(item["총대장균군수"])??0, - etc: item["기타"] ?? "", // 필드 누락 방지 - vltnDt: item["과태료 부과일(위반일)"], - vltnMttr: item["위반사항"], - levyAmt: Number(item["과태료부과금액(천원)"])??0, - rgtr: TEST_UUID, + bdstRuseFcltFk: Number(item["NO"]), + chckYmd: item["점검일"], + spwtPositn: item["유량측정방법"], + rgtr: TEST_UUID, + oilqMeasMthd: item["운영관리형태"], + anlOperCst: Number(item["연간운영비(원/년)"]) ?? 0, + operMngCd: item["유량측정방법"], + guideDetails: makeGuidDetail, + rainyDetails: [ + { + utztnUsgCd: item["이용용도"], + ph: Number(item["PH"]) ?? 0, + trbt: Number(item["탁도"]) ?? 0, + grssClfg: Number(item["총대장균군수"]) ?? 0, + etc: item["기타"] ?? "", // 필드 누락 방지 + vltnDt: item["과태료 부과일(위반일)"], + vltnMttr: item["위반사항"], + levyAmt: Number(item["과태료부과금액(천원)"]) ?? 0, + rgtr: TEST_UUID, }, ], }; diff --git a/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList.tsx b/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList.tsx new file mode 100644 index 000000000..e16da6329 --- /dev/null +++ b/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList.tsx @@ -0,0 +1,282 @@ +import type { TableUpperProps } from "@/app/store/ruseFcltMng/fcltReg/rainReg"; +import { z } from "zod"; + + + + +export const chkgdRegExcel = z.object({ + bdstInfoPk: z.number().default(111111111), + chckYr: z.string() + .default("") + .refine( + (year) => year.length === 0 || /^\d{4}$/.test(year), + { message: "4자리 연도만 입력하세요" } + ) + .refine( + (year) => { + if (year.length === 0) return true; // 빈 문자열은 통과 + const numYear = Number(year); + return !isNaN(numYear) && numYear <= new Date().getFullYear(); + }, + { message: "현재 년도보다 높은 수를 입력할 수 없습니다." } + ), + file: z.instanceof(File).optional(), + }); + + +export const guideDetail = z.object({ + chkgdnFk: z.number().nullable(), + utztnUsgCd: z.string(), + chckYmd: z.string(), + ym: z.string(), + splyUswtrUtztnQnt: z.number(), + ruseFcltUtztnQnt: z.number(), + flg: z.string(), +}) +export const rainChkZod = z.object({ + chkgdnPk: z.number().nullable(), + bdstRuseFcltFk: z.number(), + chckYmd: z.string(), + prcsCpct: z.number().nullable(), + spwtPositn: z.string().nullable(), + regDt: z.string().nullable(), + rgtr: z.string().nullable(), + mdfcnDt: z.string().nullable(), + mdfr: z.string().nullable(), + flg: z.string().nullable(), + guideDetails: z.array( + guideDetail + ), + building: z.object({ + bdstInfoPk: z.number(), + ctpvCd: z.number(), + sggCd: z.number(), + daddr: z.string().nullable(), + bdstNm: z.string(), + frstArchPrmsnDt: z.string().nullable(), + archArea: z.number(), + regDt: z.string().nullable(), + rgtr: z.string().nullable(), + mdfcnDt: z.string().nullable(), + mdfr: z.string().nullable(), + }).nullable(), + rainyDetails: z.array( + z.object({ + chkgdnFk: z.number().nullable(), + utztnUsgCd: z.string(), + ph: z.number().nullable(), + trbt: z.number().nullable(), + grssClfg: z.number().nullable(), + etc: z.string().nullable(), + vltnDt: z.string().nullable(), + vltnMttr: z.string().nullable(), + levyAmt: z.number().nullable(), + regDt: z.string().nullable(), + rgtr: z.string().nullable(), + mdfcnDt: z.string().nullable(), + mdfr: z.string().nullable(), + }) + ), +}); + + + +export type chHeadType = { + headNm: string; + text: string; + length: number; + rows?: number; + }; + + + + +export type splRuseWaterType = { + utztnUsgCd: string; + splTotal: number; + spl01: number; + spl02: number; + spl03: number; + spl04: number; + spl05: number; + spl06: number; + spl07: number; + spl08: number; + spl09: number; + spl10: number; + spl11: number; + spl12: number; + ruseTotal: number; + ruse01: number; + ruse02: number; + ruse03: number; + ruse04: number; + ruse05: number; + ruse06: number; + ruse07: number; + ruse08: number; + ruse09: number; + ruse10: number; + ruse11: number; + ruse12: number; + }; + +export const rainHeadNmExport: chHeadType[] = [ + { headNm: "useQuilty", text: "이용수질(필요시)", length: 4 }, + { headNm: "feeHist", text: "과태료 부과내역", length: 3 }, + { headNm: "ruseWater", text: "연간보급수 이용량(m³)", length: 13 }, + { headNm: "detailQulity", text: "분기", length: 11 }, + { headNm: "splWater", text: "연간빗물 이용량(m³)", length: 13 }, + +]; + + +export const rainChkgdnHeadListExport: TableUpperProps[] = [ + { id: "order", title: "순번", rowSpan: true }, + { id: "cityText", title: "시도", rowSpan: true }, + { id: "sggText", title: "시군구", rowSpan: true }, + { id: "bdstNm", title: "시설명", rowSpan: true }, + { id: "operMngCd", title: "운영관리형태", rowSpan: true }, + { id: "oilqMeasMthd", title: "유량측정방법", rowSpan: true }, + { id: "anlOperCst", title: "연간운영비(원/년)", rowSpan: true }, + { id: "dtfcltCpct", title: "빗물저류조용량(m³)", rowSpan: true }, + { id: "chckYmd", title: "점검일", rowSpan: true }, + { id: "utztnUsgCd", title: "이용용도" }, + { id: "totalSplyUswtrUtztnQnt", title: "연간보급수이용량(m³)" }, + { id: "totalRuseFcltUtztnQnt", title: "연간빗물이용량(m³)" }, + { id: "ph", title: "PH", upName: "useQuilty", upSequnce: 1, type: "number" }, + { + id: "trbt", + title: "탁도", + upName: "useQuilty", + upSequnce: 2, + type: "number", + }, + { + id: "grssClfg", + title: "총대장균군수", + upName: "useQuilty", + upSequnce: 3, + type: "number", + }, + { + id: "etc", + title: "기타", + upName: "useQuilty", + upSequnce: 4, + type: "number", + }, + { + id: "vltnDt", + title: "부과일(위반일)", + upName: "feeHist", + upSequnce: 1, + type: "text", + }, + { + id: "vltnMttr", + title: "위반사항", + upName: "feeHist", + upSequnce: 2, + type: "text", + }, + { + id: "levyAmt", + title: "부과금액(천원)", + upName: "feeHist", + upSequnce: 3, + type: "number", + }, +]; + +export const ruseHeadListExport: TableUpperProps[] = [ + { id: "utztnUsgCd", title: "이용용도" }, + { id: "ruseTotal", title: "계", upName: "ruseWater", upSequnce: 1 }, + { id: "ruse01", title: "1월", upName: "ruseWater", upSequnce: 2 }, + { id: "ruse02", title: "2월", upName: "ruseWater", upSequnce: 3 }, + { id: "ruse03", title: "3월", upName: "ruseWater", upSequnce: 4 }, + { id: "ruse04", title: "4월", upName: "ruseWater", upSequnce: 5 }, + { id: "ruse05", title: "5월", upName: "ruseWater", upSequnce: 6 }, + { id: "ruse06", title: "6월", upName: "ruseWater", upSequnce: 7 }, + { id: "ruse07", title: "7월", upName: "ruseWater", upSequnce: 8 }, + { id: "ruse08", title: "8월", upName: "ruseWater", upSequnce: 9 }, + { id: "ruse09", title: "9월", upName: "ruseWater", upSequnce: 10 }, + { id: "ruse10", title: "10월", upName: "ruseWater", upSequnce: 11 }, + { id: "ruse11", title: "11월", upName: "ruseWater", upSequnce: 12 }, + { id: "ruse12", title: "12월", upName: "ruseWater", upSequnce: 13 }, +]; + +export const splHeadListExport: TableUpperProps[] = [ + { id: "utztnUsgCd", title: "이용용도" }, + { id: "splTotal", title: "계", upName: "splWater", upSequnce: 1 }, + { id: "spl01", title: "1월", upName: "splWater", upSequnce: 2 }, + { id: "spl02", title: "2월", upName: "splWater", upSequnce: 3 }, + { id: "spl03", title: "3월", upName: "splWater", upSequnce: 4 }, + { id: "spl04", title: "4월", upName: "splWater", upSequnce: 5 }, + { id: "spl05", title: "5월", upName: "splWater", upSequnce: 6 }, + { id: "spl06", title: "6월", upName: "splWater", upSequnce: 7 }, + { id: "spl07", title: "7월", upName: "splWater", upSequnce: 8 }, + { id: "spl08", title: "8월", upName: "splWater", upSequnce: 9 }, + { id: "spl09", title: "9월", upName: "splWater", upSequnce: 10 }, + { id: "spl10", title: "10월", upName: "splWater", upSequnce: 11 }, + { id: "spl11", title: "11월", upName: "splWater", upSequnce: 12 }, + { id: "spl12", title: "12월", upName: "splWater", upSequnce: 13 }, +]; + +export const rswtDetailHeadListExport: TableUpperProps[] = [ + { id: "utztnUsgTest", title: "이용용도" }, + { + id: "avre", + title: "평균", + upName: "detailQulity", + upSequnce: 1, + edit: true, + }, + { + id: "grssClfg", + title: "총대장균군수", + upName: "detailQulity", + upSequnce: 2, + edit: true, + }, + { + id: "combResdch", + title: "결합잔류염소", + upName: "detailQulity", + upSequnce: 3, + edit: true, + }, + { + id: "trbt", + title: "탁도", + upName: "detailQulity", + upSequnce: 4, + edit: true, + }, + { id: "bod", title: "BOD", upName: "detailQulity", upSequnce: 5, edit: true }, + { + id: "smll", + title: "냄새", + upName: "detailQulity", + upSequnce: 6, + edit: true, + }, + { + id: "crmty", + title: "색도", + upName: "detailQulity", + upSequnce: 7, + edit: true, + }, + { id: "tn", title: "T-N", upName: "detailQulity", upSequnce: 8, edit: true }, + { id: "tph", title: "T-P", upName: "detailQulity", upSequnce: 9, edit: true }, + { id: "ph", title: "PH", upName: "detailQulity", upSequnce: 10, edit: true }, + { + id: "clIonQnt", + title: "염화물", + upName: "detailQulity", + upSequnce: 11, + edit: true, + }, +]; + diff --git a/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/index.tsx b/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/index.tsx index d544013d6..d3cc745ae 100644 --- a/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/index.tsx +++ b/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/index.tsx @@ -8,288 +8,20 @@ import { ToastFormUi } from "@/components/ui/toast/toastFromUi"; import type { SelectDataType } from "@common/business_components/ui"; import { z } from "zod"; import { buildingToExcelType } from "./excel"; +import { rainChkgdnHeadListExport, rainChkZod, rainHeadNmExport, rswtDetailHeadListExport, ruseHeadListExport, splHeadListExport, type chHeadType } from "./headList"; -const guideDetail = z.object({ - chkgdnFk: z.number().nullable(), - utztnUsgCd: z.string(), - chckYmd: z.string(), - ym: z.string(), - splyUswtrUtztnQnt: z.number(), - ruseFcltUtztnQnt: z.number(), - flg: z.string(), -}) -export const rainChkZod = z.object({ - chkgdnPk: z.number().nullable(), - bdstRuseFcltFk: z.number(), - chckYmd: z.string(), - prcsCpct: z.number().nullable(), - spwtPositn: z.string().nullable(), - regDt: z.string().nullable(), - rgtr: z.string().nullable(), - mdfcnDt: z.string().nullable(), - mdfr: z.string().nullable(), - flg: z.string().nullable(), - guideDetails: z.array( - guideDetail - ), - building: z.object({ - bdstInfoPk: z.number(), - ctpvCd: z.number(), - sggCd: z.number(), - daddr: z.string().nullable(), - bdstNm: z.string(), - frstArchPrmsnDt: z.string().nullable(), - archArea: z.number(), - regDt: z.string().nullable(), - rgtr: z.string().nullable(), - mdfcnDt: z.string().nullable(), - mdfr: z.string().nullable(), - }).nullable(), - rainyDetails: z.array( - z.object({ - chkgdnFk: z.number().nullable(), - utztnUsgCd: z.string(), - ph: z.number().nullable(), - trbt: z.number().nullable(), - grssClfg: z.number().nullable(), - etc: z.string().nullable(), - vltnDt: z.string().nullable(), - vltnMttr: z.string().nullable(), - levyAmt: z.number().nullable(), - regDt: z.string().nullable(), - rgtr: z.string().nullable(), - mdfcnDt: z.string().nullable(), - mdfr: z.string().nullable(), - }) - ), -}); export type rainChkZodType = z.infer<typeof rainChkZod>; - -export const rainRegExcel = z.object({ - bdstInfoPk: z.number().default(111111111), - chckYr: z.string() - .default("") - .refine( - (year) => year.length === 0 || /^\d{4}$/.test(year), - { message: "4자리 연도만 입력하세요" } - ) - .refine( - (year) => { - if (year.length === 0) return true; // 빈 문자열은 통과 - const numYear = Number(year); - return !isNaN(numYear) && numYear <= new Date().getFullYear(); - }, - { message: "현재 년도보다 높은 수를 입력할 수 없습니다." } - ), - file: z.instanceof(File).optional(), -}); - -export const fcltDiv = [ + const fcltDiv = [ { val: "RST", text: "중수도시설" }, { val: "RNB", text: "빗물이용시설" }, ]; -export type chHeadType = { - headNm: string; - text: string; - length: number; - rows?: number; -}; - -export const rainHeadNm: chHeadType[] = [ - { headNm: "useQuilty", text: "이용수질(필요시)", length: 4 }, - { headNm: "feeHist", text: "과태료 부과내역", length: 3 }, - { headNm: "ruseWater", text: "연간보급수 이용량(m³)", length: 13 }, - { headNm: "detailQulity", text: "분기", length: 11 }, - { headNm: "splWater", text: "연간빗물 이용량(m³)", length: 13 }, - -]; - - -export const rainChkgdnHeadList: TableUpperProps[] = [ - { id: "order", title: "순번", rowSpan: true }, - { id: "cityText", title: "시도", rowSpan: true }, - { id: "sggText", title: "시군구", rowSpan: true }, - { id: "bdstNm", title: "시설명", rowSpan: true }, - { id: "operMngCd", title: "운영관리형태", rowSpan: true }, - { id: "oilqMeasMthd", title: "유량측정방법", rowSpan: true }, - { id: "anlOperCst", title: "연간운영비(원/년)", rowSpan: true }, - { id: "dtfcltCpct", title: "빗물저류조용량(m³)", rowSpan: true }, - { id: "chckYmd", title: "점검일", rowSpan: true }, - { id: "utztnUsgCd", title: "이용용도" }, - { id: "totalSplyUswtrUtztnQnt", title: "연간보급수이용량(m³)" }, - { id: "totalRuseFcltUtztnQnt", title: "연간빗물이용량(m³)" }, - { id: "ph", title: "PH", upName: "useQuilty", upSequnce: 1, type: "number" }, - { - id: "trbt", - title: "탁도", - upName: "useQuilty", - upSequnce: 2, - type: "number", - }, - { - id: "grssClfg", - title: "총대장균군수", - upName: "useQuilty", - upSequnce: 3, - type: "number", - }, - { - id: "etc", - title: "기타", - upName: "useQuilty", - upSequnce: 4, - type: "number", - }, - { - id: "vltnDt", - title: "부과일(위반일)", - upName: "feeHist", - upSequnce: 1, - type: "text", - }, - { - id: "vltnMttr", - title: "위반사항", - upName: "feeHist", - upSequnce: 2, - type: "text", - }, - { - id: "levyAmt", - title: "부과금액(천원)", - upName: "feeHist", - upSequnce: 3, - type: "number", - }, -]; - -export const ruseHeadList: TableUpperProps[] = [ - { id: "utztnUsgCd", title: "이용용도" }, - { id: "ruseTotal", title: "계", upName: "ruseWater", upSequnce: 1 }, - { id: "ruse01", title: "1월", upName: "ruseWater", upSequnce: 2 }, - { id: "ruse02", title: "2월", upName: "ruseWater", upSequnce: 3 }, - { id: "ruse03", title: "3월", upName: "ruseWater", upSequnce: 4 }, - { id: "ruse04", title: "4월", upName: "ruseWater", upSequnce: 5 }, - { id: "ruse05", title: "5월", upName: "ruseWater", upSequnce: 6 }, - { id: "ruse06", title: "6월", upName: "ruseWater", upSequnce: 7 }, - { id: "ruse07", title: "7월", upName: "ruseWater", upSequnce: 8 }, - { id: "ruse08", title: "8월", upName: "ruseWater", upSequnce: 9 }, - { id: "ruse09", title: "9월", upName: "ruseWater", upSequnce: 10 }, - { id: "ruse10", title: "10월", upName: "ruseWater", upSequnce: 11 }, - { id: "ruse11", title: "11월", upName: "ruseWater", upSequnce: 12 }, - { id: "ruse12", title: "12월", upName: "ruseWater", upSequnce: 13 }, -]; - -export const splHeadList: TableUpperProps[] = [ - { id: "utztnUsgCd", title: "이용용도" }, - { id: "splTotal", title: "계", upName: "splWater", upSequnce: 1 }, - { id: "spl01", title: "1월", upName: "splWater", upSequnce: 2 }, - { id: "spl02", title: "2월", upName: "splWater", upSequnce: 3 }, - { id: "spl03", title: "3월", upName: "splWater", upSequnce: 4 }, - { id: "spl04", title: "4월", upName: "splWater", upSequnce: 5 }, - { id: "spl05", title: "5월", upName: "splWater", upSequnce: 6 }, - { id: "spl06", title: "6월", upName: "splWater", upSequnce: 7 }, - { id: "spl07", title: "7월", upName: "splWater", upSequnce: 8 }, - { id: "spl08", title: "8월", upName: "splWater", upSequnce: 9 }, - { id: "spl09", title: "9월", upName: "splWater", upSequnce: 10 }, - { id: "spl10", title: "10월", upName: "splWater", upSequnce: 11 }, - { id: "spl11", title: "11월", upName: "splWater", upSequnce: 12 }, - { id: "spl12", title: "12월", upName: "splWater", upSequnce: 13 }, -]; - -export const rswtDetailHeadList: TableUpperProps[] = [ - { id: "utztnUsgTest", title: "이용용도" }, - { - id: "avre", - title: "평균", - upName: "detailQulity", - upSequnce: 1, - edit: true, - }, - { - id: "grssClfg", - title: "총대장균군수", - upName: "detailQulity", - upSequnce: 2, - edit: true, - }, - { - id: "combResdch", - title: "결합잔류염소", - upName: "detailQulity", - upSequnce: 3, - edit: true, - }, - { - id: "trbt", - title: "탁도", - upName: "detailQulity", - upSequnce: 4, - edit: true, - }, - { id: "bod", title: "BOD", upName: "detailQulity", upSequnce: 5, edit: true }, - { - id: "smll", - title: "냄새", - upName: "detailQulity", - upSequnce: 6, - edit: true, - }, - { - id: "crmty", - title: "색도", - upName: "detailQulity", - upSequnce: 7, - edit: true, - }, - { id: "tn", title: "T-N", upName: "detailQulity", upSequnce: 8, edit: true }, - { id: "tph", title: "T-P", upName: "detailQulity", upSequnce: 9, edit: true }, - { id: "ph", title: "PH", upName: "detailQulity", upSequnce: 10, edit: true }, - { - id: "clIonQnt", - title: "염화물", - upName: "detailQulity", - upSequnce: 11, - edit: true, - }, -]; - - - -export type splRuseWaterType = { - utztnUsgCd: string; - splTotal: number; - spl01: number; - spl02: number; - spl03: number; - spl04: number; - spl05: number; - spl06: number; - spl07: number; - spl08: number; - spl09: number; - spl10: number; - spl11: number; - spl12: number; - ruseTotal: number; - ruse01: number; - ruse02: number; - ruse03: number; - ruse04: number; - ruse05: number; - ruse06: number; - ruse07: number; - ruse08: number; - ruse09: number; - ruse10: number; - ruse11: number; - ruse12: number; -}; - - - + const rainHeadNm=rainHeadNmExport; + const rainChkgdnHeadList=rainChkgdnHeadListExport; + const ruseHeadList=ruseHeadListExport; + const splHeadList=splHeadListExport; + const rswtDetailHeadList=rswtDetailHeadListExport; const rainChk: rainChkZodType[] = []; @@ -303,6 +35,7 @@ export type rainChkgdnState = { rswtDetailHeadList: TableUpperProps[]; isInit: boolean; isModalClose: boolean; + fcltDiv:SelectDataType[] excelList: []; }; }; @@ -328,7 +61,8 @@ export const rainChkgdnStateExport: rainChkgdnState = { rswtDetailHeadList: rswtDetailHeadList, isInit: false, isModalClose: false, - excelList: [] + excelList: [], + fcltDiv:fcltDiv }, }; @@ -354,12 +88,7 @@ const initFetch = async (txt: string | undefined, sgg: string | undefined) => { "wr", `/reuseChk/rainy` + (txt ? `/${txt}` : "2024") + (sgg ? `/${sgg}` : "/0") ); - const chRainRes = rainRes.map((item: rainChkZodType) => { - return { - ...item, - }; - }); - return chRainRes; + return rainRes; }; export const rainChkgdnActionsExport: (set: any) => rainChkgdnActions = ( @@ -370,7 +99,7 @@ export const rainChkgdnActionsExport: (set: any) => rainChkgdnActions = ( // 로그인한 아이디의 시도 or 시군구로 들어가야하는데 현재는 그냥 0으로 하드코딩 const data = await getFetch(fetchType.WR, `/reuseFc/buildingFlg/${buildingType.RNB}/41`) - const excelList = buildingToExcelType(data, sidoCode, sggCode, buildingType.RNB); + const excelList = buildingToExcelType(data, sidoCode, sggCode); set((state: rainChkgdnState) => ({ rainChkgdn: { ...state.rainChkgdn, diff --git a/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/excel.tsx b/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/excel.tsx new file mode 100644 index 000000000..5827dcbf5 --- /dev/null +++ b/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/excel.tsx @@ -0,0 +1,225 @@ +import type { SelectDataType } from "@common/business_components/ui"; +import { useTypes, type Buildingtype } from "../rainChkgdn/excel"; +import { TEST_UUID } from "@/app/store/ruseFcltMng/fcltReg/rainReg"; +import type { rswtChkZodType } from "."; + +export const rswtMakeExcelSample = ( + buildings: Buildingtype[], + sidoCode: SelectDataType[], + sggCdoe: SelectDataType[] +) => { + const excelSite = { + guideDetail: [] as string[][], + polutions: [] as string[][], + }; + //첫번째 시트 데이터 이용량 현황 양식만들기 + buildings.forEach((building) => { + useTypes.forEach((useType) => { + excelSite.guideDetail.push([ + building.bdstInfoPk.toString(), + sidoCode.find((sido) => sido.val === building.ctpvCd.toString()) + ?.text || "", + sggCdoe.find((sido) => sido.val === building.sggCd.toString())?.text || + "", + building.bdstNm, + building.daddr, + "", + "", + "", + "", + "", + useType, + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + ]); + }); + }); + + //두번째 시트 양식 만들기 오염도 + buildings.forEach((building) => { + useTypes.forEach((useType) => { + excelSite.polutions.push([ + building.bdstInfoPk.toString(), + sidoCode.find((sido) => sido.val === building.ctpvCd.toString()) + ?.text || "", + sggCdoe.find((sido) => sido.val === building.sggCd.toString())?.text || + "", + building.bdstNm, + building.daddr, + "", + "", + "", + "", + useType, + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + ]); + }); + }); + + return excelSite; +}; + +export const excelToRswtGuideData = (excelData: any, year: string) => { + const rainChkgdData: rswtChkZodType[] = excelData + .map((item: any) => { + if (item["점검일"] !== undefined) { + const makeGuidDetail = Array.from({ length: 12 }, (_, i) => { + let month = `${i + 1}`; + const splyData = item[`대체수_${month}월`]; + const rswtData = item[`중수_${month}월`]; + if (month.length < 2) month = "0" + month; + return { + ym: year + month, + chckYmd: item["점검일"], + splyUswtrUtztnQnt: splyData !== "" ? splyData : 0, + ruseFcltUtztnQnt: rswtData !== "" ? rswtData : 0, + }; + }); + + return { + bdstRuseFcltFk: Number(item["NO"]), + chckYmd: item["점검일"], + utztnUsgCd: item["이용용도"], + spwtPositn: item["유량측정방법"], + rgtr: TEST_UUID, + mdfr: TEST_UUID, + oilqMeasMthd: item["운영관리형태"], + anlOperCst: Number(item["연간운영비(원/년)"]) ?? 0, + operMngCd: item["유량측정방법"], + guideDetails: makeGuidDetail, + + }; + } + }) + .filter((el: any) => el !== undefined); + return rainChkgdData; +}; + +export const excelToRswtPolution = (excelData: any, year: string) => { + const rainChkgdData: rswtChkZodType[] = excelData + .map((item: any) => { + if (item["점검일"] !== undefined) { + console.log("item ", item) + const makePoluttion = Array.from({ length: 4 }, (_, i) => { + let quarter = `${i+1}`; + const grssClfgUswtr = item[`${quarter}분기_총대장균군수`]; + const combResdch = item[`${quarter}분기_결합잔류염소`]; + const trbt = item[`${quarter}분기_탁도`]; + const bod = item[`${quarter}분기_BOD`]; + const smll = item[`${quarter}분기_냄새`]; + const crmty = item[`${quarter}분기_색도`]; + const tph = item[`${quarter}분기_T-P`]; + const tn = item[`${quarter}분기_T-N`]; + const ph = item[`${quarter}분기_PH`]; + const clIonQnt = item[`${quarter}분기_염화물`]; + + return { + + node: quarter, + chckYmd: item["점검일"], + grssClfgUswtr: grssClfgUswtr !== "" ? grssClfgUswtr : 0, + combResdch: combResdch !== "" ? combResdch : 0, + trbt: trbt !== "" ? trbt : 0, + bod: bod !== "" ? bod : 0, + smll: smll !== "" ? smll : 0, + crmty: crmty !== "" ? crmty : 0, + tph: tph !== "" ? tph : 0, + tn: tn !== "" ? tn : 0, + ph: ph !== "" ? ph : 0, + clIonQnt: clIonQnt !== "" ? clIonQnt : 0, + rgtr: TEST_UUID, + mdfr: TEST_UUID, + }; + }); + + return { + bdstRuseFcltFk: Number(item["NO"]), + chckYmd: item["점검일"], + utztnUsgCd: item["이용용도"], + spwtPositn: item["유량측정방법"], + rgtr: TEST_UUID, + mdfr: TEST_UUID, + oilqMeasMthd: item["운영관리형태"], + anlOperCst: Number(item["연간운영비(원/년)"]) ?? 0, + operMngCd: item["유량측정방법"], + rswtrPolutions: makePoluttion, + rswtrDetails: [ + { + utztnUsgCd: item["이용용도"], + vltnDt: item["과태료 부과일(위반일)"], + vltnMttr: item["위반사항"], + levyAmt: Number(item["과태료 부과금액(천원)"]) ?? 0, + rgtr: TEST_UUID, + mdfr: TEST_UUID, + }, + ], + }; + } + }) + .filter((el: any) => el !== undefined); + return rainChkgdData; +}; diff --git a/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/headList.tsx b/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/headList.tsx new file mode 100644 index 000000000..a23d168e7 --- /dev/null +++ b/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/headList.tsx @@ -0,0 +1,109 @@ +import type { TableUpperProps } from "@/app/store/ruseFcltMng/fcltReg/rainReg"; +import { z } from "zod"; +import { guideDetail, type chHeadType } from "../rainChkgdn/headList"; + + + + + +const rswtrPolutionsZode =z.object({ + chkgdnDtlFk: z.string().nullish(), + utztnUsgCd: z.string(), + node: z.string(), + grssClfgUswtr: z.string(), + combResdch: z.string(), + trbt: z.string(), + bod: z.string(), + smll: z.string(), + crmty: z.string(), + tph: z.string(), + tn: z.string(), + ph: z.string(), + clIonQnt: z.string(), + regDt: z.string(), + rgtr: z.string(), + mdfcnDt: z.string(), + mdfr: z.string(), +}) + +export const rswtChkgdZod = z.object({ + chkgdnPk: z.number().nullish(), + bdstRuseFcltFk: z.number().nullish(), + chckYmd: z.string(), + prcsCpct: z.number(), + dtfcltCpct: z.number(), + spwtPositn: z.string(), + regDt: z.string().nullish(), + rgtr: z.string().nullish(), + mdfcnDt: z.string().nullish(), + mdfr: z.string().nullish(), + flg: z.string().nullish(), + oilqMeasMthd: z.string().nullish(), + anlOperCst: z.number().nullish(), + operMngCd: z.string().nullish(), + utztnUsgCd: z.string(), + guideDetails: z.array( + guideDetail + ), + rswtrDetails: z.array( + z.object({ + chkgdnDtlPk: z.string().nullish(), + chkgdnFk: z.string().nullish(), + utztnUsgCd: z.string(), + vltnDt: z.string(), + vltnMttr: z.string(), + levyAmt: z.string(), + regDt: z.string().nullish(), + rgtr: z.string().nullish(), + mdfcnDt: z.string().nullish(), + mdfr: z.string().nullish(), + }) + ), + rswtrPolutions: z.array( + rswtrPolutionsZode + ), +}); + +export const rswtHeadNmExport: chHeadType[] = [ + { headNm: "useQuilty", text: "연간 평균 이용수질", length: 10 }, + { headNm: "feeHist", text: "과태료 부과내역", length: 3 }, +]; + +export const rswtChkHeadListExport: TableUpperProps[] = [ + { id: "cityText", title: "시도" }, + { id: "sggText", title: "시군구" }, + { id: "bdstNm", title: "시설명" }, + { id: "operMngCd", title: "운영관리형태" }, + { id: "yearFee", title: "연간운영비(원/년)" }, + { id: "rainDtnbaOpct", title: "처리용량(m³)" }, + { id: "checkDay", title: "점검일" }, + { id: "checkPoint", title: "채수지점" }, + { id: "utztnUsg", title: "이용용도" }, + { id: "splyUswtrUtztnQnt", title: "연간보급수이용량(m³)" }, + { id: "rswtUsg", title: "연간중수도이용량(m³)" }, + + { + id: "grssClfgUswtr", + title: "총대장균군수", + upName: "useQuilty", + upSequnce: 1, + }, + { + id: "combResdch", + title: "결합잔류염소", + upName: "useQuilty", + upSequnce: 2, + }, + { id: "trbt", title: "탁도", upName: "useQuilty", upSequnce: 3 }, + { id: "bod", title: "BOD", upName: "useQuilty", upSequnce: 4 }, + { id: "smll", title: "냄새", upName: "useQuilty", upSequnce: 5 }, + { id: "crmty", title: "색도", upName: "useQuilty", upSequnce: 6 }, + { id: "tn", title: "T-N", upName: "useQuilty", upSequnce: 7 }, + { id: "tph", title: "T-P", upName: "useQuilty", upSequnce: 8 }, + { id: "ph", title: "PH", upName: "useQuilty", upSequnce: 9 }, + { id: "clIonQnt", title: "염화물", upName: "useQuilty", upSequnce: 10 }, + + { id: "dayFee", title: "부과일(위반일)", upName: "feeHist", upSequnce: 1 }, + { id: "guilty", title: "위반사항", upName: "feeHist", upSequnce: 2 }, + { id: "fee", title: "부과금액(천원)", upName: "feeHist", upSequnce: 3 }, +]; \ No newline at end of file diff --git a/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/index.tsx b/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/index.tsx index 9ba3e1404..7ea3902f5 100644 --- a/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/index.tsx +++ b/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/index.tsx @@ -1,89 +1,135 @@ import type { TableUpperProps } from "@/app/store/ruseFcltMng/fcltReg/rainReg"; -import type { chHeadType } from "../rainChkgdn"; -import type { SelectDataType } from "@common/business_components"; -import { fetchType, getFetch } from "@/components/lib/api"; -import { buildingToExcelType } from "../rainChkgdn/excel"; +import type { + SelectDataType, + sheetParameter, +} from "@common/business_components"; +import { fetchType, getFetch, postFectch } from "@/components/lib/api"; import { buildingType } from "@/app/store/common/code"; +import { rswtMakeExcelSample } from "./excel"; +import { + rswtChkgdZod, + rswtChkHeadListExport, + rswtHeadNmExport, +} from "./headList"; +import { z } from "zod"; +import { ToastFormUi } from "@/components/ui/toast/toastFromUi"; +import { type chHeadType } from "../rainChkgdn/headList"; - - -export const rswtHeadNm:chHeadType[] = [ - {headNm:"useQuilty", text:"연간 평균 이용수질" , length: 10}, - {headNm:"feeHist", text:"과태료 부과내역" , length: 3}, -] -export const rswtChkgdnHeadList: TableUpperProps[] = [ - { id: "cityText", title: "시도" }, - { id: "sggText", title: "시군구" }, - { id: "bdstNm", title: "시설명" }, - { id: "operMngCd", title: "운영관리형태" }, - { id: "yearFee", title: "연간운영비(원/년)" }, - { id: "rainDtnbaOpct", title: "처리용량(m³)" }, - { id: "checkDay", title: "점검일" }, - {id: "checkPoint", title: "채수지점"}, - { id: "utztnUsg", title: "이용용도" }, - { id: "splyUswtrUtztnQnt", title: "연간보급수이용량(m³)" }, - { id: "rswtUsg", title: "연간중수도이용량(m³)" }, - - - { id: "grssClfgUswtr", title: "총대장균군수", upName: "useQuilty", upSequnce: 1 }, - { id: "combResdch", title: "결합잔류염소", upName: "useQuilty", upSequnce: 2 }, - { id: "trbt", title: "탁도", upName: "useQuilty", upSequnce: 3 }, - { id: "bod", title: "BOD", upName: "useQuilty", upSequnce: 4 }, - { id: "smll", title: "냄새", upName: "useQuilty", upSequnce: 5 }, - { id: "crmty", title: "색도", upName: "useQuilty", upSequnce: 6 }, - { id: "tn", title: "T-N", upName: "useQuilty", upSequnce: 7 }, - { id: "tph", title: "T-P", upName: "useQuilty", upSequnce: 8 }, - { id: "ph", title: "PH", upName: "useQuilty", upSequnce: 9 }, - { id: "clIonQnt", title: "염화물", upName: "useQuilty", upSequnce: 10 }, - - - - { id: "dayFee", title: "부과일(위반일)", upName: "feeHist", upSequnce: 1 }, - { id: "guilty", title: "위반사항", upName: "feeHist", upSequnce: 2 }, - { id: "fee", title: "부과금액(천원)", upName: "feeHist", upSequnce: 3 }, - - -]; +const rswtChkHeadList= rswtChkHeadListExport +const rswtHeadNm =rswtHeadNmExport +export type rswtChkZodType = z.infer<typeof rswtChkgdZod>; export type rswtChkgdnState = { rswtChkgdn: { - rswtChkgdnHeadList: TableUpperProps[]; - rswtHeadNm:chHeadType[]; + rswtChkHeadList: TableUpperProps[]; + rswtHeadNm: chHeadType[]; + excelList: [typeof sheetParameter]; + rswtChkRes:rswtChkZodType[] + isInit: boolean; }; }; type rswtChkgdnActions = { - rswtChkgdnInit: (sggCd: string, searchTxt: string, flag: string) => void; - rswtrChkExcelDownload(sidoCode: SelectDataType[],sggCode: SelectDataType[]): Promise<void> + rswtChkgdnInit: (year: string, sggCd: string) => void; + rswtrChkExcelDownload( + sidoCode: SelectDataType[], + sggCode: SelectDataType[] + ): Promise<void>; + rswtChkExceDataUpload: (excelData: rswtChkZodType[]) => void; }; export type rswtChkgdnType = rswtChkgdnState & rswtChkgdnActions; export const rswtChkgdnStateExport: rswtChkgdnState = { rswtChkgdn: { - rswtChkgdnHeadList: rswtChkgdnHeadList, - rswtHeadNm:rswtHeadNm + rswtChkHeadList: rswtChkHeadList, + rswtHeadNm: rswtHeadNm, + excelList: [{ data: [[]], headerNum: 0, headerRange: "A1:V3" }], + rswtChkRes:[], + isInit: false, + }, }; +const initFetch = async (year: string | undefined, sgg: string | undefined) => { + const rswtChkRes = await getFetch( + "wr", + `/reuseChk/reuseWater/` + (year ? `${year}` : "2024") + (sgg ? `/${sgg}` : "/0") + ); + return rswtChkRes; +}; + + export const rswtChkgdnActionsExport: (set: any) => rswtChkgdnActions = ( set: any ) => { - rswtrChkExcelDownload: async (sidoCode: SelectDataType[],sggCode: SelectDataType[]) => { - const data = await getFetch(fetchType.WR, `/reuseFc/buildingFlg/${buildingType.RST}/41`) - const excelList = buildingToExcelType(data, sidoCode, sggCode, buildingType.RST); - set((state: rswtChkgdnState) => ({ - rswtChkgdn: { - ...state.rswtChkgdn, - excelList:excelList - }, - })); - } - } return { - rswtChkgdnInit: async (sggCd: string, searchTxt: string, flg: string) => { + rswtrChkExcelDownload: async ( + sidoCode: SelectDataType[], + sggCode: SelectDataType[] + ) => { + const data = await getFetch( + fetchType.WR, + `/reuseFc/buildingFlg/${buildingType.RST}/41` + ); + const excelList: any = []; + const exceldata = rswtMakeExcelSample(data, sidoCode, sggCode); + excelList.push({ + data: exceldata.guideDetail, + headerNum: 0, + headerRange: "A1:AI1", + }); + excelList.push({ + data: exceldata.polutions, + headerNum: 0, + headerRange: "A1:AZ1", + }); + return set((state: rswtChkgdnState) => ({ + rswtChkgdn: { + ...state.rswtChkgdn, + excelList: excelList, + }, + })); + }, + rswtChkgdnInit: async (year: string, sggCd: string) => { + try { + const rswtChkRes = await initFetch( '2024',sggCd); + + return set((state: rswtChkgdnState) => ({ + ...state, + rswtChkgdn: { + ...state.rswtChkgdn, + rswtChkRes: rswtChkRes, + isInit:false, + }, + })); + } catch (error) { + console.log(error); + } + }, + rswtChkExceDataUpload: async (excelData: rswtChkZodType[]) => { + try { + const res = await postFectch("/reuseChk/reuseWater", excelData); + const rswtMessage: string[] = []; + if (res.ok) { + ToastFormUi({ + body: "지도점검 등록이 완료되었습니다.", + title: "지도점검 등록", + }); + } + // const rainChkRes = await initFetch('2024', '0'); + return set((state: rswtChkgdnState) => ({ + ...state, + rswtChkgdn: { + ...state.rswtChkgdn, + rswtMessage: rswtMessage, + // rainChk: rainChkRes, + }, + })); + } catch (error) { + console.log(error); + } }, - , }; }; diff --git a/apps/wr/components/lib/func/TableData.tsx b/apps/wr/components/lib/func/TableData.tsx index 08126336e..86a58d6c7 100644 --- a/apps/wr/components/lib/func/TableData.tsx +++ b/apps/wr/components/lib/func/TableData.tsx @@ -1,4 +1,5 @@ import type { rainChkZodType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn"; +import type { rswtChkZodType } from "@/app/store/prfmncReg/chkgdnReg/rswrtChkgdn"; import type { TableUpperProps } from "@/app/store/ruseFcltMng/fcltReg/rainReg"; import { Checkbox, Input, TableCell, TableRow } from "@common/components/ui"; @@ -7,9 +8,9 @@ import { Checkbox, Input, TableCell, TableRow } from "@common/components/ui"; -export const rightTableData = (rainChk: rainChkZodType[]) => { +export const rainChkTableData = (chkData: rainChkZodType[]) => { // 1. chkgdnFk + utztnUsgCd 기준 누계 정보 만들기 - const maketotal = rainChk.map((item: rainChkZodType) => { + const maketotal = chkData.map((item: rainChkZodType) => { const selectTotal = item.guideDetails.reduce((acc: any, cur: any) => { const code = cur.utztnUsgCd; const fk = cur.chkgdnFk; @@ -36,7 +37,7 @@ export const rightTableData = (rainChk: rainChkZodType[]) => { // 3. rainyDetails에 누계 정보 주입 const updatedRainyDetails: any[] = []; - rainChk.forEach((rd: rainChkZodType) => { + chkData.forEach((rd: rainChkZodType) => { rd.rainyDetails.forEach((rainyItem: any) => { const key = `${rainyItem.chkgdnFk}_${rainyItem.utztnUsgCd}`; const totals = flatTotalMap[key] || { @@ -57,6 +58,110 @@ export const rightTableData = (rainChk: rainChkZodType[]) => { return updatedRainyDetails; }; + + + + +export const rswtChkTableData = (chkData: rswtChkZodType[]) => { + // 1. chkgdnFk + utztnUsgCd 기준 누계 정보 만들기 + const maketotal = makeTotalChk(chkData) + + // 2. 누계 정보들을 하나의 객체로 합치기 (평탄화) + const flatTotalMap = Object.assign({}, ...maketotal); + + // 3. rainyDetails에 누계 정보 주입 + + const updatedRainyDetails: any[] = []; + // chkData.forEach((rd: rswtChkZodType) => { + // rd.rswtrDetails.forEach((rswtItem: any) => { + // const keyGuide = `${rswtItem.chkgdnFk}_${rswtItem.utztnUsgCd}_guide`; + // const keyPolution = `${rswtItem.chkgdnFk}_${rswtItem.utztnUsgCd}_poliution`; + // const totalsGuide = flatTotalMap[keyGuide] || { + // totalSplyUswtrUtztnQnt: 0, + // totalRuseFcltUtztnQnt: 0, + // }; + + // updatedRainyDetails.push({ + // ...rd, + // ...rd.building, + // ...rswtItem, + // ...totalsGuide, + // averCombResdch:totalsGuide.averCombResdch/rswtItem + // totalsGuide.averTrbt:0, + // totalsGuide.averBod:0, + // totalsGuide.averSmll:0, + // totalsGuide.averCrmty:0, + // totalsGuide.averTph:0, + // totalsGuide.averTn:0, + // totalsGuide.averPh:0, + // totalsGuide.averClIonQnt:0, + // rowspan: rd.rswtrDetails?.length, + // }); + // }); + // }); + + return updatedRainyDetails; +}; + +const makeTotalChk=(chkData: rswtChkZodType[])=>{ + const maketotal = chkData.map((item: rswtChkZodType) => { + const selectTotalWater = item.guideDetails.reduce((acc: any, cur: any) => { + const code = cur.utztnUsgCd; + const fk = cur.chkgdnFk; + const flg = 'guide' + + const key = `${fk}_${code}_${flg}`; + acc[key] = acc[key] || { + totalSplyUswtrUtztnQnt: 0, + totalRuseFcltUtztnQnt: 0, + }; + + acc[key].totalSplyUswtrUtztnQnt += cur.splyUswtrUtztnQnt; + acc[key].totalRuseFcltUtztnQnt += cur.ruseFcltUtztnQnt; + + return acc; + }, {}); + const selectAverPolution = item.rswtrPolutions.reduce((acc: any, cur: any) => { + const code = cur.utztnUsgCd; + const fk = cur.chkgdnFk; + const flg = 'poliution' + + const key = `${fk}_${code}_${flg}`; + acc[key] = acc[key] || { + averCombResdch:0, + averTrbt:0, + averBod:0, + averSmll:0, + averCrmty:0, + averTph:0, + averTn:0, + averPh:0, + averClIonQnt:0, + node:0 + }; + + acc[key].averCombResdch+=cur.combResdch + acc[key].averTrbt+=cur.trbt + acc[key].averBod+=cur.bod + acc[key].averSmll+=cur.smll + acc[key].averCrmty+=cur.crmty + acc[key].averTph+=cur.tph + acc[key].averTn+=cur.tn + acc[key].averPh+=cur.ph + acc[key].averClIonQnt+=cur.clIonQnt + acc[key].nod+=1 + + return acc; + }, {}); + + + return {...selectTotalWater, ...selectAverPolution}; // 모든 키: `${chkgdnFk}_${utztnUsgCd}` 형태 + }); + return maketotal; +} + + + export const chRowspanTable = ( makeTableData: any[], headList: TableUpperProps[], @@ -90,7 +195,6 @@ export const chRowspanTable = ( renderedSet.add(rowIndex + i); } renderedMap.set(key, renderedSet); - return ( <TableCell key={key} diff --git a/apps/wr/components/lib/func/commom.tsx b/apps/wr/components/lib/func/commom.tsx index a91750fce..80b8ebf42 100644 --- a/apps/wr/components/lib/func/commom.tsx +++ b/apps/wr/components/lib/func/commom.tsx @@ -3,6 +3,8 @@ import { filePickedBlob, type SelectDataType, type ReturnType, + filePickedBlobArray, + sheetParameter, } from "@common/business_components/ui"; import type { RefObject } from "react"; @@ -62,7 +64,8 @@ export const excelDownload = ( excelList: [][], fileNm: string, sampleFile: string, - headerRange = "A1:V3" + headerRange = "A1:V3", + writeLine=4 ) => { if (excelList.length === 0) return; fetch(`/${sampleFile}`, {}).then(async (response) => { @@ -72,10 +75,29 @@ export const excelDownload = ( // 받은 파일 바인너리 처리 const blobRet = await response.arrayBuffer(); // 버퍼에 있는 엑셀데이터 전달 후 json으로 변환 - await filePickedBlob(blobRet, excelList, fileNm, 4, headerRange); + await filePickedBlob(blobRet, excelList, fileNm, writeLine, headerRange); }); }; +export const excelArrayDownload = ( + fileNm: string, + sampleFile: string, + sheetArray:[typeof sheetParameter] +) => { + if (!sheetArray) return; + fetch(`/${sampleFile}`, {}).then(async (response) => { + if (!response.ok) { + throw Error(response.statusText); + } + // 받은 파일 바인너리 처리 + const blobRet = await response.arrayBuffer(); + // 버퍼에 있는 엑셀데이터 전달 후 json으로 변환 + await filePickedBlobArray(blobRet, fileNm, sheetArray); + }); +}; + + + // onchange 텍스트를 숫자로 and ,제거 export const handleCommaNumberChange = ( e: React.ChangeEvent<HTMLInputElement>, diff --git a/apps/wr/components/ui/modal/chkgd/chkgdExcel/chkgdExcel.tsx b/apps/wr/components/ui/modal/chkgd/chkgdExcel/chkgdExcel.tsx index ebbae3481..390e17318 100644 --- a/apps/wr/components/ui/modal/chkgd/chkgdExcel/chkgdExcel.tsx +++ b/apps/wr/components/ui/modal/chkgd/chkgdExcel/chkgdExcel.tsx @@ -1,14 +1,21 @@ import { useWrStore } from "@/app/store"; -import { rainRegExcel, type rainChkZodType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn"; +import { type rainChkZodType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn"; import { excelToRainChkData } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn/excel"; +import { chkgdRegExcel } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList"; +import type { rswtChkZodType } from "@/app/store/prfmncReg/chkgdnReg/rswrtChkgdn"; +import { + excelToRswtGuideData, + excelToRswtPolution, +} from "@/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/excel"; import { ToastFormUi } from "@/components/ui/toast/toastFromUi"; import { filePicked, + filePickedArray, FileUpload, Form, FormField, FormUi, - Title + Title, } from "@common/business_components/ui"; import { Button, Input } from "@common/components/ui"; import { zodResolver } from "@hookform/resolvers/zod"; @@ -17,107 +24,199 @@ import { useForm } from "react-hook-form"; import type { z } from "zod"; export default function ChkgdExcel({ type }: { type: string }) { - const { rainChkExcelDataUPload } = useWrStore((state) => { - return state; - }); + const { + rswtChkgdn, + rainChkExcelDataUPload, + rswtChkExceDataUpload, + } = useWrStore((state) => { + return state; + }); const [files, setFiles] = useState<File[] | null>(null); - const [excelData , setExcelData] = useState<rainChkZodType[]>() - const defaultValues: z.infer<typeof rainRegExcel> = rainRegExcel.parse({}); + const [excelData, setExcelData] = useState< + rswtChkZodType[] | rainChkZodType[] + >(); + const [excelRswtData, setExcelRswtData] = useState<rswtChkZodType[]>(); - const form = useForm<z.infer<typeof rainRegExcel>>({ - resolver: zodResolver(rainRegExcel), + const defaultValues: z.infer<typeof chkgdRegExcel> = chkgdRegExcel.parse({}); + + const form = useForm<z.infer<typeof chkgdRegExcel>>({ + resolver: zodResolver(chkgdRegExcel), defaultValues: defaultValues, }); useEffect(() => { - if (files) { - const file = files[0]; - if(form.getValues("chckYr").length<1 ){ - ToastFormUi({body:"파일업로드 이전에 등록년도를 작성해주세요 " ,title:'지도점검 등록'}) - setFiles(null) - return - } - if (file) + if (files) { + const file = files[0]; + if (form.getValues("chckYr").length < 1) { + ToastFormUi({ + body: "파일업로드 이전에 등록년도를 작성해주세요 ", + title: "지도점검 등록", + }); + setFiles(null); + return; + } + if (file) + if (type === "RNB") { filePicked(file, 0).then((res) => { if (res !== undefined) - setExcelData(excelToRainChkData(res, form.getValues("chckYr"))) + setExcelData(excelToRainChkData(res, form.getValues("chckYr"))); }); - } + } else if (type === "RST") { + filePickedArray(file, 0).then((res) => { + if (res && res?.length > 1) { + const excelArray = []; + const guideData = excelToRswtGuideData( + res[0], + form.getValues("chckYr") + ); + const polutionData = excelToRswtPolution( + res[1], + form.getValues("chckYr") + ); + + + const keyGenerator = (item: rswtChkZodType) => + `${item.bdstRuseFcltFk}|${item.chckYmd}|${item.utztnUsgCd}`; + const totalData: rswtChkZodType[] = guideData.map((guide) => { + const fusion = polutionData.find( + (polution) => + guide.bdstRuseFcltFk === polution.bdstRuseFcltFk && + guide.chckYmd === polution.chckYmd && + guide.utztnUsgCd === polution.utztnUsgCd + ); + + if (fusion) { + return { + ...guide, + rswtrPolutions: fusion?.rswtrPolutions, + rswtrDetails:fusion?.rswtrDetails + }; + } else { + return { + ...guide, + }; + } + }); + const guideKey = new Set(guideData.map(keyGenerator)); + + const onlypolution = polutionData.filter( + (item) => !guideKey.has(keyGenerator(item)) + ); + + const listTotalData:rswtChkZodType[] =[ + ...totalData, + ...onlypolution + ] + + setExcelRswtData(listTotalData); + } + }); + } + } }, [files]); - - const onSubmit =(values: z.infer<typeof rainRegExcel>)=> { - if(values.chckYr ===''){ - ToastFormUi({body:"등록년도를 작성해주세요" ,title:'지도점검 등록'}) - return - }else if(excelData ===undefined || excelData?.length === 0 ){ - ToastFormUi({body:"빈 파일 및 파일 오류 입니다." ,title:'지도점검 등록'}) - return - } - if (excelData) { - rainChkExcelDataUPload(excelData); - } + + function findMatchingItems<T>( + arrayA: T[], + arrayB: T[], + keySelector: (item: T) => string, + condition?: (item: T) => boolean + ): T[] { + const bKeySet = new Set(arrayB.map(keySelector)); + return arrayA.filter( + (item) => + (!condition || condition(item)) && bKeySet.has(keySelector(item)) + ); } + + + const onSubmit = (values: z.infer<typeof chkgdRegExcel>) => { + if (values.chckYr === "") { + ToastFormUi({ body: "등록년도를 작성해주세요", title: "지도점검 등록" }); + return; + } + + if (type === "RNB") { + if (excelData === undefined || excelData?.length === 0) { + ToastFormUi({ + body: "빈 파일 및 파일 오류 입니다.", + title: "지도점검 등록", + }); + return; + } else { + rainChkExcelDataUPload(excelData as rainChkZodType[]); + } + } else if (type === "RST") { + if (excelRswtData === undefined || excelRswtData?.length === 0) { + ToastFormUi({ + body: "빈 파일 및 파일 오류 입니다.", + title: "중수지도점검 등록", + }); + return; + } else { + rswtChkExceDataUpload(excelRswtData as rswtChkZodType[]); + } + } + }; return ( <> <div> <Form {...form}> - <form onSubmit={form.handleSubmit(onSubmit)}> - <div> - <Title - title={ - type === "RNB" - ? "빗물 이용시설" - : "중수도 이용시설" + "점검 결과등록" - } - > -
-
-

- {"○점검정보 입력"} -

+
+
+ +
+
+

+ {"○점검정보 입력"} +

- -
+ +
-
- ( - - - - )} - /> +
+ ( + + + + )} + /> - ( - - { - if(files) setFiles(files) - } } - fileTypes="xlsx" - > - - )} - /> -
- + ( + + { + if (files) setFiles(files); + }} + fileTypes="xlsx" + > + + )} + /> +
+
- ); } diff --git a/apps/wr/components/ui/modal/chkgd/chkgdModal/chkgdModal.tsx b/apps/wr/components/ui/modal/chkgd/chkgdModal/chkgdModal.tsx index d6c332042..5e6727d56 100644 --- a/apps/wr/components/ui/modal/chkgd/chkgdModal/chkgdModal.tsx +++ b/apps/wr/components/ui/modal/chkgd/chkgdModal/chkgdModal.tsx @@ -2,9 +2,9 @@ import { useWrStore } from "@/app/store"; import { ScrollArea, Title, UiTable } from "@common/business_components/ui"; import { Button } from "@common/components/ui"; import { useEffect, useState } from "react"; -import type { splRuseWaterType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn"; import { headMakeprfmnc } from "@/app/ruseFcltMng/prfmncReg/chkgdnReg/TableHead"; import { GuideDetailsTable } from "@/components/ui/table/chkgdTable/guideDetailTable/guideDetailTable"; +import type { splRuseWaterType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList"; export default function ChkgdModal({ type, obj }: { type: string; obj: any }) { const { rainChkgdn, rainChkDetilUpdate, rainChkGuidUpdate } = useWrStore( diff --git a/apps/wr/components/ui/modal/sttn/sttnDetailModal/sttnDetailModal.tsx b/apps/wr/components/ui/modal/sttn/sttnDetailModal/sttnDetailModal.tsx index 2ea483364..443757f98 100644 --- a/apps/wr/components/ui/modal/sttn/sttnDetailModal/sttnDetailModal.tsx +++ b/apps/wr/components/ui/modal/sttn/sttnDetailModal/sttnDetailModal.tsx @@ -7,7 +7,7 @@ import type { customRswtBdst } from "@/app/store/ruseFcltMng/fcltReg/rswtReg"; import { useWrStore } from "@/app/store"; import { useEffect, useState } from "react"; import type { CommonTableProps } from "@/app/store/common/type/headder"; -import { rightTableData } from "@/components/lib/func/TableData"; +import { rainChkTableData } from "@/components/lib/func/TableData"; import { HeadMakeColSpan } from "@common/business_components/ui/table/HeadMakeColSpan"; export default function SttnDetailModal({ @@ -40,7 +40,7 @@ export default function SttnDetailModal({ formObj.reuseBuilding[0] && rainSttn.rainChkRes?.bdstRuseFcltFk ) { - const tableTotalData = rightTableData([rainSttn.rainChkRes]); + const tableTotalData = rainChkTableData([rainSttn.rainChkRes]); const data = tableTotalData.map((item) => { const obj = { totalSplyUswtrUtztnQnt: item.totalSplyUswtrUtztnQnt, @@ -72,7 +72,7 @@ export default function SttnDetailModal({ return (
- + 0?"h-[80vh]":"h-[70vh]"} > {type === "RNB" ? ( diff --git a/apps/wr/components/ui/table/chkgdTable/rainChkgd/rainChkgdTable.tsx b/apps/wr/components/ui/table/chkgdTable/rainChkgd/rainChkgdTable.tsx index 4aa57fe5e..a26cf0541 100644 --- a/apps/wr/components/ui/table/chkgdTable/rainChkgd/rainChkgdTable.tsx +++ b/apps/wr/components/ui/table/chkgdTable/rainChkgd/rainChkgdTable.tsx @@ -5,7 +5,7 @@ import type { rainChkZodType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn" import { tableData } from "@/components/lib/func/commom"; import { chRowspanTable, - rightTableData + rainChkTableData } from "@/components/lib/func/TableData"; import { Table, @@ -66,13 +66,11 @@ export default function RainChkgdTable({ rowspan: item.rainyDetails?.length, }; }); - const tableTotalData = rightTableData(rainChg) + const tableTotalData = rainChkTableData(rainChg) setMakeTableData(tableTotalData); }, [rainChkgdn.rainChk, code]); useEffect(() => { - - //데이터를 평탄화 한것중에 체크박스 선택한 테이블만 선택 const selectedData = makeTableData.filter((item) =>selectedIds.includes(item.chkgdnPk)); @@ -132,10 +130,6 @@ export default function RainChkgdTable({ } },[selectedIds]) - useEffect(() => { - - },[]) - return ( diff --git a/apps/wr/components/ui/table/chkgdTable/rswtChkgd/rswtChkgdTable.tsx b/apps/wr/components/ui/table/chkgdTable/rswtChkgd/rswtChkgdTable.tsx index b378167cc..ebc7d020f 100644 --- a/apps/wr/components/ui/table/chkgdTable/rswtChkgd/rswtChkgdTable.tsx +++ b/apps/wr/components/ui/table/chkgdTable/rswtChkgd/rswtChkgdTable.tsx @@ -1,30 +1,79 @@ -'use client' +"use client"; import { headMakeprfmnc } from "@/app/ruseFcltMng/prfmncReg/chkgdnReg/TableHead"; import { useWrStore } from "@/app/store"; -import { UiTable } from "@common/business_components/ui"; +import type { rswtChkZodType } from "@/app/store/prfmncReg/chkgdnReg/rswrtChkgdn"; +import { chRowspanTable } from "@/components/lib/func/TableData"; +import { Table, TableBody, TableHeader } from "@common/components/ui"; +import { useCallback, useState } from "react"; -export default function RswtChkgdTable() { +export default function RswtChkgdTable({ + detaiRswtlUpdate, + handleRswtClick = () => {}, + deleteRswtData, +}: { + detaiRswtlUpdate: (data: rswtChkZodType[]) => void; + handleRswtClick?: (data: any) => void; + deleteRswtData: (selectedIds: number[]) => void; +}) { + const { code, rswtChkgdn, rswtChkgdnInit } = useWrStore((state) => state); - const { rswtChkgdn} = useWrStore((state) => state); - const nodeList = headMakeprfmnc(rswtChkgdn?.rswtChkgdnHeadList , rswtChkgdn?.rswtHeadNm ); - + const [makeTableData, setMakeTableData] = useState([]); + const [selectedIds, setSelectedIds] = useState([]); + const [upRaintDetail, setUpRaintDetail] = useState([]); + const toggleSelection = (pk: number, checked: boolean) => { + setSelectedIds((prev) => + checked ? [...prev, pk] : prev.filter((item) => item !== pk) + ); + }; + const handleChange = ( + rowIndex: number, + field: keyof (typeof makeTableData)[0], + value: string | number + ) => { + setMakeTableData((prev) => + prev.map((row, idx) => { + return idx === rowIndex ? { ...row, [field]: value } : row; + }) + ); + }; - return( - <> - { } } onPageChange={function (page: number): void { - throw new Error("Function not implemented."); - } } > - {nodeList} - - - ) -} \ No newline at end of file + + const handleCellClick = useCallback( + (index: number, headId: string) => { + if ( + headId === "totalSplyUswtrUtztnQnt" || + headId === "totalRuseFcltUtztnQnt" + ) { + handleRswtClick(makeTableData[index]); + } + }, + [makeTableData, handleRswtClick] + ); + + + + + return ( + <> +
+ + {headMakeprfmnc(rswtChkgdn?.rswtChkHeadList, rswtChkgdn?.rswtHeadNm)} + + + + {chRowspanTable( + makeTableData, + rswtChkgdn.rswtChkHeadList, + handleChange, + toggleSelection, + selectedIds, + handleCellClick + )} + +
+ + ); +} diff --git a/apps/wr/public/sample_rs_chk.xlsx b/apps/wr/public/sample_rs_chk.xlsx index e114862bd44577b7fea40e232b736abc91181abe..1569fbbcee08b0054ed0c903dfba81442f1c7335 100644 GIT binary patch literal 12474 zcmdVAbyQr-w=UYaTj&H0?(VL^T@u_ic;ha?-QAtw4#C|mxCagH?r_=J=e>P5zjxj} zf4#)J!j7Kq zuix;PPT6h-#DEj=7NNL@eJgR;?EGfwSVtifo^KM%Y-Z?U63|;ma2Q#Wn7oV6PY*R) zM^?7E&bdi6ZlrTcv`{XiefP41iBo!41Wuzzqu0rzBOxUGsthyt-nQMF(y{?YxG}7eKLGL!K0FN?CmG1ZTS?bs4wWuVQ>Um|!eM z=K&*BJzXDa^1mE1I>yDblx9kDfd$CFx1d~Onl$E|Ts`o~4TEO(*%qJkZUd?W&khGa z^G|A?*=3sgIYo3+Uz{RA)UccIfyw_%7ZI{IEy&uAUeDT^ z&e_r;{d1@6G6QO>%09nvTg@Pyl2Tb(mKoZHe2*l~+96kLPgfPKTTqtHYH*oW%lK!| z$*3D%`v}M5zQbfMA|HjhS-rMWDS+7W`-pr^;Pv(fOtV>==eir{qw^P3crLw9qoTq; zXom^Jl7v;GEyVZAG9lkx37I}4yE*GV(F>i7`A&4Wz83B=V%X(X8N17>$cW zi8UNY?!Vay2Zzt2mOe#-OrX=!^&?+F_KueHMm>2);As?~HHGl;TVf{EvsALZ3^;-p zGD>;f9QeBPam&Dsein0Li}be!3||bZFc;>&%+eEmMz9riV3QI$P4X&Sd-Ia%R?Y?N zogh3Cw|XyQubcO(kE-^JHbn|?rVY2R@Ih$Sv%n}@=`Afd4MORHGy*!bX>=%UbWk0_ zZmM#2!C+K^vtJ`^|Gq{|1T^?gU%udwK$lCCvrxl{Dcx;ka~; zF#Ar(JOlG@%9a2^DU9z}(?}T6a?fV%>Bk!!Wi+ssZ=qeUvdA|Fi8Cc{Q5_-3F(W{k zJ9n*KDVIA$H^x~=^oA%Rfrt75*y zKE**m{)`99h_gdqZT#93(;%iEb|J0(eXDvoe>>X=J=}_0*(4{c4<`_QUW4gsVMsr*m zw!37oUt|Pw?}*5b3E*TKN;hkKloTKfLEwzo>wLa|iQFCAeQwpuzuXH}{0_M)g+4X1 zXp0I-HkhSrEEP`>EfG(bBvRP#P07k&#RSm!EjYh->FWb0f#dJp78)S=IlAL>yfl1N zJ=#PRbLdivbX-70K+(W@C8L0I07xr=mo-}ieEZ%%O6199KYJ?t+&;LO z+Pq&1oH;)-bE&((+G%)xY<{j)_407y&D7SRqd4PjHfavTLWI=5q`SG`v16ZDXnAHd zH}$728$WvHFQdn}y4<-ptJ3zgsN=J!yH+m?hfMh$1rN9U#G13FlT^vm8EF%EF&*Aj zcreN$Ox-)7^KT=MLOHfyz8V(;5&*#d+sJ=Ak^kM)e;i6{Y^T{DhvM7jd)8_bV`UV^ z)f)DxLgNO|r?4^J!?RwE>J>oEI8$Ccoroxh!pLG4qoRitGB^3pH!r|3lzaJTaV`}#um;f zFh>i(YiYPJd{8{Eq-P#ORzdmPqNT)&y^_x7plh1v*U(@s3?VS_#UESzyd5=!FP1c# zqdI6Es(<1fCUd#0e za3?|dyh1p|z!Txf{;c{4__wb7I%(T!VE_O!d;kFbZ(Z4&7+D(8|M_M7Yf&7jX@o9` z0zK(3`C(hR4?;A$+#ymOW5JpBu(krc^JO-{vzr+c#I=d%{*Yts-xZw0P3YK)95ps3Um9 zT4*#=QeKBdb@hH)&3AVAs9b3kI{u!IbiEV1(t?}S$v0N<2rgHJohY?_rb1;-#uxxQ zz|!Ww4u^18Z-O$;`{7DZkV3IX!sN6UQ7#GwVNfJ4j7a}5x(FqkUx+YNP_sPYVGH~5 zeS5-V?PbQHz@=aGZ1_e~Xq27LrRFTdyI!P#bi zf261OyO?Zx>p_Cgk4ivUhsT5OIxkmG-}^G#A5ZTG*+a(IaDV5GfK_->{B!>zszY|2R{E2x~SvamY2MJ>t5O86w zE=`qkaQ_39i)Q0@4g&^9>m6b-0wvS>JqLSOD$pNS#wMtdaB&{24@$d1O595OIvArW zNv6Rj5%Z%kycP#Zu4aevS_#RwJ1rnuAnMSa?MuXjUhM$4mV1&|$GLE=uCkz65TSgl@0PAW}QIHG1i^+BSFJ%p7=JN&dLsQE8PO;(hTP9U20D zVL&_IpC*fB@0=xG_LJvYLkPLicX=0*?Hu_)0OWA-^_fEOOGd$0Dv;1HTp9E(K#Tc^ ziME-EdW_=E;%k@5QJ(Y5w566gJRB=@or9v+)M=7R9m|^PkP?b4ujPkNy;>{3J34;$@ELJa7zGxl5SCvAbqm%^p)H&MRh4M~wvP{c(=aUA z2bhVu3&Iu~r4eU=zLx~@Y1)p6X4{mqS37pPBlI#u#|-#U!$kzRHsxm9Of#jYAnEO8 zs|}}h&;s2NjZrhJI2H-G{1HqeLwjJb&PMiZKJ>PP>g&kFAId>}o+KE7^xGWBA~8ws zk_#r!S9J~Pit&yoUQj8HnqASmml01r!Sjf2u+8=(M?bXTZD0vbqP?Xd!AU)7{1xO?-= z^d%m5A~`ZaiG8?1u{fdW8W4RRLkkrl@vFeih2{Oydo9~*5!V#F%Wx6qC8xq-*ocXm z?0KhBLm^D5rUM1MiO<80Rynnc@iM)l84_jheosEdCe)>xtGOA4)@M7GBVC5ysA9j2 zJc~Z-J5zfGL>tB{#Fgb~DWxktC;P^9IfB1T#mRQwnVuFD%sfCrZ2^Gkq`i; zZ8Xzllp)6@+md<>D)vL|!D6*7#cr!PNz0{jC0ED%<)bh7pZ3>1<*R0fLHJfqd-`|J z)LZ7D4nf;ak4}S5-=jUv_A)&A5M&kxfDMwo?PD@ZYEuR2<{{`|352QLO%4y*K znb%qx_b~5V&2klUTe3tR8S@zD4)?<^<3;x?!|^#jhW^+L)@7Wf(dpvWEvA7iGj#wX9i~vh|W;^}Me6ni+@q$-ZO1Ls*#tPY1nO@$4-c zaPbq!bE_8iWTQ;R=~bxX>&~;La1zu<*UvgI=g}=!x$qVV*c$R1Gh1f;QxD8ZM~&v{ zo3@pUeAb4_j2CjPSW!v|zbgKMq;vj0ljff1F}??ZP=FGJ$PF>o%KEgGad8@O{kWIA z_w>0hf^yA+YZs}EvAG#^RaRx}Yek^uwAD1SBW&SZ*bsDNQ!zy@-52PlLE2qdS;6p~ zcY08)1#ja6^x>yw%sD7VV`Tb_^_YP!D_9)5!Krt4_s(;`yRY|KU&n6PCt;!^>4`iO ze&?ywC|)H&I~!B(&48!GmY~JOJz|v6Yfm@!5^7?*SL|biTUA8NbP@UO=dvr_@=%6O z4i!BJA>>yGLPznko~*=f?Y%nIvf z!yyO^lyTc2XCX|C;;6cBwNhP~k$E)UPKp&1TUZz*RxgbHQM1&rUodF4<)VjJQ0vBl zUZQDg(UpiZTP!jsa31h!0;&9}iE5%V=qJOXWjxrj6{uJ`sTgc^|8mJ{?FX03QJq$9 zYl$qL@Nkp5OEY&W)y5RHfCE&)@0>^V?=%P9IZBCY43!mEq;YCVGZdy|c{wF)3}|JG z9q=&l1VbpqGlN3HCD*jCJwFci;faHqXjb1%6njX$b{++At@;!WpZ|F%Tx+&kjA_^Vt;gR?)r(qpKL4t*SijWxj@R- zHh3sl)|(m`jxHxZI&E!AboqmNJ_$(8tofJKiB&W7Pvzx~!|Mnb!Fs#VM?E;x@xc@_ zD8%EOI~)zl8t)kE6B4*=O01(#JR3w`SgA-R%G;L99a`r4jqX+1(Dny|yWodRiN3_4 zlTtNWD+>=ZhlNY_E~V=zXMjL@z%F0(DcSP&+Z#d()G-Tbs47f2Rm_B zxh!6WvCPva3cWm*zK$_eYH&*N=7SH`~>Am!z`TN=sjuCkI^~&ChdgNK+%6Arxu9`}U3p>_Y1&zgd6$X*y+f85C zl{Z~~AmjIlBxX0IU;Hwv|#r*#1XkQ#j?h?v{y$O&wm6~1-!tc#-QZvT)| z%-s1QqZrq_8)JRnjvB2N#1`C2@!eFGxCT?}UB=5;3FBT;Zo(?Eu)Fp!2qK5#V#y0K zapwmZ9K*$ucj3>ArEjo9k3M;LKS8mjF(RKC4isfBig12@{$^F2L- za6dw55!goJx$aoWIA3pIZZgiORB5dgTNVmL4?>ROejc*da{vh4bHTpW3-J?J-4qCf zbgs&1gyA)TWQ$-2k`4o0+$xqArDrjs)iIcZ*z`qfT?C&jw+2dmFqC1c`PEfFZZx4% zW=KwCTteyokyj0~fR#Q?#9~$pQ3(zo?X`=}qa) zfBuw9$N!lwcMM55di^2N&BK`lajr}EJ7f$u0oh^h9b}DXJ-YDi8uJLkDIAp$k{HPE zXP>^iPl2)!hArRKzP>wn=H@0&97@}vC0J_~RkMU@KO9p3RMbKv-VS}~MH7U^d4 zCi-otWiT4wtxqFK>-Md+mv=8Eo_nTZO!_2+j- z%TWt1v|~E@xU+^f9Q}b0Qei^ZlbwHy2ivG7_O9dNAJZ@2kvb-9J^XdaxO!0Sp1Ilm zM7r2WSYszl3M5W$UE~?;0t@UAYw-SL0iI98c@2)d%&mX<2~BFy`#~g3gF=Ur(>hW> zibP#tUGlZ-Q!R50@2yMsdW> zB%0;ME9d*)*N4{_W54=qn!S$*A&S7Ts|g&fR>zH3Gq_M7kedd;WOJmxE#Emgo{Xp% zl#k`-c>FlekfRI8DAnLb8OwZe;d|PbhG2F7(1hu!q-JV|$79##$>wn%$;SQE=W=Ut z-P=B;V>M>=e9R1lc-Me`)ue^~asZR;xcbXU_T^%s(k`y$cePU&nSa4W_t9Q%riYp1 zbc+12{*;S7o5#6^0PP1TU9vOETx8MY!!sm0&BS~EXc+%^UL1h|Bcq94ye~?Q#+gx4 z%)SyRl2qJMasx&}xI5o}rUhm`BpCEdaylp#->-h1HrMBbF&=abbY*zB_9V~{H&VlG zt|dwA$@Ur9jp`oTBna+tj)u~Z-Uu2JQVW*C&46Ygrn-1x&%fgTcg-g61=+&^3IGVf z1pt8m6;+z(*%}#swzoC4`uZohJW^Yg!&U=&f~sG-4KpmNxxUrcL7QBQjqug+&3_d- zDvg0{U`QF|W1%qcmC`eN`jr3 zf7c_a>C4b2p{5=z=B1|Kbz1J{TV%pf=%3jROQp_HSgCXUzDbMZ#Qt}&jRG@WF|5E9 z`njnPfXVy|Xfl%a-chUr8N0U9m}T;JMX}mIYO?-!k(hkZ8*@>a?+KKRwoKrDPoo`P z6+rPjc2m}BU`*z~sR3VAoVetmzY3v?>*CiZgT&aW;0EK==pj7XeA3Owpx@_+pK0XO zoqHnqlWtC8CI%FnRa_gm5!-odlmpha22w1REz9tm)S63i^72pW_w<@t_wyEW4wPK2 zMW2`Lg^*AMDaOSu+$rM8_4m6eTca2iZB=!|nnihKq-dMPS0D+vP1Ql;7GMe7jX*KM zBCuYgt0O#gc|+R672X|;Vy~qwDqs-aw&r&_w?zYJO@!Yr+kFo?=pGYd9uxf@6XYHf zgKfK0ZH23I_}4dqy|!w+dE{>6&o5QpvI2}Bvx?FYX1QRx?zaV0r*L5pk|6YsYzazL zko{PPyvONm5Y5s0$9;B1T*p~{ln*o_r#js1`)gELHswu8E-sr*>d%hk?PWKRV>mF^ z%pTFMA@E>dbSi9_vVV#+7k$!UgRAzp8c7GchKw#Cv8_X~Q)9eu!+*q3DMxXYIn0oJ4hHAzt78) zg>xg1ShPC>lq{IsTDDSIHI*W!dJ0~w7%&ZvXCEU`!alOx9XKWcJ+y8Ytd=9)rC zxhq1iDmeXknCVz|q-yEcveiZtWn(2D*;VZ`}z8)YYo&3hk+|yM;%J}roq5xWwoGHg`opy-Uk}NwsPS$Zy+7Rjv#t{{@QGm zu2Xbjqr6k?CX8dBxkX|k)MF_6y--M;020t7R+9-hcVj7aE zk{x3IqU&sK6s9uDZG)&-b^G$t9NRP7k93y45DiJAHPIM3Jtoe3B2C-|E%46lGr^#; zY>rEMoL=&W23Xzxuc&H2dWQ5vw@k^Iw^chI3Y?;gpyFAa7xm$NXc@)ry&z}?+?>0H zOBJM?ctyqVB0hIQ@_A45%MXAXqG(dPJaG4PGHyQRkc(D?@OpINumKnm$Y=mD|5=&tb1$ z;=i})zYZYmOpJ`|?dbnLk@$1-r^^24JVGf<3K^Ub73zj;#rtx_w9lUrIw>6{X%`H( zUI3RbJNWFjdfI4fhPuvul5Ay~BcW6;g9LV{oJao}`}oJfI%4R;$+TqZQTIA3b*Rly zDdD9yeb6B|>G0>Gxc+k*+6BH!m(tnRQgR+im#*t2K<+8MS=+yT%m4VLnf@wa{QvEx zng2iZ(n(RF%D-NZAvItD0L=fG9{)^?_=I8GR0g2HP5K@`!(w7!F#Z_;D}|+Oi9}|T zXYm+F6Grx}FzSd!XO%iCm{gTT4H8o9MiNG1u5-{XT0^7Dd}s*Or9PBKk<3D5*dgfH z>L>rmxWwhNR;9sX(HJb+2YZSGRHIK{?D~sh!+;I-Y9f-(u}s>!U@~ov`yuEpW?b(Z znQeasf7VscVe3kJkwZ)7SYL+WPA`=(-R~j^*>}nFUDgXR zpJO0sKhJojj39v8PM32RoViRVxwW1toLg5Tw^|e&M$6tyoRML$Tip1MJCLn0OCMHI zzoan04QHb~{(y&HH3=yN=Vzb$s1=N8y?X5)?8cB__!&W&r;vR>A18V$Ml*`md%_WI zNtO9MLivL#Q=Rnlw(7GgR4hBy;?=jaz@YUjGXP_cC%yn; z%^XBf_H{o%zo8XN0I_ixtf$hA`x~a~$^F2=3J=Xm{l^U+yjq}P6>xI1<(+4#mNIPJ$b2hDy+d7JEL<-wO_`$ zVfxf3HbKbIAV2$(_tex*Y-$V*-_M;OWxiYChDC06IJ7L}@jZA?Sk9<%$MB4rilR$k z$X6Y-*k=4hkC`aK`E=JjY_23V`&c;SwehG1)%?wpFHK`caJrEs+CoUq1o~2B)uC9& zfy@j}&AFMS>3}W2M!gDWoyJ6RHo0YM$!;it8m=+iRY=EUp8@WtE{Z!h=jTXx&(T4t zn)zyf%_1WZY7r7xTzo0@;Dwk+zr(G%acNefy%6E3mh?v?HkJw92~nKOoR=_YfA+tw z4_*vdDkvL1oWzMUrNXv3WSJ9m7n2mj_?}s>aY+{Q*;_q%@5E4uI{;9L=e(PXrP!Fg zHZ0fRNK!bgf+~d|$!x0SE@d|CGcl8T`U8TvzT+nKEO>@xOSmtRi;~&2$>C-R(_Ke* zpo}tP;uz0Mun81(Y-4M{8&?9J3iLn0Yp(TBZ!*zf~V5b)$x= z*a|uck_{}SeLJ}{(y;W?X=f3u#y)@oRKHrv=Ee)r)YgB!@%C3WIg<13{ABKnjrc*G zds?@0d@%>w*WZeF(KHJc!Sc++&%Zv2rR$T1CbnL7olG;*N4<27Ove`$A{zUHX{#$V z#U{MeY4e$&OR_o``ZY&C)0Kg(5R5O?kbQ}97>Y8Y``=bcM--v>*ZnhS4!{aXq+CUM zV&2EcP5^QDZ>Bp)Q%t`%MzxfB1v-tRCo*)@s!%79Qnt+N^?37A?wlXN2hhBCm)&Zz zmsC__{8|wOe(R3_?Q6hGFZ(M7ff6=$RJ9(*^sCpKBMc|Hp?H$lHQD;#yQx23HC%Zq z;&n}WR7`tSK5BO~(h$OzvZ-E z!Q$8}9*T=`!CIj=$r((vRI)ciB@ol=;r`Of|0MPYi0vz(PHGyX|C+!|4~I&z_Je2a zc4=NXxtVgOESaIid!8ZR)#;dtR?aUUo@ro@A7jqpHT>#Ypo9#u4ffqWk1aG}Efaqn3Pr7O-R$P;1Jh+G)h&V+ z0n(Az8;GE7mi3u>VyoJrB(*_&Tya9qkbcZ0U91ViL%0%2TV?hfcyHpiLksc5JgunE zfpsfJ3bXzdGyXEMG2>2q%lgM@*A`I-k!_%Yt3@1z*ZfDHXpUkZl|y1~#;iS5+S!&~ zAM?=nbgeG1^6M5ZI0>M=WO@9eoI%kLW}h&yp9KY6FNVA@zZ6?l37*qGVq8?ZQg91VN(oU3b5Q^Up_HDMEW z&mam(fZ=-^J!rl=Dy`4WS-%LI=fiX5%v*t`?F494J$Wi zo{8TLYTMl}@1A1w*mq3}a`w(GTZ|5wNEc#BUW%SI0c)J5Wt>b90Jl2LEh`wQFboxNLqTo6W*OG< zy$r7Eow17GHeAd`&aOz>l?>gVoB7tg^}&Tf`C~p`4Q+nCHvzUv7RfGk+sv#wQgZ^N z%3?y;!w8%Kx0KT&q1Z3jB40%v$TD)jhCA^G-F&pDdD`1#=CCjy4p;sTiK;30?#>@Ssw zOz)ftZ(|%w7hp)uYLF@2(BO@gZsNMh0lrvh&UPHJaM}0dNd?%C$wda?X6np$BQF3b z=SWzjF$jaWx%3kM-i?xxR z)?cA&Z~UND*I$k<{vKJOoPtrlsJNH}w+UAxZxY6O6Rzr%Y{>$&D)9-m?3;k#KIy^j zOZ&(MNp3u_6TEa&J%63xI1sE*lexUW^p?{l9Is2k(JfYb7FzRL=TN@hUaJd9&laip zzP_cvdg><)3Eq`ON=BB<3CbwFb}u>GG9C*2J4xb^n6DJOnzw64{*gEa2wnc2pV3ua z@(Ec*Pt#l>i4l4|8-;NKF;!+6%YXM>p+X=H^yyV7y|AmMp z$B0rjjVN4^uwdlT&!jp=fcg1gwXg>_DxLr@7ID-D6+iH#zPxvlrY=BjQv0de(UZ1A z%VIwaYDg1qEk6inA{HxQajdO2Ln}2WCzy0=pXD9R%DmYotcriWl$jo4v2SomGVtq= z!JJc}BL-G+qkadGZ;vi-;E`^vKcD9LJN|GF=TMxE8S3^D4|VOzmlEhgxBlpR`#aRf z!~U}8%-{eFmBU40rx&T(j|rL@9QgUa7E5Nt+>)lf@(*$3rNCZ;_5Yrx{2IXnUXA%* zpTD!aKk3n1!MB{=za;(^M0{=DGJM}kzom@+BYp7tdjGfd|3x5uEB==M_>Z{OD_QOh z8}hC2TVCBi!k=EJ@T>6Oqy7(;-CLZuoU?y$tX`RDuQ+cRXm0`D9;^QYFoOIC;GaYG zx1w)rr~il&Vf`)owvzf5;cbTe55gecAB6u?lK(#k^n4}d{g+Mtvuf~90{%Y-WG483 z0sJR9ehc_ERsCnkyZ-_BPvZI(Y{Dbmmgl|#a#v}irM3VnMqWl@w nTa>qZ?>{JeRIe%df9}NcQc$mR{4dfl3gG269LlHp>+XL6jfsvQ literal 12511 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