From 66126bbe621ffbed124f4f4531ade58855f32eeb Mon Sep 17 00:00:00 2001 From: swcho Date: Thu, 12 Jun 2025 18:57:49 +0900 Subject: [PATCH 001/108] =?UTF-8?q?etc:=20=EC=A4=91=EA=B0=84=20=EC=BB=A4?= =?UTF-8?q?=EB=B0=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/sn/app/components/lib/api/index.ts | 2 +- apps/sn/app/components/ui/modal.tsx | 10 +- apps/sn/app/components/ui/verticalTable.tsx | 87 ++++- apps/sn/app/map/components/common/index.ts | 32 +- apps/sn/app/map/components/common/table.ts | 93 ++++- .../app/map/components/popupComponent/Ssc.tsx | 173 ++++++--- .../components/popupComponent/SwtrmtFclt.tsx | 70 ++-- .../app/map/components/popupComponent/smh.tsx | 2 +- apps/wr/app/map/featureClickEventPopup.tsx | 201 ++++++++++ apps/wr/app/map/govSidoSigunguSearchForm.tsx | 114 ++++++ apps/wr/app/map/htdflSttn/page.tsx | 113 ++++++ apps/wr/app/map/mapSearchBar.tsx | 154 ++++++++ apps/wr/app/map/mapSearchForm.tsx | 347 ++++++++++++++++++ apps/wr/app/map/searchModal.tsx | 82 +++++ apps/wr/app/map/service/index.ts | 1 + apps/wr/app/map/service/map.ts | 47 +++ apps/wr/app/page.tsx | 12 + .../images/map/layericon/BML_CBAS_PS.png | Bin 0 -> 1547 bytes .../images/map/layericon/BML_ELEV_PS.png | Bin 0 -> 1030 bytes .../images/map/layericon/BML_ETCP_PS.png | Bin 0 -> 961 bytes .../images/map/layericon/BML_STMS_PS.png | Bin 0 -> 1542 bytes .../images/map/layericon/BML_SYMB_PS.png | Bin 0 -> 970 bytes .../images/map/layericon/RDL_BYST_PS.png | Bin 0 -> 1196 bytes .../images/map/layericon/RDL_CROS_PS.png | Bin 0 -> 1083 bytes .../images/map/layericon/RDL_ETCT_PS.png | Bin 0 -> 1016 bytes .../images/map/layericon/RDL_OCUP_PS.png | Bin 0 -> 1061 bytes .../images/map/layericon/RDL_RDSN_PS.png | Bin 0 -> 1313 bytes .../images/map/layericon/RDL_SCLT_PS.png | Bin 0 -> 1072 bytes .../images/map/layericon/RDL_SEPC_PS.png | Bin 0 -> 1068 bytes .../images/map/layericon/RDL_STAT_PS.png | Bin 0 -> 1075 bytes .../images/map/layericon/RDL_STLT_PS.png | Bin 0 -> 1089 bytes .../images/map/layericon/RDL_TFSN_PS.png | Bin 0 -> 1291 bytes .../images/map/layericon/RDL_TRCT_PS.png | Bin 0 -> 1412 bytes .../images/map/layericon/RDL_TREE_PS.png | Bin 0 -> 1136 bytes .../images/map/layericon/RDL_TRSN_PS.png | Bin 0 -> 1375 bytes .../images/map/layericon/RDL_TSCT_PS.png | Bin 0 -> 1700 bytes .../images/map/layericon/SWL_CLAY_PS.png | Bin 0 -> 1146 bytes .../images/map/layericon/SWL_CLAY_PS_RED.png | Bin 0 -> 468 bytes .../images/map/layericon/SWL_DEPT_PS.png | Bin 0 -> 1079 bytes .../images/map/layericon/SWL_MANH_PS.png | Bin 0 -> 1559 bytes .../map/layericon/SWL_MANH_PS_BLACK.png | Bin 0 -> 757 bytes .../images/map/layericon/SWL_MANH_PS_BLUE.png | Bin 0 -> 785 bytes .../images/map/layericon/SWL_MANH_PS_GRAY.png | Bin 0 -> 1054 bytes .../map/layericon/SWL_MANH_PS_GREEN.png | Bin 0 -> 791 bytes .../images/map/layericon/SWL_MANH_PS_RED.png | Bin 0 -> 782 bytes .../map/layericon/SWL_MANH_PS_YELLOW.png | Bin 0 -> 782 bytes .../images/map/layericon/SWL_SPEW_PS.png | Bin 0 -> 1217 bytes .../images/map/layericon/SWL_SPOT_PS.png | Bin 0 -> 1067 bytes .../map/layericon/SWL_SPOT_PS_BLACK.png | Bin 0 -> 217 bytes .../images/map/layericon/SWL_SPOT_PS_BLUE.png | Bin 0 -> 217 bytes .../map/layericon/SWL_SPOT_PS_GREEN.png | Bin 0 -> 217 bytes .../images/map/layericon/SWL_SPOT_PS_GREY.png | Bin 0 -> 217 bytes .../images/map/layericon/SWL_SPOT_PS_RED.png | Bin 0 -> 217 bytes .../images/map/layericon/SWL_VENT_PS.png | Bin 0 -> 1033 bytes .../images/map/layericon/TL_SPBD_ENTRC.png | Bin 0 -> 1260 bytes .../images/map/layericon/TL_SPGF_LOC.png | Bin 0 -> 1383 bytes .../images/map/layericon/TL_SPOT_NOIN.png | Bin 0 -> 1009 bytes .../images/map/layericon/TL_SPRD_BANO.png | Bin 0 -> 1044 bytes .../images/map/layericon/TL_SPRD_CRSRD.png | Bin 0 -> 1083 bytes .../images/map/layericon/TL_SPRD_NONM.png | Bin 0 -> 1049 bytes .../images/map/layericon/TL_SPRD_RENM.png | Bin 0 -> 1009 bytes .../images/map/layericon/TL_SPRD_RWNM.png | Bin 0 -> 1049 bytes .../images/map/layericon/UFL_BMAN_PS.png | Bin 0 -> 1552 bytes .../images/map/layericon/UFL_BOPC_PS.png | Bin 0 -> 1132 bytes .../images/map/layericon/UFL_BPIP_PS.png | Bin 0 -> 1017 bytes .../images/map/layericon/UFL_BPOL_PS.png | Bin 0 -> 1193 bytes .../images/map/layericon/UFL_BTRS_PS.png | Bin 0 -> 1097 bytes .../images/map/layericon/UFL_GMNT_PS.png | Bin 0 -> 1166 bytes .../images/map/layericon/UFL_GPIP_PS.png | Bin 0 -> 1103 bytes .../images/map/layericon/UFL_GPRS_PS.png | Bin 0 -> 1408 bytes .../images/map/layericon/UFL_GVAL_PS.png | Bin 0 -> 1426 bytes .../images/map/layericon/UFL_KMAN_PS.png | Bin 0 -> 1572 bytes .../images/map/layericon/UFL_KPOL_PS.png | Bin 0 -> 1122 bytes .../images/map/layericon/UFL_VALV_PS.png | Bin 0 -> 1058 bytes .../images/map/layericon/WTL_FIRE_PS.png | Bin 0 -> 1072 bytes .../images/map/layericon/WTL_FLOW_PS.png | Bin 0 -> 1137 bytes .../images/map/layericon/WTL_MANH_PS.png | Bin 0 -> 1557 bytes .../images/map/layericon/WTL_META_PS.png | Bin 0 -> 1100 bytes .../images/map/layericon/WTL_PIPE_PS.png | Bin 0 -> 1075 bytes .../images/map/layericon/WTL_RSRV_PS.png | Bin 0 -> 1057 bytes .../images/map/layericon/WTL_RSRV_PS_GRAY.png | Bin 0 -> 259 bytes .../images/map/layericon/WTL_VALV_PS.png | Bin 0 -> 1077 bytes .../public/images/map/layericon/default.png | Bin 0 -> 160 bytes apps/wr/public/images/map/layericon/reset.png | Bin 0 -> 108 bytes .../business_components/src/ui/map/Gis.js | 50 ++- .../map/{MapToggleBtn.js => GisMapToggle.js} | 0 .../src/ui/map/GisSelect.js | 55 +-- .../business_components/src/ui/map/GisTree.js | 110 ++++-- .../src/ui/map/lib/gis.core.js | 21 +- 89 files changed, 1584 insertions(+), 192 deletions(-) create mode 100644 apps/wr/app/map/featureClickEventPopup.tsx create mode 100644 apps/wr/app/map/govSidoSigunguSearchForm.tsx create mode 100644 apps/wr/app/map/htdflSttn/page.tsx create mode 100644 apps/wr/app/map/mapSearchBar.tsx create mode 100644 apps/wr/app/map/mapSearchForm.tsx create mode 100644 apps/wr/app/map/searchModal.tsx create mode 100644 apps/wr/app/map/service/index.ts create mode 100644 apps/wr/app/map/service/map.ts create mode 100644 apps/wr/public/images/map/layericon/BML_CBAS_PS.png create mode 100644 apps/wr/public/images/map/layericon/BML_ELEV_PS.png create mode 100644 apps/wr/public/images/map/layericon/BML_ETCP_PS.png create mode 100644 apps/wr/public/images/map/layericon/BML_STMS_PS.png create mode 100644 apps/wr/public/images/map/layericon/BML_SYMB_PS.png create mode 100644 apps/wr/public/images/map/layericon/RDL_BYST_PS.png create mode 100644 apps/wr/public/images/map/layericon/RDL_CROS_PS.png create mode 100644 apps/wr/public/images/map/layericon/RDL_ETCT_PS.png create mode 100644 apps/wr/public/images/map/layericon/RDL_OCUP_PS.png create mode 100644 apps/wr/public/images/map/layericon/RDL_RDSN_PS.png create mode 100644 apps/wr/public/images/map/layericon/RDL_SCLT_PS.png create mode 100644 apps/wr/public/images/map/layericon/RDL_SEPC_PS.png create mode 100644 apps/wr/public/images/map/layericon/RDL_STAT_PS.png create mode 100644 apps/wr/public/images/map/layericon/RDL_STLT_PS.png create mode 100644 apps/wr/public/images/map/layericon/RDL_TFSN_PS.png create mode 100644 apps/wr/public/images/map/layericon/RDL_TRCT_PS.png create mode 100644 apps/wr/public/images/map/layericon/RDL_TREE_PS.png create mode 100644 apps/wr/public/images/map/layericon/RDL_TRSN_PS.png create mode 100644 apps/wr/public/images/map/layericon/RDL_TSCT_PS.png create mode 100644 apps/wr/public/images/map/layericon/SWL_CLAY_PS.png create mode 100644 apps/wr/public/images/map/layericon/SWL_CLAY_PS_RED.png create mode 100644 apps/wr/public/images/map/layericon/SWL_DEPT_PS.png create mode 100644 apps/wr/public/images/map/layericon/SWL_MANH_PS.png create mode 100644 apps/wr/public/images/map/layericon/SWL_MANH_PS_BLACK.png create mode 100644 apps/wr/public/images/map/layericon/SWL_MANH_PS_BLUE.png create mode 100644 apps/wr/public/images/map/layericon/SWL_MANH_PS_GRAY.png create mode 100644 apps/wr/public/images/map/layericon/SWL_MANH_PS_GREEN.png create mode 100644 apps/wr/public/images/map/layericon/SWL_MANH_PS_RED.png create mode 100644 apps/wr/public/images/map/layericon/SWL_MANH_PS_YELLOW.png create mode 100644 apps/wr/public/images/map/layericon/SWL_SPEW_PS.png create mode 100644 apps/wr/public/images/map/layericon/SWL_SPOT_PS.png create mode 100644 apps/wr/public/images/map/layericon/SWL_SPOT_PS_BLACK.png create mode 100644 apps/wr/public/images/map/layericon/SWL_SPOT_PS_BLUE.png create mode 100644 apps/wr/public/images/map/layericon/SWL_SPOT_PS_GREEN.png create mode 100644 apps/wr/public/images/map/layericon/SWL_SPOT_PS_GREY.png create mode 100644 apps/wr/public/images/map/layericon/SWL_SPOT_PS_RED.png create mode 100644 apps/wr/public/images/map/layericon/SWL_VENT_PS.png create mode 100644 apps/wr/public/images/map/layericon/TL_SPBD_ENTRC.png create mode 100644 apps/wr/public/images/map/layericon/TL_SPGF_LOC.png create mode 100644 apps/wr/public/images/map/layericon/TL_SPOT_NOIN.png create mode 100644 apps/wr/public/images/map/layericon/TL_SPRD_BANO.png create mode 100644 apps/wr/public/images/map/layericon/TL_SPRD_CRSRD.png create mode 100644 apps/wr/public/images/map/layericon/TL_SPRD_NONM.png create mode 100644 apps/wr/public/images/map/layericon/TL_SPRD_RENM.png create mode 100644 apps/wr/public/images/map/layericon/TL_SPRD_RWNM.png create mode 100644 apps/wr/public/images/map/layericon/UFL_BMAN_PS.png create mode 100644 apps/wr/public/images/map/layericon/UFL_BOPC_PS.png create mode 100644 apps/wr/public/images/map/layericon/UFL_BPIP_PS.png create mode 100644 apps/wr/public/images/map/layericon/UFL_BPOL_PS.png create mode 100644 apps/wr/public/images/map/layericon/UFL_BTRS_PS.png create mode 100644 apps/wr/public/images/map/layericon/UFL_GMNT_PS.png create mode 100644 apps/wr/public/images/map/layericon/UFL_GPIP_PS.png create mode 100644 apps/wr/public/images/map/layericon/UFL_GPRS_PS.png create mode 100644 apps/wr/public/images/map/layericon/UFL_GVAL_PS.png create mode 100644 apps/wr/public/images/map/layericon/UFL_KMAN_PS.png create mode 100644 apps/wr/public/images/map/layericon/UFL_KPOL_PS.png create mode 100644 apps/wr/public/images/map/layericon/UFL_VALV_PS.png create mode 100644 apps/wr/public/images/map/layericon/WTL_FIRE_PS.png create mode 100644 apps/wr/public/images/map/layericon/WTL_FLOW_PS.png create mode 100644 apps/wr/public/images/map/layericon/WTL_MANH_PS.png create mode 100644 apps/wr/public/images/map/layericon/WTL_META_PS.png create mode 100644 apps/wr/public/images/map/layericon/WTL_PIPE_PS.png create mode 100644 apps/wr/public/images/map/layericon/WTL_RSRV_PS.png create mode 100644 apps/wr/public/images/map/layericon/WTL_RSRV_PS_GRAY.png create mode 100644 apps/wr/public/images/map/layericon/WTL_VALV_PS.png create mode 100644 apps/wr/public/images/map/layericon/default.png create mode 100644 apps/wr/public/images/map/layericon/reset.png rename packages/common/business_components/src/ui/map/{MapToggleBtn.js => GisMapToggle.js} (100%) diff --git a/apps/sn/app/components/lib/api/index.ts b/apps/sn/app/components/lib/api/index.ts index a171844a1..24f15ed10 100644 --- a/apps/sn/app/components/lib/api/index.ts +++ b/apps/sn/app/components/lib/api/index.ts @@ -1,4 +1,4 @@ -const BASE_URL = process.env.NEXT_PUBLIC_SEWERAGE_API_URL || ''; +const BASE_URL = 'http://con.mind-one.co.kr:11083/sewerage-service/api'; export const postData = async (url: string, data: object): Promise => { const options: RequestInit = { diff --git a/apps/sn/app/components/ui/modal.tsx b/apps/sn/app/components/ui/modal.tsx index bc8085f71..f1dda48a6 100644 --- a/apps/sn/app/components/ui/modal.tsx +++ b/apps/sn/app/components/ui/modal.tsx @@ -7,20 +7,24 @@ interface ModalProps { title?: string; children: React.ReactNode; closeModal: () => void; + saveModal?: () => void } -const Modal: React.FC = ({ isModalOpen, setIsModalOpen, title, children, closeModal }) => { +const Modal: React.FC = ({ isModalOpen, setIsModalOpen, title, children, closeModal,saveModal }) => { return ( - - + + {title ? {title} : null} {children} +
+ {saveModal ? : null} +
diff --git a/apps/sn/app/components/ui/verticalTable.tsx b/apps/sn/app/components/ui/verticalTable.tsx index 36451bfbd..e5e506796 100644 --- a/apps/sn/app/components/ui/verticalTable.tsx +++ b/apps/sn/app/components/ui/verticalTable.tsx @@ -4,16 +4,29 @@ import { useEffect, useState } from "react"; type UiTableProps = { headName: React.ReactNode | undefined; + tableName1?: React.ReactNode | undefined; tableData: { [key: string]: string | number | boolean | object }[]; headlist: UiHeadProps[]; pageSize: number; total: number; - children: React.ReactNode | undefined; + children: Record | undefined; cellClick: ((index: number | undefined, headId: string) => void) | undefined; onPageChange: (page: number) => void; className?: string; resetPageNo?: boolean; isEditable?: boolean; + tableName2?: React.ReactNode | undefined; + tableData2?: { [key: string]: string | number | boolean | object }[]; + headlist2?: UiHeadProps[]; + pageSize2?: number; + total2?: number; + children2?: Record | undefined; + cellClick2?: ((index: number | undefined, headId: string) => void) | undefined; + onPageChange2?: (page: number) => void; + className2?: string; + resetPageNo2?: boolean; + isEditable2?: boolean; + showSecondTable?: boolean; }; type UiHeadProps = { @@ -25,17 +38,30 @@ type UiHeadProps = { }; export function VerticalTable({ + headName, + tableName1, tableData, headlist, pageSize, total, children, - headName, cellClick, onPageChange, className = "", resetPageNo = false, isEditable = false, + tableName2, + tableData2, + headlist2, + pageSize2, + total2, + children2, + cellClick2, + onPageChange2, + className2 = "", + resetPageNo2 = false, + isEditable2 = false, + showSecondTable = false, }: UiTableProps) { const [cur, setCur] = useState(1); const [formData, setFormData] = useState<{ [key: string]: string | number | boolean }>({}); @@ -45,7 +71,7 @@ export function VerticalTable({ onPageChange(1); } }, [onPageChange, resetPageNo]); - + useEffect(() => { if (tableData.length > 0 && tableData[0]) { setFormData(tableData[0] as { [key: string]: string | number | boolean }); @@ -72,6 +98,9 @@ export function VerticalTable({ ) : null}
+ {tableName1 && ( // tableName prop이 있으면 표시 +
{tableName1}
+ )} {headlist?.map((head) => ( @@ -82,9 +111,10 @@ export function VerticalTable({ {/* 데이터 값 또는 입력 필드 */} - {isEditable ? ( + {children && children[head.id] ? children[head.id] : isEditable ? ( head.type === "select" ? ( ) : head.type === "input" ? ( handleInputChange(head.id, e.target.value)} @@ -113,6 +144,54 @@ export function VerticalTable({ ))}
+ {tableName2 && ( // tableName prop이 있으면 표시 +
{tableName2}
+ )} + {showSecondTable && ( + + + {headlist2?.map((head) => ( + + {/* 컬럼 제목 */} + + {head.title} + + {/* 데이터 값 또는 입력 필드 */} + + {children && children[head.id] ? children[head.id] : isEditable2 ? ( + head.type === "select" ? ( + + ) : head.type === "input" ? ( + handleInputChange(head.id, e.target.value)} + className="w-full text-xl border-radius-0 border border-gray-300 rounded-sm px-2 py-1" + /> + ) : ( + {formData[head.id] ?? "-"} + ) + ) : ( + {formData[head.id] ?? "-"} + )} + + + ))} + +
+ )} {total > 0 && ( ; + props?: any; + }; +}; // 레이어 이름과 컴포넌트를 매핑 -const layerContentMap: { [key: string]: { component?: React.FC ; props?: any; } } = { +// 레이어 선택시 변경되는 팝업 컨텐츠 동적으로 변경하도록 작업 +const layerContentMap: LayerContentMap = { // TODO: gis데이터 추가되면 추가 작업 필요 - "하수처리시설": { - component: SwtrmtFclt, - }, + "하수처리시설": { component: SwtrmtFclt, }, "하수처리구역": { }, - "중점우수토실":{component: Ssc,}, - "하수관로":{component: SwtrmtFclt,}, + "우수토실":{component: Ssc,}, + "중점우수토실":{component: Ssc, }, // 중점우수토실 여부 설정 + // "하수관로":{component: MsSwagePpl,}, "하수맨홀":{component: smh,}, + // "빗물펌프":{component:StormWaterPump}, + // "오수펌프":{component:sewagePump}, }; // 동적 컴포넌트 반환 함수 -export const getLayerContentComponent = (layerNm: string): React.FC => { +export const getLayerContentComponent = (layerNm: string): React.FC | null => { const component = layerContentMap[layerNm]; if (!component || !component.component) { // component가 없으면 null 반환 return () => null; } - return component.component; + return component.component }; // 사용자 입력 필드 (보수 및 갱신, 청소 및 준설, 점검 및 진단) export const handleInputChange = (section: string, diff --git a/apps/sn/app/map/components/common/table.ts b/apps/sn/app/map/components/common/table.ts index a64e610d3..6c641f9be 100644 --- a/apps/sn/app/map/components/common/table.ts +++ b/apps/sn/app/map/components/common/table.ts @@ -7,11 +7,11 @@ export const SwtrmtFcltCol = [ { id: 'oprtngStrtYmd', title: '가동개시일자' }, { id: 'clsgYmd', title: '폐쇄일자' }, { id: 'prcsMsrcCd', title: '처리공법코드' }, - { id: 'laeCd', title: '지자체코드' }, + // { id: 'laeCd', title: '지자체코드' }, { id: 'laeNm', title: '지자체명' }, { id: 'ctpvNm', title: '시도명' }, { id: 'sggNm', title: '시군구명' }, - { id: 'sareaCd', title: '권역코드' }, + // { id: 'sareaCd', title: '권역코드' }, { id: 'sareaNm', title: '권역명' }, { id: 'lrgeSareaNm', title: '대권역명' }, { id: 'rvrNm', title: '하천명' }, @@ -26,7 +26,7 @@ export const SwtrmtFcltCol = [ { id: 'prcsZoneAreaSum', title: '처리구역면적합계' }, { id: 'xcrd', title: 'X좌표' }, { id: 'ycrd', title: 'Y좌표' }, - { id: 'oprtngSttsCd', title: '가동상태코드' }, + // { id: 'oprtngSttsCd', title: '가동상태코드' }, { id: 'rprsMsrcCn', title: '대표공법내용' }, { id: 'sttyCrtrBod', title: '법정기준생물화학적산소요구량' }, { id: 'sttyCrtrToc', title: '법정기준TOC' }, @@ -35,17 +35,94 @@ export const SwtrmtFcltCol = [ { id: 'sttyCrtrTph', title: '법정기준총인' }, { id: 'sttyCrtrClbclCnt', title: '법정기준대장균수' }, { id: 'bsnNm', title: '유역명' }, - { id: 'obsrvnSptCd', title: '관측지점코드' }, + // { id: 'obsrvnSptCd', title: '관측지점코드' }, ]; // 하수처리구역 export const SwtrmtPrcsZoneCol = [ { id: 'swtrmtZoneSn', title: '하수처리구역 일련번호' }, { id: 'swtrmtZoneNm', title: '하수처리구역명' }, - { id: 'swtrmtFcltCd', title: '하수처리시설코드' }, - { id: 'ctpvCd', title: '시도코드' }, + // { id: 'swtrmtFcltCd', title: '하수처리시설코드' }, + // { id: 'ctpvCd', title: '시도코드' }, { id: 'ctpvNm', title: '시도명' }, - { id: 'sggCd', title: '시군구코드' }, + // { id: 'sggCd', title: '시군구코드' }, { id: 'sggNm', title: '시군구명' }, { id: 'swtrmtFcltNm', title: '하수처리시설명' }, -] +]; + +// 하수맨홀 +export const MsSwageMhlCol = [ + { id: 'fid', title: 'FID' }, + { id: 'geom', title: 'geometry' }, + { id: 'ftreSmblCd', title: '지형지물부호코드' }, + { id: 'mngNo', title: '관리번호' }, + // { id: 'pbadmsEmdCd', title: '행정읍면동코드' }, + { id: 'mapshtNo', title: '도엽번호' }, + // { id: 'mngInstCd', title: '관리기관코드' }, + { id: 'instlYmd', title: '설치일자' }, + { id: 'lastDrdgnYmd', title: '최종준설일자' }, + { id: 'usgCd', title: '용도코드' }, + // { id: 'fcltyShpCd', title: '시설물형태코드' }, + { id: 'mhlKndCd', title: '맨홀종류코드' }, + { id: 'swageMhDiam', title: '하수맨홀구경' }, + { id: 'wdthLen', title: '가로길이' }, + { id: 'vrtcLen', title: '세로길이' }, + // { id: 'mqltCd', title: '재질코드' }, + { id: 'altdVl', title: '표고값' }, + { id: 'dpthlVl', title: '심도값' }, + { id: 'instMngNo', title:'기관관리번호'} +]; + +// 중점우수토실 측정지점 +export const untrtSwgRainCol1 = [ + { id: 'manage_no', title: '관리번호' }, + { id: 'swr_ppl_eqp_nm', title: '처리구역' }, + { id: 'swr_ppl_trt_nm', title: '처리분구' }, + { id: 'addr', title: '인근주소' }, + { id: 'chmbr_xcnts', title: '토실 위도' }, + { id: 'chmbr_ydnts', title: '토실 경도' } +]; + +// 중점우수토실 측정지점 시설현황 +export const untrtSwgRainCol2 = [ + { id: 'flwmtr_mesure_tp_etc', title: '구분' }, + { id: 'discharge_pipe_spec', title: '방류관로 규격' }, + { id: 'swr_ppl_eqp_ar', title: '배수구역 면적(m²)' }, + { id: 'swr_trt_popl', title: '하수처리 인구(인)' }, + { id: 'screen_instl_at', title: '스크린설치 여부' }, + { id: 'weir_hg', title: '위어 높이(m)' }, + { id: 'weir_lt', title: '위어 길이(m)' } +]; + +// 중점우수토실 강우정보 (id값 틀릴 수 있음) +export const untrtSwgRainCol3 = [ + { id: 'rain_begin_dt', title: '강우시작일시' }, + { id: 'rain_end_dt', title: '강우종료일시' }, + { id: 'precdng_dry', title: '선행건기(h)' }, + { id: 'rain_info', title: '강우정보' }, + { id: 'rain_fall', title: '누적강우량(mm)' }, + { id: 'cntnc_time', title: '지속시간(h)' }, + { id: 'rain_in', title: '강우강도(mm/h)' } +]; + +// 중점우수토실 측정정보 (id값 틀릴 수 있음) +export const untrtSwgRainCol4 = [ + { id: 'mesure_begin_dt', title: '측정시작일시' }, + { id: 'mesure_end_dt', title: '측정종료일시' }, + { id: 'mesure_intrvl', title: '측정간격(분)' }, + { id: 'mesure_mhrls_instl_lc', title: '측정기기 설치위치' }, + { id: 'flwmtr_combined', title: '유량계 종류 및 모델명' }, // 합쳐진 컬럼 + { id: 'qltwtr_begin_dt', title: '채수시작일시' }, + { id: 'qltwtr_end_dt', title: '채수종료일시' }, + { id: 'qltwtr_co', title: '채수횟수' } +]; + +// 중점우수토실 수량/수질측정 정보 +export const untrtSwgRainCol5 = [ + { id: 'odr', title: '회차' }, + { id: 'mesureDt', title: '측정일자' }, + { id: 'mesureQy', title: '수량(m)' }, + { id: 'mesureBod', title: 'BOD(mg/L)' }, + { id: 'mesureToc', title: 'TOC(mg/L)' }, + { id: 'mesureLoad', title: '오염부하량(kg)' } +]; \ No newline at end of file diff --git a/apps/sn/app/map/components/popupComponent/Ssc.tsx b/apps/sn/app/map/components/popupComponent/Ssc.tsx index a6e783ac9..2ca7c174d 100644 --- a/apps/sn/app/map/components/popupComponent/Ssc.tsx +++ b/apps/sn/app/map/components/popupComponent/Ssc.tsx @@ -1,18 +1,16 @@ // 우수토실 -import Modal from '@/components/ui/modal'; -import { VerticalTable } from '@/components/ui/verticalTable'; -import { UiTable } from '@common/business_components/ui'; +import { UiModal, UiTable } from '@common/business_components/ui'; import { Button, TableHead, TableRow } from '@common/components/ui'; -import React from 'react' -const inputColumn = [ - { id: "guyeok", title: "처리구역", type: "select", options: ["구역1", "구역2"] }, - { id: "mainSscYn", title: "중점우수토실 대상여부", type: "select", options: ['Y', 'N'] }, -]; -const headlist = [ - { id: 'startrain', title: '강우시작일시' }, { id: 'endrain', title: '강우종료일시' }, { id: '선행건기', title: '선행건기(h)' }, { id: '강우정보', title: '강우정보' }, { id: '누적강우량', title: '누적강우량(mm)' }, { id: '지속시간', title: '지속시간(h)' }, { id: '강우강도', title: '강우강도(mm/h)' }, -] -const headlist3 = [{ id: 'nm', title: '회차' }, { id: 'date', title: '측정일자' }, { id: 'measure', title: '수량(m)' }, { id: 'bod', title: 'BOD(mg/L)' }, { id: 'toc', title: 'TOC(mg/L)' }, { id: '오염부하량', title: '오염부하량(kg)' }] -const Ssc = () => { +import React from 'react'; +import { untrtSwgRainCol3, untrtSwgRainCol4, untrtSwgRainCol5 } from '../common/table'; + +const Ssc = ({ featureData, chkMainSsc, additionalData }: { + featureData: any, + chkMainSsc?: boolean, + additionalData?: any +}) => { + console.log('Additional data received:', additionalData); // 추가 데이터 확인용 + const [isModalOpen, setIsModalOpen] = React.useState(false); const openModal = () => { setIsModalOpen(true); @@ -20,45 +18,120 @@ const Ssc = () => { const closeModal = () => { setIsModalOpen(false); }; + + // 데이터 전처리: 합쳐진 필드 추가 + const preprocessedData = featureData ? [{ + ...featureData, + flwmtr_combined: `${featureData.flwmtr_mesure_tp_etc || '-'} / ${featureData.flwmtr_knd || '-'}` + }] : []; + + const renderModalBody = () => ( +
+ {/* 강우정보 테이블 */} + + + {/* 측정정보 테이블 */} + + {/* 커스텀 헤더 */} + + 수량 + 수질 + + + 측정시작일시 + 측정종료일시 + 측정간격(분) + 측정기기설치위치 + 유량계 종류 및 모델명 + 채수시작일시 + 채수종료일시 + 채수횟수 + + + + {/* 수량/수질측정 정보 테이블 */} + +
+ ); + return ( -
-
-
-

중점우수토실 운영정보

+
+ {/* */} + + {!chkMainSsc ? null : +
+
+

중점우수토실 운영정보

+
+ + +
- - -
- {/* 커스텀 헤더 */} - - - 수량 - - - 수질 - - - - 측정시작일시 - 측정종료일시 - 측정간격(분) - 측정기기설치위치 - 유량계 종류 및 모델명 - 채수시작일시 - 채수종료일시 - 채수횟수 - -
-
-
+ }
) } diff --git a/apps/sn/app/map/components/popupComponent/SwtrmtFclt.tsx b/apps/sn/app/map/components/popupComponent/SwtrmtFclt.tsx index b6d785414..0d1c3ef2e 100644 --- a/apps/sn/app/map/components/popupComponent/SwtrmtFclt.tsx +++ b/apps/sn/app/map/components/popupComponent/SwtrmtFclt.tsx @@ -1,44 +1,48 @@ -import { VerticalTable } from '@/components/ui/verticalTable'; +import Modal from '../../../components/ui/modal'; +import { VerticalTable } from '../../../components/ui/verticalTable'; +import { UiTable } from '@common/business_components/ui'; +import { Button, TableCell } from '@common/components/ui'; import React, { useState } from 'react' -import InputSection from '../InputSection'; -import { handleInputChange } from '../common'; + // 하수처리시설 -// TODO: 컬럼에 보고서 엉 ? 이거 버튼누르면 팝업으로 상세화면 띄워주는 건지 확인 필요 -const inputColumn = [ - { id: "title", title: "설치주제", type: "select", options: ["BTL", "재정사업"] }, - { id: "guyeok", title: "처리구역", type: "select", options: ["구역1", "구역2"] }, - { id: "basu", title: "배수구역", type: "input" }, - { id: "bungu", title: "배수분구", type: "input" }, - { id: "wltjs", title: "지선/간선", type: "select", options: ["지선", "간선"] }, - { id: "flow", title: "흐름방식", type: "select", options: ["자연유하", "압송"] }, - { id: "place", title: "매설위치", type: "select", options: ["도로", "하상"] }, -]; +const headList = [ + { id: "title", title: "하수도정비 기본계획보고서" }, + { id: "cad", title: "하수도대장 평면도" },] +const modalHeadList = [ + { id: "no", title: "번호" }, + { id: "year", title: "승인연도" }, + { id: "cad", title: '평면도'}, + { id: "fileNm", title: "파일명" }, + { id: "date", title: "입력시간" }, +] + const SwtrmtFclt = () => { - const [repairData, setRepairData] = useState([ - { year: "2025", type: "개대체" }, - { year: "2025", type: "보수" }, - ]); - const [cleaningData, setCleaningData] = useState([ - { year: "2025", type: "청소" }, - { year: "2025", type: "준설" }, - ]); - const [inspectionData, setInspectionData] = useState([ - { year: "2025", type: "관로기술진단" }, - { year: "2025", type: "관로기술진단" }, - ]); - const dataMap = { - repair: { data: repairData, setData: setRepairData }, - cleaning: { data: cleaningData, setData: setCleaningData }, - inspection: { data: inspectionData, setData: setInspectionData }, + const [isModalOpen, setIsModalOpen] = useState(false); + const [modalTitle, setModalTitle] = useState(''); + const openModal = (title:string) => { + setIsModalOpen(true); + setModalTitle(title) + }; + const closeModal = () => { + setIsModalOpen(false); }; return (
- openModal('하수도정비 기본계획보고서')}>보고서, + cad: , + }} headName='디지털 자료관 연계정보' tableData={[]} headlist={headList} pageSize={20} total={0} cellClick={undefined} isEditable={true} onPageChange={function (page: number): void { throw new Error("Function not implemented."); }}> - handleInputChange(section, index, field, value, dataMap)} /> - handleInputChange(section, index, field, value, dataMap)} /> - handleInputChange(section, index, field, value, dataMap)} />
+ +
+ +
+
+
) } diff --git a/apps/sn/app/map/components/popupComponent/smh.tsx b/apps/sn/app/map/components/popupComponent/smh.tsx index e693a28ce..39920232b 100644 --- a/apps/sn/app/map/components/popupComponent/smh.tsx +++ b/apps/sn/app/map/components/popupComponent/smh.tsx @@ -1,5 +1,5 @@ // 하수맨홀 -import { VerticalTable } from '@/components/ui/verticalTable'; +import { VerticalTable } from '../../../components/ui/verticalTable'; import React from 'react' const inputColumn = [ { id: "guyeok", title: "처리구역", type: "select", options: ["구역1", "구역2"] }, diff --git a/apps/wr/app/map/featureClickEventPopup.tsx b/apps/wr/app/map/featureClickEventPopup.tsx new file mode 100644 index 000000000..8048d0997 --- /dev/null +++ b/apps/wr/app/map/featureClickEventPopup.tsx @@ -0,0 +1,201 @@ +'use client' +import { getData, putData } from "../../../sn/app/components/lib/api"; +import Modal from "../../../sn/app/components/ui/modal"; +import { VerticalTable } from "../../../sn/app/components/ui/verticalTable"; +import { useGisContext } from "@common/business_components/ui/map/Gis"; +import { Button } from "@common/components/ui"; +import { useEffect, useRef, useState } from "react"; +import { getLayerContentComponent } from "../../../sn/app/map/components/common"; +// TODO: +const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void, featureData: any }) => { + const { gisMap } = useGisContext() as unknown as { checkedLayerIds: React.MutableRefObject; treeData: { lyrSn: number }[] | null; gisCore: any; gisMap: any; }; + // const featureId = featureData.getId(); // 피처의 id 가져오기 + const featureId = typeof featureData.getId === 'function' + ? featureData.getId() + : featureData.fid; + const [layerNm, setLayerNm] = useState(""); // 레이어 이름 상태 + const [tableData, setTableData] = useState(null); // 피처 데이터 + const [mntrngsData, setMntrngsData] = useState(false); // 수량/수질 측정 정보 + const [headlist, setHeadlist] = useState<{ id: string; title: string; }[]>([]); // 컬럼리스트 변경상태 + const [headlist2, setHeadlist2] = useState<{ id: string; title: string; }[]>([]); // 컬럼리스트 변경상태 + const [buttonChk, setButtonChk] = useState(true) + const [fetchData, setfetchData] = useState(); // api 호출여부 + const contentRef = useRef(null); + const [isModalOpen, setIsModalOpen] = useState(false); // 모달 열기 상태 + const [chkMainSsc, setChkMainSsc] = useState(false); // 중점우수토실 여부 + + // 피쳐데이터의 현재 레이어 이름 가져오는 함수 + const getSelectedLayers = () => { + let result = null; + const layers = gisMap.getLayers().getArray(); // gisMap에서 모든 레이어 가져오기 + let featureLayer: { get: (key: string) => any } = { get: (key: string) => null }; // 피처가 속한 레이어를 찾기 위한 변수 + layers.forEach((layer: any) => { + const source = layer.getSource(); + if (source && source.hasFeature && featureData + && typeof featureData.getId === 'function' && source.hasFeature(featureData)) { + featureLayer = layer; // 피처가 속한 레이어 찾기 + } + }); + // SearchModal 클릭 이벤트와 Gis 클릭 이벤트 구분하기 + if (featureLayer.get('lyrNm') === null) { + if (featureData.lyrTypeNm === 'tb_il_swage_prcs_fclt') result = '하수처리시설'; + else if (featureData.lyrTypeNm === 'tb_ms_swage_prcs_zone') result = '하수처리구역'; + else if (featureData.lyrTypeNm === 'om_dt_untrt_swg_rain') result = '중점우수토실'; + } + else result = featureLayer.get('lyrNm'); + + return result; + // return featureLayer.get('lyrNm'); // 레이어 이름 출력 + }; + + useEffect(() => { + const fetchData = async () => { + if (featureId) { + const selectedLayerName = getSelectedLayers(); // 레이어 이름 가져오기 + setLayerNm(selectedLayerName); // 상태 업데이트 + const layerId = featureId.split('.')[0] + let data = await getData(`v1/fclts/${featureId}?lyrTypeNm=${layerId}`); + setfetchData(data) + switch (selectedLayerName) { + case '하수처리시설': + setButtonChk(false) + break; + case '하수처리구역': + setButtonChk(false) + break; + case '중점우수토실': + setButtonChk(false) + setChkMainSsc(true) + + // 데이터 전처리: 합쳐진 필드 추가 + data = data ? { + ...data, + discharge_pipe_spec: `직경: ${data.pipe_diameter || '-'} / 높이: ${data.pipe_hg || '-'} / 길이: ${data.pipe_lt || '-'}` + } : {}; + + // 수량/수질측정 정보 데이터 호출 + const additionalResponse = await getData(`v1/onsemiro/swgRain/${data.adst_dstr_cd}/${data.manage_no}/mntrngs`); + setMntrngsData(additionalResponse.content); + break; + case '우수토실': + setButtonChk(true) + break; + case '하수관로': + setButtonChk(true) + break; + case '오수펌프': + setButtonChk(true) + break; + case '빗물펌프': + setButtonChk(true) + break; + case '하수맨홀': + setButtonChk(true) + break; + default: + break; + } + setTableData(data); + }; + } + fetchData(); + }, [featureId]); + + // 저장 시 모달 띄우기 + const handleSaveClick = () => { + setIsModalOpen(true); // 모달 열기 + }; + + const handleModalSave = () => { + setIsModalOpen(false); // 모달 닫기 + btnClickEvent(); // 저장 이벤트 실행 + }; + + const handleModalClose = () => { + setIsModalOpen(false); // 모달 닫기 + }; + + const btnClickEvent = async () => { + const bodyData: Record = {} + if (buttonChk && contentRef.current) { + const inputElements = contentRef.current.querySelectorAll('input'); + const selectElements = contentRef.current.querySelectorAll('select'); + // 카멜케이스 변환 함수 + const toCamelCase = (str: string) => + str.replace(/_([a-z])/g, (_, char) => char.toUpperCase()); + // select 태그의 값 가져오기 + selectElements.forEach((select) => { + const camelCaseId = toCamelCase(select.id); + bodyData[camelCaseId] = select.value; + }); + // input 태그의 값 가져오기 + inputElements.forEach((input) => { + const camelCaseId = toCamelCase(input.id); + bodyData[camelCaseId] = input.value; + }); + if (layerNm === '하수관로') bodyData['lyrTypeNm'] = 'tb_ms_swage_ppl' + else if (layerNm === '우수토실') bodyData['lyrTypeNm'] = 'tb_ms_stofcbr' + else if (layerNm === '오수펌프') bodyData['lyrTypeNm'] = 'tb_ms_swage_pmstn' + else if (layerNm === '빗물펌프') bodyData['lyrTypeNm'] = 'tb_ms_swage_pmstn' + else if (layerNm === '하수맨홀') bodyData['lyrTypeNm'] = 'tb_ms_swage_mnhl' + bodyData['fid'] = featureId + const data = await putData(`v1/fclts/${featureId}`, bodyData); + if (data.status == 200) { + alert('저장되었습니다') + setTableData(data) + setfetchData(data) + } + } + } + type ContentComponentProps = { + featureData: any; + chkMainSsc: boolean; + additionalData: any; + }; + const ContentComponent = layerNm ? getLayerContentComponent(layerNm) as React.ComponentType : null; + return ( + + ) +} +export default FeatureClickEventPopup; \ No newline at end of file diff --git a/apps/wr/app/map/govSidoSigunguSearchForm.tsx b/apps/wr/app/map/govSidoSigunguSearchForm.tsx new file mode 100644 index 000000000..127d02f78 --- /dev/null +++ b/apps/wr/app/map/govSidoSigunguSearchForm.tsx @@ -0,0 +1,114 @@ +import { useWrStore } from "@/app/store"; +import { getCityOrSgg } from "@/components/lib/func/commom"; +import { + type ReturnType +} from "@common/business_components/ui"; +import { SelectBox } from '@common/business_components/ui/select'; +import React, { useEffect, useRef, useState } from 'react'; + +export type comCodeState = { + code: { + isInit: boolean; + chBuCd: SelectDataType[] + rainFcltCd: SelectDataType[]; + bdstUsgCd: SelectDataType[]; + cityCd: SelectDataType[]; + sggCd: SelectDataType[]; + rvbsnCd: SelectDataType[]; + mdmbsnCd: SelectDataType[]; + lgbsnCd: SelectDataType[]; + lclgvrnmntCd: EnvPartSelectDataType[]; + orgwtKndCd: SelectDataType[]; + yn: SelectDataType[]; + buildingType: { + RST: string, + RNB: string, + HTB: string + } + }; +}; + +type SelectDataType = { + text: string; + val: string; + sido?: string; // 시도 코드 (선택 사항) +} + +type EnvPartSelectDataType = SelectDataType & { + adstDliStrCd: string, +} + +type SearchFormLeftProps = { + isShowPart?: boolean | undefined; + children: React.ReactNode; + selectSidoData?: SelectDataType[] | []; + selectSigunData?: SelectDataType[] | []; + selectRegionData?: SelectDataType[] | []; + selectUnitWaterShedData?: SelectDataType[] | []; + selectMiddleRegionData?: SelectDataType[] | []; + selectEnvPartData?: EnvPartSelectDataType[] | []; + selectEnvPartSidoData?: SelectDataType[] | []; + selectEnvPartSigunData?: SelectDataType[] | []; + + setRtnPartValue?: (value: string) => void; + setRtnSidoValue?: (value: string) => void; + setRtnSigunValue?: (value: string) => void; + setRtnRegionValue?: (value: string) => void; + setRtnUnitWaterShedValue?: (value: string) => void; + setRtnMiddleRegionValue?: (value: string) => void; + setRtnEnvPartValue?: (value: string) => void; + setRtnEnvPartSidoValue?: (value: string) => void; + setRtnEnvPartSigunValue?: (value: { val: string, text: string }) => void; // setRtnEnvPartSigunValue prop 타입을 객체로 변경 + resetTrigger?: boolean; +} + + + +function GovSidoSigunguSearchForm({ + isShowPart = false, + children, + selectSidoData = [], + selectSigunData = [], + selectRegionData = [], + selectUnitWaterShedData = [], + selectMiddleRegionData = [], + selectEnvPartData = [], + setRtnPartValue, + setRtnSidoValue, + setRtnSigunValue, + setRtnRegionValue, + setRtnUnitWaterShedValue, + setRtnMiddleRegionValue, + setRtnEnvPartValue, + setRtnEnvPartSidoValue, + setRtnEnvPartSigunValue, + resetTrigger = false +}: SearchFormLeftProps) { + const { code, pbnstSttn, pbnstSttnSearch } = useWrStore((state) => state); + + const ref = useRef({} as ReturnType); + const codeData = getCityOrSgg(ref); + //환경청별 시도 시군구 + const [envPartValue, setEnvPartValue] = useState('0'); + // 환경청 + const [filteredEnvPartSigunDataSelected, setFilteredEnvPartSigunDataSelected] = useState([]); + + return ( + isShowPart ? ( +
+ <> + onSelectValue(val.toString(), 'envPart')} value={envPartValue} selectClass={'rounded-lg bg-white truncate'} /> + onSelectValue(val.toString(), 'envPartSido')} value={envPartSidoValue} selectClass={'rounded-lg bg-white truncate'} /> + onSelectValue(val.toString(), 'envPartSigun')} value={envPartSigunValue} selectClass={'rounded-lg bg-white truncate'} /> + + {children} +
+ ) : ( +
+ {children} +
+ ) + ) +} + +export { GovSidoSigunguSearchForm }; diff --git a/apps/wr/app/map/htdflSttn/page.tsx b/apps/wr/app/map/htdflSttn/page.tsx new file mode 100644 index 000000000..f6b967c05 --- /dev/null +++ b/apps/wr/app/map/htdflSttn/page.tsx @@ -0,0 +1,113 @@ +"use client"; + +import Gis from "@common/business_components/ui/map/Gis"; +import GisApp from "@common/business_components/ui/map/lib/gis.core"; +import { useEffect, useRef, useState } from "react"; +import SearchModal from "../searchModal"; +import FeatureClickEventPopup from "../featureClickEventPopup"; +import { mapService } from "../service"; + +const Map = () => { + const [treeData, setTreeData] = useState([]); // 초기값을 객체 또는 null로 설정 + const [selectedFeature, setSelectedFeature] = useState(null); // 선택된 피처 상태 + const gisMaps = useRef(null); + const [isOpen, setIsOpen] = useState(false); // 팝업 열기/닫기 상태 + const [isSearchModalOpen, setIsSearchModalOpen] = useState(true); // SearchModal 열림 상태 + const [featureData, setFeatureData] = useState(null); // 선택된 피쳐 데이터 + const fetchData = async () => { + try { + const response = await mapService.getLyrStngs(); + setTreeData(response); // API 응답 저장 + } catch (error) { + console.error("데이터 불러오기 실패:", error); + } + }; + + // 컴포넌트 마운트 시 1회 실행 + useEffect(() => { + fetchData(); + }, []); + + // SearchModal, GisContent 등에서 호출 + // const onFeatureSelect = (data: any) => { + // let map = gisMaps.current; + // if (!map) { + // console.warn("지도가 초기화되지 않았습니다."); + // return; + // } + + // // 항상 같은 select 객체 사용 + // const select = GisApp.Module.select as any; + // if (!select) return; + + // // 1. 기존 선택 초기화 + // select.resetSelectedFeature(); + + // // 2. data가 OpenLayers Feature 인스턴스인지 확인 + // let foundFeature = null; + // if ( + // data && + // typeof data.getGeometry === "function" && + // typeof data.setStyle === "function" + // ) { + // // 이미 Feature 인스턴스라면 그대로 사용 + // foundFeature = data; + // } else { + // // 아니면 featureId로 맵에서 Feature 인스턴스를 찾아서 사용 + // const featureId = data.fid || data.ftrIdn || data.id; + // map + // .getLayers() + // .getArray() + // .forEach((layer: any) => { + // const source = layer.getSource?.(); + // if (source?.getFeatureById) { + // const feature = source.getFeatureById(String(featureId).trim()); + // if (feature) foundFeature = feature; + // } + // }); + // } + + // // 3. 피처 선택 + // if (foundFeature) { + // let geometry = foundFeature.getGeometry(); + // if (geometry) { + // let extent = geometry.getExtent(); + + // map.getView().fit(extent, { + // duration: 300, + // padding: [50, 50, 50, 50], + // maxZoom: 16, + // constrainResolution: true, + // nearest: true, + // }); + // } + // select.selectFeature(foundFeature, true); + // } else { + // console.warn("OpenLayers Feature를 찾을 수 없습니다:", data); + // } + + // setFeatureData(data); + // setIsOpen(true); + // setIsSearchModalOpen(false); + // }; + return ( +
+ +
+ ); +}; + +export default Map; diff --git a/apps/wr/app/map/mapSearchBar.tsx b/apps/wr/app/map/mapSearchBar.tsx new file mode 100644 index 000000000..a578dfac2 --- /dev/null +++ b/apps/wr/app/map/mapSearchBar.tsx @@ -0,0 +1,154 @@ +"use client" + +import { Button } from "@common/components/ui"; +import { useCallback, useEffect, useState } from "react"; +import type { EnvPartSelectDataType } from "@common/business_components/ui/search_form_left"; +import { SearchFormRight } from "@common/business_components/ui/search_form_right"; +import type { SelectDataType } from "@common/business_components/ui/search_input_select"; +import type { ReturnType } from "@common/business_components/ui/search_base_form"; +import { MapSearchForm } from "./mapSearchForm"; + +type SearchBaseType = { + selectSidoData?: SelectDataType[] | []; + selectSigunData?: SelectDataType[] | []; + selectRegionData?: SelectDataType[] | []; + selectUnitWaterShedData?: SelectDataType[] | []; + selectMiddleRegionData?: SelectDataType[] | []; + selectEnvPartData?: EnvPartSelectDataType[] | []; + queryClick: (returnType: ReturnType) => void; + excelName?: string; + excelClick: (returnType: ReturnType) => void; + initClick: () => void; + btnClass: string; + ref: React.RefObject; + isAdmin: boolean; + leftClassName?: string; + rightClassName?: string; + showQueryButton?: boolean; + showInitButton?: boolean; + showApplyButton?: boolean; +} + +export function MapSearchBar({ + selectSidoData, + selectSigunData, + selectRegionData, + selectUnitWaterShedData, + selectMiddleRegionData, + selectEnvPartData, + btnClass, + excelName, + queryClick, + excelClick, + initClick, + ref, + isAdmin = false, + leftClassName, + rightClassName, + showQueryButton = true, + showInitButton = true, + showApplyButton = false +}: SearchBaseType) { + const [partValue, setPartValue] = useState("A"); + const [sidoValue, setSidoValue] = useState("0"); + const [sigunValue, setSigunValue] = useState("0"); + const [sidoText, setSidoText] = useState(""); + const [sigunText, setSigunText] = useState(""); + const [regionValue, setRegionValue] = useState("0"); + const [untitWaterShedValue, setUntitWaterShedValue] = useState("0"); + const [middleRegionValue, setMiddleRegionValue] = useState("0"); + const [envPartValue, setEnvPartValue] = useState("0"); + const [resetTrigger, setResetTrigger] = useState(false); + + const queryVal = () => { + console.log("조회버튼 클릭"); + queryClick({ + partValue: partValue, + sidoValue: sidoValue, + sigunValue: sigunValue, + regionValue: regionValue, + unitWaterShedValue: untitWaterShedValue, + middleRegionValue: middleRegionValue, + envPartValue: envPartValue, + sidoText: sidoText, + sigunText: sigunText, + }); + } + + const initVal = useCallback(() => { + setResetTrigger(true); + initClick(); + }, [setResetTrigger, initClick]); + + useEffect(() => { + if (resetTrigger) { + setResetTrigger(false); + } + }, [resetTrigger]); + + const excelDownload = () => { + excelClick({ + partValue: partValue, + sidoValue: sidoValue, + sigunValue: sigunValue, + regionValue: regionValue, + unitWaterShedValue: untitWaterShedValue, + middleRegionValue: middleRegionValue, + envPartValue: envPartValue, + sidoText: sidoText, + sigunText: sigunText, + }) + }; + + useEffect(() => { + if (ref && ref.current) { + const customRef = ref.current; + customRef.partValue = partValue; + customRef.sidoValue = sidoValue; + customRef.sigunValue = sigunValue; + customRef.regionValue = regionValue; + customRef.unitWaterShedValue = untitWaterShedValue; + customRef.middleRegionValue = middleRegionValue; + customRef.envPartValue = envPartValue; + customRef.sidoText = sidoText; + customRef.sigunText = sigunText; + } + }, [partValue, sidoValue, sigunValue, regionValue, untitWaterShedValue, middleRegionValue, envPartValue, sidoText, sigunText, ref]); + + return ( +
+ {/* 검색 조건 부분 */} + + {/* 버튼 부분 */} +
+
+ {showQueryButton && } + {showInitButton && } + {showApplyButton && } +
+
+
+ ) +} diff --git a/apps/wr/app/map/mapSearchForm.tsx b/apps/wr/app/map/mapSearchForm.tsx new file mode 100644 index 000000000..00ba8e6f0 --- /dev/null +++ b/apps/wr/app/map/mapSearchForm.tsx @@ -0,0 +1,347 @@ +"use client" + +import React, { useEffect, useState } from 'react'; +import type { SelectDataType } from "@common/business_components/ui/search_input_select"; +import { SelectBox } from '@common/business_components/ui/select'; + +/** + * 검색 폼 왼쪽 컴포넌트 타입 + * + * @param isShowPart 구분 조회 여부 + * @param children 자식 컴포넌트 + * @param selectSidoData 시도 데이터 + * @param selectSigunData 시군구 데이터 + * @param selectRegionData 유역 데이터 + * @param selectUnitWaterShedData 단위유역 데이터 + * @param selectMiddleRegionData 중권역 데이터 + * @param selectEnvPartData 환경청 데이터 + * @param selectEnvPartSidoData 환경청 시도 데이터 + * @param selectEnvPartSigunData 환경청 시군구 데이터 + * @param setRtnPartValue 부분 값 변경 핸들러 + */ +type SearchFormLeftProps = { + isShowPart?: boolean | undefined; + children: React.ReactNode; + selectSidoData?: SelectDataType[] | []; + selectSigunData?: SelectDataType[] | []; + selectRegionData?: SelectDataType[] | []; + selectUnitWaterShedData?: SelectDataType[] | []; + selectMiddleRegionData?: SelectDataType[] | []; + selectEnvPartData?: EnvPartSelectDataType[] | []; + selectEnvPartSidoData?: SelectDataType[] | []; + selectEnvPartSigunData?: SelectDataType[] | []; + + setRtnPartValue?: (value: string) => void; + setRtnSidoValue?: (value: string) => void; + setRtnSigunValue?: (value: string) => void; + setRtnRegionValue?: (value: string) => void; + setRtnUnitWaterShedValue?: (value: string) => void; + setRtnMiddleRegionValue?: (value: string) => void; + setRtnEnvPartValue?: (value: string) => void; + setRtnEnvPartSidoValue?: (value: string) => void; + setRtnEnvPartSigunValue?: (value: string) => void; + setRtnSidoText?: (value: string) => void; + setRtnSigunText?: (value: string) => void; + resetTrigger?: boolean; + isAdmin?: boolean; +} + +export type EnvPartSelectDataType = SelectDataType & { + adstDliStrCd: string, +} + +const selectPartData: SelectDataType[] = [ + { text: "행정별", val: 'A' }, + { text: "유역별", val: 'R' }, + { text: "환경청별", val: 'G' }, +]; + +const selectType: SelectDataType[] = [ + { text: "행정별", val: 'A' }, + { text: "유역별", val: 'R' }, + { text: "환경청별", val: 'G' }, +]; + +function MapSearchForm({ isShowPart = false, children, selectSidoData = [], selectSigunData = [], selectRegionData = [], selectUnitWaterShedData = [], selectMiddleRegionData = [], selectEnvPartData = [], setRtnPartValue, setRtnSidoValue, setRtnSidoText, setRtnSigunValue, setRtnSigunText, setRtnRegionValue, setRtnUnitWaterShedValue, setRtnMiddleRegionValue, setRtnEnvPartValue, setRtnEnvPartSidoValue, setRtnEnvPartSigunValue, resetTrigger = false, isAdmin = false }: SearchFormLeftProps) { + /******************선택된 데이터******************/ + const [partValue, setPartValue] = useState('A'); + //행정별 시도 시군구 + const [sidoValue, setSidoValue] = useState('0'); + const [sigunValue, setSigunValue] = useState('0'); + const [sidoText, setSidoText] = useState(''); + const [sigunText, setSigunText] = useState(''); + //유역별 + const [regionValue, setRegionValue] = useState('0'); + const [unitWaterShedValue, setUnitWaterShedValue] = useState('0'); + const [middleRegionValue, setMiddleRegionValue] = useState('0'); + //환경청별 시도 시군구 + const [envPartValue, setEnvPartValue] = useState('0'); + // 환경청 + const [filteredEnvPartSigunDataSelected, setFilteredEnvPartSigunDataSelected] = useState([]); + + /****************** 셀렉트 리스트 데이터. ******************/ + // 조회된 조건에 맞는 시도 시군 유역 + const [filteredSigunData, setFilteredSigunData] = useState([{ text: "전체", val: '0' }]); + const [filteredUnitWaterShedData, setFilteredUnitWaterShedData] = useState([{ text: "전체", val: '0' }]); + const [filteredMiddleRegionData, setFilteredMiddleRegionData] = useState([{ text: "전체", val: '0' }]); + // 조회된 조건에 맞는 환경청 시도 시군 + const [filteredEnvPartData, setFilteredEnvPartData] = useState([{ text: "전체", val: '0', adstDliStrCd: '' }]); + const [filteredEnvPartSidoData, setFilteredEnvPartSidoData] = useState([{ text: "전체", val: '0', adstDliStrCd: '' }]); + const [filteredEnvPartSigunData, setFilteredEnvPartSigunData] = useState([{ text: "전체", val: '0' }]); + + // 시군구 세팅 + const initializeSigunData = (sidoCd: string) => { + if (sidoCd === '0') { + setSigunValue('0'); + setFilteredSigunData([{ text: "전체", val: '0' }]); + } else { + const newFilteredData = [ + { text: "전체", val: '0' }, + ...selectSigunData.filter((item: SelectDataType) => item.val.substring(0, 2) === sidoCd) + ]; + setSigunValue('0'); + setFilteredSigunData(newFilteredData); + } + } + + // 단위유역 세팅 + const initializeUnitWaterShedData = (regionCd: string) => { + if (regionCd === '0') { + setUnitWaterShedValue('0'); + setFilteredUnitWaterShedData([{ text: "전체", val: '0' }]); + } else { + const newFilteredData = [ + { text: "전체", val: '0' }, + ...selectUnitWaterShedData.filter((item: SelectDataType) => item.val.substring(0, 1) === regionCd) + ]; + setUnitWaterShedValue('0'); + setFilteredUnitWaterShedData(newFilteredData); + } + } + + // 중권역 세팅 + const initializeMiddleRegionData = (unitWaterShedCd: string) => { + if (unitWaterShedCd === '0') { + setMiddleRegionValue('0'); + setFilteredMiddleRegionData([{ text: "전체", val: '0' }]); + } else { + const newFilteredData = [ + { text: "전체", val: '0' }, + ...selectMiddleRegionData.filter((item: SelectDataType) => item.val.substring(0, 2) === unitWaterShedCd) + ]; + setMiddleRegionValue('0'); + setFilteredMiddleRegionData(newFilteredData); + } + } + + // 환경청 세팅 - 시도 + const initializeCityDataByEnvPartSido = (envPartCd: string) => { + const foundEnvPart = selectEnvPartData.find((item: EnvPartSelectDataType) => item.val === envPartCd); + const cityArray = foundEnvPart?.adstDliStrCd.toString().split('|').join('').split(','); + + // 시군구 데이터 + const envSigunData = selectSigunData.filter((item: SelectDataType) => cityArray?.includes(item.val)); + + // 환경청 시도 데이터 + const sidoArray = [...new Set(envSigunData.map((item: SelectDataType) => item.val.substring(0, 2)))]; + const envSidoData = selectSidoData.filter((item: SelectDataType) => sidoArray?.includes(item.val)); + setFilteredEnvPartSigunDataSelected(envSigunData); + + setFilteredEnvPartSidoData([ + { text: "전체", val: '0', adstDliStrCd: '' }, + ...envSidoData.map((item: SelectDataType) => ({ text: item.text, val: item.val, adstDliStrCd: '' })) + ]); + } + + // 환경청 세팅 - 시군구 + const initializeCityDataByEnvPartSigun = (sidoCd: string) => { + const envSigunData = filteredEnvPartSigunDataSelected.filter((item: SelectDataType) => item.val.substring(0, 2) === sidoCd); + setFilteredEnvPartSigunData([ + { text: "전체", val: '0' }, + ...envSigunData.map((item: SelectDataType) => ({ text: item.text, val: item.val })) + ]); + } + + const onSelectValue = (val: string, type: string) => { + let selectedSido; + let selectedSigun; + let selectedEnvPartSido; + let selectedEnvPartSigun; + + switch (type) { + case 'part': + setPartValue(val); + if (setRtnPartValue) { + setRtnPartValue(val); + } + // 구분 + if (val == 'R') { + initializeMiddleRegionData('0'); + initializeUnitWaterShedData('0'); + } else if (val == 'G') { + setFilteredEnvPartData([ + { text: "전체", val: '0', adstDliStrCd: '' }, + ...selectEnvPartData.filter((item: EnvPartSelectDataType) => item.val !== null && item.adstDliStrCd !== '') + ]); + setEnvPartValue('0'); + initializeCityDataByEnvPartSido('0'); + } else { + initializeSigunData('0'); + onSelectValue(selectSigunData[0]?.val || '0', 'sigun'); + } + + // 다른 selectbox 전체로 초기화 + onSelectValue(selectSidoData[0]?.val || '0', 'sido'); + onSelectValue(selectSigunData[0]?.val || '0', 'sigun'); + break; + case 'sido': + setSidoValue(val); + selectedSido = selectSidoData.find(item => item.val === val); + setSidoText(selectedSido?.text || ''); + if (setRtnSidoValue) { + setRtnSidoValue(val); + } + if (setRtnSidoText) { + setRtnSidoText(selectedSido?.text || ''); + } + initializeSigunData(val); + onSelectValue(selectSigunData[0]?.val || '0', 'sigun'); + break; + case 'sigun': + if (setRtnSigunValue) { + setRtnSigunValue(val); + } + setSigunValue(val); + selectedSigun = filteredSigunData.find(item => item.val === val); + if (setRtnSigunText) { + setRtnSigunText(selectedSigun?.text || ''); + } + setSigunText(selectedSigun?.text || ''); + break; + + // 유역 세팅 + case 'region': + setRegionValue(val); + if (setRtnRegionValue) { + setRtnRegionValue(val); + } + initializeUnitWaterShedData(val); + onSelectValue(selectUnitWaterShedData[0]?.val || '0', 'unitWaterShed'); + break; + case 'unitWaterShed': + setUnitWaterShedValue(val); + if (setRtnUnitWaterShedValue) { + setRtnUnitWaterShedValue(val); + } + initializeMiddleRegionData(val); + onSelectValue(selectMiddleRegionData[0]?.val || '0', 'middleRegion'); + break; + case 'middleRegion': + setMiddleRegionValue(val); + if (setRtnMiddleRegionValue) { + setRtnMiddleRegionValue(val); + } + break; + + // 환경청 세팅 + case 'envPart': + setEnvPartValue(val); + if (setRtnEnvPartValue) { + setRtnEnvPartValue(val); + } + initializeCityDataByEnvPartSido(val); + onSelectValue(filteredEnvPartSidoData[0]?.val || '0', 'envPartSido'); + break; + case 'envPartSido': + setSidoValue(val); + selectedEnvPartSido = filteredEnvPartSidoData.find(item => item.val === val); + setSidoText(selectedEnvPartSido?.text || ''); + if (setRtnEnvPartSidoValue) { + setRtnEnvPartSidoValue(val); + } + initializeCityDataByEnvPartSigun(val); + onSelectValue(filteredEnvPartSigunData[0]?.val || '0', 'envPartSigun'); + break; + case 'envPartSigun': + setSigunValue(val); + selectedEnvPartSigun = filteredEnvPartSigunData.find(item => item.val === val); + setSigunText(selectedEnvPartSigun?.text || ''); + if (setRtnEnvPartSigunValue) { + setRtnEnvPartSigunValue(val); + } + break; + } + } + + // 리셋 함수 + const resetAllValues = () => { + setPartValue('A'); + setSidoValue('0'); + setSigunValue('0'); + setRegionValue('0'); + setUnitWaterShedValue('0'); + setMiddleRegionValue('0'); + setEnvPartValue('0'); + + setFilteredSigunData([{ text: "전체", val: '0' }]); + setFilteredUnitWaterShedData([{ text: "전체", val: '0' }]); + setFilteredMiddleRegionData([{ text: "전체", val: '0' }]); + setFilteredEnvPartData([{ text: "전체", val: '0', adstDliStrCd: '' }]); + setFilteredEnvPartSidoData([{ text: "전체", val: '0', adstDliStrCd: '' }]); + setFilteredEnvPartSigunData([{ text: "전체", val: '0' }]); + + if (setRtnPartValue) setRtnPartValue('A'); + if (setRtnSidoValue) setRtnSidoValue('0'); + if (setRtnSigunValue) setRtnSigunValue('0'); + if (setRtnRegionValue) setRtnRegionValue('0'); + if (setRtnUnitWaterShedValue) setRtnUnitWaterShedValue('0'); + if (setRtnMiddleRegionValue) setRtnMiddleRegionValue('0'); + if (setRtnEnvPartValue) setRtnEnvPartValue('0'); + if (setRtnEnvPartSidoValue) setRtnEnvPartSidoValue('0'); + if (setRtnEnvPartSigunValue) setRtnEnvPartSigunValue('0'); + }; + + // 리셋 트리거 변경 감지 + useEffect(() => { + if (resetTrigger) { + resetAllValues(); + // 리셋 후 트리거를 false로 설정 + if (setRtnPartValue) { + setRtnPartValue('0'); + } + } + }, [resetTrigger]); + + return ( + isShowPart ? ( +
+ onSelectValue(val.toString(), 'part')} value={partValue} selectClass={undefined} /> + {partValue === 'R' ? ( + <> + onSelectValue(val.toString(), 'region')} value={regionValue} selectClass={undefined} /> + onSelectValue(val.toString(), 'unitWaterShed')} value={unitWaterShedValue} selectClass={undefined} /> + onSelectValue(val.toString(), 'middleRegion')} value={middleRegionValue} selectClass={undefined} /> + + ) : partValue === 'G' ? ( + <> + onSelectValue(val.toString(), 'envPart')} value={envPartValue} selectClass={undefined} /> + onSelectValue(val.toString(), 'envPartSido')} value={sidoValue} selectClass={undefined} /> + onSelectValue(val.toString(), 'envPartSigun')} value={sigunValue} selectClass={undefined} /> + + ) : ( + <> + onSelectValue(val.toString(), 'sido')} value={sidoValue} selectClass={undefined} /> + onSelectValue(val.toString(), 'sigun')} value={sigunValue} selectClass={undefined} /> + + )} + {children} +
+ ) : ( +
+ {children} +
+ ) + ) +} + +export { MapSearchForm }; diff --git a/apps/wr/app/map/searchModal.tsx b/apps/wr/app/map/searchModal.tsx new file mode 100644 index 000000000..d5dfaccc2 --- /dev/null +++ b/apps/wr/app/map/searchModal.tsx @@ -0,0 +1,82 @@ +"use client" + +import { useWrStore } from "@/app/store"; +import { btnClass } from "@/app/store/common/code"; +import React, { useCallback, useEffect, useRef, useState } from 'react'; +import { MapSearchBar, type ReturnType } from "./mapSearchBar"; +import { Button } from "@common/components/ui"; +import { SearchFormLeft, type EnvPartSelectDataType } from "@common/business_components/ui/search_form_left"; +import { SearchFormRight } from "@common/business_components/ui/search_form_right"; +import type { SelectDataType } from "@common/business_components/ui/search_input_select"; + +const API_URL = `${process.env.NEXT_PUBLIC_SEWERAGE_API_URL}`; + +const YEARS = Array.from({ length: 100 }, (_, i) => `${2025 - i}`); + +interface SearchModalProps { + open: boolean; + onOpenChange: (open: boolean) => void; + onFeatureSelect: (row: any) => void; +} + +// 날짜 조합 함수 +const getDateString = ( + year: string, + month: string, + day: string, + defaultDate: string, +) => { + if (!year && !month && !day) return defaultDate; + const y = year || defaultDate.slice(0, 4); + const m = month || defaultDate.slice(4, 6); + const d = day || defaultDate.slice(6, 8); + return `${y}${m}${d}`; +}; + +const SearchModal: React.FC = ({ + open, + onOpenChange, + onFeatureSelect, +}) => { + + // 로딩바 + const [isLoading, setIsLoading] = useState(false); + // 상세 정보 조회를 위한 상태 추가 + const [popupFeature, setPopupFeature] = useState(null); + const { code, pbnstSttn, pbnstSttnSearch } = useWrStore((state) => state); + const [isChHead, setIsChHead] = useState(false); + const ref = useRef({} as ReturnType); + + return ( +
+ +
+ ); +}; + +export default React.memo(SearchModal); diff --git a/apps/wr/app/map/service/index.ts b/apps/wr/app/map/service/index.ts new file mode 100644 index 000000000..c6bfcff72 --- /dev/null +++ b/apps/wr/app/map/service/index.ts @@ -0,0 +1 @@ +export * from './map' diff --git a/apps/wr/app/map/service/map.ts b/apps/wr/app/map/service/map.ts new file mode 100644 index 000000000..bf0c8a694 --- /dev/null +++ b/apps/wr/app/map/service/map.ts @@ -0,0 +1,47 @@ + +/** + * 관망도 API + */ +// const API_URL = `${process.env.NEXT_PUBLIC_SEWERAGE_API_URL}/v1/map` +const API_URL = "http://con.mind-one.co.kr:11083/sewerage-service/api/v1/map"; + +export type LyrStngProps = { + lyr_sn: string + lyr_nm: string + upper_lyr_sn: string + drawOrdr: string + flterSql: string + gobjTy: string + lyrTpNm: string + useYn: string + minViewSize: string + iconNm: string + lblAtrbNm: string + fontNm: string + fontIntnColor: string + fontExtnColor: string + fontExtnWdth: string + lnWdth: string + lnColor: string + polgnColor: string + initEprssYn: string + clickPsblYn: string +} + +export const mapService = { + //레이어 설정 조회 + getLyrStngs: async () => { + const response = await fetch(`${API_URL}/lyrStngs/WR_NETWORK`, { + method: 'GET', + headers: { + 'Accept': 'application/json', + }, + }); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + return response.json(); + } +} diff --git a/apps/wr/app/page.tsx b/apps/wr/app/page.tsx index ff6ef558d..c24d49500 100644 --- a/apps/wr/app/page.tsx +++ b/apps/wr/app/page.tsx @@ -136,6 +136,18 @@ export default function Home() {
+ + {/* 시설 현황(지도) */} +
+

시설 현황(지도)

+
+
+ +
+
+
); diff --git a/apps/wr/public/images/map/layericon/BML_CBAS_PS.png b/apps/wr/public/images/map/layericon/BML_CBAS_PS.png new file mode 100644 index 0000000000000000000000000000000000000000..5ff4add3ef67899ca56ef065952b919b8426d086 GIT binary patch literal 1547 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+n3Xa^B1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`f(~1RD^r68eAMwS&*t9 zlvv ztM~P_^2{qPNz6-5^>ndS0-B(gnVDi`$o& z6x?pH#Hm*w=oo!a#3DsBObD2IKumbD1#;jCKQ#}S+KYh6+H&5{UIqrHdQTU}kcwM# zCdYbA4wN|lzxuh^-KyftM`9}$b|eHj#ydx=Nojj7R_fibWOhtktBRwzr;AGL6JLcX zP9asojgm^Nvyv>YoZNEAS66w8U5Los;>%~ht$SX1yNy%n%YpY#=RBYD{eR7zVv*&a zHg7yw)KPTEN8<3l%tLM_g0*X7YcHPdDrJgV)VE?sNS)uzi4)u2J~Hz-$nY#Z{b5O+ zn|F2jglp2BSwbJzieC`a_ZJb3;`&^C;m;P%xwd6GyY5cNP>d5PEO@UsWuC6v_uqA2 ztlg|m)V8_oF?{%<)YbL4?(SgT9XV^8jz4d=GUMYsCQ?~=c0bqc2kI7fy9%~0;^Ao3 z`RnX=Zd=fT{>}|zGDZ)d-L3kexg^7*Ma_@fQ6Qz_jGbq|BgbUpPoIh+uUG9-IKJeJ zkN3)jv-tO9Wgb5Agg?Wzc8g~tflT*nqwc24D_r^eK(M07myPQdht9O1E(DK!JCiZNB zzeiH|raMOx65h_&*cMm)<@t$<#hNS+^COS+R2N!$F45U>`_2aQz0QG*+fLXNDYaDy zde2LpcWy(^SyL;u`NavPsi);W%v-$krbKb?sgs)yJid0_cHw8$;{vP29>hoX2aQPz!58&o`d My85}Sb4q9e0Czo2EdT%j literal 0 HcmV?d00001 diff --git a/apps/wr/public/images/map/layericon/BML_ELEV_PS.png b/apps/wr/public/images/map/layericon/BML_ELEV_PS.png new file mode 100644 index 0000000000000000000000000000000000000000..83970a3311924d9ae070ef863066a89d337b9ced GIT binary patch literal 1030 zcmaJ=O=#0#7!J-&H=Oh5qB14R5EX3xnr3ZS99`47f|;c&tQ+Xel5cI;k}pfX*{mSq zK(HP}$G{1C5cQ}>FT&gof)g+5%^y4ndKjXhh)2J4ZRf!nl6*fr&-1?D`#xVeJ#@0Y z?LZqr5bdc!C4}dC39#PPNJgb0wQj}2|%VSEe|q4Gv;R2KrcZw zQD!!Wa_T8bhZd~`7}~cS%qEE5KHt&wDS*fckT-3a`u5~0MVf|8jYL#dbrPUp4whUn zTpG&ir71mTP<Ae$+H~Ku=o{2oFGL+iSHq+4~3(-#-x-{2CA{}N~Q`3ITFJZi$%H^ zrlFf>xL7O}Xz+XpBSPMsjWj=Gd)+k!1$eq^I>>}J87OKKa2ClF&UE7lmQ$Cty=s|o z!5Ck27>;IxBh`Sa`hTcp)zKcxfX#gWDePtE9Kd9N2WMR!H*T^!2<1o#7ib8&SqP_V zRZJHkf?feSWMWt(kB*qO0gK+T3SL#El@H^S~Hzp+#1XESZgS>1n7} z|c$pZDIEW)4FZ(qHcU(W4F85)>pg6^Y}`XQc67Qzg=8-*GhEl z*!szpb4$(T=Fz1y@8_M4z4tyJX?=NxZrN=~p3x%oe+21+&n literal 0 HcmV?d00001 diff --git a/apps/wr/public/images/map/layericon/BML_ETCP_PS.png b/apps/wr/public/images/map/layericon/BML_ETCP_PS.png new file mode 100644 index 0000000000000000000000000000000000000000..df9e4957ba7412d8684ee344371ee72728a40899 GIT binary patch literal 961 zcmaJ=zi-n(7&Q$8tyBrYQkYIo2at$=#dZ@brfPi&jfATxv6PH;>}z5r_8I$voeqV< z0NsF*m7R$NF@O*YLnS1{!o@n9pC$mDh!ijJ*z`H=3ULf zK4)hbF7-poW|+!m8rseP5zs|FFVNXv@7}Y(b9J_@n1UG^sPF9@M5uMJYdHr4r|hzu zw?QS}gURvjGeoA)s)@f+L6`P*qNp6;%~) z0Z9-gUZAg95M@nKHE9D(AC^Xo+B;k@Ghoc@ZmdoW#Ly`&< zQHaL@vC~2j=VuBK#ZKgf#KQr|6zwh^5uK%(o*uyu=dwXOEfXympV}cWazb{b8PGKU z5B2>yIwnnYmhZX3u{916-$XGUMGkG;em)BoYDR=?f+Gv#;jD^{J|;Ns;}94v72Ifh zfs2#)<^*q=T0Mw~9XO~Cb(Sh{p66;Mp|&Ln5|owVhA39$vLM4!8J22>3^!y!m~kO? zMm`G2jO(6ps|&fA75tD!hA8sxBexb|A54y{d5d$|UZ^+ax{Gr$7IJx-44*ajpGMEN zsC%;M+_rQvH$Doe+aqf0C*Rb^^hzjoxMii^yT3P&j@Cacefo0+KDAEX{8+u3JASx) t!h9UQuyPmjU%tLNTw6ZeTUW-pi)+l=m;BEM?Vg!ks(N)7zTVz@^cR&XC_w-K literal 0 HcmV?d00001 diff --git a/apps/wr/public/images/map/layericon/BML_STMS_PS.png b/apps/wr/public/images/map/layericon/BML_STMS_PS.png new file mode 100644 index 0000000000000000000000000000000000000000..423f2e8fcb87d6c1b9192d1547a4cf97c3bb7292 GIT binary patch literal 1542 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+n3Xa^B1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`f(~1RD^r68eAMwS&*t9 zlvv ztM~P_^2{qPNz6-5^>ndS0-B(gnVDkc?Br}XH{644~kf%h=vIPQxAv!Z9mxW?6};@ zEo2|D%REmdk7w!IEnAsXx3Fee%g-}U76loozD!P z&)&Lq>vfBCkG(7`X&G_Lf*+s096UWhW9E%h;$|vw%~()q&4|I^Q@J~*G4D0Fh-ibWa?D}1IL z51*v6Bq*7&YTk!CH|nJ>%zXC!xsRIdY>y*~lO3j=&^@SJFq1VhVC9tnohe>l^nO*X zPnWL4S3e!)m$$clI?+9yv+1q9 z*{4e3_h$DN<(4m4J!?M0E|$|ij}FdN)#zfs_*bZgY4=sDBp2q})uC5rm1=ByboJdl z4oOayi!VzOt?$;mghbZQ`F8Kc3kw;yU3SLpZofGuJ+6AWS}R$@h)0*f*HnPlo#mZa zVdSe1!6_edtg0)s3{(zoGqu^X_eG->i}{RD4GRY41&6okY|)*q%AvciH|O7ve@!<; z9XA-woavCdymN6H^VUyh{3qjfojsf!BVmxh!!o0{)g_C`O{3{bg_KIf2h)PO>--B| zvZ<+0iiof8uQ}MVj8kwWqkQ*7VP(TL&+-z!A2_srZf)b%KiyAuP3k&0q0y_9{rFox zrd@uHp_4S`nU?gb-qe_7I&I14c^MYBSwknL=H!MamP>c=x+`vEeq{D5w?u~hVSvEz zuU4gr?rloTWkpWE+rB>DZ+(f4+~>6Yfm-4_#s0os7x9nFfMHhRx5p)mI~zeIv!|<{ J%Q~loCIHG1Mo<6% literal 0 HcmV?d00001 diff --git a/apps/wr/public/images/map/layericon/BML_SYMB_PS.png b/apps/wr/public/images/map/layericon/BML_SYMB_PS.png new file mode 100644 index 0000000000000000000000000000000000000000..497589bea2f5a6aa1ee6c8f4756a96a4ec8b6bf7 GIT binary patch literal 970 zcmaJ=O>5LZ7!DMr6beE;XfKYl2o>CXZ8mm?rfZvI+ZD5v?Lya!G@DF!Lz~PrnY!76 zSiSi(`~md`sL&NW)RR~7CVKPW!K+Xw+uf~)t|7^M@I24^&ig*|YNNI`J2O8c2*PZ2 z-Kz8PTJ}v}<^Nm1&s;v-WOkEnk{#A{V=S!rq=i8>blbR&UB9>g2`>r46bu?o)^r{r zkA$L|VZ=0~oGl1T<&?VKE@q&G+d*W&U++#J2z&!>6&=~3Chi35hcVtftTnvDU9aTB z@-kRT5f=zCb3q#JMF~m`IM79Wo^4AI3?OXRfaggyoef};7=xl%$a}J)09_Xqt*GnD zJ)p{pD#`rS^NNOwI#Tb0;RAWJ*xx~Qt1^tmR|f1bMv)|SyIrwc5J}vYlv1gbX{c(R zBl1ZvVs4s`lH5qa!ig6Llm#RLnWEbw2h4yx)AJ*QbSxVs!!q%LNvTUEMU=B6jew5x ze`pwv(Fv>Li+nE?P8vOmr8-W?LG1Cy?c}mh6qzx08HpQ&?2W3p(IJc^9YTS*se^^B zAo58!xjVo+4yr~8b0ZH|Edz1|F$jF5n59+Iv}LWRX^LWNEB0!^UepR^MJ;K%x;Wxm z#5)La#712Ig3H@9fty(&q&%{P%BV~>K zr_rM=?w)Kqwk=yNSre)!A4}Si+a6FgYuBu(L-mGrF F{0katFXsRN literal 0 HcmV?d00001 diff --git a/apps/wr/public/images/map/layericon/RDL_BYST_PS.png b/apps/wr/public/images/map/layericon/RDL_BYST_PS.png new file mode 100644 index 0000000000000000000000000000000000000000..1e78cba6874b6dd9f8f3d1ce95a120e0ec52dd5e GIT binary patch literal 1196 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+n3Xa^B1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`f(~1RD^r68eAMwS&*t9 zlvv ztM~P_^2{qPNz6-5^>ndS0-B(gnVDi`Vr*gN=xSkTV&ZIRXy|HU;pXCG>E>i^;OJuN zH{644~kf%h=vIPQxAvIJCflpNy)(IT{C~%tb#?nlypKc4j32kqDx;@2v0ppbYTLR2w=B#nrQhxMN z0~6ou_h|hk2c&?`pw(~ZH5-Rb%% z>W_l_=KW`mR7zYRop6!M=Ak)1)?9S%C(mwwxC`_SR{9qWy( z>`Nzha87l7^inrrRmIZD+()1IyuQV;=ZLbNYoCeyqW;5N`DTH>;l?gXf-{%Me3a78 zU#$Fl&YwHVmoH8<&Uno@^U2T=Lp>FVdQ I&MBb@01j)NZvX%Q literal 0 HcmV?d00001 diff --git a/apps/wr/public/images/map/layericon/RDL_CROS_PS.png b/apps/wr/public/images/map/layericon/RDL_CROS_PS.png new file mode 100644 index 0000000000000000000000000000000000000000..57b5f2ca51d03eb08728f311091e33ba37747c4f GIT binary patch literal 1083 zcmaJ=T}TvB7#$Kz{443Dg6K98CA2#;yX)vqZlXK8?nWcFyP!dV>bp*LIYk zh<*rs2`mW0iU@0a5zcpcp&Y>KL`aZFiBLkwDByJm#wuMfv)ps69csX}5UGYmVl zSdRshJq;0-3wp3=I|_I31!FlWLY4K^R#O zRCxU!h2=QF^Ik4c!SmcM;A1%-!;+WxZ~>u$7krgq>7hw9OKlSpVst4MS%v5hj7@=I zGMS7wdaQLu>)abcOVmllRVhjq!}v8*yRg&NfP3Qjb%fDaWO;_1+S*50vq-P{FPN= zAP}zQxJV!v;H#?GfUhnb^>ds*nB|H{>C&Nrvs`tB8_CIat)QDEvIs5h2vnmM(!s)! z1ub_jbvgBxxN7cP!a2DNNrrJ7`&Xm0Tf{x?blJ9Kv21*35Vu>z)(>9$zL6_&J1*9y zoar~onUV=mJbd%$z2fu~Y$<&TipFc!4;2^PABhd`+`e~uZeYiOyRUwHIs?kC-a3%p zesbtE7p<-S@Yuj*1NoQi%SA8k`9o{o{gnIfT+tKhUvuXZsr2>4i&)=4|J%G%qhrsM zgV*xQpG@rU9m$+yKTf{ypV}~yoV*y_{kfisK8hTldOR~xcY*hxZ0tQX8kN!<`(?J^ lhPG<1=9s*;mmi$nI!?vb^=)?A=1bk{9FH`JgLN$@{s3DZXz>65 literal 0 HcmV?d00001 diff --git a/apps/wr/public/images/map/layericon/RDL_ETCT_PS.png b/apps/wr/public/images/map/layericon/RDL_ETCT_PS.png new file mode 100644 index 0000000000000000000000000000000000000000..29c0b77b4ee74276df8de9416fe284e4c8bc15ec GIT binary patch literal 1016 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 literal 0 HcmV?d00001 diff --git a/apps/wr/public/images/map/layericon/RDL_STAT_PS.png b/apps/wr/public/images/map/layericon/RDL_STAT_PS.png new file mode 100644 index 0000000000000000000000000000000000000000..13d8ea6899c02aa0d0917b630ca9f697783fd6ff GIT binary patch literal 1075 zcmaJ=&rj1(9512<`H{#)kqa+fOw_P--MZ=;1{vKZiMjYDVVY{1~JIMQOyX^pH}bFKvP5XU_kOprU zru$RM^q3M<>7H)TRS<}P4zUah`nX{Wg%G`?E0Dc&%+O#5g2zJiZcu3{0Yqd$5b*fj zikD>p&wE%d!1HV;@OfDu<0UWeW;r3i3%=7}@1cn|OC1rCk=ULuVuk1|#-_k9`F!4! z_j`~v%CNy;(9!Vu+yvparwl9?+=lI{C`6#GSel77WB^A|&Y&C)(InE{6m+vHYuI~b zA_Zd#vdOR>uai;*C`tc^>UtGz<0SkO?>~j@)RYOCB(zb^Qb^-QT#hSK5G^QUWTg-q zuT(LSMHtyxWCC%32PXzKLq&P}GZk(GDLEY)xWZNcaQT{CXB2dkc#c3zn}ll2LOR$|c$p9Fgg9w$-sEMs@I^L8jdzV}1OY-y&DyRy-0;6<%ew%H^JV zzVupXjlM;>ZWo{HyvOCIS2I^-|6J?Z0Wkk`VB^ijLzBFB=EJsl<;(3=cId&}xjPGs zpsi_Q`Qf|pQtxK_1*XV#|N6FYsjW08nU6nin%`GDdOo)o{msL(3qL#dFVEMxR;76pH}7 QeD7T2c(gyV)H{6nH~m6mg#Z8m literal 0 HcmV?d00001 diff --git a/apps/wr/public/images/map/layericon/RDL_STLT_PS.png b/apps/wr/public/images/map/layericon/RDL_STLT_PS.png new file mode 100644 index 0000000000000000000000000000000000000000..489524b5c4cd57a22db674f7ad8897be209f617b GIT binary patch literal 1089 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 literal 0 HcmV?d00001 diff --git a/apps/wr/public/images/map/layericon/RDL_TRSN_PS.png b/apps/wr/public/images/map/layericon/RDL_TRSN_PS.png new file mode 100644 index 0000000000000000000000000000000000000000..15675cb315243b6ee8ea7977c337e3387ddcb6d5 GIT binary patch literal 1375 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+n3Xa^B1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`f(~1RD^r68eAMwS&*t9 zlvv ztM~P_^2{qPNz6-5^>ndS0-B(gnVDi`W@&0v4~Pj*wm=R%;iu*SQ+p9GS(i^N{Kvq+c-hm%F{I+w zl*x|%w+%#&|KAwATq|_9qlSXwEWfDRvdiW!D|~*e)mnr_zESx;<3U!=PVP&6yWdnR z_zKIIB}lCnXrJ1`HN_-%(zSzzOB|#XD=tl*|MvU5xAX4@A2M)J=~{GRW7OJz!p#RQ z-Iq8D9Qawtw@IK0h(ntNYjR+Tv2bp;(aD_jaL(XxR-@KLd*iK2hQ(1 zx@sX;^uhN2`4dj2O?>fthNIqe?&L#t#}5{rJ#=YO=d#Kshd(YVvzE24TJ?_qu0oQ6 z*>l%LM$cofWT}~%N&TJ7*MDk#g2KAmf(ebe!8_|})+ipmp)DaGnqDd!pJqJi;O-*U zqcbl~H=eNd0{4YkL5coBCc2yD4NfR*W+CNHncK3J8d++=o7*be86k9Pwol1?dak#)BomwnS08u~!#wj5t!CX~C0F*K&VFQg% z=BwGDLK$w*fYF9^La8A^8XzMCv4B7w0~1hzs2I?x5+Q`4V-m)78Q9#qO(Fo}5Hx{F zc$buj9|^Ew4G7SPKHgF?g#ys&M2atsPN%E^sALM2M8_6zk3poCKWLTQs+ z4lGSlfe<>$l}&N|$kTGIR#2(2$Q)3kOa^6P8dwF4m&{O3p9^PNy$P;t+Fa<|X}KgU z8IrZJ|7!H)7Umx7bjr5aV#@d+gt=XV*_sLU$g!_w5swoh)IA6k#40}H;>T{9-nPDk z^6DR$io9uMF|)VLSzRp(NnBP0h`4K5_Pf4Ugq%pTXYFw)v!8uZqzHa|>VEJxvg_3k z(!a@kRhYK^PUP*JqRj7F^KbNCPBDAlGQ}?S_d(=0hw{zMi<=fo@XMQ5@4480GKa-) z!5{Iz5`(*I^ILMAc4vf*?d>gwjc$BLBt_x5qj1%tr2Dp8Z`%pFzi{hz4|jL6Dah6f zU#=(vo4VJoxw@jFYa``~c!^i=^PN?v3XJm_n}*@k_Xm$$ADnl>h|7s+`lPnUE~mAr zzO42M`Im5|sj6h)_P4V%wAK z(6BYJ@`tl$+)VagHk{*?_zxW4Z7y)kZQ{XpiVJv;!?JSQr!Ivp$zUHFvYqP~QZvf~ zZ;9^EuN`VVACaXZ8m^axX{*w68ok<&8Oisd&Xoa1wbAv_>?H>qf+RunJHL8WwD)Oq zuF;+KI;?$XiqEC~cxQcV+nKDqit>1M*RV_Aa3fA$dO_Xx!lhoa(423YQ*)Xn>`QV{ znIxA6tLr-tIhFa$y0cr|J44(j$w;Wt?HLl5Sw3pZMVuqPxOeSplxsB_|E#;iZMb2y zAHEdn{b(>VGu!fdW@`WS%YmY(I%9LkdtC+fmK)t#{Mi2abALpnZ|-e)t$Ul_8+edd zpOC)3E;r{%-KWy7Itt4oEVkeNsu9=ULaC>*~S)^ X+MJ>?k7n6e{~J8+I!ZAjE$9G`_Em1P7{TC`@E(cNvkx9-+mYBw(rogH^qT0}UvM;+JptnIn(G%C;_ z45Ipy5q&5M`&K~_LPcqPDItjHi}X4EV+a1vA2m1V+$9phKza zGC;hmF(Gv&CBIBnZ6yO44il&ViDX9IubEsXNX_zccyArk6gdk)$sqMRsb(QchM)mR zkHck`7?vfyUI**;c)hHRbTX`yX7KH`vu@7g<(!+zxr@Tl47rVq@!`2x*a}h|2wsI?!&V>vh zB4{Ka+@I}Yv;!h&c0ipB#l2)@lcLEmZEl>!3j!C>OeAU&i10xQGdL7Q<`|~N$#_Ce zw}%a}Y^@txe5ls%^M%7K>ka#|zC4stD$r2YSN`Mc%IRxKLDg|&9vI3&Acqa8lCvdq zO72{koP2Y>a_(HPWOCF&<78-Suzw9YdxX_vZRcf+jd|e%4XfS2vMw0j_85N>Un6`? zA~SL&Uca=PDIPCgvAMF8c`$PCxZZ1Q2^8+F4lGPgy=;{~G{i6NTv4%ZylnLD)2sP> zB58|mQl%c-@R9!KQ$b&T<-kB+>FFi5o->`d>uwZQP|Tr{hFve8MEVDh4Qj>9hYA12 zZ{J(W)(D@5F6HI5mv8UB-YZ_1d{(kJRnao>Y{TidPy7o?s!`vVZSY04KAnOEACHbr z+*`Ij`oi9?oH#Y}ZeQ8p`6iD&w)*g!l{=2Vsyxegoh(}F8FiIZN%5hZhc3_Td4$}) zFW38A=PFLTp03|xpB&mfT}^z>e>n2~$MP0ppx^AB>A5v#{S6|ujr_H3E#1EWplg9C literal 0 HcmV?d00001 diff --git a/apps/wr/public/images/map/layericon/SWL_CLAY_PS_RED.png b/apps/wr/public/images/map/layericon/SWL_CLAY_PS_RED.png new file mode 100644 index 0000000000000000000000000000000000000000..0e5450688f9dda207fc2f3679b15d7b2a3e3c982 GIT binary patch literal 468 zcmV;_0W1EAP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0clA@K~y+TrIXuj z+b|S{KdLGu%C0GrvMt4y^VA~Qpx5(TnDUjyZ0ojg!gEY(1p z^aqG@0r~0YMM^~YA4TKFOQm)l9Q*9Ac7Fc4Rl9W$IQE&GetI9eqCox0YL(*4kDCD` zV!Rk=qYkp8R)zZXZ2^eWU?lDg#pxpeZ`1IxIBNk&5_*M~`29@A4`-+? z@a8#JF`Rr>^oN*iA$b$_(G+mpmVRf|0>I%ykZP5u6mnLUz*pd^IGnBt$!xtZ%G8F`6vIj*G6a*)z58{J}GN1Itm!arH5X5>i*Xn~cBsmwp@B9Dr|KB;$ z-?yiswz-y~sD@;Zm?GnP_gh;{{#%xdG8vk1_W(|#VO)?cNX1l?g&?WRIhca7I)3OG z?4+nFTFVUJ0jZZ)knWdVjNj2s!ltOsF2|IW5r{z+<}@Qpf4cvW2AUeB_l2c^WC}2^ z^-NeWJ<*p@CPtKqN_XuBoeoa~bcki(=%a?sJ5hQ?mnU;~o1wu91dl}N-$@Nf{Xjq# z1Yv)hPYJLr;5a`U3UeIW3W5PP$OOpC`PdL2=J;ScSbb;`%~FT?l$cnJMOINdkFm)! zOrcQl7ux*D$}wyt5^*(xK_5Z*>~RCjj?b{YB?S@Milv!YLk4ga`v|&O zmNo3vGLeEYj%+flKj0pz1eB!zLv_83ws8vn$@ibac4pj!ObXg)%u-0>hP`emlNT%~ zV`OCz8ZA|^KaVi7^T-53ngcucX@-gl_O2DYB=JeZ##`6wmTGSPb7qncsLS^#N!>YaEU7-WlV<#E^*aAT%jV@wSsPv$Rf0~!%$6FNCzuN z=C#VXL@Mg7a@ESY#42(bk__WE_OC{lwupP&Y1y`9Q8qp_h}$h<>&C@z8_AWJONz0K zvzYx+EOu43Of}AUTCe2THN~6U@U}d4EAisa{de!*9zAfV`SCGndG=cN`d#m5cCg0s ztZkdV_`O*C;N7sa@aD|c>b*6usvh;;`*CU=n9Ie#-DaMo9yG_kJe8}s`Wbq1vi|ku zy3wDFr>{0$I{T99&s>?_%`NP|dXOCwz4PLcIxsiAW7}6ewKRxl&NU1@+J5|F@8E?S hvzxA;-<c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`f(~1RD^r68eAMwS&*t9 zlvv ztM~P_^2{qPNz6-5^>ndS0-B(gnVDi`W?|%N}T^vIy zZY>G+^bZacvCDt+IwkY%lxaq0S2#F2yJs%*&;kG)y6C8B4ovh9#bUt|9$Gh>UDL!ihPm6Q3V zoc>LW?^-g;lvc03j5f!aGB3X0_FOfx&|P?D=)nUY z-5Qm5*fK5A=P>&{Y0odqs-$0XzoX|%m0p$C)n_!hZhgPe*+;~kUsIqpYKN3nZYm45H zocd-bw?pI8zuRXV%`ss({qOmI5l;rs4>HP@Z^fo2M+W=mtbP2nyOBxYqQRTVDp$jn z^S6H5887e<9B~= zygn`9o3b!O(0bnOZABGzQalz#F)BN(pFThQ`C!xKJl#c%DgW>D38&2Edo8{G^tXda zVhxFs=br9RZGG?i`ckL;i{;$qM;lXoZT2{y`L=X>!%5p5=VPI@eVfE*zg?$4QSX%< zhYde_yXPkHw3sy-r!VLW7L~j;e8~S>ngFVjS~ zp*D&%hdOP4!W1`ikp-F{h+?3{hAU*2b2c-pM?CPJm-l&}^PJ}#ZDs~1n0AIn5JWIH zmBqt-^56tgkQoUl__#gwvAo*q08YmG*uN~IEu#ogWA za=E;`yj-i*c6D`uTv1U0T1`z&V`HP$Y6S;^ot>RPT3T8lVltT^qE@SgLLr!Nf?AzU z*VfhsCq+d?P$(`g)@U@PrKQN|1x|l|KlsqEudfFO9a~#lr>3UhUm}snWHLAu2n2!YW9{wj z@UPeFv7ElXJ~%O(%@&JgbaWK{pI&$!f}L_7@iO>CBqA;@IhoDoaxr#C$Jp4L@rjAI zi;JIESJ#|Qr_1G9ce}qMHa0w-O~jVhyS=@$v*Y*w-rL(p{6%>7d`S>JR-T$GAqe{P z!6CgD4Lkt2%w;jN8T-C238lTDOAss^+w;}azMPiaEo%Qa9V`1gyhx2Kw zv$4_c`>_QE&-zP2l;^S|Fl_!4{mR0e&v%;}^y~UMh335I-+jypp!)qw%d#0Y^?6h; zd7q*t@8`J0WF|T5iS_3s)0{skoQsGiv8C}VS2yjW(f<_lNo|CU&ARhqM1N1AVh)U) z%T%z@mltzshQm*r%^?_ed(0QnNYAOSy+er!A8Da^Yher8*&zClYvFNS%Grd0X`wFK=Gj5Nzu8p8`eIVwEur#cJGuu|NV3OChhs3-nq4S%89tT6?ID< z1ZMUGW>1)~?|)Fv1fa60(zzuwPEXwbKOnOwAhR!`cy`g$;|X=ELi48-O*8G6CCN?e)7!VEcWle-+>zO}GrM$I2>t;<>9w76fu?d6ctjR6Fz_7) zVaDV6D^h@hH$7b(Lo|Yu53t=};nmX8n(|}j!Wuro%a7Jv*`i|0sb?_X*X63pt=s3_ zZyH8Ul2%c8k)@LzA|SIScd~@e5e^XpjZV|+n%q1mPy2n?{D7G&B!#tIWm!w>&)>Ii zS7|k{1kIVF>~vUJ+3Cs?@57EvCxeP+GES`g$oNjLRe@2JKYMr0{trJ+OxQ5PVgked z)Fii{2LFN?ixPb%*abAWTHHGAWvqV5-#hBAFN4N`1Im)G76i#Xp1oR8`4pE>!UK`( zzWQBU+y=tQ?4KniW>knCI(+ymtHLLZhDJt)YxngOh3bmifu3aWboFyt=akR{03h6M Ao&W#< literal 0 HcmV?d00001 diff --git a/apps/wr/public/images/map/layericon/SWL_MANH_PS_GRAY.png b/apps/wr/public/images/map/layericon/SWL_MANH_PS_GRAY.png new file mode 100644 index 0000000000000000000000000000000000000000..e19f3d8b36c0d5c05f2bd0156cde3a42819d0733 GIT binary patch literal 1054 zcmX|9dq`7p6uxS{W{M1_SrG(+mJc#5qoUBvC8e2)FwNA|d{&~SNl+mv^+)tjs1Qpb zQ6$ZUt_RM?bZ$PcZnNp;eCwuOXD(BlufBDo1NYo>&+nY`ec$=rCn3RXYsOv%K@iq~ z7yLunJhM52Yy@7!N znkotahnOVEv9U2=p$3zY2O@}}7@)|Y1$zecf0=Y8Es!pzR|G`N1To;^3NV>Qq_smP z2tzw=Q>9V?bbNdq3kP#t7IdlA>V<^`tyT-|*h2}+wg%UqpB)+;MwpwMGnsaFES86d zM?e6ZeGQhBl$?^1T3A?ER#skKSI6ZxL8q_-+(jh$U7=8o0>A+Acl@kct#m> z7PU)DKYjoQKtB!kYjt&Pef_t=ut2?j1BQif{j=?D>5EJa*jQ#<;A9bmO`G8{65Xy! zz@K^`(BC)QB`m(h=C!F^P^vGpus$ zSbd(DC`fm>!^>G&;eGb;Q9IoTI!Z7&zs_gm1hvLUF$KKK{)#73_zbO5o->0`*PraP$y&H0OnXz(n98C)DhF|>VT6Enx zr(R!jQvSZ%s9=x(N_AxX)mPa*iKpJ=rTol)bV8V&>Y|oyXP%S!up87^TGbLezEy~00DNQ ACIA2c literal 0 HcmV?d00001 diff --git a/apps/wr/public/images/map/layericon/SWL_MANH_PS_GREEN.png b/apps/wr/public/images/map/layericon/SWL_MANH_PS_GREEN.png new file mode 100644 index 0000000000000000000000000000000000000000..4503c13c63f182dc291aaa8184b523c09d5363ea GIT binary patch literal 791 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJV{wqX6T`Z5GB1G~mUKs7M+SzC z{oH>NS%Gqe0X`wFK=GhSDbcfYJD<#+{eNwEZ|bc7>mvG6X8+%izqPLHL}%)X;?@VV zLVFU!CnV4KzX3?j{J%bKZf@o2-dX?G1I0u8l453Ommlv+T~!)6C9UFQSHi;l@QJC} zo2p9>w}thmL`+GW`F}&g{M@XKRoyQaL`+FfSzcUvxIKPeZr=8~%G2F77bb-EC#9__ z4V#bxR2efXD|=&A{;r0C-HkK;t`F*nid)x-6t8A$@fj(5~27Sxq;lhV>@{ zwWKU73hIoHn3S5oqhZ>wHNiazQPVTx=H|rB%k^)L_HT&}?1%#z64VJq@xfj3!QBZ! zb3%F(LqRAB=zg#{$>9@I!Y8Ey&54+t3iL?i)byyS>CrPXV`gRnofJDOJ7Ioa;-Z42 z#f3>r3X_)>r7SB>U0DKjPv-i{+^w~_+v@Um)aUPPDBRmra~6*gPoKYd@d^muym|NTJrI2Q{Q1k5Z{NQC`0?xa@4rCs4+u)H?VJlVm9xMj zvY3H^?=T269?xHq0u;RI>Ealo5uAL0?FI|)s#U9|{Fu40hEMSFr8!@EL~J?r4Cec~ zTvfSt`@H*2!_Z06Dhe;Mbh1N4gx2Iv7STGwA!4B6ZE$@h2aom%-%pz#Fmr`GZD~NS%GpH0X`wFK=DZq4znE`o5Y+y6N^bDf+{hlT>VKyjer*^Z9KLqb-$yHByRI~f|f(AjySt?ec+ufst> z{Wdm0L7>X{&dwXXyMOQ3>IE2}Omt8ObRpdr0rXZP9IKuiF-7N{L)3(##-?d@kc zILvf#06GI`3((n%TwE3dk*n(xSJ$PkuFKrqR=T?bow452bE}WfHXolIzP>yC{Pz0$ z9}EmU5)^bSIQV*0)Q#xqJ25ej;^UttBmljyCL^)~7}j1TL4LsuGC-iFW^3!}>gn0o z*f@Fe&Yin<@7}ZL(6wuK?%#j#`|;z~ z@85rc;2#i_UfVerXewucM`SSr1K(i~W;~w1A_XXT)6>NvC1))a~o;Hw^I^E9WE9-F zk@1}#s{*4cfA(&h`x|a_Jh<>eVgked)S4=%2LFH+ixNu~*abAWTAVuVWo&)O-#hBA zFN4N`1Im(*76ggCoxE64`4pE>!UK`(zS=!JyavL_?4KniW>knCI&|m@tHLLZhDJt) Y#s0d7PA`?`1A3Cd)78&qol`;+05Lm9=Kufz literal 0 HcmV?d00001 diff --git a/apps/wr/public/images/map/layericon/SWL_MANH_PS_YELLOW.png b/apps/wr/public/images/map/layericon/SWL_MANH_PS_YELLOW.png new file mode 100644 index 0000000000000000000000000000000000000000..b7de1fbfdfdb1b373ac6c53c3541dbf14a53cc43 GIT binary patch literal 782 zcmW+ydrVSc6u*>KQl^!)thOx7(aYJ~np@chgBh0Q&_Albw8v~cmg})K*B{XPfj~&h zG<#@IZI(4lq?Sq33KcHOD=?5i;oi%=m*-GZJM`Q4o%4O?obPvj=et=_T#(FM#Y7M! zS+JWY1fLq;3+94lZ%ru$$2co2;392E#pJnk1G6V zLp181ot?$?HDEDR3YH%22>ZSV>@Q;hC$5*^1~~)?ecbv@jS-&~4!BN&>!f~TL)6Vq2E_98r{AU0orUdUj?S8@xdnYO%qa^hhhFlhF27=tUaqW0Zx^%isxReGXZ| zpa{7-XQn4Xl7ILfX=((1U;kxa{}pWD29Q7vHh2ru-Nxe!WY@s3xL%6uWjHDa!N35G zKrJXFhVJ@@>-@(1eq%i-rz}mBwVAR$rL51WU(adVE07WR{U+$_2#$UTj&_Dzs?eX$ zVb9md__xS-Z;Uj9yb0Pq8FEdAJuv=u1Ns2|y6~K^xD;6f$jZvk=ko;uu~=MNt5S8T z)t^4~m`rwu!|8UrJs!`v*NXuNf+Q(GFcb=hqtR$AHaRsl4VVF(H>nzdHK}U%fwKs* zSQFojzSA#{K+r1Samv`Ghr8!l5|V#vwRdCppIltLWT>fMIn2vX%FcOI#4~38vgrz} z+XRZ$nHk>WTTUp~E`5EfHg)~C1!*~KX|8!=@}hx(Li)XPE>l@~?MhbW^{W$8He2;3 zQG%lJ0gJ0)vA9O}o*vE|f$&T^n%A*@GMAP+)67MiCCr66%MQ;E nrLU~Ju)Ig3i6mwO*&;Cm>D!;3^kS^F7=}ay{9;}kxBSvSe_-5< literal 0 HcmV?d00001 diff --git a/apps/wr/public/images/map/layericon/SWL_SPEW_PS.png b/apps/wr/public/images/map/layericon/SWL_SPEW_PS.png new file mode 100644 index 0000000000000000000000000000000000000000..d260ea3a668f2d7fba866de62fb10c4220409ca9 GIT binary patch literal 1217 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+n3Xa^B1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`f(~1RD^r68eAMwS&*t9 zlvv ztM~P_^2{qPNz6-5^>ndS0-B(gnVDkcWM*z=V&ZCSVrt}UXy|HUX=-NdXzJu@Y3^d? z=H_Aq)9aF-T$-DjR|3Y4 z5WOk5-C~4OuRhQ*`k;tKifEV+F!g|#@MH_*z!QFI9x$~R0h6`2pt1!61EZ;@i(^Q| zt)xHy|JyUGHn7fo{OXzCg)Mh?ml-9QGA!A-f4#Wt$M^R258ssM-#>6wQThy@e{k1zgC6apd*5wu4cAOPFNvKfD zApKrVTPwG?(S}V1#v)>(&9RK-j6R=#`_C_wVwlw5-=Dr>O}-27k<;7XHCZa0^`CFs z?7gq@XOp8#4AV&tO@o!Im-T#TS~t(m=9SKxH4mgNGM-r=e7x`AQN7qb2Tn|yIMp@i zkfDKrgRh6UUQ9wkNohOxoBMl(eT21-F%=7W2&r_uaY;HdLqdZ^MEZcm)07wgV;W6) z{-^&&T#n)DpWjO{an^LB{Ts5>LH|o literal 0 HcmV?d00001 diff --git a/apps/wr/public/images/map/layericon/SWL_SPOT_PS.png b/apps/wr/public/images/map/layericon/SWL_SPOT_PS.png new file mode 100644 index 0000000000000000000000000000000000000000..84c7992e41cc3c681f1b6ea44d7059de78fa2a9a GIT binary patch literal 1067 zcmaJ=O-$2Z7_NeZAS8HlkQi!_D8}_?KZO;>&(`5)ERr#Wi-xZKHX3NZ+J3mPi-3s{ zNIa+qO-wW#^g=Y=NQ}{hEQ%)+Pa0!L1;jG|HLte`-hmK_Y8)lJMMi0r|IMdxD=w?~g zu#08F1!JB5038lMD~@*Xix|L|8uJ3w8Ltom{xY6`?Yv0|S+~>K@lyk?UANH*sVUSlSh!4p>kpcaAJ* zm2+V$>J_%=rIjQcW&Qo>3Unc)bwn6pL*j$in+GD#MPE9}4+= zzVPMNW8D>vMz@cjJldAt7(4&Tf%U#)>ifmRjBBfzeRyv^Sw-xxC*B-Hr>^D$ QPo3)NS%G|s0G|+7AT1;$q@$x_YisN8@1K^IR#H;Z+uOU`V#QvN0VP3x!3+-1ZlnP@ zoCO|{#S9F5he4R}c>anMprEa%i(`mJ@YEhlJ_ZF27DmB;|C4zmKX$bGZ8bi6NqUmP zgc-Z9PLfOg>^NS%G|s0G|+7AZ-}A$iHYqa^ud5Nrxt{IJbD~)%|B4Z8@v20y3Z^$S;_|;n|He zAcwQSBeIx*f$uN~Gak=hkpdL7^>lFz(FmT}W68&$z`?>O`0sx*Z{)|0R==&rM=wcF zQkXDf_ti;qsh_NS%G|s0G|+7ARRTqtY|@S)9Uoe+p1R_ox1hx(ld7tn!Zc302xpcNS%G|s0G|+7AYETyKXKy3dGqG2UcGwHo;^p89=(43`o{-TdO!x01o;IsI6S+N z2IO!SctjR6Fz_7)VaDV6D^h@hww^AIAsWF`do1}F6gXHI1^@j|=8gQ=(dxI=_~<3+ zNeUBY?7lilF7>nbgm>8;@jeTB3@wzopr E07Dc;WdHyG literal 0 HcmV?d00001 diff --git a/apps/wr/public/images/map/layericon/SWL_SPOT_PS_RED.png b/apps/wr/public/images/map/layericon/SWL_SPOT_PS_RED.png new file mode 100644 index 0000000000000000000000000000000000000000..a2da36f9bd0c68654350c5ed8840a5a513e05c5b GIT binary patch literal 217 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbKJV{wqX6T`Z5GB1G~mUKs7M+SzC z{oH>NS%G|s0G|+7AT7?tWho)yuc(-&t=(#2G2PyNyRWbE`itTq14@GYf*Bm1-ADs+ zI14-?iy0XB4ude`@%$AjKtWqi7sn8d;Hf>9d<+U4ER2Hx{wMQBe(Y%V+iHCDlJq2n z2{U$Iog|m~*?Ypf?2dS!1wDoq%;y&@fxHa7-4%z4m{Oi^LYPn%|KSU0+4KLt~^wz7fAwAdM zd!m=3sQ%Qvk|E<@@Ez?T|5Lwe8W~3LOde-Z5mz-AQb_|9AV^u-3d}&wSiABDPEb@Q zZRYYguU?RJWHDNRVSLLWY>Jwg@*PbtLktRV#k3Rj{>xW1FpUJg6jeFZkzvW4Uw2`4 zJ)P6n%X-|PrzXLKFA)I?Vh#9K#r7mWLDzL9G7q*{8q^`UoS+X(<<$isBNu`w6A9@Y z&jV3pcp)l^{1^yxe3<3PD~5PMii%Qr95f!9M01Uzlu>3IvB)YxmoRoDmaSH+Of|wF zcZKES@pzyS4u=RLWQ7fQToS=!*gA^>MC2M<)VT!-1gh%)p_bJ`dpH9R{r*$f%dI((%|H*Wx;iObaWn|!NU{qxjNBYTmF6if zln_Q<2{}N{is0OmX&b2Oov-6nRZ7_&)@&W7lmtx_7}GQ)IVlT*m>d@bd79^EgjhVn z%h4DwipiJ~my=Ddg7j4j+PKLz4!N9vv`M$Sq^<957f+HaaV@1JbN*i8cda(nd1j;g z!TT*^Wbx6(9fjZCy~7P}2$w$Y4#FSLN9R7@_A|IO3kE|r_;+f{s2UANB{r; literal 0 HcmV?d00001 diff --git a/apps/wr/public/images/map/layericon/TL_SPBD_ENTRC.png b/apps/wr/public/images/map/layericon/TL_SPBD_ENTRC.png new file mode 100644 index 0000000000000000000000000000000000000000..8e7de8593acb28bd70cc17bc07f51fe41104a3d4 GIT binary patch literal 1260 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+n3Xa^B1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`f(~1RD^r68eAMwS&*t9 zlvv ztM~P_^2{qPNz6-5^>ndS0-B(gnVDi`Vrt-GY+`0%VrpV;Xy|I1JVR zVPRv4~Pj*wm=R%;iu*SQ+p9GSzA2OwPs*oOz?DZ45_#^ zCD_*ch=V}w?%4$eVnq+wJEkUNK9=9Gb@S%yr~J6S1xR`N| zJvQ5C?o3u`PthfQ5-al93`?KC+#KK4)4bnrwtm;#UOhuS$xiNBH3#lG?~)07@@xLf z#2-q(E=e`?ujg1?p%eG;x!ZZrDK;@68tDnm{r-UW|S17>0 literal 0 HcmV?d00001 diff --git a/apps/wr/public/images/map/layericon/TL_SPGF_LOC.png b/apps/wr/public/images/map/layericon/TL_SPGF_LOC.png new file mode 100644 index 0000000000000000000000000000000000000000..74b0adac361882af61b2f8d1a29c7a3f4c8063ad GIT binary patch literal 1383 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+n3Xa^B1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`f(~1RD^r68eAMwS&*t9 zlvv ztM~P_^2{qPNz6-5^>ndS0-B(gnVDi`Y~<|b;_T>bVrt}UXy|HU;p}W+;cDb!Vq)a# zW@79F)9aF-T$-DjR|3v4~Pj*wm=R%;iu*SQ+p9GS-(v#`NF`!c-zy(F{I+w zlF6sNr2|Edoj-f#$;Ca(9JefR6p#;PcHZT2l>INWj@BaXi5gdYx8E%dytG!^wII^9 z;Q6Ai0P%Mr0$G}wZ`C-QF0}G!?F%r@RGVL(=OMwJEN)q_&H28UHD=1T`s$be@TJYmI(%^28qHs(l4VPW8$&Ht~udsoCUlg3EBf%aQk< za+8_=3w>f;RDC(J(RRk{aIKoHR-!w579Do=_EN3?e(=nfB(E-2ls8_x|9s z?Gjfu@T|SeZF`sJvA?p~y*EU-oi*rrdt!T>V>egLkY4Wy)EqO6{v)NAK zVJLX;ASi<1P4OTeM3EutO)p+NO+>wU5k)sIz_C?!K5Ynwuc4|ly%C_< zjY7%VSg{nFoj3u;BS-~YL`)F5t6m5rm96VSI*+$`7SthRMP+xBDjPFE!vO+mE}5_d zQ3SHgiK(mD(qgclAq6?7C@f#VYgdmvUcD|n5lEX{QH2(I6f^}>$cD#DWbi6|0x`n)_uemQHa+9i#Bd)C=TUAEkGv0K?&p4W))|u znBcIAeW1KX>xUJt+~h}O|HGimD+M+E4V(5tfRm=kL+B4T~I$VblT>U&b8HRaP9WF z2yMAMO@@yf`&XlzThu-Av}IemXc-@Q)a?Pa_2WyMkX{K|y~$Gaw(@IdXQE3Il1Xjg zWapj2t%`oodU5Z;_tfT%g{|Q^ZNc9@@Z@;s>$685bzYa=b$@)LU+d^Kra!}=_pSv# z?0flp>cUpJZ?ST_|H_~+();}8)i3X#o_g59baXS{J`Ef`^K))HzJ&Q~LBF3_y!Zzu Cr9T`1 literal 0 HcmV?d00001 diff --git a/apps/wr/public/images/map/layericon/TL_SPRD_BANO.png b/apps/wr/public/images/map/layericon/TL_SPRD_BANO.png new file mode 100644 index 0000000000000000000000000000000000000000..8d08dca2996d0ea7bc0c52c6296323f9721077a4 GIT binary patch literal 1044 zcmaJ=PiWIn9F8j+b2uD$*;MqAq0G7FU)yXAtJ~VFu42pRit9nJY4WyaTk>M^X45gD z4h9Y;cb2r@l+n;-~+c#SjoqNvWqgq$X0m-n@|k^k`@y9yb4@OTzap&49MY)Fl1C=WrxP-bBoDq89C8#qi+ zK3dOYaW;8gQjx(Z9)@uZi?Atbc*M07bsl1nhqJmFp}%fEr-80T=;=_BOIk5l&?lB` zIJK0@s7v!|SffYIfMHi60tUniaE%4ik=zJf)s@KH+h%D{h2Z%Jy_ZxrISFFOh9JZQ z{3^%uKol817!pN(00lW6L2|Pt5j}~!+CYkOX!LS;#rc)~u zDH!W27Rxi7cceNnnfyQ0FdAqFr{SM`|0(QbN)}|(&_Ro~N*XuQ>xHtUm<<(->;vR3>uq|0Mj1NuXcAMDxacX&)T#4%mc`W0;&+qK+ zj`-T;c>LF;V^3DDb$31OUr~O3|GW;wL%!8=>xQ~*p1XAt!lP~F(OeG1AUtv5Mx|0Y z@a5futxAiws+IP&^@r{~-250nqWDHr7vGjo4{U!L9PJ6?*WX@#*etHCx5AxA<%4&( oT3+qfySraR`)*DM>bp*LIYk zh<*rs2`mW0iU@0a5zcpcp&Y>KL`aZFiBLkwDByJm#wuMfv)ps69csX}5UGYmVl zSdRshJq;0-3wp3=I|_I31!FlWLY4K^R#O zRCxU!h2=QF^Ik4c!SmcM;A1%-!;+WxZ~>u$7krgq>7hw9OKlSpVst4MS%v5hj7@=I zGMS7wdaQLu>)abcOVmllRVhjq!}v8*yRg&NfP3Qjb%fDaWO;_1+S*50vq-P{FPN= zAP}zQxJV!v;H#?GfUhnb^>ds*nB|H{>C&Nrvs`tB8_CIat)QDEvIs5h2vnmM(!s)! z1ub_jbvgBxxN7cP!a2DNNrrJ7`&Xm0Tf{x?blJ9Kv21*35Vu>z)(>9$zL6_&J1*9y zoar~onUV=mJbd%$z2fu~Y$<&TipFc!4;2^PABhd`+`e~uZeYiOyRUwHIs?kC-a3%p zesbtE7p<-S@Yuj*1NoQi%SA8k`9o{o{gnIfT+tKhUvuXZsr2>4i&)=4|J%G%qhrsM zgV*xQpG@rU9m$+yKTf{ypV}~yoV*y_{kfisK8hTldOR~xcY*hxZ0tQX8kN!<`(?J^ lhPG<1=9s*;mmi$nI!?vb^=)?A=1bk{9FH`JgLN$@{s3DZXz>65 literal 0 HcmV?d00001 diff --git a/apps/wr/public/images/map/layericon/TL_SPRD_NONM.png b/apps/wr/public/images/map/layericon/TL_SPRD_NONM.png new file mode 100644 index 0000000000000000000000000000000000000000..e68c05ab606783899526d999e99e088f96d1fa5e GIT binary patch literal 1049 zcmaJ=&rj1}7%o{D1R>Fr;)O5e0@1Z=*A~_=eyy7X5z(gFvb}F0F{V|2TivvH3b>*=kebj@9)g6a7GVae`r^VzI6_fA z+Q{Z`E_qqjkjbbXhN+r1VN=w|Sk+dwIfy|X77Z&x@4bFY14EC{Gh#BBw4<9J8TQk98-39$;Q=Dg*~)d*eJmC4-OW@%7|;JFBWWGa_TgD7$!5SdUw z3vwKgB!&}2N#ZU7KFINGki1fW6J$}6`5|!p&?K6p7vzi*KaNFK5xRu2EwgN;Qei3~ z202BR3x~s=2G0iwBH%7sSgi&ux4)sFKv#1N8ym<1o}!vZWgMZ2)1xDpc2m}JkJCgF z##U9E<(Qy%qy{jV{6Ew*n`jqj;ECUV3cJ}w8?qVbqOzlr!WH_xP_`U(po)=`MQFZ} z#dHZ_PqSF!>m@zCJRouZkKADsgmWx$Og9#-<69vXFbeWq}h9y205=36&xR?;; zL*j5q9Fk&TNg9rZ8e9cwWfNMs!PQT=LQAgK3Z_jWE6_2nLp|;w6V#6^8?C*NvbXYb z#MN7S;ahT9Vutk!`&XeGTcmruX|rv~qS^S+BHiwgwqAJN`<+~g)r2yUt$xiP93GDO zq^ULtlzqGv8aphab)BS@{E|puN^3+s_ zS>2(&JX!7=;&}e&EdkNb&ODv~(MRV7+QHeiQ{%O(v;Ot%b~~5;;d|l#wEJu2^N0D3 q9x&K__lB^tIlk_*VKLt}ezT3bGx}!tT6|ORE^8u|QeIBZuKWSyOihgd literal 0 HcmV?d00001 diff --git a/apps/wr/public/images/map/layericon/TL_SPRD_RENM.png b/apps/wr/public/images/map/layericon/TL_SPRD_RENM.png new file mode 100644 index 0000000000000000000000000000000000000000..4800beaeb5d42b54c5c28451bcecc023bbbf7506 GIT binary patch literal 1009 zcmaJ=PiWIn9L_czGB(&{iUakLU7W1>*EU-oi*rrdt!T>V>egLkY4Wy)EqO6{v)NAK zVJLX;ASi<1P4OTeM3EutO)p+NO+>wU5k)sIz_C?!K5Ynwuc4|ly%C_< zjY7%VSg{nFoj3u;BS-~YL`)F5t6m5rm96VSI*+$`7SthRMP+xBDjPFE!vO+mE}5_d zQ3SHgiK(mD(qgclAq6?7C@f#VYgdmvUcD|n5lEX{QH2(I6f^}>$cD#DWbi6|0x`n)_uemQHa+9i#Bd)C=TUAEkGv0K?&p4W))|u znBcIAeW1KX>xUJt+~h}O|HGimD+M+E4V(5tfRm=kL+B4T~I$VblT>U&b8HRaP9WF z2yMAMO@@yf`&XlzThu-Av}IemXc-@Q)a?Pa_2WyMkX{K|y~$Gaw(@IdXQE3Il1Xjg zWapj2t%`oodU5Z;_tfT%g{|Q^ZNc9@@Z@;s>$685bzYa=b$@)LU+d^Kra!}=_pSv# z?0flp>cUpJZ?ST_|H_~+();}8)i3X#o_g59baXS{J`Ef`^K))HzJ&Q~LBF3_y!Zzu Cr9T`1 literal 0 HcmV?d00001 diff --git a/apps/wr/public/images/map/layericon/TL_SPRD_RWNM.png b/apps/wr/public/images/map/layericon/TL_SPRD_RWNM.png new file mode 100644 index 0000000000000000000000000000000000000000..e68c05ab606783899526d999e99e088f96d1fa5e GIT binary patch literal 1049 zcmaJ=&rj1}7%o{D1R>Fr;)O5e0@1Z=*A~_=eyy7X5z(gFvb}F0F{V|2TivvH3b>*=kebj@9)g6a7GVae`r^VzI6_fA z+Q{Z`E_qqjkjbbXhN+r1VN=w|Sk+dwIfy|X77Z&x@4bFY14EC{Gh#BBw4<9J8TQk98-39$;Q=Dg*~)d*eJmC4-OW@%7|;JFBWWGa_TgD7$!5SdUw z3vwKgB!&}2N#ZU7KFINGki1fW6J$}6`5|!p&?K6p7vzi*KaNFK5xRu2EwgN;Qei3~ z202BR3x~s=2G0iwBH%7sSgi&ux4)sFKv#1N8ym<1o}!vZWgMZ2)1xDpc2m}JkJCgF z##U9E<(Qy%qy{jV{6Ew*n`jqj;ECUV3cJ}w8?qVbqOzlr!WH_xP_`U(po)=`MQFZ} z#dHZ_PqSF!>m@zCJRouZkKADsgmWx$Og9#-<69vXFbeWq}h9y205=36&xR?;; zL*j5q9Fk&TNg9rZ8e9cwWfNMs!PQT=LQAgK3Z_jWE6_2nLp|;w6V#6^8?C*NvbXYb z#MN7S;ahT9Vutk!`&XeGTcmruX|rv~qS^S+BHiwgwqAJN`<+~g)r2yUt$xiP93GDO zq^ULtlzqGv8aphab)BS@{E|puN^3+s_ zS>2(&JX!7=;&}e&EdkNb&ODv~(MRV7+QHeiQ{%O(v;Ot%b~~5;;d|l#wEJu2^N0D3 q9x&K__lB^tIlk_*VKLt}ezT3bGx}!tT6|ORE^8u|QeIBZuKWSyOihgd literal 0 HcmV?d00001 diff --git a/apps/wr/public/images/map/layericon/UFL_BMAN_PS.png b/apps/wr/public/images/map/layericon/UFL_BMAN_PS.png new file mode 100644 index 0000000000000000000000000000000000000000..f2f64a641080ceee0479ecc735869a2d146d2513 GIT binary patch literal 1552 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+n3Xa^B1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`f(~1RD^r68eAMwS&*t9 zlvv ztM~P_^2{qPNz6-5^>ndS0-B(gnVDi`U~b~(=3?e-V&V)8bypKdCpQZhM*~xHL!gkQ zs~JqMOMY@`Zfaf$Om7N8uM1ASprnvn0JPa9wJ5VJHN~wcKUV?lWvfiwZgIqE9#n4% zZnrq$)TknMZjcjY$NByz`)ev>EaktaVsa- zJ6kwV!nRzla-zv(6WwjgwuYFqF7;R`7OvFs-{DB>gH<~MjndRf` z8>)5!_Q!hHZrtpfBeZ@_lgl^c3alKiw!qDJTCl(e+cotnRx`{wgL(9y>Vk6&s_@7d|kjXWkvY@3-Zsea-q_pjo!R&$M&xmSp+iFZ8p;1CbbH`P92 zo~r^Yh0?7oc${x)hJX$z{qB|hbo5x@QV*jS;TNsM#Lx+ey4d~5DZlhm9f;x6qf#Huy< z=z-Gyg>A3qT{~#^@!Nw3;zenUOM@&gH|DK+CcW~+tY!DLr&{Hht-t<0Yg5>x>l1~_ zAFD*Y{&uU|@9IN|(-(GKP;vCk4$RbCt})+X_VyrBK7@LqWSUvzOU+aNBgp&6P&Bbs=8@=Wpjmv0Z%; zBZ?jh%K{-Rqn@M7Lt#WkP$WTxLC{N^6j@;wMFpC6MqTZpZJ3#J;rqV-KmY%ob$9n^AoWYK9q)c%PB$@|AZnsHQ&KtrB4yB~84+sg_Fan9)CjddC^DiM1MONv zj|G~0n$k*7hZ0t)=q|D*$72Bo{JG=pEx!v^>u$NNI$;zQwRmYU&HLVPh6UPY*OgiM~M zv)QaS>-R#djb_8)u%qGgc`(9bcN<8`c?`R%pdbKSu{0BD&>$T}Nrqi0Lg7poj-Z=M zvWC4_CR{K&Cz&+sWt<}wfTH++sID)eZIlAb`TkSbPIsGtP5~QsSqg4krpgIr@-YiY z2wG_f4;HGJY=;Qi?a(A+%^bOHzh9NC*)MD|j_cwxQcUwM9fTsCdnwWhJL(U*5Uy%1e7cKgtz{OiYLWwrb5yAAhB zFBAP+l7m;tHT|xn+&45d)Hg7&{z(P^kZ*locB6M@X7tPSPsmJ9|D1A1duNY)lH}pF htwPmy;!IUZshi-wpAg^66XVYPPt-RFSL#|1{{cK~fpGu; literal 0 HcmV?d00001 diff --git a/apps/wr/public/images/map/layericon/UFL_BPIP_PS.png b/apps/wr/public/images/map/layericon/UFL_BPIP_PS.png new file mode 100644 index 0000000000000000000000000000000000000000..dcc3622a76eeccb7c555f8fd9f30f76ce8591f08 GIT binary patch literal 1017 zcmaJ=U2D@&7|tBbF*i^Ybl!N#5EN{_+N3nBt6j6Xf+?dbtRjA-$ypoL-_i7Wh(Q4sO*>A1d;Nw6rV*!SMU_*X6fBuj3oe{l z$mH~ec|B&(i7_zh%S6C}SOdOQwmsR8(@kBO%!6%~22BW_kJI}}<<)7BLM{X%6AtMd z&jU$f_=qS;`~@Izyufngl|p<(7A0ATg4RQmXs$6QXO+oTEV7EzC5#=JWvkUHQw=l7 zEwX$p77H{4Aw&=%uV!P-582*GTS0-I?wSrZkqrVxt$->xPLoXck6<|+S=(!si4=_W zHHYOHE;v#fsH*>mT2=?`;VeAJ_n*REuI4~C3q4eEb<()Gksy>Kr(CFE(nS_%9$7ZK=Mw9x*Ww!8 zb4hmPvLqQcXzX8&Zf_Cy1k;Xf$)aO?XcM=)#MbYA(#Oe_u+mC0=YKBjHX4cE!52$Q zjp+NAaN|R`uu-q~EH2l!A8&nHDh{$w6VU(h>+I^Q#RtQ8e($dL*H<5w?=COzHEw*l zvQoZ2*2C@X)DQO=Z`XF7Y;CUG|2TB2vhwW6ZDZ54zNH<*u#Rk>P(9XCg5894sLM$cg6$rAb zI0TDOlt)ke1mhUif-9x6UCzDENxDjiK8A2r1F>taQq_JTsCgaI{AntNVK!vslT=lqSac%-%)#cFK-zIUe3bBV1 z__L(SxdPyI6M`tw6P75N1}sa^ktoa3A;3^HLsIBv!*qm;vK+G;tUWl2X3B$HQOK;t zLaPK`wQYkVNyl*rr-#tZA(D>8V!j5$gb^ZajcT^&hBd3Zp&&p@G8MyCbPf25VnrXZ z6FAEBSqZAKE~{B<$Ak`ybVY-t3Cb_20nFw8AF8VBXv;3b=lTApuvHp0AX$W#K4MC! zaf97{D1+loDB8MN()HtwQ!G?>N-uS$C{gSMj+Vm(?s=)FhY{5;#&I z6h-FJDL$3qX+9E-Ff^Tx>|yzgAjBAoN-+YxXLo}u=+cM^HM_x;pL2z#T;B?+fg%gg zR8B%UW9lkcEtylA=aOowx5ky5=W?Jamqf{seq;aD=*AXuk3U_vEn2J_A8N?$CbIRf z`Q9ZA3v9~@sgnCk<&ozlTUtN5k@a4v1liM{eB3b=3eE<)H+e6e{VZ^+uj z-%G({`6v}1ot`Yz7H-tvOSfOVmn6u^4X4HVcb(wG?3+EGXOD>P^5V9>o|f9+r`V6b zZ*V*FcNX7XSbAgom*);19`77KmIf_PpDxeTA3oUIdE&gZ@WFHTj9{rL6yx4f>>1uxL?sQ0S?_~Jr( z{G3OfQ7|fa=r8YPePud+x5hC0^S5VfB!jh5*vQm3mCMO1bN=O#P3MIx2adh-4>{eP A^#A|> literal 0 HcmV?d00001 diff --git a/apps/wr/public/images/map/layericon/UFL_BTRS_PS.png b/apps/wr/public/images/map/layericon/UFL_BTRS_PS.png new file mode 100644 index 0000000000000000000000000000000000000000..557c17089a8e791685cbd7fed39d5418549c6531 GIT binary patch literal 1097 zcmaJ=O-K}B7#=mH{OONMvxCD(v4uOcv+j=0uu&^FA>_rvo%@B6*)^Ihv` zJ6%#(UPur`Nw`&v;Bmk6?JmIoiXUki4@XgR40Xa@l$0$%G^#KT$gnQ=fe4V*k-_&M zKoEJ97LB2pbWTv9PRkC4PU$9Q6GWgsWy(rFKx7>BX-1G*pPr>iO$}0Ae#s-5A&}5o zM=j7f+7?wt`xRcL>g&irO27g-Kr)%q2Mk+C1*r{P0neRnh9Wm0s6R+;CKZ!9$Plyu z>8GpRiic%Mj-y$hpX1mn((7Tpj0eA*oAn8PPVm-{TMvb!S!%Bk5nHxm;Z=}IAY=** zlT0S*WHk+~K8EFa-qG-S-5BAvM+_vV+=gA5Q4oQxSel76XpoMg9EU?FNa0L3kD!~| zvWC4?CR{KkC7TROdz>R>fRglosIG6LZ4?1J`TkSbj*ggsi2xf8Sqg4kZ>1B;6han| z5wxNZ4rHp>k$?!=322g`PL6bSX@&}u_Q?&rBne@|MzW!Ruo$GU0FO@h27&!w{43T+r|e5cDsdbtqfh7!B=7?EH*|{ z%kf|7bbaXT$lO@LQjQ{h$t^fi*4`SslX$Sc^5*J``(>_ORbO+LyjJY6G5PY)#QN&D z;_qDL!ra`=Ds_B?4ERDxyVKI7CrCo xxeoQ?r#_5*PUp`zlwR>vp6PB_lpho0L>>{GGM>WaPig0Jhnw5PsivOGe*l2=X6*m~ literal 0 HcmV?d00001 diff --git a/apps/wr/public/images/map/layericon/UFL_GMNT_PS.png b/apps/wr/public/images/map/layericon/UFL_GMNT_PS.png new file mode 100644 index 0000000000000000000000000000000000000000..6081d8b00f9a9d5a9e9ee32b353bd1ad1042a5b5 GIT binary patch literal 1166 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+n3Xa^B1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`f(~1RD^r68eAMwS&*t9 zlvv ztM~P_^2{qPNz6-5^>ndS0-B(gnVDi`?qX)?WMJuRVrpV+Xy|I<v4~Pj*wm=R%;iu*SQ+p9GS>M`mbt(e`Ba^3#V@SoV zHIr?*4mk+4syjM<5SS*XkX!Y3?Vg4kw>T^tm#Tkc2s0~t#h7E$u%JV+cT&&ivUG)i zOQP8AJno&TKDlZ4T4Tf75Vf9`eHTL43iKWFin!9gILbjwrTudz? z7MwhvG>c2qpIvHB&$xE|=9StQ=CX`CZCP2XE}hRfagNDoVN=Gl<843f68C+vsc1`6 zlw$C2YuNjD+sySRZ@;%%5b2R?S literal 0 HcmV?d00001 diff --git a/apps/wr/public/images/map/layericon/UFL_GPIP_PS.png b/apps/wr/public/images/map/layericon/UFL_GPIP_PS.png new file mode 100644 index 0000000000000000000000000000000000000000..5d7e86fd28d67d9256ec0e6f5b6c5a70b5e3038f GIT binary patch literal 1103 zcmaJ=Pe>F|93HbIBuS&fBJweU6}0nb-JP0gH{IQFYoQfe7u?XI*_r1$+02_}-nOer zsnx;i&>_lBNd$JP=p+egWnee2b*S(lRD^*A9fIwRx!R#^n3?y7@B4ng_xpWsq^kF(WJBO* z8a*n<^FR<7-scwten0SXyqD$3D|mRH$DL$j-35D2lFz*Xtn&kJD#jCGRnv`l5mi9o5z?tRoY+ib@(~agZjN{yBnSt;(9t zN|{K(*t}w~Jj1z1Dgxv2|3eL9746^zT+8>L!cMZ!f@}ghC~K>vaozQ9C`*diP{GJf zB6PM`#kL-Vk<)`L5NQ{{-VWW=P|n%6jE~2qnCW1}RAEdG(nNvLbxq=b;w0kpY&EEa|0l zIapF}g{zg$MJ&l>NiwY4*uNTG+#>FAr>nLli&f)8lepa`wtg_}NaRYqipim5{$2WK zq0m%b{p;S?*tddm;%(va9cE}f7;YBBqa$Ti)~CQmTedLpY+`Uj zM=j^|&S%%nUA>X28WigmwrQ6`>Dldrn_h4qkN1hE_FSKt8JpQUTl++x#?5n6LyBTW zCVqSe@0%;X7Orsp>GDq-E|i@)R4;TcEq=(1K79DIPMhq?>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`f(~1RD^r68eAMwS&*t9 zlvv ztM~P_^2{qPNz6-5^>ndS0-B(gnVDi`X=vzX>1bkVV&dd%Xy|I<v4~Pj*wm=R%;iu*SQ+p9GSu@w9+cPjQe(`j145_%4 z^dv1Q)q#EY&yXbzAyNYEPOV|gA9b$#m(NpN{rl5*ac1t0hvKud&z?E6<;~U8&u#eM z-LfTkNLNsleevy_Vd1VN%Z=EzbnNuUhAkR ziV7TLdMtZqQx)@sUF=6<)FbS_6>s?PBzseVM8lJFe}C5HKRy5bdgB@03kO=6#Z0=b z9FHu#AMfx(VL~SNkz)ZnH#^iw+)dTt*}#oXD@?|_rvpU1_F9>umrf8@oU^o38j z_CECbrITrCkAEE4@Ks?M<0+#xJo$YmmdyR0w1e$Zk4)>f_xrWpTr!z;=9ucC56`}G z`-tDE{q&n*LK#Ehy+7#_Qw|9IUz4+jXOYk(oHPAIbBZ|UOqpmbtRc{t zxzBx0=TTdC1zP@JP6>&yTnds^2=d#Wzp$Pz-{}=QC literal 0 HcmV?d00001 diff --git a/apps/wr/public/images/map/layericon/UFL_GVAL_PS.png b/apps/wr/public/images/map/layericon/UFL_GVAL_PS.png new file mode 100644 index 0000000000000000000000000000000000000000..e8f7cde2e6b5547f88f9d06f44527a8fdf956cda GIT binary patch literal 1426 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+n3Xa^B1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`f(~1RD^r68eAMwS&*t9 zlvv ztM~P_^2{qPNz6-5^>ndS0-B(gnVDi`Vrgt-X5?&PVrt}KXy|HU>Fna_V&rOS;p%MW z=4xyV)9aF-T$-DjR|3v4~Pj*wm=R%;iu*SQ+p9GS=UH!EoWe0V)Jxy45_%4 z^ymM7duFR02hX=_axnHt$=fgF*l;J>SpMA4^Cyn{u-5!8UtOwq_^q>hpvbCQk;Z>= z=Kh)g@L{1^+y46h#tv%}YHG{mC!FPYwx{&VZ~jYvl7D*ntp5KuJ)yskLGlno3G3sf zSBn+&ejGk@{J@zb97oP1FgjLdwgq1@;wfrBzfj`CiEAAPc{IK=othqSG2sXI7rWD& z=i5uh+3h{Apr~*lfx|@dNxkKYMQZ7?2C>_}bKN)~?!dE2R^{8P`|)gcj*4u{>>Yyd z)l{7?`1sCrN4DdSzxQO%sXgQ4@jCU2N8e1pxPeVU+2`WxetF4#|9>7nz#_zGIlpyg z>%(Vfl?&AOR~8*-KKAeP*Ujpi%FoTY!*}H{%f^>qPA=vNUqA2J&Wdjr(!VrJo*0_` zz`u+X=F%7fIZ{M(9pi%Mjld-c`mE7TFyq_hn>BXO0 zkehI9!OEp)7`peBu6|b6<#pm?Lge0>PkH|@@U(4TpJ#AqZ?SmZf$Q=;8fwihCdvJ8~^Mu=p4~|a{=Q+RM;l`v(n|KZ-NjY3&WE0Cd zDe58;{&my$ZKD4fHeTMGZSiLA-nyn6u9gQiPRbl^*r%DDcxL|TYqH5H3JfzOG*}YE sb7E|pReTq0XnL7=WZGO$Mg~SU2FAd1S2y18F$0y7p00i_>zopr0Fdb%6951J literal 0 HcmV?d00001 diff --git a/apps/wr/public/images/map/layericon/UFL_KMAN_PS.png b/apps/wr/public/images/map/layericon/UFL_KMAN_PS.png new file mode 100644 index 0000000000000000000000000000000000000000..764ce8ba5dcfbccd8ca87ccd6a270597c2d441a0 GIT binary patch literal 1572 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*9U+n3Xa^B1$5BeXNr6bM+EIYV;~{3xK*A7;Nk-3KEmEQ%e+* zQqwc@Y?a>c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`f(~1RD^r68eAMwS&*t9 zlvv ztM~P_^2{qPNz6-5^>ndS0-B(gnVDi`XyR($o& z6x?pHz^PXs=oo!a#3DsBObD2IKumbD1#;jCKQ#}S+KYh6`qIybz?{W2!_&nvq~cai zuywXbqKNH%bK~Dhljq(|xt_9EDJ#*zQ?MyXwdqD=XGg0-m{LasN0?&o*0~%>s$z!q ziYikDgr}64bqQ>8+`Rexn=@(8pYC)Pn*73iPHOtIdDZ7_zxU0Wxvwqxxl&>O4#zo4 zA{#w*gx`1S&%Zv6uj%SS7BNG2o6X%zx8#Zox(Z%esk3Jv_x9_qiyyMo{BNu9+cD{c zjmtlSm<{>iTFDwy-5yrz-FnM5r7uu#x?um5z?C6qdwb{iSMPntdhGrrmET-(n;vxw zCkt~NX4t)3?~ullvbZS4zNCBZ8n=`KP95ogfB(Y!>h~wASNt^AoOEpUrM#ZGaomiP zCE6I*>PDnV-OS-DlzI65n^?_#=bb@Q7x8zTzI5pCC6AjIq7Ii52Y45#-e@tZ3rERx&D||?l)ZDhQJH$#^(RHtRdf2kaCv1*C%8%6; ze~1i~T$AB+S^Pe~K&#**gN3TecNQ&PeA9=o@x9thmw#{WT&}lSan&qwhE}HOl9e{O zo*QoDndyuA-F|UQCU(unZM#0j^Xpvp5=o0V^Sb&|M(nqQuKIoR?9EqRy2&%kPxeyx zBKe6w99Fdh_2j&d{onU46u4r@WNb z!9|sG=Wd%Iej?gPZB@F$X^*i`<=X`KL{ae#??A6LmrpcX+5WM)|VWo{v?1 z&Uf*?{{vxeM}Z#e-d`f>i*&SIzo$-}sFJmPmP>Q?yR*jM^e$TcGkrII^%3QySrT`p li<8%f{uC1DXa6sl!0;kmk>gzTt=FJ(+tbz0Wt~$(69BpkPN4t* literal 0 HcmV?d00001 diff --git a/apps/wr/public/images/map/layericon/UFL_KPOL_PS.png b/apps/wr/public/images/map/layericon/UFL_KPOL_PS.png new file mode 100644 index 0000000000000000000000000000000000000000..d9fa712cf6c3528d5673c19c6c3cc6aaa42fe604 GIT binary patch literal 1122 zcmaJ=TWHfz7|xtjom2NBIzc_lKoo4!)HbuBb#}?BMO#K&aOydsF z6J(%1i6{!9h$y~@^8(Hx-awoeHWV*UKBxngDT0XgWUkW(Ye;e~eBbx~=l{QRpdq|z*4Hwl}f!nKYmD{T;iENsz?2>tQ;O&VxwgkBd+_!6cFTea2Q z7EE_1GfH=x5>n~f6(H;gL_mjF29Dlt*n$(G$8-fUcehy@j6rZ)g#MjWW1<0w$bulq zEb}TpjsrZ;aDgDtb4!8W$N5z_Q~t?COGo1Z~CAOspXTxQcQXb>Ik1GW~r7-JFm$ z?C~;@g0YTlvK-@ck5m9A690$l`UKj>DflPfe+t`~E)%jTXrm5GA&qOUc0-whXh9hx zD}zvbp^6Qy2qU`{nLteQVDUQ5P*KiaGKNnigt%d2*-&6yiqJ%X(KJ;E1*K>#Aaa3# z=;yflK!^|WVzkaD)`X;(CqG;o2d{^9zIa$PIvCW$OTOY4Mc%tAUCJF=h^ z&803@RBxQC7SF|3l*^K2Shul%HM+1x+~ZCsY)cjs#)k%RyG3mMF*#gKuEbDWie{V_ z*&q3QZ7JW7-ZZ*d6pzb~PDILIhQsCQvq^aSOz*yuS%ZVmCO@q~uLjQTpLT3ZXD9!1 zJ(zQ2+qd4Tsue$LAA3%g>iYaAO*?14zPWA9(UL3Z&7$X&({sJA&v);^{Zl=MMkZ~i z&X@Erzgw~Kl37(6f4B0>UB=V9YF1@);KJUiftn)=dMe*mJUq3-(>Ht9VL#t~Vc`3l zE7@pKD3{fhAENU1j3f%eFk6Cwc({*PzH(XnG7xdEEnWH=E%$a76?J8kt zq6rojArweP5K&MU-2|2u84I(`Yuw+v;kP3CuR+Lh35cw&b>BbQ>qbRFe z`7+^xF=EHg{|a}0hlBF$48?=M7NNn%OE29DXc)Nsv@{KJ{S&p!)}io zS3TrzPeu4O{kIUM;6r5 zxrin8@?524F5XZ{E`yU{?8g4p=)x9uk3B8g7B7m%2Re4UiEaJMq_*M9u^ncLGeHr>)p%vD@!;U``# z#gk%1;(Kl((_k##n3z3teeLa=n$a&0_rF`&E~f8~tkl&$seXE(aqmEP>n9@ja(Q`t z{4gE3Sv?<{yu8J`?>4#3e9(QcqCGHPdG=hINX%@$SUGk46tSt2m{>hMKY8!ibNkjt LLLK6r*6y+26oFXm literal 0 HcmV?d00001 diff --git a/apps/wr/public/images/map/layericon/WTL_FIRE_PS.png b/apps/wr/public/images/map/layericon/WTL_FIRE_PS.png new file mode 100644 index 0000000000000000000000000000000000000000..4137a3f74526c367650a0bf2b998b01a3fe31e12 GIT binary patch literal 1072 zcmaJ=TWHfz7>=xLV>qt}I&g;UMP*Hzu1(jlI@@NKf~m8st`91v$ypn=!U=7x6k}n~cG{AWnTzaD(m17bo*Y5a-FZZa!E;l5^qvzW+b}|DFD} zXiH^9RRuv1m628kzr^8 z(o5GlWf#km97nSrFUPU9q}#>185e#zC+p$89Pjp#s}F^vnMx-g6Pj0J;Z=Z2BV_Ol zlgVW0OdSo)6vO)cep|!sc4CCn>eZ2yb?R14K|uhPY^nxQp-$S0QWEx{0EIJMIf7=a z$?Dc>nQ+0FtYk1O?Xr(l0E*)Op_;aawonYL=lf4#E8c4WCI&3nW6HR3oi%nSgAbcP zLePvu*j=b%TN)y0rJ+HF+c|P?hpH2(Jg4*z1+KErZ7#~St)Lk=vH(o=3{aX)sFBM@ z=GEf4gp2B}a+Tt_2t~OJPKL1?`&Xk2Ti8AJbj`MSv1WXrW4D{w)=&Gl-@sSmc0>rq zv!5nn?4~@q(UF>&gTJo*o-Es0HZwLc-xpVF?;PD$I&eJQ{iHgjm5<+LKabRx4n0U* zCg{!Ie{3C!#B+-C zZxL_8C6$Z$s>tgPfrXyxdB<2qBKhThwB;)`{_5e5U~b_2+|t>F=Pz4t4x7<~nZfCa a_WTAyAH6Vm<>PMAzQd8GsBkZIs_zd&Kw|U& literal 0 HcmV?d00001 diff --git a/apps/wr/public/images/map/layericon/WTL_FLOW_PS.png b/apps/wr/public/images/map/layericon/WTL_FLOW_PS.png new file mode 100644 index 0000000000000000000000000000000000000000..285f7edfa81fce7e403067fa779164e206500b58 GIT binary patch literal 1137 zcmaJ>TWHi+7)~t?T3gu_#S1Pyr0!O6XOc6Q*u=)}?4+Z!I(3|mwm#WRa@xi=IWak% znbw!O%Ibn3!ipj&7D4u*%C0P|s}EL0t>S}XABvz9K~O=e)Z2qtPim+7;A}{8{&V=g z@4qI$r<&GRRV=Gu7^W(*K}pl~8}FIpqwng8NtG^3Nnioj&@|DmQigLX+8eUA65!fvM5MqC=XxX00G$V!MM9v#Z)II*zLqNh&2mfWs70yIPb2S!Y7kb!g7gfX(*w{EL8{?hAxG| zaZ!MLI2?g72phvuYKTIe2*WTQ7eh*st6;6iL>4J>_0QZ$Nv<~vrcFI7$T4zAk2}}| zQz=VE=~#j#^`^Oc=~zM~xf~6K^BVg_ql-s$dOUMxZ0TlZ@R3EQ-JxT>ZQq>%`bj)a zDA7#ee)j$3WJ9?ybo~Av_T}!}>>8ox;AkadG;RE4-^9_Amv6jy{XRPQ+h6<`b8m1( zYSEd0w5|K^^$%*?kZ_e5o~`WO^9SF?EbX5+u~~jo{p8i;yAd9y=GfO~3D;I!oAvL2 zTv6Qug5THHo*wl*+OfJSTlbu;TwMNlG&?$SerUbren?a^<$r2hB=_J^&5Pnm_Mm@*%;YWbnPrw70R?c-mj#PnPRIHZt82`Ti~3Uk?B!Ylp0*+7m{3+ootz+WN)WnQ(*-(AUCxn zQK2F?C$HG5!d3}vt`(3C64qBz04piUwpD^SD#ABF!8yMuRl!uxSU1_g&``n5OwZ87 z)XdCKN5ROz&`93^h|F{iO{`4Ktc=VRpg;*|TTx1yRgjAt)Gi>;Rw<*Tq`*pFzr4I$ zuiRKKzbIYb(9+TpWQLKEE>MMTab;dfVufyAu`f(~1RD^r68eAMwS&*t9 zlvv ztM~P_^2{qPNz6-5^>ndS0-B(gnVDi`VQ%K?Vrt-QVruAYXy|HUY3X8N>11x~Z0PJ} zU~1_G)9aF-T$-DjR|3v4~Pj*wm=R%;iu*SQ+p9GS^s~$av=i)Q>UkkV@SoV zCBeS_!hs@t-=Cd*-EX;r_i_O*(bR?9_DyP9oQa7Yf7qR4B*Ts!Int^vw(HVdPU~Lp z$NmR8)(P~e{b;;D%faP9(2TNMm&)w!e=cK?EnKc|d2-L5Io0QPe&?AObm+g#uIF_e zhMgLUDJ>P?0zRbO6 z@_2e*sf4rSvU`(S{N7KBSn~Nu(Bi_Bs$|_6Yr_^^SmPX%cwy6x^)Iv6O^H2!>gdE4 zr^-ofdseP!JzXZyV`IYIIrGx8(va(Gxqmhv*l4ZJbWW6Gv5;w1H~&xt;H zvY7SQvux2{r?i$g2tD4hu3CL^t^fn4uE;FEZ5OtfYSw;WZB0Ec?R2i9+oNooLcU&I zYvt)9=NWiR+1~aXG{4Qada+f`QinMW)6;wkT0bs&y~b9;V)Fm3t&_5ue~HW~3>0LV z_U0|)sws`@0s=M`^aK^}FbxutmQ)}j&`ZOcI;!o7cQg*F*v!hpp9=6QT zVe2>VR%m!3v1a?6P0}7eH+_$9a9~P{`Kh5QdpsiE<;@zeq%~7NZOgIR>D|Zna{hUL aMmC1RZ)#d6L=Wu;71W-telF{r5}E*KPgV5* literal 0 HcmV?d00001 diff --git a/apps/wr/public/images/map/layericon/WTL_META_PS.png b/apps/wr/public/images/map/layericon/WTL_META_PS.png new file mode 100644 index 0000000000000000000000000000000000000000..22c7af31b32386e501fbc0948bcf4bdf44d58332 GIT binary patch literal 1100 zcmaJ=TWHfz7>-oSm~Ko^)Cqc)sheP%OWSC()wwmDE7~!-$_j!uP0rT1B_}3lHmgI2 zV;fT+L=;4P5?_4Lf#8b|-f%Eb@J$p%MCOY(ydd7^$+k`(tRcy{@O|I^pa1{Pct`ur zit_4milQpwZBmkqRqj{jCI9swXJj(0!Lc;%MBO+qTaap2Q5J%@F89DBl+}TQQ?QYu zJhYZd<8)%Ts36@hyBNQtn}khKjZKazE4>hdEbP&Y2>tHHO&VxwgzgF@*n}B{IjyZ= z!OlW^N-6XzVU=#$4jLVi2w(A(MnQ>bDfqxb9jvlqp6nC}U)$ z5b7&du_K2tvUA7;(M|!>cWH)-^7iIgd?F#n4I9gb0^?GICJKH{Q$;qw@`A867z}X| z$HjupEj$};4e?S-fR9CET#+jwrC)~zE^^gB+)zobYX#jTktJwphoIVOAsx&fS=37B z!k5&W=c=W136$hABpJqS>|c#8ZV~sm(*@g-#e(snLELT;Tfg#5?I2g;R$OXMIZv`b zW@ef^8;6!ZrQQq$FKpWPls&Qh>LT#q`lPr`Zy5YsK3ZdXGUspp{5HHNuxjnn^xN0d zYwJfRGM!G>WaV9Ix^8Tu`lL*?UAufdI6ShV?(TTcv*({QZ-aO0@OO4u=Faqf^#Q0} zmAU;gbxH^3w@S|i@A%Q$Now%dJ?})JdGDFwntPAdjcl5tb}h9>U(~fAicuN=xS0Z>&&g AEdT%j literal 0 HcmV?d00001 diff --git a/apps/wr/public/images/map/layericon/WTL_PIPE_PS.png b/apps/wr/public/images/map/layericon/WTL_PIPE_PS.png new file mode 100644 index 0000000000000000000000000000000000000000..c9c6863f68f8ce9b39d7758ae2bc1d0814d46948 GIT binary patch literal 1075 zcmaJ=OK8+k6n(8)wNlV3s9k7CD=0e2WI7W&Vbb4Zq9dl1=|HDa#58$rV@+O~ymr!$ zQi@g-E3QK2{g=0waKAdyzefOSsrnO~X zMR`>@0H7kdUrLd2z5lH#BmeE+aw-|N;zS0wp>CX29SE9q)CH-esrJAWRP~|$=P(Aq z3ff3#a7H;OXvkz#AH#U2P1pd$8a!Lo`XHvdV2@#m^q2cHG-c=_-5ybbiXDf&#{OXk zwhgzWwc$Q3s?!biRLm2IfC;fmdFC<86+Ds7>k4G>+0E;xq^!E`=ds)_U zm&!y6#(JvFa!k-aQURza|A(69GTOx{_$S|g3cKkc8?q_rq5(%Ejq9%VL)k*yfhtB$ z8lhu_Dz^3_jND#iQ}H&Q+SzVcI?B4c@_0oNl9r2AOM^*Cq=^D!7`o70$2Ui0IUbI1 zagIxb>*S_LBE<8u6qiGCG+5wDNE%p4(iBF9TP|r^o zxm<4aemu|@!+!)pRYV9y)9peIrC#+9iRB|V*xxFn|{A$ z(~E`Csj{)@a`F3}tuNm`I#p6#)xV>%6Lg3h4=j`_r{4mXIVWGde)PZ%>+rQhR?RA~ Zatrt{J2@{+9zW|}<7A>my48GS{1?p)VJH9q literal 0 HcmV?d00001 diff --git a/apps/wr/public/images/map/layericon/WTL_RSRV_PS.png b/apps/wr/public/images/map/layericon/WTL_RSRV_PS.png new file mode 100644 index 0000000000000000000000000000000000000000..d7b307a61bcaa6059fe5565713ae6055c29d209b GIT binary patch literal 1057 zcmaJ=O=#0#7>?|RF*leyZM!UFtW31|=}%_EYU!GGE80=o!is_{O}?#RO}?0Xv)vFK zia+zyB!Hw$pL`Am z2%?TsQ)!fr9TjA#(UOg!3z~u11ThdO7?L~*5SalJs_v)0E-X@{s`#l(M|u=wSjoJa8Tf~%h_KNOB;D&s;zjFe;Hm7mHYWC#qC z&*$m9n}+5D!})wZTf^mYVuaJ0){#_j>Q+xhK?Ig;ss>V_PTGo622LSAg)`kLK{Kkd zx>Y_Vd|*sLG8m3#?UE|MSnU5$O{=0UlmNT={!`dWO&fqo01Hl;GH%>>j~&VoLMD(9 zG*b}fDyJCFLIkZWG{{hrC;LWKU4eOPe+eIp2~pibk}iX&=%=s(t*VOPW?4SSdt4qb zCvsfa6LdwmLwwll@kK)3aKEp@6`?$(0UcGi$}ZPklWSW+GjL=PnCdB@L`1jXD^d3}ZL;uSQq4uzT!j)wXz1H9pX>+f8ii?@j3=_)1)hiosOj zZRTgO7^v%=ZJ57pJZo=BZmeCFR_nDQTHIAGDY zWXc1lPdC`srZos0TDLEC?t;)gN!Fb+Bv)nKtA18_%+mdO>6NIHmdpWOiIHMnnpdJ` zF3X(yChSMs{s-bLm%jhK&0eB%Ok;Wck14mcZ5S1f`OHr}BH0XdBZH@_pUXO@geCxp CKw=aC literal 0 HcmV?d00001 diff --git a/apps/wr/public/images/map/layericon/WTL_VALV_PS.png b/apps/wr/public/images/map/layericon/WTL_VALV_PS.png new file mode 100644 index 0000000000000000000000000000000000000000..ca2b4788b74c2c48d2c6a71fee593031ab421a82 GIT binary patch literal 1077 zcmaJ=TSyd97#=k&P0b(>Lvk2}GTfP+buaGVD(lRu8;vgOf*bVE?95RIojKFYv0Z&p zQ$&j(Gcw6udJuXjdI^Fal7cQq?-3$hjGijGJP0LrMt9d9+J>1q7ryWN|MUOfnTd3^ zmlhu`CJ3T5+#y8qSZ;s&3h}@C$CiYLN)(EtZrF>Gk_m{Q0uz7?Yf>MG0!bM@`vNo( zL;JC~_Nu1_IPhQgJat^3VjN zhi-7lPL?ITUYd1#yk54RbU9fU7oyb4hL2pJs1 zB$G)x*+4_Hk750OzpdeNIWWRu4eLlsIdrQgqaXlFHdOX( zvbvQn6D}B&k_?8Wo%WG3KvDcZRMWC(3q`?hzW)@qV#5YtqQHVfri>fcTVsbZINk&j zf@TcD!Aun+{SZN`9~vaz?In-(sJa4^R^2vU6uGc&AxW1(SO`#9fmT(83%Pv0#-OLs z?O}PA4Y~axC*RoW6Ik9AbOzg6Gh6}6LmJRghO6vy{W-a|6*L1!7J#Xq14^3-HFEpN zoSHiqZ%(~5SIM1=FDIA5$uM?f|7vt*3%kdjW^IcXS>pp8yWPaLzWJ)_6TT9+!$L5Y zdXw1L+G;5%xv{XY*}gP0EAKh@e0g=Xs^0j%9&RgikBMda*u=!fBr7i5*;tR{udJ8IEDy!;YEe%*XruYA&OU*vG8Q@GoD=KLQ_Vqx_F literal 0 HcmV?d00001 diff --git a/apps/wr/public/images/map/layericon/default.png b/apps/wr/public/images/map/layericon/default.png new file mode 100644 index 0000000000000000000000000000000000000000..92bf1f587e549c99cabe90492d4421dd2f8a8dbc GIT binary patch literal 160 zcmeAS@N?(olHy`uVBq!ia0vp^(jd&j3?%D+y-WjAJOMr-t{fa3iV^}#7cKqw|Nnm$ z%?m(rmXaX9U?7jdL2Km~AfL0qBeIx*f$sEak7F*Vu1jg5_``2YX^v17+v zG)>uiB)-dvm@BaA1RpYY5rpXW7lzz`{*&wxTB^!gCocK!zUrl|Wb_P#ZKbLh* G2~7Ysgdv9j literal 0 HcmV?d00001 diff --git a/packages/common/business_components/src/ui/map/Gis.js b/packages/common/business_components/src/ui/map/Gis.js index f68d0798a..42ef97f70 100644 --- a/packages/common/business_components/src/ui/map/Gis.js +++ b/packages/common/business_components/src/ui/map/Gis.js @@ -1,25 +1,38 @@ -import './lib/css/gis.common.css'; -import React, { createContext, useContext, useEffect, useMemo, useRef, useState, useCallback } from 'react'; +import { Search } from 'lucide-react'; import OlMap from 'ol/Map'; import View from 'ol/View'; import { defaults as defaultControls, OverviewMap } from 'ol/control'; import { defaults } from 'ol/interaction'; import * as olProj from 'ol/proj'; -import proj4 from 'proj4'; import { register } from 'ol/proj/proj4'; -import GisApp from './lib/gis.core'; -import GisContent from './GisContent'; -import GisTree from './GisTree'; +import proj4 from 'proj4'; +import React, { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react'; import GisLonLat from './GisLonLat'; +import GisMapToggle from './GisMapToggle'; import GisSelect from './GisSelect'; import GisToolBar from './GisToolBar'; -import GisMapToggle from './GisMapToggle'; -import { Search } from 'lucide-react'; +import GisTree from './GisTree'; +import './lib/css/gis.common.css'; +import GisApp from './lib/gis.core'; +import GisContent from './GisContent'; export const GisContext = createContext({ gisCore: null, gisMap: null, treeData: null, checkedLayerIds: null }); export const useGisContext = () => useContext(GisContext); -function Gis({ isOpen , gisMaps, isSearchModalOpen, treeData = [], isSearch = false, isMiniMap = false, setIsOpen, setFeatureData, featureData, searchView : SearchView, detailView: DetailView, onFeatureSelect, setIsSearchModalOpen }) { +function Gis({ + isOpen , + gisMaps, + isSearchModalOpen, + treeData = [], + isSearch = false, + isMiniMap = false, + setIsOpen, + setFeatureData, + featureData, + searchView : SearchView, + detailView: DetailView, + onFeatureSelect = null, + setIsSearchModalOpen }) { const mapRef = useRef(); const [mapObject, setMapObject] = useState({}); const [tabVisible, setTabVisible] = useState(true); @@ -92,6 +105,7 @@ function Gis({ isOpen , gisMaps, isSearchModalOpen, treeData = [], isSearch = fa setIsSearchModalOpen(false); }, [mapInstanceRef]); + // 지도 생성 및 초기화 useEffect(() => { if (!mapRef.current) return; if (mapInstanceRef.current) return; // 이미 생성된 맵이 있다면 중단 @@ -105,7 +119,7 @@ function Gis({ isOpen , gisMaps, isSearchModalOpen, treeData = [], isSearch = fa ]); register(proj4); - //맵 생성 + // OpenLayers 지도 객체 생성 const map = new OlMap({ layers: [gisCore.baseLayers['base'], gisCore.baseLayers['satellite']], renderer: 'webgl', @@ -133,7 +147,7 @@ function Gis({ isOpen , gisMaps, isSearchModalOpen, treeData = [], isSearch = fa gisMaps.current = map - //미니맵 + // 미니맵 추가 if(isMiniMap){ const overviewMap = new OverviewMap({ layers: [GisApp.Module.core.baseMiniMapLayers['base']], @@ -148,6 +162,7 @@ function Gis({ isOpen , gisMaps, isSearchModalOpen, treeData = [], isSearch = fa map.addControl(overviewMap); } + // 스케일 컨트롤 및 베이스 레이어 설정 gisCore.__proto__.map = map; gisCore.scaleControl(); gisCore.changeBaseLayer('base'); @@ -155,20 +170,25 @@ function Gis({ isOpen , gisMaps, isSearchModalOpen, treeData = [], isSearch = fa mapInstanceRef.current = map; // 생성 완료 플래그 설정 }, []); + // treeData 변경 시 레이어 코드 생성 useEffect(() => { if (!treeData || treeData.length === 0) return; + // 기존 오버레이 및 레이어 모두 삭제 gisCore.deleteAllOverlay(); gisCore.deleteAllLayer(); + // 트리 데이터를 레이어 코드 객체로 변환 const layerCode = gisCore.convertLayerInfosToLayerCode(treeData, 'lyrSn'); - GisApp.layerCode = layerCode; + GisApp.layerCode = layerCode; // 전역 레이어 코드 설정 //setTreeData(data); + // 편집 가능한 레이어 목록 생성 const layerItems = getLayerItems(layerCode); //setEditLayerItems(prev => [...layerItems]) }, [treeData]); + // 레이어 코드에서 편집 가능한 레이어만 추출 const getLayerItems = (layerCode) => { const layerItems = Object.entries(layerCode) .filter(([key, obj], idx) => obj?.updtPsblYn === 'Y') @@ -213,12 +233,12 @@ function Gis({ isOpen , gisMaps, isSearchModalOpen, treeData = [], isSearch = fa - + {isSearch && ( @@ -243,4 +263,4 @@ function Gis({ isOpen , gisMaps, isSearchModalOpen, treeData = [], isSearch = fa ); } -export default React.memo(Gis); +export default React.memo(Gis); \ No newline at end of file diff --git a/packages/common/business_components/src/ui/map/MapToggleBtn.js b/packages/common/business_components/src/ui/map/GisMapToggle.js similarity index 100% rename from packages/common/business_components/src/ui/map/MapToggleBtn.js rename to packages/common/business_components/src/ui/map/GisMapToggle.js diff --git a/packages/common/business_components/src/ui/map/GisSelect.js b/packages/common/business_components/src/ui/map/GisSelect.js index 4aa8f3049..e9deb72f7 100644 --- a/packages/common/business_components/src/ui/map/GisSelect.js +++ b/packages/common/business_components/src/ui/map/GisSelect.js @@ -1,37 +1,42 @@ -import React, { useCallback, useContext, useEffect, useRef, useState } from 'react'; +import React, { useContext, useEffect } from 'react'; +import { GisContext } from './Gis'; import GisApp from './lib/gis.core'; import GisOlSelect from './lib/gis.select'; -import { GisContext } from './Gis'; export default React.memo(function GisSelect({ onFeatureSelect }) { const { gisMap } = useContext(GisContext); - - - // useEffect(() => { - // if (!gisMap) return; // 아직 초기화 안 됐으면 아무것도 안 함 - - // const select = new GisOlSelect(gisMap); - // GisApp.Module.select = select; - - // select.getSelectedInfo(function (features) { - // if (features?.[0]) { - // // onFeatureClick?.(features[0]); // props로 넘긴 함수 실행 - // onFeatureSelect(layer.getProperties()); //피쳐 속성데이터 상위컴포넌트로 전달 - // } - // }); - // }, [gisMap, onFeatureSelect]); - const select = new GisOlSelect(gisMap); - GisApp.Module.select = select; - select.getSelectedInfo(function (features) { - //clickFeature(features[0]); - if(features.length > 0) { - const layer = features[0]; - onFeatureSelect(layer); //피쳐 속성데이터 상위컴포넌트로 전달 + useEffect(() => { + if (!gisMap) return; // 아직 초기화 안 됐으면 아무것도 안 함 + + // 이미 select가 있으면 재생성하지 않음 + if (!GisApp.Module.select) { + const select = new GisOlSelect(gisMap); + GisApp.Module.select = select; + + // 피처 선택 시 콜백 등록 + select.getSelectedInfo(function (features) { + if (features?.[0]) { + console.log('GisSelect features', features); + onFeatureSelect(features[0]); + } + }); } - }); + }, [gisMap, onFeatureSelect]); + + // const select = new GisOlSelect(gisMap); + // GisApp.Module.select = select; + + // select.getSelectedInfo(function (features) { + // //clickFeature(features[0]); + // if(features.length > 0) { + // const layer = features[0]; + // // console.log('GisSelect layer', layer); + // onFeatureSelect(layer); //피쳐 속성데이터 상위컴포넌트로 전달 + // } + // }); return ( <> diff --git a/packages/common/business_components/src/ui/map/GisTree.js b/packages/common/business_components/src/ui/map/GisTree.js index d01786187..dec618926 100644 --- a/packages/common/business_components/src/ui/map/GisTree.js +++ b/packages/common/business_components/src/ui/map/GisTree.js @@ -1,21 +1,20 @@ -import GisApp from './lib/gis.core'; -import { util } from './lib/util'; -import React, { useEffect, useState } from 'react'; -import { useGisContext } from './Gis'; -import { DndProvider } from "react-dnd"; import { - Tree, MultiBackend, + Tree, getBackendOptions } from "@minoru/react-dnd-treeview"; -import { Tabs, TabsList, TabsTrigger, TabsContent } from "@common/components/ui/tabs"; +import { ChevronLeft, ChevronRight } from 'lucide-react'; +import React, { useEffect, useState } from 'react'; +import { DndProvider } from "react-dnd"; +import { useGisContext } from './Gis'; +import GisApp from './lib/gis.core'; -export default React.memo(function GisTree({ checkedNodes = [], setCheckedNodes = () => {} }) { +export default React.memo(function GisTree({ checkedNodes = [], setCheckedNodes = () => { }, visible, setVisible }) { const { gisCore, treeData } = useGisContext(); const [checked, setChecked] = useState([]); const [expanded, setExpanded] = useState([]); const [nodes, setNodes] = useState([]); - const [isSidebarOpen, setIsSidebarOpen] = useState(true); + const [activeTab, setActiveTab] = useState('layers'); useEffect(() => { if (treeData && treeData.length > 0) { @@ -25,19 +24,27 @@ export default React.memo(function GisTree({ checkedNodes = [], setCheckedNodes } }, [treeData]); + // 트리 노드 체크 시 레이어 표시/숨김 useEffect(() => { if (nodes.length > 0) { + // 기본으로 체크된 노드들 찾기 const defaultChecked = getDefaultCheckedNodes(nodes); setChecked(defaultChecked); + console.log('defaultChecked : ', defaultChecked); + + // 기본으로 확장된 노드들 설정 const defaultExpanded = Array.from(new Set([ ...defaultChecked, ...defaultChecked.flatMap((id) => getAncestors(id, nodes)) ])); setExpanded(defaultExpanded); + + // 기본 체크된 레이어들을 지도에 표시 gisCore.setLayerOnMap(defaultChecked, true); } }, [nodes]); + // 초기 표시 여부가 'Y'인 레이어들 찾기 const getDefaultCheckedNodes = (nodes, values = []) => { for (let i = 0; i < nodes.length; i++) { const node = nodes[i]; @@ -115,7 +122,7 @@ export default React.memo(function GisTree({ checkedNodes = [], setCheckedNodes }; const makeLegendSrc = layerinfo => { - if(!layerinfo.gobjTy) return null; + if (!layerinfo.gobjTy) return null; let src; if (layerinfo.iconNm) { @@ -176,34 +183,61 @@ export default React.memo(function GisTree({ checkedNodes = [], setCheckedNodes return ( <> - - ( -
- {node.droppable && ( - - - )} - handleCheck(node.id)} className="cursor-pointer"/> - {node.icon && {node.icon}} - {node.text} -
- )} - sort={(a, b) => { - const aOrder = parseInt(a.data?.sortOrdr ?? '0'); - const bOrder = parseInt(b.data?.sortOrdr ?? '0'); - return aOrder - bOrder; - }} - /> -
+
+
+

레이어

+
+ + ( +
+ {node.droppable && ( + + + )} + handleCheck(node.id)} className="cursor-pointer" /> + {node.icon && {node.icon}} + {node.text} +
+ )} + sort={(a, b) => { + const aOrder = parseInt(a.data?.sortOrdr ?? '0'); + const bOrder = parseInt(b.data?.sortOrdr ?? '0'); + return aOrder - bOrder; + }} + /> +
+
+ {/* 접기 버튼 */} + +
+ {/* 열기 버튼 */} + {!visible && ( + + )} ); }); diff --git a/packages/common/business_components/src/ui/map/lib/gis.core.js b/packages/common/business_components/src/ui/map/lib/gis.core.js index faebb90d3..309012a44 100644 --- a/packages/common/business_components/src/ui/map/lib/gis.core.js +++ b/packages/common/business_components/src/ui/map/lib/gis.core.js @@ -26,10 +26,13 @@ const GisApp = {}; //레이어 정보 GisApp.layerInfos = []; //기본정보 -//GisApp.GEOSERVER_URL = 'http://rsone.iptime.org:10010/geoserver/hasudo/ows'; -//GisApp.GEOSERVER_WMS_URL = 'http://rsone.iptime.org:10010/geoserver/hasudo/wms'; -GisApp.GEOSERVER_URL = '/sn/geoserver/hasudo/ows'; -GisApp.GEOSERVER_WMS_URL = '/sn/geoserver/hasudo/wms'; +GisApp.GEOSERVER_URL = 'http://rsone.iptime.org:10010/geoserver/hasudo/ows'; +GisApp.GEOSERVER_WMS_URL = 'http://rsone.iptime.org:10010/geoserver/hasudo/wms'; +// GisApp.GEOSERVER_URL = '/sn/geoserver/hasudo/ows'; +// GisApp.GEOSERVER_WMS_URL = '/sn/geoserver/hasudo/wms'; +// GisApp.GEOSERVER_URL = '/geoserver/hasudo/ows'; +// GisApp.SN_GEOSERVER_URL = '/sewerage-service/api/v1/map/geometry'; +// GisApp.GEOSERVER_WMS_URL = '/geoserver/hasudo/wms'; //맵 정보 GisApp.EMAP_URL = '/emap/'; @@ -47,7 +50,8 @@ GisApp.BASE_MAP_TYPES = []; GisApp.FROM_PROJ = 'EPSG:4326'; GisApp.TO_PROJ = 'EPSG:3857'; GisApp.TO_DATA_PROJ = 'EPSG:4326'; -GisApp.CONTEXT_LAYER_PATH = '/sn/images/map/layericon/'; +// GisApp.CONTEXT_LAYER_PATH = '/sn/images/map/layericon/'; +GisApp.CONTEXT_LAYER_PATH = '/images/map/layericon/'; GisApp.layerCode = {}; GisApp.hlLayerId = 'LightFeatureList'; GisApp.hLayerId = 'highLightFeature'; @@ -507,6 +511,7 @@ GisApp.core.prototype = { * * Returns: */ + // 레이어를 지도에 표시/숨김 setLayerOnMap: function (layerIds, visible, addQurryFlter) { for (let i in layerIds) { let id = layerIds[i]; @@ -517,10 +522,14 @@ GisApp.core.prototype = { const layerCode = GisApp.layerCode[id]; const queryFlter = layerCode?.flterSql || '' + ' ' + (addQurryFlter || ''); layerCode['flterSql'] = queryFlter; - //layer = this.addWmsLayer(layerCode); + //layer = this.addWmsLayer(layerCode); + + // WFS 레이어 생성 및 지도에 추가 layer = this.addWfsLayer(layerCode); } } + + // 선택 인터랙션 및 스냅 기능 설정 if (GisApp.Module.select) { this.setSelectInteraction(); } From 6b2b5b56d4bbf067b2fabccc3845cfac224e625b Mon Sep 17 00:00:00 2001 From: swcho Date: Fri, 13 Jun 2025 11:26:08 +0900 Subject: [PATCH 002/108] =?UTF-8?q?fix:=20merge=20=ED=9B=84=20import=20?= =?UTF-8?q?=EC=97=90=EB=9F=AC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/sn/app/map/components/popupComponent/MsSwagePpl.tsx | 2 +- apps/sn/app/map/components/popupComponent/SwtrmtFclt.tsx | 4 ++-- .../sn/app/map/components/popupComponent/chart/infilChart.tsx | 2 +- apps/sn/app/map/components/popupComponent/sewagePump.tsx | 2 +- apps/sn/app/map/components/popupComponent/stormWaterPump.tsx | 2 +- packages/common/business_components/src/ui/map/Gis.js | 4 ---- 6 files changed, 6 insertions(+), 10 deletions(-) diff --git a/apps/sn/app/map/components/popupComponent/MsSwagePpl.tsx b/apps/sn/app/map/components/popupComponent/MsSwagePpl.tsx index 1db284c8e..1fc0cef80 100644 --- a/apps/sn/app/map/components/popupComponent/MsSwagePpl.tsx +++ b/apps/sn/app/map/components/popupComponent/MsSwagePpl.tsx @@ -1,4 +1,4 @@ -import { VerticalTable } from '@/components/ui/verticalTable'; +import { VerticalTable } from '../../../components/ui/verticalTable'; import React, { useState } from 'react' import InputSection from '../InputSection'; import { handleInputChange } from '../common'; diff --git a/apps/sn/app/map/components/popupComponent/SwtrmtFclt.tsx b/apps/sn/app/map/components/popupComponent/SwtrmtFclt.tsx index eef14ea5e..0d1c3ef2e 100644 --- a/apps/sn/app/map/components/popupComponent/SwtrmtFclt.tsx +++ b/apps/sn/app/map/components/popupComponent/SwtrmtFclt.tsx @@ -1,5 +1,5 @@ -import Modal from '@/components/ui/modal'; -import { VerticalTable } from '@/components/ui/verticalTable'; +import Modal from '../../../components/ui/modal'; +import { VerticalTable } from '../../../components/ui/verticalTable'; import { UiTable } from '@common/business_components/ui'; import { Button, TableCell } from '@common/components/ui'; import React, { useState } from 'react' diff --git a/apps/sn/app/map/components/popupComponent/chart/infilChart.tsx b/apps/sn/app/map/components/popupComponent/chart/infilChart.tsx index ba0b70455..06c5b5fec 100644 --- a/apps/sn/app/map/components/popupComponent/chart/infilChart.tsx +++ b/apps/sn/app/map/components/popupComponent/chart/infilChart.tsx @@ -1,6 +1,6 @@ import React, { useState } from 'react' import { type ChartConfig, MainChart, CHART_FORM } from "@common/business_components"; -import Modal from '@/components/ui/modal'; +import Modal from '../../../../components/ui/modal'; import { Button } from '@common/components/ui'; type ModalOpenProp = { isModalOpen: boolean, diff --git a/apps/sn/app/map/components/popupComponent/sewagePump.tsx b/apps/sn/app/map/components/popupComponent/sewagePump.tsx index 5776df5b2..79103a40e 100644 --- a/apps/sn/app/map/components/popupComponent/sewagePump.tsx +++ b/apps/sn/app/map/components/popupComponent/sewagePump.tsx @@ -1,4 +1,4 @@ -import { VerticalTable } from '@/components/ui/verticalTable'; +import { VerticalTable } from '../../../components/ui/verticalTable'; import React from 'react' const inputColumn = [ { id: "swtrmt_zone_cd", title: "처리구역", type: "select", options: ["구역1", "구역2"] }, diff --git a/apps/sn/app/map/components/popupComponent/stormWaterPump.tsx b/apps/sn/app/map/components/popupComponent/stormWaterPump.tsx index eefef8b3b..172022f31 100644 --- a/apps/sn/app/map/components/popupComponent/stormWaterPump.tsx +++ b/apps/sn/app/map/components/popupComponent/stormWaterPump.tsx @@ -1,4 +1,4 @@ -import { VerticalTable } from '@/components/ui/verticalTable'; +import { VerticalTable } from '../../../components/ui/verticalTable'; import React from 'react' const inputColumn = [ { id: "swtrmt_zone_cd", title: "처리구역", type: "input" }, diff --git a/packages/common/business_components/src/ui/map/Gis.js b/packages/common/business_components/src/ui/map/Gis.js index 3d498f40c..48dce9284 100644 --- a/packages/common/business_components/src/ui/map/Gis.js +++ b/packages/common/business_components/src/ui/map/Gis.js @@ -15,10 +15,6 @@ import GisLonLat from './GisLonLat'; import GisMapToggle from './GisMapToggle'; import GisSelect from './GisSelect'; import GisToolBar from './GisToolBar'; -import GisTree from './GisTree'; -import './lib/css/gis.common.css'; -import GisApp from './lib/gis.core'; -import GisContent from './GisContent'; export const GisContext = createContext({ gisCore: null, gisMap: null, treeData: null, checkedLayerIds: null }); export const useGisContext = () => useContext(GisContext); From 0ebe5e8ab8efd6f7fd2b54c5df2bf9a42f8961e0 Mon Sep 17 00:00:00 2001 From: swcho Date: Fri, 13 Jun 2025 12:50:43 +0900 Subject: [PATCH 003/108] =?UTF-8?q?-=20refactor:=20=EC=95=88=20=EC=93=B0?= =?UTF-8?q?=EB=8A=94=20import=20=EC=A0=95=EB=A6=AC=20-=20feat:=20=EC=A7=80?= =?UTF-8?q?=EB=8F=84=EC=97=90=EC=84=9C=20Search=20=EB=B2=84=ED=8A=BC=20?= =?UTF-8?q?=EC=88=A8=EA=B9=80,=20searchModal.tsx=20props=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wr/app/map/htdflSttn/page.tsx | 2 +- apps/wr/app/map/searchModal.tsx | 2 -- .../business_components/src/ui/map/GisContent.js | 12 ++++-------- .../business_components/src/ui/map/GisMapToggle.js | 2 -- .../business_components/src/ui/map/GisTree.js | 8 ++++---- .../business_components/src/ui/map/lib/gis.core.js | 14 ++++++-------- 6 files changed, 15 insertions(+), 25 deletions(-) diff --git a/apps/wr/app/map/htdflSttn/page.tsx b/apps/wr/app/map/htdflSttn/page.tsx index f6b967c05..f3ff3da1c 100644 --- a/apps/wr/app/map/htdflSttn/page.tsx +++ b/apps/wr/app/map/htdflSttn/page.tsx @@ -94,7 +94,7 @@ const Map = () => {
= ({ selectUnitWaterShedData={code.lgbsnCd} selectMiddleRegionData={code.mdmbsnCd} selectEnvPartData={code.lclgvrnmntCd} - rightChildren={undefined} - leftChildren={undefined} queryClick={function (returnType: ReturnType): void { // '{partValue: 'A', sidoValue: '0', sigunValue: '0', regionValue: '0', unitWaterShedValue: '0', …}' let con; diff --git a/packages/common/business_components/src/ui/map/GisContent.js b/packages/common/business_components/src/ui/map/GisContent.js index a8ac408e8..4133beed0 100644 --- a/packages/common/business_components/src/ui/map/GisContent.js +++ b/packages/common/business_components/src/ui/map/GisContent.js @@ -1,12 +1,8 @@ -import React, { useCallback, useEffect, useRef, useState } from 'react'; -import GisApp from './lib/gis.core'; -import { useGisContext } from './Gis'; -import GisLonLat from './GisLonLat'; -import GisSelect from './GisSelect'; -import GisToolBar from './GisToolBar'; -import './lib/css/gis.edit.css'; -import GisMapToggle from './GisMapToggle'; import { Search } from 'lucide-react'; +import { useCallback, useEffect, useRef, useState } from 'react'; +import { useGisContext } from './Gis'; +import './lib/css/gis.edit.css'; +import GisApp from './lib/gis.core'; export default function GisContent({ edit = false, onFeatureSelect, isSearch, setIsSearchModalOpen }) { const { gisMap, gisCore } = useGisContext(); diff --git a/packages/common/business_components/src/ui/map/GisMapToggle.js b/packages/common/business_components/src/ui/map/GisMapToggle.js index b157dc141..c890f75f8 100644 --- a/packages/common/business_components/src/ui/map/GisMapToggle.js +++ b/packages/common/business_components/src/ui/map/GisMapToggle.js @@ -1,7 +1,5 @@ import React, { useContext, useState } from 'react'; -import { Button } from "@common/components"; import { GisContext } from './Gis'; -import GisApp from './lib/gis.core'; export default React.memo(function MapToggleBtn() { const { gisCore } = useContext(GisContext); diff --git a/packages/common/business_components/src/ui/map/GisTree.js b/packages/common/business_components/src/ui/map/GisTree.js index babdf69b2..dec618926 100644 --- a/packages/common/business_components/src/ui/map/GisTree.js +++ b/packages/common/business_components/src/ui/map/GisTree.js @@ -1,13 +1,13 @@ -import GisApp from './lib/gis.core'; -import React, { useEffect, useState } from 'react'; -import { useGisContext } from './Gis'; -import { DndProvider } from "react-dnd"; import { MultiBackend, Tree, getBackendOptions } from "@minoru/react-dnd-treeview"; import { ChevronLeft, ChevronRight } from 'lucide-react'; +import React, { useEffect, useState } from 'react'; +import { DndProvider } from "react-dnd"; +import { useGisContext } from './Gis'; +import GisApp from './lib/gis.core'; export default React.memo(function GisTree({ checkedNodes = [], setCheckedNodes = () => { }, visible, setVisible }) { const { gisCore, treeData } = useGisContext(); diff --git a/packages/common/business_components/src/ui/map/lib/gis.core.js b/packages/common/business_components/src/ui/map/lib/gis.core.js index 693fbfa0c..72a584970 100644 --- a/packages/common/business_components/src/ui/map/lib/gis.core.js +++ b/packages/common/business_components/src/ui/map/lib/gis.core.js @@ -1,26 +1,24 @@ import MousePosition from 'ol/control/MousePosition'; import ScaleLine from 'ol/control/ScaleLine'; +import { getTopLeft } from 'ol/extent'; import Feature from 'ol/Feature'; +import GeoSJON from 'ol/format/GeoJSON'; import WKT from 'ol/format/WKT'; +import { Point } from 'ol/geom'; import LinearRing from 'ol/geom/LinearRing'; import Draw from 'ol/interaction/Draw'; import TileLayer from 'ol/layer/Tile'; +import VectorLayer from 'ol/layer/Vector'; import * as olProj from 'ol/proj'; import VectorSource from 'ol/source/Vector'; +import TileWMTS from 'ol/source/WMTS'; import XYZ from 'ol/source/XYZ'; -import GeoSJON from 'ol/format/GeoJSON'; import { Fill, Stroke, Style } from 'ol/style'; import CircleStyle from 'ol/style/Circle'; -import VectorLayer from 'ol/layer/Vector'; -import { Point } from 'ol/geom'; -import { getTopLeft } from 'ol/extent'; -import TileWMTS from 'ol/source/WMTS'; import WMTSTileGrid from 'ol/tilegrid/WMTS'; // import { get as getProjection } from 'ol/proj'; -import proj4 from 'proj4'; import { register } from 'ol/proj/proj4'; -import olSourceImageWMS from 'ol/source/ImageWMS'; -import { GeoJSON } from 'ol/format'; +import proj4 from 'proj4'; import GisOlEditLayer from './gis.edit'; import GisOlLayer from './gis.layer'; From 16bed417723b53a399df51bdd829b9aed25d06ee Mon Sep 17 00:00:00 2001 From: swcho Date: Tue, 17 Jun 2025 18:17:21 +0900 Subject: [PATCH 004/108] =?UTF-8?q?feat:=20-=20wr=20middleware.ts=EC=97=90?= =?UTF-8?q?=20geoserver=20path=20=EC=B6=94=EA=B0=80=20-=20mapSearchBar.tsx?= =?UTF-8?q?=20UI=20=EA=B5=AC=ED=98=84=20(API,=20=ED=81=B4=EB=A6=AD=20?= =?UTF-8?q?=EB=A6=AC=EC=8A=A4=EB=84=88=20=EC=9E=91=EC=97=85=20=ED=95=84?= =?UTF-8?q?=EC=9A=94)=20-=20mapSearchForm.tsx=20css=20=EC=88=98=EC=A0=95?= =?UTF-8?q?=20-=20=EC=A7=80=EB=8F=84=20=ED=99=94=EB=A9=B4=EC=97=90?= =?UTF-8?q?=EC=84=9C=20searchView=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wr/app/map/govSidoSigunguSearchForm.tsx | 114 ------- apps/wr/app/map/htdflSttn/page.tsx | 17 +- apps/wr/app/map/mapSearchBar.tsx | 328 +++++++++++++++---- apps/wr/app/map/mapSearchForm.tsx | 33 +- apps/wr/app/map/searchModal.tsx | 80 ----- apps/wr/middleware.ts | 13 +- 6 files changed, 304 insertions(+), 281 deletions(-) delete mode 100644 apps/wr/app/map/govSidoSigunguSearchForm.tsx delete mode 100644 apps/wr/app/map/searchModal.tsx diff --git a/apps/wr/app/map/govSidoSigunguSearchForm.tsx b/apps/wr/app/map/govSidoSigunguSearchForm.tsx deleted file mode 100644 index 127d02f78..000000000 --- a/apps/wr/app/map/govSidoSigunguSearchForm.tsx +++ /dev/null @@ -1,114 +0,0 @@ -import { useWrStore } from "@/app/store"; -import { getCityOrSgg } from "@/components/lib/func/commom"; -import { - type ReturnType -} from "@common/business_components/ui"; -import { SelectBox } from '@common/business_components/ui/select'; -import React, { useEffect, useRef, useState } from 'react'; - -export type comCodeState = { - code: { - isInit: boolean; - chBuCd: SelectDataType[] - rainFcltCd: SelectDataType[]; - bdstUsgCd: SelectDataType[]; - cityCd: SelectDataType[]; - sggCd: SelectDataType[]; - rvbsnCd: SelectDataType[]; - mdmbsnCd: SelectDataType[]; - lgbsnCd: SelectDataType[]; - lclgvrnmntCd: EnvPartSelectDataType[]; - orgwtKndCd: SelectDataType[]; - yn: SelectDataType[]; - buildingType: { - RST: string, - RNB: string, - HTB: string - } - }; -}; - -type SelectDataType = { - text: string; - val: string; - sido?: string; // 시도 코드 (선택 사항) -} - -type EnvPartSelectDataType = SelectDataType & { - adstDliStrCd: string, -} - -type SearchFormLeftProps = { - isShowPart?: boolean | undefined; - children: React.ReactNode; - selectSidoData?: SelectDataType[] | []; - selectSigunData?: SelectDataType[] | []; - selectRegionData?: SelectDataType[] | []; - selectUnitWaterShedData?: SelectDataType[] | []; - selectMiddleRegionData?: SelectDataType[] | []; - selectEnvPartData?: EnvPartSelectDataType[] | []; - selectEnvPartSidoData?: SelectDataType[] | []; - selectEnvPartSigunData?: SelectDataType[] | []; - - setRtnPartValue?: (value: string) => void; - setRtnSidoValue?: (value: string) => void; - setRtnSigunValue?: (value: string) => void; - setRtnRegionValue?: (value: string) => void; - setRtnUnitWaterShedValue?: (value: string) => void; - setRtnMiddleRegionValue?: (value: string) => void; - setRtnEnvPartValue?: (value: string) => void; - setRtnEnvPartSidoValue?: (value: string) => void; - setRtnEnvPartSigunValue?: (value: { val: string, text: string }) => void; // setRtnEnvPartSigunValue prop 타입을 객체로 변경 - resetTrigger?: boolean; -} - - - -function GovSidoSigunguSearchForm({ - isShowPart = false, - children, - selectSidoData = [], - selectSigunData = [], - selectRegionData = [], - selectUnitWaterShedData = [], - selectMiddleRegionData = [], - selectEnvPartData = [], - setRtnPartValue, - setRtnSidoValue, - setRtnSigunValue, - setRtnRegionValue, - setRtnUnitWaterShedValue, - setRtnMiddleRegionValue, - setRtnEnvPartValue, - setRtnEnvPartSidoValue, - setRtnEnvPartSigunValue, - resetTrigger = false -}: SearchFormLeftProps) { - const { code, pbnstSttn, pbnstSttnSearch } = useWrStore((state) => state); - - const ref = useRef({} as ReturnType); - const codeData = getCityOrSgg(ref); - //환경청별 시도 시군구 - const [envPartValue, setEnvPartValue] = useState('0'); - // 환경청 - const [filteredEnvPartSigunDataSelected, setFilteredEnvPartSigunDataSelected] = useState([]); - - return ( - isShowPart ? ( -
- <> - onSelectValue(val.toString(), 'envPart')} value={envPartValue} selectClass={'rounded-lg bg-white truncate'} /> - onSelectValue(val.toString(), 'envPartSido')} value={envPartSidoValue} selectClass={'rounded-lg bg-white truncate'} /> - onSelectValue(val.toString(), 'envPartSigun')} value={envPartSigunValue} selectClass={'rounded-lg bg-white truncate'} /> - - {children} -
- ) : ( -
- {children} -
- ) - ) -} - -export { GovSidoSigunguSearchForm }; diff --git a/apps/wr/app/map/htdflSttn/page.tsx b/apps/wr/app/map/htdflSttn/page.tsx index f3ff3da1c..6c75edb92 100644 --- a/apps/wr/app/map/htdflSttn/page.tsx +++ b/apps/wr/app/map/htdflSttn/page.tsx @@ -1,11 +1,10 @@ "use client"; import Gis from "@common/business_components/ui/map/Gis"; -import GisApp from "@common/business_components/ui/map/lib/gis.core"; import { useEffect, useRef, useState } from "react"; -import SearchModal from "../searchModal"; import FeatureClickEventPopup from "../featureClickEventPopup"; import { mapService } from "../service"; +import { MapSearchBar } from "../mapSearchBar"; const Map = () => { const [treeData, setTreeData] = useState([]); // 초기값을 객체 또는 null로 설정 @@ -90,6 +89,18 @@ const Map = () => { // setIsOpen(true); // setIsSearchModalOpen(false); // }; + + // MapSearchBar 래퍼 컴포넌트 + const CustomMapSearchBar = (props: any) => { + return ( + + ); + }; + return (
{ isSearch={false} gisMaps={gisMaps} isMiniMap={true} - searchView={SearchModal} + searchView={CustomMapSearchBar} detailView={FeatureClickEventPopup} setIsOpen={setIsOpen} setFeatureData={setFeatureData} diff --git a/apps/wr/app/map/mapSearchBar.tsx b/apps/wr/app/map/mapSearchBar.tsx index a578dfac2..ca746ccce 100644 --- a/apps/wr/app/map/mapSearchBar.tsx +++ b/apps/wr/app/map/mapSearchBar.tsx @@ -1,14 +1,19 @@ "use client" -import { Button } from "@common/components/ui"; -import { useCallback, useEffect, useState } from "react"; -import type { EnvPartSelectDataType } from "@common/business_components/ui/search_form_left"; +import { Button, Input, Label } from "@common/components/ui"; +import React, { useCallback, useEffect, useRef, useState, useMemo } from "react"; +import { SearchFormLeft, type EnvPartSelectDataType } from "@common/business_components/ui/search_form_left"; import { SearchFormRight } from "@common/business_components/ui/search_form_right"; import type { SelectDataType } from "@common/business_components/ui/search_input_select"; import type { ReturnType } from "@common/business_components/ui/search_base_form"; -import { MapSearchForm } from "./mapSearchForm"; +import { MapSearchFormLeft } from "./mapSearchForm"; +import { useWrStore } from "@/app/store"; +import { btnClass } from "@/app/store/common/code"; +import { SelectBox } from "@common/business_components/ui/select"; +import { SearchBaseForm } from "@common/business_components"; type SearchBaseType = { + searchType: string; selectSidoData?: SelectDataType[] | []; selectSigunData?: SelectDataType[] | []; selectRegionData?: SelectDataType[] | []; @@ -16,12 +21,11 @@ type SearchBaseType = { selectMiddleRegionData?: SelectDataType[] | []; selectEnvPartData?: EnvPartSelectDataType[] | []; queryClick: (returnType: ReturnType) => void; - excelName?: string; - excelClick: (returnType: ReturnType) => void; initClick: () => void; - btnClass: string; - ref: React.RefObject; - isAdmin: boolean; + applyClick: () => void; + btnClass?: string; + ref?: React.RefObject; + isAdmin?: boolean; leftClassName?: string; rightClassName?: string; showQueryButton?: boolean; @@ -29,7 +33,24 @@ type SearchBaseType = { showApplyButton?: boolean; } +const API_URL = `${process.env.NEXT_PUBLIC_SEWERAGE_API_URL}`; + +const YEARS = Array.from({ length: 100 }, (_, i) => `${2025 - i}`); + +interface SearchModalProps { + open: boolean; + onOpenChange: (open: boolean) => void; + onFeatureSelect: (row: any) => void; +} + +interface SelectedDataTypeWithSggNm { + text: string; + val: string; + sggNm: string; +} + export function MapSearchBar({ + searchType, selectSidoData, selectSigunData, selectRegionData, @@ -37,28 +58,77 @@ export function MapSearchBar({ selectMiddleRegionData, selectEnvPartData, btnClass, - excelName, queryClick, - excelClick, initClick, - ref, + applyClick, isAdmin = false, leftClassName, rightClassName, showQueryButton = true, showInitButton = true, - showApplyButton = false + showApplyButton = true }: SearchBaseType) { + + // 로딩바 + const [isLoading, setIsLoading] = useState(false); + // 상세 정보 조회를 위한 상태 추가 + const [popupFeature, setPopupFeature] = useState(null); + const { code } = useWrStore((state) => state); + const [isChHead, setIsChHead] = useState(false); + const ref = useRef({} as ReturnType); + + // 전체 목록 const [partValue, setPartValue] = useState("A"); const [sidoValue, setSidoValue] = useState("0"); const [sigunValue, setSigunValue] = useState("0"); - const [sidoText, setSidoText] = useState(""); - const [sigunText, setSigunText] = useState(""); const [regionValue, setRegionValue] = useState("0"); const [untitWaterShedValue, setUntitWaterShedValue] = useState("0"); const [middleRegionValue, setMiddleRegionValue] = useState("0"); const [envPartValue, setEnvPartValue] = useState("0"); const [resetTrigger, setResetTrigger] = useState(false); + const [sidoText, setSidoText] = useState("0"); + const [sigunText, setSigunText] = useState("0"); + // 공급처 후보지 조회 필터 목록 + const [selectedSido, setSelectedSido] = useState({ text: "전체", val: '0' }); + const [selectedSigun, setSelectedSigun] = useState({ text: "전체", val: '0' }); + const [selectedFclt, setSelectedFclt] = useState({ text: "전체", val: '0' }); + // 전체 시설 + let allFclt: SelectedDataTypeWithSggNm[] = [{ text: "전체", val: '0', sggNm: '전체' }]; + // 우리동네가뭄지도 필터 목록 + const [selectedDong, setSelectedDong] = useState({ text: "전체", val: '0' }); // 행정동 + + const [allSido, setAllSido] = useState([ + { text: "전체", val: '0' }, + ...(code?.cityCd || []) + ]); + const [allRegion, setAllRegion] = useState([ + { text: "전체", val: '0' }, + ...(code?.rvbsnCd || []) + ]); + + const filteredSigunData = useMemo(() => { + if (!selectedSido || selectedSido.val === '0' || !selectedSido.val) return [{ text: "전체", val: '0' }]; + return [ + { text: "전체", val: '0' }, + ...code.sggCd.filter(item => item.val.substring(0, 2) === selectedSido.val.substring(0, 2)) + ]; + }, [selectedSido]); + + const filteredFcltData = useMemo(() => { + if (!selectedSigun || selectedSigun.val === '0' || !selectedSigun.val) return [{ text: "전체", val: '0', sggNm: '전체' }]; + return [ + { text: "전체", val: '0', sggNm: '전체' }, + ...allFclt.filter(item => item.sggNm === selectedSido.text) + ]; + }, [selectedSigun]); + + const filteredDong = useMemo(() => { + if (!selectedSigun || selectedSigun.val === '0' || !selectedSigun.val) return [{ text: "전체", val: '0', sggNm: '전체' }]; + return [ + { text: "전체", val: '0', sggNm: '전체' }, + // ...code.dong.filter(item => item.sggNm === selectedSido.text) + ]; + }, [selectedSigun]); const queryVal = () => { console.log("조회버튼 클릭"); @@ -70,8 +140,8 @@ export function MapSearchBar({ unitWaterShedValue: untitWaterShedValue, middleRegionValue: middleRegionValue, envPartValue: envPartValue, - sidoText: sidoText, - sigunText: sigunText, + sidoText: selectedSido.text, + sigunText: selectedSigun.text, }); } @@ -80,25 +150,68 @@ export function MapSearchBar({ initClick(); }, [setResetTrigger, initClick]); + const applyVal = useCallback(() => { + if (searchType == '공급처 후보지 조회') { + } else if (searchType == '우리동네가뭄지도보기') { + } else if (searchType == '지도보기') { + + } + }, [setResetTrigger, initClick]); + useEffect(() => { if (resetTrigger) { setResetTrigger(false); } }, [resetTrigger]); - const excelDownload = () => { - excelClick({ - partValue: partValue, - sidoValue: sidoValue, - sigunValue: sigunValue, - regionValue: regionValue, - unitWaterShedValue: untitWaterShedValue, - middleRegionValue: middleRegionValue, - envPartValue: envPartValue, - sidoText: sidoText, - sigunText: sigunText, - }) - }; + // 시설 조회 + useEffect(() => { + const fetchData = async () => { + setIsLoading(true); + if (!selectedSigun || selectedSigun.val === '0' || !selectedSigun.val) { + allFclt = ([{ text: "전체", val: '0', sggNm: '전체' }]); + setIsLoading(false); + return; + } + + try { + const queryParamsFacility = new URLSearchParams({ + lyrTypeNm: 'tb_il_swage_prcs_fclt', + page: '0', + size: '100000', + sort: 'string', + }); + const cqlFiltersFacility: string[] = []; + if (selectedSigun.text && selectedSigun.val !== '0') { + cqlFiltersFacility.push(`sgg_nm ILIKE '%${selectedSigun.text}%'`); + } + if (cqlFiltersFacility.length > 0) { + queryParamsFacility.append('cqlFiter', cqlFiltersFacility.join(' and ')); + } + console.log("BaseLayout API URL for Facilities:", `v1/fclts?${queryParamsFacility.toString()}`); + + const facilityRes = await fetch(`${API_URL}/v1/fclts?${queryParamsFacility.toString()}`, { + method: "GET", + headers: { accept: "application/json" } + }).then(res => res.json()); + + const facilities = (facilityRes.data || []).map((item: any) => ({ + text: item.swtrmtFcltNm, + val: item.fid, + sggNm: item.sggNm + })); + allFclt = (facilities); + + } catch (error) { + console.error("BaseLayout: 지역/시설 데이터 조회 중 오류 발생:", error); + allFclt = ([{ text: "전체", val: '0', sggNm: '전체' }]); + } finally { + setIsLoading(false); + } + }; + + fetchData(); + }, [selectedSigun]); useEffect(() => { if (ref && ref.current) { @@ -110,43 +223,132 @@ export function MapSearchBar({ customRef.unitWaterShedValue = untitWaterShedValue; customRef.middleRegionValue = middleRegionValue; customRef.envPartValue = envPartValue; - customRef.sidoText = sidoText; - customRef.sigunText = sigunText; + customRef.sidoText = selectedSido.text; + customRef.sigunText = selectedSigun.text; } - }, [partValue, sidoValue, sigunValue, regionValue, untitWaterShedValue, middleRegionValue, envPartValue, sidoText, sigunText, ref]); - + }, [partValue, sidoValue, sigunValue, regionValue, untitWaterShedValue, middleRegionValue, envPartValue, ref]); + return ( -
- {/* 검색 조건 부분 */} - - {/* 버튼 부분 */} -
-
+
+
+
+ {searchType == "공급처 후보지 조회" && ( + <> + { + const selected = code.cityCd.find(item => (item.val === val)); + setSelectedSido(selected || { text: "전체", val: '0' }); + }} + value={selectedSido.val} + selectClass={'w-32'} + /> + { + const selected = filteredSigunData.find(item => (item.val === val)); + setSelectedSigun(selected || { text: "전체", val: '0' }); + }} + value={selectedSigun.val} + selectClass={'w-32'} + /> + { + const selected = allFclt.find(item => (item.val === val)); + setSelectedFclt(selected || { text: "전체", val: '0', sggNm: '전체' }); + }} + value={selectedFclt.val} + selectClass={'w-32'} + /> + +
+ +
+ + )} + {searchType == "우리동네가뭄지도보기" && ( + <> + { + const selected = code.cityCd.find(item => (item.val === val)); + setSelectedSido(selected || { text: "전체", val: '0' }); + }} + value={selectedSido.val} + selectClass={'w-32'} + /> + { + const selected = filteredSigunData.find(item => (item.val === val)); + setSelectedSigun(selected || { text: "전체", val: '0' }); + }} + value={selectedSigun.val} + selectClass={'w-32'} + /> + { + const selected = filteredDong.find(item => (item.val === val)); + setSelectedDong(selected || { text: "전체", val: '0', sggNm: '전체' }); + }} + value={selectedDong.val} + selectClass={'w-32'} + /> + + )} + {searchType == "지도보기" && ( +
+
{/* SearchFormLeft 래퍼 */} + +
+ +
+ +
+
+ )} +
+ {/* 버튼 부분 */} +
{showQueryButton && } {showInitButton && } - {showApplyButton && } + {showApplyButton && }
diff --git a/apps/wr/app/map/mapSearchForm.tsx b/apps/wr/app/map/mapSearchForm.tsx index 00ba8e6f0..a148fdf67 100644 --- a/apps/wr/app/map/mapSearchForm.tsx +++ b/apps/wr/app/map/mapSearchForm.tsx @@ -1,7 +1,7 @@ "use client" import React, { useEffect, useState } from 'react'; -import type { SelectDataType } from "@common/business_components/ui/search_input_select"; +import type { SelectDataType } from '@common/business_components/ui/search_input_select'; import { SelectBox } from '@common/business_components/ui/select'; /** @@ -56,13 +56,7 @@ const selectPartData: SelectDataType[] = [ { text: "환경청별", val: 'G' }, ]; -const selectType: SelectDataType[] = [ - { text: "행정별", val: 'A' }, - { text: "유역별", val: 'R' }, - { text: "환경청별", val: 'G' }, -]; - -function MapSearchForm({ isShowPart = false, children, selectSidoData = [], selectSigunData = [], selectRegionData = [], selectUnitWaterShedData = [], selectMiddleRegionData = [], selectEnvPartData = [], setRtnPartValue, setRtnSidoValue, setRtnSidoText, setRtnSigunValue, setRtnSigunText, setRtnRegionValue, setRtnUnitWaterShedValue, setRtnMiddleRegionValue, setRtnEnvPartValue, setRtnEnvPartSidoValue, setRtnEnvPartSigunValue, resetTrigger = false, isAdmin = false }: SearchFormLeftProps) { +function MapSearchFormLeft({ isShowPart = false, children, selectSidoData = [], selectSigunData = [], selectRegionData = [], selectUnitWaterShedData = [], selectMiddleRegionData = [], selectEnvPartData = [], setRtnPartValue, setRtnSidoValue, setRtnSidoText, setRtnSigunValue, setRtnSigunText, setRtnRegionValue, setRtnUnitWaterShedValue, setRtnMiddleRegionValue, setRtnEnvPartValue, setRtnEnvPartSidoValue, setRtnEnvPartSigunValue, resetTrigger = false, isAdmin = false }: SearchFormLeftProps) { /******************선택된 데이터******************/ const [partValue, setPartValue] = useState('A'); //행정별 시도 시군구 @@ -314,27 +308,26 @@ function MapSearchForm({ isShowPart = false, children, selectSidoData = [], sele return ( isShowPart ? ( -
- onSelectValue(val.toString(), 'part')} value={partValue} selectClass={undefined} /> +
+ onSelectValue(val.toString(), 'part')} value={partValue} selectClass={'w-32'} /> {partValue === 'R' ? ( <> - onSelectValue(val.toString(), 'region')} value={regionValue} selectClass={undefined} /> - onSelectValue(val.toString(), 'unitWaterShed')} value={unitWaterShedValue} selectClass={undefined} /> - onSelectValue(val.toString(), 'middleRegion')} value={middleRegionValue} selectClass={undefined} /> + onSelectValue(val.toString(), 'region')} value={regionValue} selectClass={'w-44'} /> + onSelectValue(val.toString(), 'unitWaterShed')} value={unitWaterShedValue} selectClass={'w-44'} /> + onSelectValue(val.toString(), 'middleRegion')} value={middleRegionValue} selectClass={'w-44'} /> ) : partValue === 'G' ? ( <> - onSelectValue(val.toString(), 'envPart')} value={envPartValue} selectClass={undefined} /> - onSelectValue(val.toString(), 'envPartSido')} value={sidoValue} selectClass={undefined} /> - onSelectValue(val.toString(), 'envPartSigun')} value={sigunValue} selectClass={undefined} /> + onSelectValue(val.toString(), 'envPart')} value={envPartValue} selectClass={'w-44'} /> + onSelectValue(val.toString(), 'envPartSido')} value={sidoValue} selectClass={'w-44'} /> + onSelectValue(val.toString(), 'envPartSigun')} value={sigunValue} selectClass={'w-44'} /> ) : ( <> - onSelectValue(val.toString(), 'sido')} value={sidoValue} selectClass={undefined} /> - onSelectValue(val.toString(), 'sigun')} value={sigunValue} selectClass={undefined} /> + onSelectValue(val.toString(), 'sido')} value={sidoValue} selectClass={'w-40'} /> + onSelectValue(val.toString(), 'sigun')} value={sigunValue} selectClass={'w-40'} /> )} - {children}
) : (
@@ -344,4 +337,4 @@ function MapSearchForm({ isShowPart = false, children, selectSidoData = [], sele ) } -export { MapSearchForm }; +export { MapSearchFormLeft }; diff --git a/apps/wr/app/map/searchModal.tsx b/apps/wr/app/map/searchModal.tsx deleted file mode 100644 index 658c9545f..000000000 --- a/apps/wr/app/map/searchModal.tsx +++ /dev/null @@ -1,80 +0,0 @@ -"use client" - -import { useWrStore } from "@/app/store"; -import { btnClass } from "@/app/store/common/code"; -import React, { useCallback, useEffect, useRef, useState } from 'react'; -import { MapSearchBar, type ReturnType } from "./mapSearchBar"; -import { Button } from "@common/components/ui"; -import { SearchFormLeft, type EnvPartSelectDataType } from "@common/business_components/ui/search_form_left"; -import { SearchFormRight } from "@common/business_components/ui/search_form_right"; -import type { SelectDataType } from "@common/business_components/ui/search_input_select"; - -const API_URL = `${process.env.NEXT_PUBLIC_SEWERAGE_API_URL}`; - -const YEARS = Array.from({ length: 100 }, (_, i) => `${2025 - i}`); - -interface SearchModalProps { - open: boolean; - onOpenChange: (open: boolean) => void; - onFeatureSelect: (row: any) => void; -} - -// 날짜 조합 함수 -const getDateString = ( - year: string, - month: string, - day: string, - defaultDate: string, -) => { - if (!year && !month && !day) return defaultDate; - const y = year || defaultDate.slice(0, 4); - const m = month || defaultDate.slice(4, 6); - const d = day || defaultDate.slice(6, 8); - return `${y}${m}${d}`; -}; - -const SearchModal: React.FC = ({ - open, - onOpenChange, - onFeatureSelect, -}) => { - - // 로딩바 - const [isLoading, setIsLoading] = useState(false); - // 상세 정보 조회를 위한 상태 추가 - const [popupFeature, setPopupFeature] = useState(null); - const { code, pbnstSttn, pbnstSttnSearch } = useWrStore((state) => state); - const [isChHead, setIsChHead] = useState(false); - const ref = useRef({} as ReturnType); - - return ( -
- -
- ); -}; - -export default React.memo(SearchModal); diff --git a/apps/wr/middleware.ts b/apps/wr/middleware.ts index 330252074..3fabc529d 100644 --- a/apps/wr/middleware.ts +++ b/apps/wr/middleware.ts @@ -5,6 +5,8 @@ import { CS_URL } from "./components/lib/config"; const CLAIM_NAME = "Authorization"; const AUTH_USER_ID = "token-id"; const serverApiUrl = CS_URL; +const GEOSERVER_URL = process.env.NEXT_PUBLIC_GEOSERVER_URL || ""; + export function middleware(req: NextRequest) { const pathname = req.nextUrl.pathname; console.log(`[Middleware] Request URL: ${req.url}`); @@ -44,9 +46,18 @@ export function middleware(req: NextRequest) { }); } + if (pathname.startsWith("/geoserver")) { + const queryParams = searchParams.toString(); + const newUrl = `${GEOSERVER_URL}${pathname.replace("/geoserver", "")}${ + queryParams ? `?${queryParams}` : "" + }`; + console.log(`[Middleware] Rewriting from '/geoserver' path to: ${newUrl}`); + return NextResponse.rewrite(new URL(newUrl)); + } + return NextResponse.next(); } export const config = { - matcher: ["/server/:path*", "/attach-service/:path*"], + matcher: ["/server/:path*", "/attach-service/:path*", "/geoserver/:path*"], }; From b095dda5adca7512e1928120d950e647d7a510e0 Mon Sep 17 00:00:00 2001 From: swcho Date: Tue, 17 Jun 2025 19:01:02 +0900 Subject: [PATCH 005/108] =?UTF-8?q?feat:=20=EC=A7=80=EB=8F=84=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=ED=94=BC=EC=B2=98=20=EC=84=A0=ED=83=9D=EC=8B=9C=20?= =?UTF-8?q?=ED=98=B8=EC=B6=9C=ED=95=9C=20=ED=99=94=EB=A9=B4=EC=97=90=20?= =?UTF-8?q?=ED=94=BC=EC=B2=98=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20return?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/common/business_components/src/ui/map/Gis.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/common/business_components/src/ui/map/Gis.js b/packages/common/business_components/src/ui/map/Gis.js index 48dce9284..887207530 100644 --- a/packages/common/business_components/src/ui/map/Gis.js +++ b/packages/common/business_components/src/ui/map/Gis.js @@ -103,6 +103,7 @@ function Gis({ setFeatureData(data); setIsOpen(true); setIsSearchModalOpen(false); + onFeatureSelect?.(data); // 선택된 피쳐 데이터 콜백 호출 }, [mapInstanceRef]); // 지도 생성 및 초기화 From 3208b02fb2379920407a5f8f54db34b1abac8524 Mon Sep 17 00:00:00 2001 From: swcho Date: Tue, 17 Jun 2025 19:14:10 +0900 Subject: [PATCH 006/108] =?UTF-8?q?feat:=20=EC=B4=88=EA=B8=B0=ED=99=94,=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9=20=EB=B2=84=ED=8A=BC=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wr/app/map/mapSearchBar.tsx | 66 +++++++++++++++++++------------- 1 file changed, 40 insertions(+), 26 deletions(-) diff --git a/apps/wr/app/map/mapSearchBar.tsx b/apps/wr/app/map/mapSearchBar.tsx index ca746ccce..86e9f939a 100644 --- a/apps/wr/app/map/mapSearchBar.tsx +++ b/apps/wr/app/map/mapSearchBar.tsx @@ -22,7 +22,7 @@ type SearchBaseType = { selectEnvPartData?: EnvPartSelectDataType[] | []; queryClick: (returnType: ReturnType) => void; initClick: () => void; - applyClick: () => void; + onFeatureSelect: () => void; btnClass?: string; ref?: React.RefObject; isAdmin?: boolean; @@ -60,7 +60,7 @@ export function MapSearchBar({ btnClass, queryClick, initClick, - applyClick, + onFeatureSelect, isAdmin = false, leftClassName, rightClassName, @@ -91,11 +91,11 @@ export function MapSearchBar({ // 공급처 후보지 조회 필터 목록 const [selectedSido, setSelectedSido] = useState({ text: "전체", val: '0' }); const [selectedSigun, setSelectedSigun] = useState({ text: "전체", val: '0' }); - const [selectedFclt, setSelectedFclt] = useState({ text: "전체", val: '0' }); + const [selectedFclt, setSelectedFclt] = useState({ text: "전체", val: '0', sggNm: '전체' }); // 시설명 선택 상태 // 전체 시설 let allFclt: SelectedDataTypeWithSggNm[] = [{ text: "전체", val: '0', sggNm: '전체' }]; // 우리동네가뭄지도 필터 목록 - const [selectedDong, setSelectedDong] = useState({ text: "전체", val: '0' }); // 행정동 + const [selectedDong, setSelectedDong] = useState({ text: "전체", val: '0', sggNm: '전체' }); // 시설명 선택 상태 // 행정동 const [allSido, setAllSido] = useState([ { text: "전체", val: '0' }, @@ -131,32 +131,46 @@ export function MapSearchBar({ }, [selectedSigun]); const queryVal = () => { - console.log("조회버튼 클릭"); - queryClick({ - partValue: partValue, - sidoValue: sidoValue, - sigunValue: sigunValue, - regionValue: regionValue, - unitWaterShedValue: untitWaterShedValue, - middleRegionValue: middleRegionValue, - envPartValue: envPartValue, - sidoText: selectedSido.text, - sigunText: selectedSigun.text, - }); - } - - const initVal = useCallback(() => { - setResetTrigger(true); - initClick(); - }, [setResetTrigger, initClick]); - - const applyVal = useCallback(() => { if (searchType == '공급처 후보지 조회') { } else if (searchType == '우리동네가뭄지도보기') { } else if (searchType == '지도보기') { - + console.log("조회버튼 클릭"); + queryClick({ + partValue: partValue, + sidoValue: sidoValue, + sigunValue: sigunValue, + regionValue: regionValue, + unitWaterShedValue: untitWaterShedValue, + middleRegionValue: middleRegionValue, + envPartValue: envPartValue, + sidoText: selectedSido.text, + sigunText: selectedSigun.text, + }); } - }, [setResetTrigger, initClick]); + } + + const initVal = useCallback(() => { + setPartValue("A"); + setSidoValue("0"); + setSigunValue("0"); + setRegionValue("0"); + setUntitWaterShedValue("0"); + setMiddleRegionValue("0"); + setEnvPartValue("0"); + setSidoText("전체"); + setSigunText("전체"); + setSelectedSido({ text: "전체", val: '0' }); + setSelectedSigun({ text: "전체", val: '0' }); + setSelectedFclt({ text: "전체", val: '0', sggNm: '전체' }); + setSelectedDong({ text: "전체", val: '0', sggNm: '전체' }); + setResetTrigger(true); + + initClick(); // 부모로부터 받은 초기화 함수 호출 + }, [initClick]); + + const applyVal = useCallback(() => { + onFeatureSelect + }, [onFeatureSelect]); useEffect(() => { if (resetTrigger) { From 04d43408f7ae71f3d37747b95f508f48e2e869f2 Mon Sep 17 00:00:00 2001 From: swcho Date: Tue, 24 Jun 2025 12:14:54 +0900 Subject: [PATCH 007/108] =?UTF-8?q?feat:=20-=20=ED=95=98=EC=88=98=EC=9E=AC?= =?UTF-8?q?=EC=9D=B4=EC=9A=A9=EC=8B=9C=EC=84=A4,=20=EB=B9=97=EB=AC=BC?= =?UTF-8?q?=EC=9D=B4=EC=9A=A9=EC=8B=9C=EC=84=A4,=20=EC=A4=91=EC=88=98?= =?UTF-8?q?=EB=8F=84=EC=8B=9C=EC=84=A4,=20=EC=98=A8=EB=B0=B0=EC=88=98?= =?UTF-8?q?=EC=8B=9C=EC=84=A4=20=ED=99=94=EB=A9=B4=EC=9D=98=20=ED=98=84?= =?UTF-8?q?=ED=99=A9(=EC=A7=80=EB=8F=84),=20=EB=AA=A9=EB=A1=9D,=20?= =?UTF-8?q?=EC=83=81=EC=84=B8=EC=A0=95=EB=B3=B4=20UI=20=EA=B5=AC=ED=98=84?= =?UTF-8?q?=20(=EC=A7=80=EB=8F=84=EC=A0=90=EA=B2=80=20=EA=B2=B0=EA=B3=BC?= =?UTF-8?q?=20=EB=AF=B8=EA=B5=AC=ED=98=84)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wr/app/map/htdflPrfmncSttnPopup.tsx | 621 ++++++++++++++++++ apps/wr/app/map/htdflSttn/page.tsx | 142 ++-- apps/wr/app/map/mapSearchBar.tsx | 192 ++++-- apps/wr/app/map/mapSearchForm.tsx | 4 +- apps/wr/app/map/newWindowPopup.tsx | 380 +++++++++++ apps/wr/app/map/newWindowPortal.tsx | 90 +++ apps/wr/app/map/pbnstPrfmncSttnPopup.tsx | 596 +++++++++++++++++ apps/wr/app/map/rainPrfmncSttnPopup.tsx | 421 ++++++++++++ apps/wr/app/map/rswtrPrfmncSttnPopup.tsx | 404 ++++++++++++ apps/wr/app/page.tsx | 2 +- .../ruseFcltMng/fcltSttn/rainSttn/page.tsx | 34 +- .../sttn/pbnstSttnModal/pbnstSttnModal.tsx | 18 +- .../business_components/src/ui/map/Gis.js | 182 ++++- .../business_components/src/ui/map/GisTree.js | 7 +- 14 files changed, 2921 insertions(+), 172 deletions(-) create mode 100644 apps/wr/app/map/htdflPrfmncSttnPopup.tsx create mode 100644 apps/wr/app/map/newWindowPopup.tsx create mode 100644 apps/wr/app/map/newWindowPortal.tsx create mode 100644 apps/wr/app/map/pbnstPrfmncSttnPopup.tsx create mode 100644 apps/wr/app/map/rainPrfmncSttnPopup.tsx create mode 100644 apps/wr/app/map/rswtrPrfmncSttnPopup.tsx diff --git a/apps/wr/app/map/htdflPrfmncSttnPopup.tsx b/apps/wr/app/map/htdflPrfmncSttnPopup.tsx new file mode 100644 index 000000000..8d1bec486 --- /dev/null +++ b/apps/wr/app/map/htdflPrfmncSttnPopup.tsx @@ -0,0 +1,621 @@ +"use client"; + +import { + Form, + FormField, + FormUi, + SelectBox, +} from "@common/business_components/ui"; +import { Button, Input } from "@common/components/ui"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { useForm } from "react-hook-form"; +import { z } from "zod"; +import { useWrStore } from "@/app/store"; +import { selectClass } from "@/app/store/ruseFcltMng/fcltReg/rainReg"; +import { + hdtflZod, + type customHtdflBdst, +} from "@/app/store/ruseFcltMng/fcltReg/htdflReg"; +import { useEffect } from "react"; +import BdstReg from "@/components/ui/form/fcltReg/bdstReg/bdstReg"; +import { + formatCommaNumber, + handleCommaNumberChange, +} from "@/components/lib/func/commom"; +import { formH1Class } from "@/components/lib/class/comClass"; + +export default function HtdflPrfmncSttnPopup({ + htdflForm, + isModal, +}: { + htdflForm: customHtdflBdst; + isModal: boolean; +}) { + const { code, htdflBdstUpdate, htdflBdstReg } = useWrStore((state) => state); + + const defaultValues: z.infer = hdtflZod.parse({}); + + const form = useForm>({ + resolver: zodResolver(hdtflZod), + defaultValues: defaultValues, + }); + + const onSubmit = (values: z.infer) => { + if (isModal) { + htdflBdstReg(values, '41'); + } else { + htdflBdstUpdate(values, '41'); + } + }; + + + const cleanHtdflData = (raw: any): z.infer => { + return { + ...raw, + frstArchPrmsnDt: new Date(raw?.frstArchPrmsnDt || null), + archChgPrmsnDt: raw?.archChgPrmsnDt + ? new Date(raw?.archChgPrmsnDt) + : null, + chgBuilding: (raw?.chgBuilding || []).map((item: any) => ({ + ...item, + archChgPrmsnDt: + item?.archChgPrmsnDt === null + ? new Date() + : new Date(item?.archChgPrmsnDt), + bdstChgNm: item?.bdstChgNm ?? "", // null → "" + })), + reuseBuilding: (raw?.reuseBuilding || []).map((item: any) => ({ + ...item, + instlCmptnDt: new Date(item?.instlCmptnDt), + htdflObj: { + ...item?.htdflObj, + }, + })), + }; + }; + + useEffect(() => { + console.log("form errors:", form.formState.errors); + }, [form.formState.errors]); + + useEffect(() => { + if (hdtflZod) form.reset(cleanHtdflData(htdflForm)); + }, [htdflForm]); + + return ( +
+
+ + {/*
*/} +
+
+

온배수 시설 정보

+
+
+ + {isModal && htdflForm != undefined ? : <>} + {isModal ? ( +

+ 온배수 시설 정보{" "} +

+ ) : ( + <> + )} + +
+
+ ( + + + + )} + /> +
+ +
+ ( + + { + handleCommaNumberChange(e, field.onChange); + }} + value={formatCommaNumber(field.value)} + readOnly={true} + /> + + )} + /> +
+
+ ( + + + + )} + /> +
+
+
+

+ 온배수 시설 운영현황 +

+
+ +
+
+ ( + + { + field.onChange(Number(e.target.value)); + }} + readOnly={true} + /> + + )} + /> +
+
+ ( + + + + )} + /> +
+
+ ( + + { + handleCommaNumberChange(e, field.onChange); + }} + value={formatCommaNumber(field.value)} + readOnly={true} + /> + + )} + /> +
+
+ ( + + { + handleCommaNumberChange(e, field.onChange); + }} + value={formatCommaNumber(field.value)} + readOnly={true} + /> + + )} + /> +
+
+ ( + + + + )} + /> +
+
+ ( + + { + handleCommaNumberChange(e, field.onChange); + }} + value={formatCommaNumber(field.value)} + readOnly={true} + /> + + )} + /> +
+
+ ( + + + + )} + /> +
+
+ ( + + { + handleCommaNumberChange(e, field.onChange); + }} + value={formatCommaNumber(field.value)} + readOnly={true} + /> + + )} + /> +
+
+ ( + + + + )} + /> +
+
+ +
+

+ 요구수질 +

+
+ +
+
+ ( + + { + handleCommaNumberChange(e, field.onChange); + }} + value={formatCommaNumber(field.value)} + readOnly={true} + /> + + )} + /> +
+ +
+ ( + + { + handleCommaNumberChange(e, field.onChange); + }} + value={formatCommaNumber(field.value)} + readOnly={true} + /> + + )} + /> +
+
+ +
+
+ ( + + { + handleCommaNumberChange(e, field.onChange); + }} + value={formatCommaNumber(field.value)} + readOnly={true} + /> + + )} + /> +
+ +
+ ( + + { + handleCommaNumberChange(e, field.onChange); + }} + value={formatCommaNumber(field.value)} + readOnly={true} + /> + + )} + /> +
+
+ +
+
+ ( + + { + handleCommaNumberChange(e, field.onChange); + }} + value={formatCommaNumber(field.value)} + readOnly={true} + /> + + )} + /> +
+ +
+ ( + + { + handleCommaNumberChange(e, field.onChange); + }} + value={formatCommaNumber(field.value)} + readOnly={true} + /> + + )} + /> +
+
+ +
+
+ ( + + { + handleCommaNumberChange(e, field.onChange); + }} + value={formatCommaNumber(field.value)} + readOnly={true} + /> + + )} + /> +
+ +
+ ( + + { + handleCommaNumberChange(e, field.onChange); + }} + value={formatCommaNumber(field.value)} + readOnly={true} + /> + + )} + /> +
+
+ +
+
+ ( + + { + handleCommaNumberChange(e, field.onChange); + }} + value={formatCommaNumber(field.value)} + readOnly={true} + /> + + )} + /> +
+ +
+ ( + + { + handleCommaNumberChange(e, field.onChange); + }} + value={formatCommaNumber(field.value)} + readOnly={true} + /> + + )} + /> +
+
+ +
+
+ ( + + { + handleCommaNumberChange(e, field.onChange); + }} + value={formatCommaNumber(field.value)} + readOnly={true} + /> + + )} + /> +
+ +
+ ( + + { + handleCommaNumberChange(e, field.onChange); + }} + value={formatCommaNumber(field.value)} + readOnly={true} + /> + + )} + /> +
+
+ + +
+ ); +} diff --git a/apps/wr/app/map/htdflSttn/page.tsx b/apps/wr/app/map/htdflSttn/page.tsx index 6c75edb92..5ba199c8c 100644 --- a/apps/wr/app/map/htdflSttn/page.tsx +++ b/apps/wr/app/map/htdflSttn/page.tsx @@ -1,18 +1,23 @@ "use client"; import Gis from "@common/business_components/ui/map/Gis"; -import { useEffect, useRef, useState } from "react"; +import { useEffect, useRef, useState, useCallback } from "react"; import FeatureClickEventPopup from "../featureClickEventPopup"; import { mapService } from "../service"; import { MapSearchBar } from "../mapSearchBar"; +import NewWindowPopup from "../newWindowPopup"; +import GisApp from "@common/business_components/ui/map/lib/gis.core"; const Map = () => { const [treeData, setTreeData] = useState([]); // 초기값을 객체 또는 null로 설정 const [selectedFeature, setSelectedFeature] = useState(null); // 선택된 피처 상태 - const gisMaps = useRef(null); + const gisMaps = useRef(null); const [isOpen, setIsOpen] = useState(false); // 팝업 열기/닫기 상태 const [isSearchModalOpen, setIsSearchModalOpen] = useState(true); // SearchModal 열림 상태 const [featureData, setFeatureData] = useState(null); // 선택된 피쳐 데이터 + const [checkedLayers, setCheckedLayers] = useState([]); + const [checkedLayersData, setCheckedLayersData] = useState([]); + const fetchData = async () => { try { const response = await mapService.getLyrStngs(); @@ -22,80 +27,92 @@ const Map = () => { } }; + // 팝업 닫기 핸들러를 useCallback으로 메모이제이션 + const handlePopupClose = useCallback(() => { + setIsOpen(false); + setFeatureData(null); + }, []); + // 컴포넌트 마운트 시 1회 실행 useEffect(() => { fetchData(); }, []); + // 체크된 레이어 정보를 받는 핸들러 + const handleCheckedLayersChange = (checkedIds: string[], checkedNodesData: any[]) => { + setCheckedLayers(checkedIds); + setCheckedLayersData(checkedNodesData); + }; + // SearchModal, GisContent 등에서 호출 - // const onFeatureSelect = (data: any) => { - // let map = gisMaps.current; - // if (!map) { - // console.warn("지도가 초기화되지 않았습니다."); - // return; - // } + const onFeatureSelect = (data: any) => { + let map = gisMaps.current; + if (!map) { + console.warn("지도가 초기화되지 않았습니다."); + return; + } - // // 항상 같은 select 객체 사용 - // const select = GisApp.Module.select as any; - // if (!select) return; + // 항상 같은 select 객체 사용 + const select = GisApp.Module.select as any; + if (!select) return; - // // 1. 기존 선택 초기화 - // select.resetSelectedFeature(); + // 1. 기존 선택 초기화 + select.resetSelectedFeature(); - // // 2. data가 OpenLayers Feature 인스턴스인지 확인 - // let foundFeature = null; - // if ( - // data && - // typeof data.getGeometry === "function" && - // typeof data.setStyle === "function" - // ) { - // // 이미 Feature 인스턴스라면 그대로 사용 - // foundFeature = data; - // } else { - // // 아니면 featureId로 맵에서 Feature 인스턴스를 찾아서 사용 - // const featureId = data.fid || data.ftrIdn || data.id; - // map - // .getLayers() - // .getArray() - // .forEach((layer: any) => { - // const source = layer.getSource?.(); - // if (source?.getFeatureById) { - // const feature = source.getFeatureById(String(featureId).trim()); - // if (feature) foundFeature = feature; - // } - // }); - // } + // 2. data가 OpenLayers Feature 인스턴스인지 확인 + let foundFeature = null; + if ( + data && + typeof data.getGeometry === "function" && + typeof data.setStyle === "function" + ) { + // 이미 Feature 인스턴스라면 그대로 사용 + foundFeature = data; + } else { + // 아니면 featureId로 맵에서 Feature 인스턴스를 찾아서 사용 + const featureId = data.fid || data.ftrIdn || data.id; + map + .getLayers() + .getArray() + .forEach((layer: any) => { + const source = layer.getSource?.(); + if (source?.getFeatureById) { + const feature = source.getFeatureById(String(featureId).trim()); + if (feature) foundFeature = feature; + } + }); + } - // // 3. 피처 선택 - // if (foundFeature) { - // let geometry = foundFeature.getGeometry(); - // if (geometry) { - // let extent = geometry.getExtent(); + // 3. 피처 선택 + if (foundFeature) { + let geometry = foundFeature.getGeometry(); + if (geometry) { + let extent = geometry.getExtent(); - // map.getView().fit(extent, { - // duration: 300, - // padding: [50, 50, 50, 50], - // maxZoom: 16, - // constrainResolution: true, - // nearest: true, - // }); - // } - // select.selectFeature(foundFeature, true); - // } else { - // console.warn("OpenLayers Feature를 찾을 수 없습니다:", data); - // } + map.getView().fit(extent, { + duration: 300, + padding: [50, 50, 50, 50], + maxZoom: 16, + constrainResolution: true, + nearest: true, + }); + } + select.selectFeature(foundFeature, true); + } else { + console.warn("OpenLayers Feature를 찾을 수 없습니다:", data); + } - // setFeatureData(data); - // setIsOpen(true); - // setIsSearchModalOpen(false); - // }; + setFeatureData(data); + setIsOpen(true); + setIsSearchModalOpen(false); + }; // MapSearchBar 래퍼 컴포넌트 const CustomMapSearchBar = (props: any) => { return ( ); @@ -109,13 +126,20 @@ const Map = () => { gisMaps={gisMaps} isMiniMap={true} searchView={CustomMapSearchBar} - detailView={FeatureClickEventPopup} + detailView={(props: any) => ( + + )} + onFeatureSelect={undefined} setIsOpen={setIsOpen} setFeatureData={setFeatureData} featureData={featureData} setIsSearchModalOpen={setIsSearchModalOpen} isOpen={isOpen} isSearchModalOpen={isSearchModalOpen} + onCheckedLayersChange={handleCheckedLayersChange} />
); diff --git a/apps/wr/app/map/mapSearchBar.tsx b/apps/wr/app/map/mapSearchBar.tsx index 86e9f939a..f5d693ee3 100644 --- a/apps/wr/app/map/mapSearchBar.tsx +++ b/apps/wr/app/map/mapSearchBar.tsx @@ -2,15 +2,35 @@ import { Button, Input, Label } from "@common/components/ui"; import React, { useCallback, useEffect, useRef, useState, useMemo } from "react"; -import { SearchFormLeft, type EnvPartSelectDataType } from "@common/business_components/ui/search_form_left"; -import { SearchFormRight } from "@common/business_components/ui/search_form_right"; -import type { SelectDataType } from "@common/business_components/ui/search_input_select"; -import type { ReturnType } from "@common/business_components/ui/search_base_form"; +import { useForm } from "react-hook-form"; import { MapSearchFormLeft } from "./mapSearchForm"; import { useWrStore } from "@/app/store"; import { btnClass } from "@/app/store/common/code"; -import { SelectBox } from "@common/business_components/ui/select"; import { SearchBaseForm } from "@common/business_components"; +import { FormUi, SelectBox, SearchFormLeft, SearchFormRight, Form, FormField } from "@common/business_components/ui"; +import type { ReturnType, SelectDataType, EnvPartSelectDataType } from "@common/business_components/ui"; +import SearchInputText from "@/components/ui/searchInput/searchInput"; +import { zodResolver } from "@hookform/resolvers/zod"; + + +// 건물 검색 타입 정의 +interface BuildingInfo { + bdstInfoPk: number; + ctpvCd: number; + sggCd: number; + daddr: string; + bdstNm: string; + frstArchPrmsnDt: string; + archArea: number; + ctpvNm: string | null; + sggNm: string | null; + lat: number | null; + lot: number | null; + regDt: string; + rgtr: string | null; + mdfcnDt: string; + mdfr: string | null; +} type SearchBaseType = { searchType: string; @@ -20,8 +40,6 @@ type SearchBaseType = { selectUnitWaterShedData?: SelectDataType[] | []; selectMiddleRegionData?: SelectDataType[] | []; selectEnvPartData?: EnvPartSelectDataType[] | []; - queryClick: (returnType: ReturnType) => void; - initClick: () => void; onFeatureSelect: () => void; btnClass?: string; ref?: React.RefObject; @@ -58,8 +76,6 @@ export function MapSearchBar({ selectMiddleRegionData, selectEnvPartData, btnClass, - queryClick, - initClick, onFeatureSelect, isAdmin = false, leftClassName, @@ -77,6 +93,10 @@ export function MapSearchBar({ const [isChHead, setIsChHead] = useState(false); const ref = useRef({} as ReturnType); + const [selectedBuilding, setSelectedBuilding] = useState(null); + const [buildingSearchText, setBuildingSearchText] = useState(""); + const [buildingList, setBuildingList] = useState([]); + // 전체 목록 const [partValue, setPartValue] = useState("A"); const [sidoValue, setSidoValue] = useState("0"); @@ -97,15 +117,6 @@ export function MapSearchBar({ // 우리동네가뭄지도 필터 목록 const [selectedDong, setSelectedDong] = useState({ text: "전체", val: '0', sggNm: '전체' }); // 시설명 선택 상태 // 행정동 - const [allSido, setAllSido] = useState([ - { text: "전체", val: '0' }, - ...(code?.cityCd || []) - ]); - const [allRegion, setAllRegion] = useState([ - { text: "전체", val: '0' }, - ...(code?.rvbsnCd || []) - ]); - const filteredSigunData = useMemo(() => { if (!selectedSido || selectedSido.val === '0' || !selectedSido.val) return [{ text: "전체", val: '0' }]; return [ @@ -130,26 +141,82 @@ export function MapSearchBar({ ]; }, [selectedSigun]); + // 건물 검색 API 호출 함수 + const searchBuildings = async (searchText: string) => { + if (!searchText.trim()) { + setBuildingList([]); + return; + } + + try { + const flg = "41"; // 적절한 플래그 값으로 설정 + const sigunCode = selectedSigun.val !== '0' ? selectedSigun.val : ''; + const apiEndpoint = `${API_URL}/v1/reuseFc/building/${flg}/${sigunCode}/${encodeURIComponent(searchText)}`; + + const response = await fetch(apiEndpoint, { + method: "GET", + headers: { + accept: "application/json", + "Content-Type": "application/json" + } + }); + + if (!response.ok) { + throw new Error(`API 호출 실패: ${response.status}`); + } + + const result = await response.json(); + + if (result.status === 200 && Array.isArray(result.data)) { + // 건물 검색 결과를 demandCompList 형태로 변환 + const convertedList = result.data.map((building: BuildingInfo) => ({ + dmdInfoPk: String(building.bdstInfoPk), + dmdCoNm: building.bdstNm, + daddr: building.daddr, + useUsgCd: "", + splyCpct: 0, + splyFmYn: "Y", + // 추가 필드들... + })); + setBuildingList(result.data); + // 글로벌 상태에도 업데이트 (SearchInputText가 사용할 수 있도록) + // getDemandCompList를 직접 호출하는 대신 상태를 직접 설정 + const { setBuildingSearchResults } = useWrStore.getState(); + if (setBuildingSearchResults) { + setBuildingSearchResults(convertedList); + } + } else { + setBuildingList([]); + } + } catch (error) { + console.error("건물 검색 중 오류 발생:", error); + setBuildingList([]); + } + }; + + // 조회 함수 const queryVal = () => { + console.log("조회버튼 클릭"); if (searchType == '공급처 후보지 조회') { + // queryClick({ + // partValue: partValue, + // sidoValue: sidoValue, + // sigunValue: sigunValue, + // regionValue: regionValue, + // unitWaterShedValue: untitWaterShedValue, + // middleRegionValue: middleRegionValue, + // envPartValue: envPartValue, + // sidoText: selectedSido.text, + // sigunText: selectedSigun.text, + // }); } else if (searchType == '우리동네가뭄지도보기') { } else if (searchType == '지도보기') { - console.log("조회버튼 클릭"); - queryClick({ - partValue: partValue, - sidoValue: sidoValue, - sigunValue: sigunValue, - regionValue: regionValue, - unitWaterShedValue: untitWaterShedValue, - middleRegionValue: middleRegionValue, - envPartValue: envPartValue, - sidoText: selectedSido.text, - sigunText: selectedSigun.text, - }); + } } - const initVal = useCallback(() => { + // 초기화 함수 + const initVal = () => { setPartValue("A"); setSidoValue("0"); setSigunValue("0"); @@ -164,13 +231,12 @@ export function MapSearchBar({ setSelectedFclt({ text: "전체", val: '0', sggNm: '전체' }); setSelectedDong({ text: "전체", val: '0', sggNm: '전체' }); setResetTrigger(true); - - initClick(); // 부모로부터 받은 초기화 함수 호출 - }, [initClick]); + }; - const applyVal = useCallback(() => { - onFeatureSelect - }, [onFeatureSelect]); + // 적용 버튼 + const applyVal = () => { + onFeatureSelect(); + }; useEffect(() => { if (resetTrigger) { @@ -251,10 +317,11 @@ export function MapSearchBar({ { const selected = code.cityCd.find(item => (item.val === val)); setSelectedSido(selected || { text: "전체", val: '0' }); + setSelectedSigun({ text: "전체", val: '0' }); }} value={selectedSido.val} selectClass={'w-32'} @@ -266,24 +333,39 @@ export function MapSearchBar({ onSelectValue={(val) => { const selected = filteredSigunData.find(item => (item.val === val)); setSelectedSigun(selected || { text: "전체", val: '0' }); + setSelectedFclt({ text: "전체", val: '0', sggNm: '전체' }); }} value={selectedSigun.val} selectClass={'w-32'} /> - { - const selected = allFclt.find(item => (item.val === val)); - setSelectedFclt(selected || { text: "전체", val: '0', sggNm: '전체' }); - }} - value={selectedFclt.val} - selectClass={'w-32'} - /> - + {/*
+
*/} +
+ +
+ { + setBuildingSearchText(searchText || ""); + if (searchText) { + searchBuildings(searchText); + } + }} + setRtnData={(buildingData) => { + if (buildingData && buildingData.length > 0 && buildingData[0]) { + // 선택된 건물 정보를 찾아서 설정 + const selectedBuildingInfo = buildingList.find(building => + building.bdstNm === buildingData[0].dmdCoNm + ); + setSelectedBuilding(selectedBuildingInfo || null); + console.log("선택된 시설:", selectedBuildingInfo); + } + }} + /> +
)} @@ -292,13 +374,14 @@ export function MapSearchBar({ { const selected = code.cityCd.find(item => (item.val === val)); setSelectedSido(selected || { text: "전체", val: '0' }); + setSelectedSigun({ text: "전체", val: '0' }); }} value={selectedSido.val} - selectClass={'w-32'} + selectClass={'w-44'} /> { const selected = filteredSigunData.find(item => (item.val === val)); setSelectedSigun(selected || { text: "전체", val: '0' }); + setSelectedDong({ text: "전체", val: '0', sggNm: '전체' }); }} value={selectedSigun.val} selectClass={'w-32'} @@ -329,9 +413,9 @@ export function MapSearchBar({
{/* SearchFormLeft 래퍼 */} ) : ( <> - onSelectValue(val.toString(), 'sido')} value={sidoValue} selectClass={'w-40'} /> - onSelectValue(val.toString(), 'sigun')} value={sigunValue} selectClass={'w-40'} /> + onSelectValue(val.toString(), 'sido')} value={sidoValue} selectClass={'w-44'} /> + onSelectValue(val.toString(), 'sigun')} value={sigunValue} selectClass={'w-44'} /> )}
diff --git a/apps/wr/app/map/newWindowPopup.tsx b/apps/wr/app/map/newWindowPopup.tsx new file mode 100644 index 000000000..e67b2b0fb --- /dev/null +++ b/apps/wr/app/map/newWindowPopup.tsx @@ -0,0 +1,380 @@ +import { useWrStore } from "@/app/store"; +import { btnClass } from "@/app/store/common/code"; +import type { customPbnst } from "@/app/store/ruseFcltMng/fcltReg/pbnstReg/type"; +import { excelDownload } from "@/components/lib/func/commom"; +import { Title, UiTable, UITab, ContentsDiv } from '@common/business_components'; +import { HeadMakeColSpan } from "@common/business_components/ui/table/HeadMakeColSpan"; +import { Button } from "@common/components/ui"; +import React, { useCallback, useState, useEffect } from 'react'; +import NewWindowPortal from '@/app/map/newWindowPortal'; +import PbnstPrfmncSttnPopup from "@/app/map/pbnstPrfmncSttnPopup"; +import { PbnstSttnModal } from "@/components/ui/modal/sttn/pbnstSttnModal/pbnstSttnModal"; +import type { WaterReuseFacility } from "@/app/store/ruseFcltMng/fcltSttn/pbnstSttn/type"; +import type { chgBuilding, customRainBdst } from "@/app/store/ruseFcltMng/fcltReg/rainReg"; +import RainPrfmncSttnPopup from "@/app/map/rainPrfmncSttnPopup"; +import RswtrPrfmncsSttnPopup from "@/app/map/rswtrPrfmncSttnPopup"; +import type { customRswtBdst } from "@/app/store/ruseFcltMng/fcltReg/rswtReg"; +import HtdflPrfmncSttnPopup from "@/app/map/htdflPrfmncSttnPopup"; +import type { customHtdflBdst } from "@/app/store/ruseFcltMng/fcltReg/htdflReg"; + +interface NewWindowPopupProps { + featureData: any; + checkedLayers?: string[]; + checkedLayersData?: any[]; + onClose: () => void; +} + +// 탭 +const TABS = [ + { val: "0", text: "재이용시설1" }, + { val: "1", text: "재이용시설2" }, + { val: "2", text: "재이용시설n" }, +]; + +// 데이터의 속성(key)을 사용자에게 보여줄 한글 이름으로 변환하기 위한 맵 +// 실제 데이터에 맞게 이 부분을 확장해야 합니다. +const propertyNameMap: { [key: string]: string } = { + fid: '고유 ID', + fcltNm: '시설명', + opertDe: '운영일자', + addr: '주소', + // ... 필요한 만큼 속성 매핑 추가 +}; + +const NewWindowPopup: React.FC = ({ + featureData, + checkedLayers = [], + checkedLayersData = [], + onClose +}) => { + const [activeTab, setActiveTab] = useState(TABS[0]?.val ?? "0"); + const [showDetailView, setShowDetailView] = useState(false); // 상세정보 화면 표시 상태 추가 + + // onClose 함수를 useCallback으로 메모이제이션 + const memoizedOnClose = useCallback(() => { + onClose(); + }, [onClose]); + + // 하수재이용시설 변수 + const { + code, + pbnstSttn, + pbnstSttnSearch, + pbnSttnInit, + getPbnStnDetail, + pbnNmSearch, + } = useWrStore((state) => state); + const [bdstTable, setBdstTable] = useState(); + const [pageNation, setPageNation] = useState(1); + const [pbnDetaileData, setPbnDetaileData] = useState<{ + isModal: boolean; + data: customPbnst; + }>({ isModal: false, data: {} as customPbnst }); + + // 빗물재이용시설 변수 + const { rainSttn, rainSttnSearch, rainSttnInit } = useWrStore( + (state) => state + ); + const { rainSttnRes } = rainSttn; + const [rainTable, setRainTable] = useState(); + const [rainBdstChgProps, setRainBdstChgProps] = useState<{ + isOpen: boolean; + chBuilding: chgBuilding[]; + }>({ isOpen: false, chBuilding: [] }); + const [rainDetailModal, setRainDetailModal] = useState<{ isOpen: boolean, data: customRainBdst | undefined }>({ data: undefined, isOpen: false }); + + // 중수도시설 변수 + const { rswtSttn, rswtSttnSearch, rswtSttnInit } = useWrStore( + (state) => state + ); + const { rswtSttnRes } = rswtSttn; + const [rswtrTable, setRswtrTable] = useState(); + const [rswtrDetailModal, setRswtrDetailModal] = useState<{ isOpen: boolean, data: customRswtBdst | undefined }>({ data: undefined, isOpen: false }); + const [rswtrBdstChgProps, setRswtrBdstChgProps] = useState<{ + isOpen: boolean; + chBuilding: chgBuilding[]; + }>({ isOpen: false, chBuilding: [] }); + + // 온배수시설 변수 + const { htdflSttn, htdflSttnSearch, initHtdfl } = useWrStore((state) => state); + const { htdflSttnRes } = htdflSttn; + const [htdflTable, setHtdflTable] = useState(); + const [htdflDetailModal, setHtdflDetailModal] = useState<{ isOpen: boolean, data: customHtdflBdst | undefined }>({ data: undefined, isOpen: false }); + const [bdstChgProps, setBdstChgProps] = useState<{ isOpen: boolean, chBuilding: chgBuilding[] }>({ isOpen: false, chBuilding: [] }); + + if (!featureData) { + return null; + } + + // 2. RuseMngTable에 필요한 tableData를 생성합니다. (단일 행 데이터) + const tableData = [featureData]; + console.log("tableData", tableData); + + // 물재이용시설 cellClick 이벤트 (상세정보) + const pbnstCellClick = (index: number | undefined) => { + if ( + index !== undefined && + pbnstSttn.pbnstSttnRes && + pbnstSttn.pbnstSttnRes[index] + ) { + const pbnstRuseFcltPk = pbnstSttn.pbnstSttnRes[index].pbnstRuseFcltPk; + getPbnStnDetail(pbnstRuseFcltPk); + } + }; + + useEffect(() => { + setBdstTable(pbnstSttn.pbnstSttnRes); + }, [pbnstSttn.pbnstSttnRes]); + + useEffect(() => { + if (pbnstSttn.pbnstDetail.pbnstRuseFcltPk) setShowDetailView(true); + }, [pbnstSttn.pbnstDetail]); + + + // 빗물재이용시설 cellClick 이벤트 (상세정보) + const rainCellClick = (index: number | undefined, headId: string) => { + if ( + index !== undefined && + rainTable !== undefined && + headId !== 'chBuildingYn') { + setRainDetailModal({ isOpen: true, data: rainTable[index] }) + } else if (headId === 'chBuildingYn' && index !== undefined && rainTable !== undefined) { + if (rainTable[index] && rainTable[index]?.chgBuilding[0]?.bdstChgNm !== null) + setRainBdstChgProps({ isOpen: true, chBuilding: rainTable[index].chgBuilding }); + } + } + + // 중수도시설 cellClick 이벤트 (상세정보) + const rswtCellClick = (index: number | undefined, headId: string) => { + if (headId !== 'chBuildingYn' && index !== undefined && rswtrTable !== undefined) { + setRswtrDetailModal({ isOpen: true, data: rswtrTable[index] }) + } else if (headId === 'chBuildingYn' && index !== undefined && rswtrTable !== undefined) { + if (rswtrTable[index] && rswtrTable[index]?.chBuildingYn === 'Y') { + setRswtrBdstChgProps({ isOpen: true, chBuilding: rswtrTable[index].chgBuilding }); + } + } + } + + // 온배수시설 cellClick 이벤트 (상세정보) + const htdflCellClick = (index: number | undefined, headId: string) => { + if ( + headId !== "chBuildingYn" && + index !== undefined && + htdflTable !== undefined + ) { + setHtdflDetailModal({ isOpen: true, data: htdflTable[index] }); + } + }; + + const excelClick = useCallback(() => { + // if (codeData) pbnstSttnSearch(1, "10000", codeData); + excelDownload( + [[]], + "하수재이용시설 현황", + "sample_pbnstSttn.xlsx", + "A1:U2" + ); + }, [pbnstSttnSearch]); + + // 체크된 레이어에 따른 조건부 컴포넌트 렌더링 함수 + const renderConditionalContent = () => { + // 특정 레이어가 체크되었는지 확인 + const hasWaterTreatmentLayer = checkedLayersData.some(layer => + layer.data?.lyrNm?.includes('하수처리') || layer.data?.lyrNm?.includes('재이용시설') + ); + + // 재이용시설 + const isPbnst = false; + // 빗물이용시설 + const isRain = false; + // 중수도시설 + const isRswt = false; + // 온배수시설 + const isHtdfl = true; + + + return ( +
+ {/* 체크된 레이어가 없는 경우 (재이용시설 목록) */} + {isPbnst && ( + <> + {showDetailView ? ( + // 재이용시설 상세 + <> +
+ + </div> + <div className="flex justify-between items-center mb-8"> + <UITab + defaultValue={TABS[0]?.val} + tabList={TABS.map((tab) => ({ + value: tab.val, + label: tab.text, + onClick: () => setActiveTab(tab.val), + }))} + children={undefined} + /> + </div> + <PbnstSttnModal + data={pbnDetaileData.data || ({} as customPbnst)} + titleVisible={false} + /> + <PbnstPrfmncSttnPopup isVisible={false} /> + </> + ) : ( + // 재이용시설 목록 + <> + <div className="flex justify-between items-center"> + <Title title='하수재이용시설 현황' /> + <div className="flex gap-2 mt-8"> + <Button className={btnClass} color="primary" onClick={excelClick}>엑셀다운로드</Button> + </div> + </div> + <UiTable + headName={[]} + tableData={bdstTable || []} + children={ + <HeadMakeColSpan upHeadList={pbnstSttn.pbnstSttNodeList} /> + } + headlist={pbnstSttn.pbnstSttHeadList} + pageSize={20} + total={100000} + cellClick={(index: number | undefined, headId: string) => { + pbnstCellClick(index); + }} + onPageChange={setPageNation} + /> + </> + )} + </> + )} + {/* 빗물이용시설 */} + {isRain && ( + <> + {showDetailView ? ( + // 빗물이용시설 상세 + <> + <div className="flex justify-between items-center mb-4"> + <Title title='빗물이용시설 상세정보' /> + </div> + <RainPrfmncSttnPopup rainForm={rainDetailModal.data as customRainBdst} isModal={false} /> + {/* TODO: 지도 점검 결과 현황 추가 할 것 */} + </> + ) : ( + // 빗물이용시설 목록 + <> + <div className="flex justify-between items-center"> + <Title title='빗물이용시설 현황' /> + <div className="flex gap-2 mt-8"> + <Button className={btnClass} color="primary" onClick={excelClick}>엑셀다운로드</Button> + </div> + </div> + <UiTable + headName={[]} + tableData={rainTable || []} + children={ + <HeadMakeColSpan upHeadList={rainSttn.rainSttNodeList} /> + } + headlist={rainSttn.rainSttHeadList} + pageSize={20} + total={100000} + cellClick={(index: number | undefined, headId: string) => { + rainCellClick(index ?? undefined, headId) + }} + onPageChange={setPageNation} + /> + </> + )} + </> + )} + {/* 중수도시설 */} + {isRswt && ( + <> + {showDetailView ? ( + // 재이용시설 상세 + <> + <div className="flex justify-between items-center mb-4"> + <Title title='중수도시설 상세정보' /> + </div> + <RswtrPrfmncsSttnPopup rswtForm={rswtrDetailModal.data as customRswtBdst} isModal={false} /> + {/* TODO: 지도 점검 결과 현황 추가 할 것 */} + </> + ) : ( + // 재이용시설 목록 + <> + <div className="flex justify-between items-center"> + <Title title='중수도시설 현황' /> + <div className="flex gap-2 mt-8"> + <Button className={btnClass} color="primary" onClick={excelClick}>엑셀다운로드</Button> + </div> + </div> + <UiTable + headName={[]} + tableData={rswtrTable || []} + children={ + <HeadMakeColSpan upHeadList={rswtSttn.rswtSttNodeList} /> + } + headlist={rswtSttn.rswtSttHeadList} + pageSize={20} + total={100000} + cellClick={(index: number | undefined, headId: string) => { + rswtCellClick(index, headId); + }} + onPageChange={setPageNation} + /> + </> + )} + </> + )} + {/* 온배수시설 */} + {isHtdfl && ( + <> + {showDetailView ? ( + // 재이용시설 상세 + <> + <div className="flex justify-between items-center mb-4"> + <Title title='온배수시설 상세정보' /> + </div> + {/* <HtdflPrfmncSttnPopup htdflForm={htdflDetailModal.data as customHtdflBdst} isModal={false} /> */} + <HtdflPrfmncSttnPopup htdflForm={htdflDetailModal.data as customHtdflBdst} isModal={false} /> + </> + ) : ( + // 재이용시설 목록 + <> + <div className="flex justify-between items-center"> + <Title title='온배수시설 현황' /> + <div className="flex gap-2 mt-8"> + <Button className={btnClass} color="primary" onClick={excelClick}>엑셀다운로드</Button> + </div> + </div> + <UiTable + headName={[]} + tableData={bdstTable || []} + children={ + <HeadMakeColSpan upHeadList={htdflSttn.htdflSttNodeList} /> + } + headlist={htdflSttn.htdflSttHeadList} + pageSize={20} + total={100000} + cellClick={(index: number | undefined, headId: string) => { + htdflCellClick(index ?? undefined, headId); + }} + onPageChange={setPageNation} + /> + </> + )} + </> + )} + </div> + ); + }; + + return ( + <NewWindowPortal onClose={memoizedOnClose} title="시설 상세 정보"> + {/* 체크된 레이어에 따른 조건부 렌더링 */} + {renderConditionalContent()} + </NewWindowPortal> + ); +}; + +export default NewWindowPopup; \ No newline at end of file diff --git a/apps/wr/app/map/newWindowPortal.tsx b/apps/wr/app/map/newWindowPortal.tsx new file mode 100644 index 000000000..49949e0f4 --- /dev/null +++ b/apps/wr/app/map/newWindowPortal.tsx @@ -0,0 +1,90 @@ +import React, { useState, useEffect, useRef, useCallback } from 'react'; +import { createPortal } from 'react-dom'; + +// 현재 문서의 스타일(CSS)을 새 창으로 복사하는 함수 +const copyStyles = (sourceDoc: Document, targetDoc: Document) => { + Array.from(sourceDoc.styleSheets).forEach(styleSheet => { + try { + if (styleSheet.cssRules) { // <style> 태그로 주입된 CSS + const newStyleEl = sourceDoc.createElement('style'); + Array.from(styleSheet.cssRules).forEach(cssRule => { + newStyleEl.appendChild(sourceDoc.createTextNode(cssRule.cssText)); + }); + targetDoc.head.appendChild(newStyleEl); + } else if (styleSheet.href) { // <link> 태그로 연결된 CSS 파일 + const newLinkEl = sourceDoc.createElement('link'); + newLinkEl.rel = 'stylesheet'; + newLinkEl.href = styleSheet.href; + targetDoc.head.appendChild(newLinkEl); + } + } catch (e) { + console.warn('스타일시트를 복사할 수 없습니다:', e); + } + }); +}; + +interface NewWindowPortalProps { + children: React.ReactNode; + onClose: () => void; + title?: string; +} + +const NewWindowPortal: React.FC<NewWindowPortalProps> = ({ children, onClose, title = "Detail" }) => { + const [container, setContainer] = useState<HTMLElement | null>(null); + const newWindowRef = useRef<Window | null>(null); + const onCloseRef = useRef(onClose); + + // onClose 함수를 ref로 저장하여 의존성 문제 해결 + useEffect(() => { + onCloseRef.current = onClose; + }, [onClose]); + + const handleUnload = useCallback(() => { + onCloseRef.current(); + }, []); + + useEffect(() => { + // 이미 창이 열려있으면 새로 열지 않음 + if (newWindowRef.current && !newWindowRef.current.closed) return; + + // 1. 새 창을 열고 참조를 저장합니다. + newWindowRef.current = window.open('', title, 'width=1400,height=1024,resizable=yes,scrollbars=yes'); + + if (newWindowRef.current) { + newWindowRef.current.document.title = title; + + // 2. React 컴포넌트를 마운트할 컨테이너 div를 생성하여 새 창의 body에 추가합니다. + const div = newWindowRef.current.document.createElement('div'); + newWindowRef.current.document.body.appendChild(div); + setContainer(div); + + // 3. 메인 창의 스타일을 새 창으로 복사합니다. (매우 중요) + copyStyles(document, newWindowRef.current.document); + + // 4. 사용자가 창을 닫을 때 부모에게 알리기 위한 이벤트 리스너를 설정합니다. + // const handleUnload = () => { + // onClose(); + // }; + newWindowRef.current.addEventListener('unload', handleUnload); + + // 5. 컴포넌트가 사라질 때 창을 닫는 클린업 함수를 반환합니다. + return () => { + if (newWindowRef.current) { + newWindowRef.current.removeEventListener('unload', handleUnload); + if (!newWindowRef.current.closed) { + newWindowRef.current.close(); + } + } + }; + } else { + alert('팝업이 차단되었습니다. 팝업 차단을 해제하고 다시 시도해주세요.'); + onCloseRef.current(); + // onClose(); + } + }, [onClose, title]); + + // `createPortal`을 사용해 children을 새 창의 container div에 렌더링합니다. + return container ? createPortal(children, container) : null; +}; + +export default NewWindowPortal; \ No newline at end of file diff --git a/apps/wr/app/map/pbnstPrfmncSttnPopup.tsx b/apps/wr/app/map/pbnstPrfmncSttnPopup.tsx new file mode 100644 index 000000000..f6d870092 --- /dev/null +++ b/apps/wr/app/map/pbnstPrfmncSttnPopup.tsx @@ -0,0 +1,596 @@ +"use client"; + +import { useWrStore } from "@/app/store"; +import { initValInUsage, initValOutUsage, subTotalObj, totalObj, type FcPrfType, type PrfType } from "@/app/store/prfmncReg/pbnstPrfmncReg/type"; +import { CHART_FORM, ContentsDiv, MainChart, ScrollArea, SelectBox, UiTable, type ChartConfig, type ReturnType } from "@common/business_components"; +import ListDiv from "@common/business_components/ui/list_div"; +import { useEffect, useRef, useState } from "react"; +import { Toaster } from "react-hot-toast"; + +interface PbnstPrfmncSttnPopupProps { + isVisible?: boolean; +} + +export default function PbnstPrfmncSttnPopup({ isVisible = true }: PbnstPrfmncSttnPopupProps) { + const { code, tableHeadMaker, pbnstPrfmncSttn, pbnstPrfmncInit, pbnstPrfmncSearch, prfmnc } = useWrStore((state) => state); + const { monthHeadList } = prfmnc + const { pbnstPrfHeadList, pbnstPrfmncRes, prfmncRes, rawData, selectYearList } = pbnstPrfmncSttn; + const [isChHead, setIsChHead] = useState(false); + const [fromDate, setFromDate] = useState<string>(String(new Date().getFullYear())); + const [toDate, setToDate] = useState<string>(String(new Date().getFullYear())); + const [year, setYear] = useState<string>(String(new Date().getFullYear())); + const [bdstTable, setbdstTable] = useState<FcPrfType[]>(); + const ref = useRef<ReturnType>({} as ReturnType); + const [swtrmtFcltNm, setSwtrmtFcltNm] = useState<string>(); + const [chartConfig, setChartConfig] = useState<ChartConfig>({}); + const [chartData, setChartData] = useState<dnChartForm[]>([]); + const [dnInUsage, setDnInUsage] = useState<dnChartForm[]>([]); + const [dnInChartConfig, setDnInChartConfig] = useState<ChartConfig>({}); + const [dnOutUsage, setDnOutUsage] = useState<dnChartForm[]>([]); + const [dnOutChartConfig, setDnOutChartConfig] = useState<ChartConfig>({}); + const [barData, setBarData] = useState<barChartForm[]>([]); + const [barChartConfig, setBarChartConfig] = useState<ChartConfig>({}); + const [prfData, setPrfData] = useState<any[]>(); + const [lnInChartConfig, setLnInChartConfig] = useState<ChartConfig>({}); + const [lnOutChartConfig, setLnOutChartConfig] = useState<ChartConfig>({}); + const [inLineData, setInLineData] = useState<{ + chartData1: lineChartDataProps[]; + chartData2: lineChartDataProps[]; + chartData3: lineChartDataProps[]; + chartData4: lineChartDataProps[]; + chartData5: lineChartDataProps[]; + chartData6: lineChartDataProps[]; + chartData7: lineChartDataProps[]; + chartData8: lineChartDataProps[]; + chartData9: lineChartDataProps[]; + }>(); + // const [lnInChartConfig, setLnInChartConfig] = useState<ChartConfig>({}); + const [outLineData, setOutLineData] = useState<{ + chartData1: lineChartDataProps[]; + chartData2: lineChartDataProps[]; + chartData3: lineChartDataProps[]; + chartData4: lineChartDataProps[]; + chartData5: lineChartDataProps[]; + chartData6: lineChartDataProps[]; + }>(); + + + + type dnChartForm = { label: string, val: number, fill?: string }; + type barChartForm = { label: string, "장내용수": number, "장외용수": number }; + type lineChartDataProps = { label: string, val: number, color: string }; + + const prfTableData = (prfList: PrfType[], year: string) => { + const inUsageData = initValInUsage; + const outUsageData = initValOutUsage; + + const inSubtotal = subTotalObj; + const outSubtotal = subTotalObj; + + console.log(' prfList ', prfList) + const prfFilter = prfList?.filter((e) => e.ym.slice(0, 4) === year) + if (prfList && prfFilter.length > 0) { + prfList?.forEach((prfItem) => { + const month = Number(prfItem?.ym?.slice(4)); // "MM" + const monthKey = `${month}월`; + + inUsageData.forEach((inData: any) => { + const usageKey = inData.usage; + const value = (prfItem as any)[usageKey] ?? 0; + + inData[monthKey] = value; + inData.total += value; + + if (inSubtotal && usageKey !== "subtotal") { + (inSubtotal as any)[monthKey] = + ((inSubtotal as any)[monthKey] ?? 0) + value; + inSubtotal.total += value; + } + }); + + outUsageData.forEach((outData: any) => { + const usageKey = outData.usage; + const value = (prfItem as any)[usageKey] ?? 0; + + outData[monthKey] = value; + outData.total += value; + + if (usageKey !== "subtotal") { + (outSubtotal as any)[monthKey] = + ((outSubtotal as any)[monthKey] ?? 0) + value; + outSubtotal.total += value; + } + }); + }); + inUsageData.push(inSubtotal); + outUsageData.push(outSubtotal); + const usageTotalObj: { [key: string]: any } = totalObj; + for (const key in usageTotalObj) { + if (["usage", "gubun", "edit"].includes(key)) continue; + const val = + (Number((inSubtotal as any)[key]) || 0) + + (Number((outSubtotal as any)[key]) || 0); + + (usageTotalObj as any)[key] = val; + } + const tableData = [...inUsageData, ...outUsageData, usageTotalObj]; + setPrfData(tableData); + + } else { + setPrfData([]); + } + } + useEffect(() => { + let wshwtr = 0; + let coolUswtr = 0; + let cleanUswtr = 0; + let drwtUswtr = 0; + let dltnUswtr = 0; + let ithlRswtr = 0; + let ithlEtcUswtr = 0; + let cleanToilUswtr = 0; + let resn = 0; + let lndwtr = 0; + let hydUswtr = 0; + let ugwtChr = 0; + let farmUswtr = 0; + let rvrKeepUswtr = 0; + let indstUswtr = 0; + if (rawData) { + rawData.forEach((item) => { + prfTableData(item.prfList, year); + item.prfList.filter((e) => e.ym.slice(0, 4) === year).forEach((element) => { + + wshwtr += element.wshwtr; //세척수 + coolUswtr += element.coolUswtr; //냉각수 + cleanUswtr += element.cleanUswtr; //청소수 + drwtUswtr += element.drwtUswtr; //식수대 + dltnUswtr += element.dltnUswtr //희석용수 + ithlRswtr += element.ithlRswtr //중수도 + ithlEtcUswtr += element.ithlEtcUswtr //기타 + cleanToilUswtr += element.cleanToilUswtr //청소화장실용수 + resn += element.resn //세척살수용수 + + lndwtr += element.lndwtr; //조경용수 + hydUswtr += element.hydUswtr; //친수용수 + ugwtChr += element.ugwtChr; //지하수충전 + farmUswtr += element.farmUswtr //농업용수 + rvrKeepUswtr += element.rvrKeepUswtr //하천등유지용수 + indstUswtr += element.indstUswtr //공업용수 + + }); + }) + const data: dnChartForm[] = [] + data?.push({ label: "세척수", val: wshwtr, fill: "red" }) + data?.push({ label: "냉각수", val: coolUswtr, fill: "orange" }) + data?.push({ label: "청소수", val: cleanUswtr, fill: "yellow" }) + data?.push({ label: "식수대", val: drwtUswtr, fill: "green" }) + data?.push({ label: "희석용수", val: dltnUswtr, fill: "skyblue" }) + data?.push({ label: "중수도", val: ithlRswtr, fill: "blue" }) + data?.push({ label: "기타", val: ithlEtcUswtr, fill: "purple" }) + data?.push({ label: "청소화장실용수", val: cleanToilUswtr, fill: "red" }) + data?.push({ label: "세척살수용수", val: resn, fill: "orange" }) + data?.push({ label: "조경용수", val: lndwtr, fill: "yellow" }) + data?.push({ label: "친수용수", val: hydUswtr, fill: "green" }) + data?.push({ label: "지하수충전", val: ugwtChr, fill: "skyblue" }) + data?.push({ label: "농업용수", val: farmUswtr, fill: "blue" }) + data?.push({ label: "하천등유지용수", val: rvrKeepUswtr, fill: "purple" }) + data?.push({ label: "공업용수", val: indstUswtr, fill: "black" }) + setChartData(data); + } + }, [rawData, year]) + + + useEffect(() => { + const newChartConfig = chartData.reduce((config, element) => { + config[element.label] = { + label: element.label, + fill: element.fill + }; + return config; + }, {} as Record<string, { label: string; fill?: string }>) satisfies ChartConfig; + setChartConfig(newChartConfig); + }, [chartData]); + + // 장내용수 도넛차트 + useEffect(() => { + let wshwtr = 0; + let coolUswtr = 0; + let cleanUswtr = 0; + let drwtUswtr = 0; + let dltnUswtr = 0; + let ithlRswtr = 0; + let ithlEtcUswtr = 0; + let cleanToilUswtr = 0; + let resn = 0; + if (rawData) { + rawData.forEach((item) => { + item.prfList.filter((e) => e.ym.slice(0, 4) === year).forEach((element) => { + + wshwtr += element.wshwtr; //세척수 + coolUswtr += element.coolUswtr; //냉각수 + cleanUswtr += element.cleanUswtr; //청소수 + drwtUswtr += element.drwtUswtr; //식수대 + dltnUswtr += element.dltnUswtr //희석용수 + ithlRswtr += element.ithlRswtr //중수도 + ithlEtcUswtr += element.ithlEtcUswtr //기타 + cleanToilUswtr += element.cleanToilUswtr //청소화장실용수 + resn += element.resn //세척살수용수 + }); + }) + const data: dnChartForm[] = [] + data?.push({ label: "세척수", val: wshwtr, fill: "yellow" }) + data?.push({ label: "냉각수", val: coolUswtr, fill: "green" }) + data?.push({ label: "청소수", val: cleanUswtr, fill: "yellow" }) + data?.push({ label: "식수대", val: drwtUswtr, fill: "green" }) + data?.push({ label: "희석용수", val: dltnUswtr, fill: "yellow" }) + data?.push({ label: "중수도", val: ithlRswtr, fill: "green" }) + data?.push({ label: "기타", val: ithlEtcUswtr, fill: "yellow" }) + data?.push({ label: "청소화장실용수", val: cleanToilUswtr, fill: "green" }) + data?.push({ label: "세척살수용수", val: resn, fill: "yellow" }) + setDnInUsage(data); + } + }, [rawData, year]) + + useEffect(() => { + const newChartConfig = dnInUsage.reduce((config, element) => { + config[element.label] = { + label: element.label, + fill: element.fill + }; + return config; + }, {} as Record<string, { label: string; color: string; }>) satisfies ChartConfig; + setDnInChartConfig(newChartConfig); + }, [dnInUsage]); + + // 장외용수 도넛차트 + useEffect(() => { + let lndwtr = 0; + let hydUswtr = 0; + let ugwtChr = 0; + let farmUswtr = 0; + let rvrKeepUswtr = 0; + let indstUswtr = 0; + if (rawData) { + rawData.forEach((item) => { + item.prfList.filter((e) => e.ym.slice(0, 4) === year).forEach((element) => { + lndwtr += element.lndwtr; //조경용수 + hydUswtr += element.hydUswtr; //친수용수 + ugwtChr += element.ugwtChr; //지하수충전 + farmUswtr += element.farmUswtr //농업용수 + rvrKeepUswtr += element.rvrKeepUswtr //하천등유지용수 + indstUswtr += element.indstUswtr //공업용수 + + }); + }) + const data: dnChartForm[] = [] + data?.push({ label: "조경용수", val: lndwtr, fill: "yellow" }) + data?.push({ label: "친수용수", val: hydUswtr, fill: "black" }) + data?.push({ label: "지하수충전", val: ugwtChr, fill: "yellow" }) + data?.push({ label: "농업용수", val: farmUswtr, fill: "black" }) + data?.push({ label: "하천등유지용수", val: rvrKeepUswtr, fill: "yellow" }) + data?.push({ label: "공업용수", val: indstUswtr, fill: "black" }) + setDnOutUsage(data); + } + }, [rawData, year]) + + useEffect(() => { + const newChartConfig = dnOutUsage.reduce((config, element) => { + config[element.label] = { + label: element.label, + fill: element.fill + }; + return config; + }, {} as Record<string, { label: string; color: string; }>) satisfies ChartConfig; + setDnOutChartConfig(newChartConfig); + }, [dnOutUsage]); + + //월별 장내외 용수 총 실적 막대차트 + useEffect(() => { + let wshwtr = 0; + let coolUswtr = 0; + let cleanUswtr = 0; + let drwtUswtr = 0; + let dltnUswtr = 0; + let ithlRswtr = 0; + let ithlEtcUswtr = 0; + let cleanToilUswtr = 0; + let resn = 0; + let lndwtr = 0; + let hydUswtr = 0; + let ugwtChr = 0; + let farmUswtr = 0; + let rvrKeepUswtr = 0; + let indstUswtr = 0; + let inUsageSum = 0; + let outUsageSum = 0; + let inUsageArr = []; + let outUsageArr = []; + + + if (rawData) { + const data: barChartForm[] = [] + rawData.forEach((item) => { + item.prfList + .filter((e) => e.ym.slice(0, 4) === year).forEach((element) => { + + wshwtr = element.wshwtr; //세척수 + coolUswtr = element.coolUswtr; //냉각수 + cleanUswtr = element.cleanUswtr; //청소수 + drwtUswtr = element.drwtUswtr; //식수대 + dltnUswtr = element.dltnUswtr //희석용수 + ithlRswtr = element.ithlRswtr //중수도 + ithlEtcUswtr = element.ithlEtcUswtr //기타 + cleanToilUswtr = element.cleanToilUswtr //청소화장실용수 + resn = element.resn //세척살수용수 + + lndwtr = element.lndwtr; //조경용수 + hydUswtr = element.hydUswtr; //친수용수 + ugwtChr = element.ugwtChr; //지하수충전 + farmUswtr = element.farmUswtr //농업용수 + rvrKeepUswtr = element.rvrKeepUswtr //하천등유지용수 + indstUswtr = element.indstUswtr //공업용수 + + inUsageSum = wshwtr + coolUswtr + cleanUswtr + drwtUswtr + dltnUswtr + ithlRswtr + ithlEtcUswtr + cleanToilUswtr + resn; + inUsageArr.push(inUsageSum) + outUsageSum = lndwtr + hydUswtr + ugwtChr + farmUswtr + rvrKeepUswtr + indstUswtr; + outUsageArr.push(outUsageSum) + + const month = Number(element.ym.slice(4, 6)) + data.push({ label: `${month}월`, "장내용수": inUsageSum, "장외용수": outUsageSum }) + + }, + ); + + }) + + setBarData(data); + } + }, [rawData, year]) + + useEffect(() => { + const newChartConfig = barData.reduce((config, element) => { + config[element.label] = { + label: element.label, + }; + return config; + + }, {} as Record<string, { label: string }>) satisfies ChartConfig; + setBarChartConfig(newChartConfig); + }, [barData]); + + //월별 장내 용수 실적 라인차트 + useEffect(() => { + + if (rawData) { + const wshwtrData: lineChartDataProps[] = [] + const coolUswtrData: lineChartDataProps[] = [] + const cleanUswtrData: lineChartDataProps[] = [] + const drwtUswtrData: lineChartDataProps[] = [] + const dltnUswtrData: lineChartDataProps[] = [] + const ithlRswtrData: lineChartDataProps[] = [] + const ithlEtcUswtrData: lineChartDataProps[] = [] + const cleanToilUswtrData: lineChartDataProps[] = [] + const resnData: lineChartDataProps[] = [] + rawData.forEach((item) => { + item.prfList.filter((e) => e.ym.slice(0, 4) === year).forEach((element) => { + const month = Number(element.ym.slice(4, 6)) + wshwtrData.push({ label: `${month}월`, val: element.wshwtr, color: "red" }) + coolUswtrData.push({ label: `${month}월`, val: element.coolUswtr, color: "red" }) + cleanUswtrData.push({ label: `${month}월`, val: element.cleanUswtr, color: "red" }) + drwtUswtrData.push({ label: `${month}월`, val: element.drwtUswtr, color: "red" }) + dltnUswtrData.push({ label: `${month}월`, val: element.dltnUswtr, color: "red" }) + ithlRswtrData.push({ label: `${month}월`, val: element.ithlRswtr, color: "red" }) + ithlEtcUswtrData.push({ label: `${month}월`, val: element.ithlEtcUswtr, color: "red" }) + cleanToilUswtrData.push({ label: `${month}월`, val: element.cleanToilUswtr, color: "red" }) + resnData.push({ label: `${month}월`, val: element.resn, color: "red" }) + + }, + ); + }) + const rtndata = { + chartData1: wshwtrData, + chartData2: coolUswtrData, + chartData3: cleanUswtrData, + chartData4: drwtUswtrData, + chartData5: dltnUswtrData, + chartData6: ithlRswtrData, + chartData7: ithlEtcUswtrData, + chartData8: cleanToilUswtrData, + chartData9: resnData + } + + setInLineData(rtndata) + } + }, [rawData, year]) + + useEffect(() => { + if (inLineData?.chartData1) { + const newChartConfig = { + chartData1: { + label: '세척수', + color: "red", + }, + chartData2: { + label: '냉각수', + color: "orange", + }, + chartData3: { + label: '청소수', + color: "yellow", + }, + chartData4: { + label: '식수대', + color: "pink", + }, + chartData5: { + label: '희석용수', + color: "purple", + }, + chartData6: { + label: '중수도', + color: "green", + }, + chartData7: { + label: '기타', + color: "blue", + }, + chartData8: { + label: '청소화장실용수', + color: "magenta", + }, + chartData9: { + label: '세척살수용수', + color: "skyblue", + }, + } satisfies ChartConfig; + setLnInChartConfig(newChartConfig); + } + }, [inLineData]); + + //월별 장외 용수 실적 라인차트 + useEffect(() => { + if (rawData) { + const lndwtrData: lineChartDataProps[] = [] + const hydUswtrData: lineChartDataProps[] = [] + const ugwtChrData: lineChartDataProps[] = [] + const farmUswtrData: lineChartDataProps[] = [] + const rvrKeepUswtrData: lineChartDataProps[] = [] + const indstUswtrData: lineChartDataProps[] = [] + rawData.forEach((item) => { + item.prfList.filter((e) => e.ym.slice(0, 4) === year).forEach((element) => { + const month = Number(element.ym.slice(4, 6)) + lndwtrData.push({ label: `${month}월`, val: element.lndwtr, color: "red" }) + hydUswtrData.push({ label: `${month}월`, val: element.hydUswtr, color: "red" }) + ugwtChrData.push({ label: `${month}월`, val: element.ugwtChr, color: "red" }) + farmUswtrData.push({ label: `${month}월`, val: element.farmUswtr, color: "red" }) + rvrKeepUswtrData.push({ label: `${month}월`, val: element.rvrKeepUswtr, color: "red" }) + indstUswtrData.push({ label: `${month}월`, val: element.indstUswtr, color: "red" }) + }, + ); + }) + setOutLineData({ + chartData1: lndwtrData, + chartData2: hydUswtrData, + chartData3: ugwtChrData, + chartData4: farmUswtrData, + chartData5: rvrKeepUswtrData, + chartData6: indstUswtrData, + }) + } + }, [rawData, year]) + + useEffect(() => { + if (outLineData?.chartData1) { + const newChartConfig = { + chartData1: { + label: '조경용수', + color: "red", + }, + chartData2: { + label: '친수용수', + color: "orange", + }, + chartData3: { + label: '지하수충전', + color: "yellow", + }, + chartData4: { + label: '농업용수', + color: "green", + }, + chartData5: { + label: '하천등유지용수', + color: "blue", + }, + chartData6: { + label: '공업용수', + color: "purple", + }, + } satisfies ChartConfig; + setLnOutChartConfig(newChartConfig); + } + }, [outLineData]); + + useEffect(() => { + if (!pbnstPrfmncSttn.isInit) { + pbnstPrfmncInit("1", "11"); + } + }, [pbnstPrfmncSttn.isInit, pbnstPrfmncInit]); + + useEffect(() => { + setbdstTable(prfmncRes); + }, [prfmncRes]); + + return ( + <> + <ContentsDiv> + <ListDiv> + <hr /> + <h3 className="mt-4 ml-2 text-2xl font-medium text-label">하수재이용실적 상세현황</h3> + + <SelectBox + className="w-1/12 text-2xl mt-4 ml-2" + label="연도" + selectClass={""} + selectArray={selectYearList} + value={year} + onSelectValue={(val: string | number) => setYear(String(val))} /> + <h1 className="mt-4 ml-2">장내·외 용도별 하수재이용실적 현황(계)</h1> + + <div className="flex"> + <div className="w-1/3"> + <MainChart chartConfig={chartConfig} chartData={chartData} chartTitle={"장내·외 용수 총 실적"} chartForm={CHART_FORM.DN} /> + </div> + <div className="w-1/3"> + <MainChart chartConfig={dnInChartConfig} chartData={dnInUsage} chartTitle={"장내용수 실적"} chartForm={CHART_FORM.DN} /> + </div> + <div className="w-1/3"> + <MainChart chartConfig={dnOutChartConfig} chartData={dnOutUsage} chartTitle={"장외용수 실적"} chartForm={CHART_FORM.DN} /> + </div> + </div> + <h1 className="">장내·외 월별 하수재이용실적 현황</h1> + <div className="flex"> + <div className="w-1/3"> + <MainChart chartConfig={barChartConfig} chartData={barData} chartTitle={"월별 장내·외 용수 총 실적"} chartForm={CHART_FORM.DOUBLEBAR} /> + </div> + <div className="w-1/3"> + <MainChart chartConfig={lnInChartConfig} chartData={inLineData} chartTitle={"월별 장내 용수 실적"} chartForm={CHART_FORM.LINE} /> + </div> + <div className="w-1/3"> + <MainChart chartConfig={lnOutChartConfig} chartData={outLineData} chartTitle={"월별 장외 용수 총 실적"} chartForm={CHART_FORM.LINE} /> + </div> + </div> + {/* 하수재이용실적 현황 - 조건부 렌더링 */} + {isVisible && ( + <> + <h1 className="">하수재이용실적 현황</h1> + <ListDiv className="col-span-8 pr-6 "> + <ScrollArea className="h-[60vh]"> + <div className="card-header"> + <h3 className="text-2xl font-medium text-label"> + {"하수처리수재이용 실적(m3) 등록결과"} + </h3> + </div> + + <UiTable + headName={undefined} + tableData={prfData || []} + headlist={monthHeadList} + pageSize={0} + total={0} + cellClick={undefined} + onPageChange={function (page: number): void { + throw new Error("Function not implemented."); + }}> + </UiTable> + </ScrollArea> + </ListDiv> + </> + )} + </ListDiv> + </ContentsDiv> + <Toaster position="top-center" /> + </> + ) +} diff --git a/apps/wr/app/map/rainPrfmncSttnPopup.tsx b/apps/wr/app/map/rainPrfmncSttnPopup.tsx new file mode 100644 index 000000000..18cdac8e6 --- /dev/null +++ b/apps/wr/app/map/rainPrfmncSttnPopup.tsx @@ -0,0 +1,421 @@ +"use client"; + +import { useWrStore } from "@/app/store"; +import { + rainZod, + selectClass, + TEST_UUID, + type customRainBdst, +} from "@/app/store/ruseFcltMng/fcltReg/rainReg"; +import { DatePicker, FormUi } from "@common/business_components"; +import { Form, FormField, SelectBox } from "@common/business_components/ui"; +import { Button, Input } from "@common/components/ui"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { useEffect, useState } from "react"; +import { useForm, useWatch, type UseFormReturn } from "react-hook-form"; +import { z } from "zod"; +import BdstReg from "@/components/ui/form/fcltReg/bdstReg/bdstReg"; +import { + formatCommaNumber, + handleCommaNumberChange, +} from "@/components/lib/func/commom"; +import { formH1Class, formH2Class } from "@/components/lib/class/comClass"; +import { Label } from "@common/business_components/ui/form/lib/label"; + +export default function RainPrfmncSttnPopup({ + rainForm, + isModal, +}: { + rainForm: customRainBdst; + isModal: boolean; +}) { + const { code, rainBdstReg, rainBdstUpdate, rain } = useWrStore( + (state) => state + ); + const { rainFcltCd } = code; + const defaultValues: z.infer<typeof rainZod> = rainZod.parse({}); + + const form = useForm<z.infer<typeof rainZod>>({ + resolver: zodResolver(rainZod), + defaultValues: defaultValues, + }); + + const [grntchBdstCertYn, instlCstSprtYn, filtrTrtmfctYn] = form.watch([ + "reuseBuilding.0.grntchBdstCertYn", + "reuseBuilding.0.instlCstSprtYn", + "reuseBuilding.0.rainyObj.filtrTrtmfctYn", + ]); + + const cleanRainData = (raw: any): z.infer<typeof rainZod> => { + return { + ...raw, + frstArchPrmsnDt: new Date(raw?.frstArchPrmsnDt || null), + archChgPrmsnDt: raw?.archChgPrmsnDt + ? new Date(raw?.archChgPrmsnDt) + : null, + reuseBuilding: (raw?.reuseBuilding || []).map((item: any) => ({ + ...item, + instlCmptnDt: new Date(item?.instlCmptnDt), + operMngCd: item?.operMngCd ?? "", // null → "" + grntchBdstCertYr: + typeof item?.grntchBdstCertYr === "string" + ? item?.grntchBdstCertYr + : "", // null → "" + rainObj: { + ...item?.rainObj, + }, + })), + }; + }; + + useEffect(() => { + if (grntchBdstCertYn === "N") { + form.setValue("reuseBuilding.0.grntchBdstCertYr", ""); + form.setValue("reuseBuilding.0.grntchBdstCertNo", ""); + form.setValue("reuseBuilding.0.grntchBdstCertGrd", ""); + } else if (instlCstSprtYn === "N") { + form.setValue("reuseBuilding.0.instlCstSprtAmt", 0); + } else if (filtrTrtmfctYn === "N") { + form.setValue("reuseBuilding.0.rainyObj.filtrTrtmfctCpct", 0); + form.setValue("reuseBuilding.0.rainyObj.filtrTrtmfctPrs", ""); + } + }, [grntchBdstCertYn, instlCstSprtYn, filtrTrtmfctYn]); + + // form 값이 변경될 때마다 로그 출력 + useEffect(() => { + const subscription = form.watch((value) => { + }); + + + return () => subscription.unsubscribe(); + }, [form]); + + useEffect(() => { + console.log("form errors:", form.formState.errors); + }, [form.formState.errors]); + + const onSubmit = (values: z.infer<typeof rainZod>) => { + if (isModal) { + rainBdstReg(values, "41"); + } else { + rainBdstUpdate(values, "41"); + } + }; + + useEffect(() => { + if (rainZod) form.reset(cleanRainData(rainForm)); + }, [rainForm]); + + + return ( + <div className="!mx-[1.6rem] !p-0r"> + <Form {...form}> + <form onSubmit={form.handleSubmit(onSubmit)}> + <div> + <div className="grid grid-cols-12 flex items-center card-header"> + <h1 className="text-2xl font-medium text-label col-span-10"> + {isModal ? "건축물 운영정보" : "빗물이용시설 정보"} + </h1> + </div> + </div> + {isModal && rainForm != undefined ? <BdstReg form={form} /> : <></>} + {isModal ? ( + <h1 className="pl-3 text-xl col-span-10 mt-4"> + 빗물이용시설 정보{" "} + </h1> + ) : ( + <></> + )} + <h1 className={formH1Class}> + 기본정보 + </h1> + + <div className="grid grid-cols-4 gap-2 mt-4"> + <div className="col-span-2"> + <FormField + control={form.control} + name="reuseBuilding.0.instlCmptnDt" + render={({ field }) => ( + <FormUi label="설치완료일"> + <DatePicker + mode="single" + field={{ + value: + field.value && !isNaN(new Date(field.value).getTime()) + ? new Date(field.value) + : new Date(), // Provide a default Date value + onChange: (date) => field.onChange(date), + }} + selectClass={"w-full"} + readOnly={true} + /> + </FormUi> + )} + /> + </div> + + <div className="col-span-2"> + <FormField + control={form.control} + name="reuseBuilding.0.instlCst" + render={({ field }) => ( + <FormUi label="설치비"> + <Input + className={'w-full !text-xl'} + placeholder="1,000,000" + type="text" + onChange={(e) => { + handleCommaNumberChange(e, field.onChange); + }} + value={formatCommaNumber(field.value)} + readOnly={true} + /> + </FormUi> + )} + /> + </div> + </div> + <div className=""> + <h1 className={formH1Class}> + 빗물이용시설 정보 + </h1> + </div> + <div> + <h2 className={formH2Class}>집수면</h2> + </div> + <div className="grid grid-cols-10 gap-4 mt-4 "> + <div className="col-span-5"> + <FormField + control={form.control} + name="reuseBuilding.0.rainyObj.pntfColKnd" + render={({ field }) => ( + <FormUi label="종류"> + <SelectBox + className={""} + selectClass={selectClass} + label={""} + selectArray={rainFcltCd} + onSelectValue={field.onChange} + value={field.value ?? rainFcltCd[0]?.val} + readOnly={true} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-5"> + <FormField + control={form.control} + name="reuseBuilding.0.rainyObj.pntfColArea" + render={({ field }) => ( + <FormUi label="면적(m³)"> + <Input + className={'w-full !text-xl'} + placeholder="1,000,000" + type="text" + onChange={(e) => { + handleCommaNumberChange(e, field.onChange); + }} + value={formatCommaNumber(field.value)} + /> + </FormUi> + )} + /> + </div> + </div> + + <div> + <h2 className={formH2Class}>여과 등 처리시설</h2> + </div> + <div className="grid grid-cols-12 gap-2 mt-4"> + <div className="col-span-4"> + <FormField + control={form.control} + name="reuseBuilding.0.rainyObj.filtrTrtmfctYn" + render={({ field }) => ( + <FormUi label="유무"> + <SelectBox + className={""} + selectClass={selectClass} + label={""} + selectArray={code.yn} + onSelectValue={field.onChange} + value={field.value ?? code.yn[0]?.val} + readOnly={true} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-4"> + <FormField + control={form.control} + name="reuseBuilding.0.rainyObj.filtrTrtmfctCpct" + render={({ field }) => ( + <FormUi label="용량(m³/일)"> + <Input + className={'w-full !text-xl'} + placeholder="1,000,000" + type="text" + onChange={(e) => { + handleCommaNumberChange(e, field.onChange); + }} + value={formatCommaNumber(field.value)} + readOnly={filtrTrtmfctYn !== "Y"} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-4"> + <FormField + control={form.control} + name="reuseBuilding.0.rainyObj.filtrTrtmfctPrs" + render={({ field }) => ( + <FormUi label="처리공정"> + <Input + className={'w-full !text-xl'} + placeholder="1,000,000" + type="" + value={field.value ?? ""} + onChange={field.onChange} + readOnly={filtrTrtmfctYn !== "Y"} + /> + </FormUi> + )} + /> + </div> + </div> + <div className=" my-2"> + <h1 className={formH1Class}> + 인센티브 정보 + </h1> + </div> + <div> + <h2 className={formH2Class}>설치비 지원</h2> + </div> + <div className="grid grid-cols-10 gap-2 mt-4"> + <div className="col-span-5"> + <FormField + control={form.control} + name="reuseBuilding.0.instlCstSprtYn" + render={({ field }) => ( + <FormUi label="지원 여부"> + <SelectBox + className={""} + selectClass={selectClass} + label={""} + selectArray={code.yn} + onSelectValue={field.onChange} + value={field.value ?? code.yn[0]?.val} + readOnly={true} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-5"> + <FormField + control={form.control} + name="reuseBuilding.0.instlCstSprtAmt" + render={({ field }) => ( + <FormUi label="지원금액(원)"> + <Input + className={'w-full !text-xl'} + placeholder="1,000,000" + type="text" + onChange={(e) => { + handleCommaNumberChange(e, field.onChange); + }} + value={formatCommaNumber(field.value)} + readOnly={instlCstSprtYn !== "Y"} + /> + </FormUi> + )} + /> + </div> + </div> + + <div> + <h2 className={formH2Class}>녹색건축물 인증</h2> + </div> + <div className="grid grid-cols-6 gap-2 mt-4"> + <div className="col-span-3"> + <FormField + control={form.control} + name="reuseBuilding.0.grntchBdstCertYn" + render={({ field }) => ( + <FormUi label="인증여부"> + <SelectBox + className={""} + selectClass={selectClass} + label={""} + selectArray={code.yn} + onSelectValue={field.onChange} + value={field.value ?? code.yn[0]?.val} + readOnly={true} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-3"> + <FormField + control={form.control} + name="reuseBuilding.0.grntchBdstCertYr" + render={({ field }) => ( + <FormUi label="인증년도"> + <Input + className={'w-full !text-xl'} + placeholder="2024" + type="" + value={field.value ?? ""} + onChange={field.onChange} + readOnly={grntchBdstCertYn !== "Y"} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-3 "> + <FormField + control={form.control} + name="reuseBuilding.0.grntchBdstCertNo" + render={({ field }) => ( + <FormUi label="인증번호"> + <Input + className={'w-full !text-xl'} + placeholder="ab1234678" + type="" + value={field.value ?? ""} + onChange={field.onChange} + readOnly={grntchBdstCertYn !== "Y"} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-3"> + <FormField + control={form.control} + name="reuseBuilding.0.grntchBdstCertGrd" + render={({ field }) => ( + <FormUi label="인증등급"> + <Input + className={'w-full !text-xl'} + placeholder="A" + type="" + value={field.value ?? ""} + onChange={field.onChange} + readOnly={grntchBdstCertYn !== "Y"} + /> + </FormUi> + )} + /> + </div> + </div> + </form> + </Form> + </div> + ); +} diff --git a/apps/wr/app/map/rswtrPrfmncSttnPopup.tsx b/apps/wr/app/map/rswtrPrfmncSttnPopup.tsx new file mode 100644 index 000000000..e7f94a3b5 --- /dev/null +++ b/apps/wr/app/map/rswtrPrfmncSttnPopup.tsx @@ -0,0 +1,404 @@ +"use client"; + +import { + DatePicker, + Form, + FormField, + FormUi, + SelectBox, +} from "@common/business_components/ui"; +import { Button, Input } from "@common/components/ui"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { useForm } from "react-hook-form"; +import { z } from "zod"; +import { useWrStore } from "@/app/store"; +import { selectClass } from "@/app/store/ruseFcltMng/fcltReg/rainReg"; +import { + rswtZod, + type customRswtBdst, +} from "@/app/store/ruseFcltMng/fcltReg/rswtReg"; +import { useEffect } from "react"; +import BdstReg from "@/components/ui/form/fcltReg/bdstReg/bdstReg"; +import { + formatCommaNumber, + handleCommaNumberChange, +} from "@/components/lib/func/commom"; +import { formH1Class, formH2Class } from "@/components/lib/class/comClass"; +import { Label } from "@common/business_components/ui/form/lib/label"; + +export default function RswtrPrfmncsSttnPopup({ + rswtForm, + isModal, +}: { + rswtForm: customRswtBdst; + isModal: boolean; +}) { + const { rswt, code, rswtBdstUpdate, rswtBdstReg } = useWrStore( + (state) => state + ); + + const defaultValues: z.infer<typeof rswtZod> = rswtZod.parse({}); + + const form = useForm<z.infer<typeof rswtZod>>({ + resolver: zodResolver(rswtZod), + defaultValues: defaultValues, + }); + + const [grntchBdstCertYn, instlCstSprtYn, filtrTrtmfctProcs] = form.watch([ + "reuseBuilding.0.grntchBdstCertYn", + "reuseBuilding.0.instlCstSprtYn", + "reuseBuilding.0.rswtrObj.filtrTrtmfctProcs", + ]); + + const onSubmit = (values: z.infer<typeof rswtZod>) => { + if (isModal) { + rswtBdstReg(values, "41"); + } else { + rswtBdstUpdate(values, "41"); + } + }; + const cleanRswtData = (raw: any): z.infer<typeof rswtZod> => { + return { + ...raw, + frstArchPrmsnDt: new Date(raw?.frstArchPrmsnDt || null), + archChgPrmsnDt: raw?.archChgPrmsnDt + ? new Date(raw?.archChgPrmsnDt) + : null, + reuseBuilding: (raw?.reuseBuilding || []).map((item: any) => ({ + ...item, + instlCmptnDt: new Date(item?.instlCmptnDt), + operMngCd: item?.operMngCd ?? "", // null → "" + grntchBdstCertYr: + typeof item?.grntchBdstCertYr === "string" + ? item?.grntchBdstCertYr + : "", // null → "" + rswtrObj: { + ...item?.rswtrObj, + }, + })), + }; + }; + + useEffect(() => { + console.log("form errors:", form.formState.errors); + }, [form.formState.errors]); + + useEffect(() => { + console.log("filtrTrtmfctProcs ", filtrTrtmfctProcs); + if (grntchBdstCertYn === "N") { + form.setValue("reuseBuilding.0.grntchBdstCertYr", ""); + form.setValue("reuseBuilding.0.grntchBdstCertNo", ""); + form.setValue("reuseBuilding.0.grntchBdstCertGrd", ""); + } else if (instlCstSprtYn === "N") { + form.setValue("reuseBuilding.0.instlCstSprtAmt", 0); + } + }, [grntchBdstCertYn, instlCstSprtYn, filtrTrtmfctProcs]); + + useEffect(() => { + if (rswtZod) form.reset(cleanRswtData(rswtForm)); + }, [rswtForm]); + + return ( + <div className="!mx-[1.6rem] !p-0r"> + <Form {...form}> + <form onSubmit={form.handleSubmit(onSubmit)}> + {/* <div className="flex justify-between items-center mx-3"> */} + <div> + <div className="grid grid-cols-12 flex items-center card-header"> + <h1 className="text-2xl font-medium text-label col-span-10"> + 건축물 운영정보 + </h1> + </div> + </div> + + {isModal && rswtForm != undefined ? <BdstReg form={form} /> : <></>} + {isModal ? ( + <h1 className="pl-3 text-xl col-span-10 mt-4 "> + 중수도 시설 정보{" "} + </h1> + ) : ( + <></> + )} + <h1 className={formH1Class}>기본정보</h1> + <div className="grid grid-cols-4 gap-2 mt-4"> + <div className="col-span-2"> + <FormField + control={form.control} + name="reuseBuilding.0.instlCmptnDt" + render={({ field }) => ( + <FormUi label="설치완료일"> + <DatePicker + mode="single" + field={{ + value: + field.value && !isNaN(new Date(field.value).getTime()) + ? new Date(field.value) + : new Date(), // Provide a default Date value + onChange: (date) => field.onChange(date), + }} + selectClass={"w-full"} + readOnly={true} + /> + </FormUi> + )} + /> + </div> + + <div className="col-span-2"> + <FormField + control={form.control} + name="reuseBuilding.0.instlCst" + render={({ field }) => ( + <FormUi label="설치비"> + <Input + className={'w-full !text-xl'} + placeholder="1,000,000" + type="text" + onChange={(e) => { + handleCommaNumberChange(e, field.onChange); + }} + value={formatCommaNumber(field.value)} + readOnly={true} + /> + </FormUi> + )} + /> + </div> + </div> + <div className="grid grid-cols-4 gap-2 mt-4"> + <div className="col-span-2"> + <FormField + control={form.control} + name="reuseBuilding.0.rswtrObj.wtrUsqty" + render={({ field }) => ( + <FormUi label="물사용량(m³)"> + <Input + className={'w-full !text-xl'} + placeholder="1,000,000" + type="text" + onChange={(e) => { + handleCommaNumberChange(e, field.onChange); + }} + value={formatCommaNumber(field.value)} + readOnly={true} + /> + </FormUi> + )} + /> + </div> + + <div className="col-span-2"> + <FormField + control={form.control} + name="reuseBuilding.0.rswtrObj.oblgTrgtWtrUsqty" + render={({ field }) => ( + <FormUi label="의무대상 물사용량(m³)"> + <Input + className={'w-full !text-xl'} + placeholder="1,000,000" + type="text" + onChange={(e) => { + handleCommaNumberChange(e, field.onChange); + }} + value={formatCommaNumber(field.value)} + readOnly={true} + /> + </FormUi> + )} + /> + </div> + </div> + <div className=" my-4"> + <h1 className={formH1Class}>중수도 시설 정보</h1> + </div> + + <div className="grid grid-cols-12 gap-1 mt-4"> + <div className="col-span-4"> + <FormField + control={form.control} + name="reuseBuilding.0.rswtrObj.filtrTrtmfctCpct" + render={({ field }) => ( + <FormUi label="처리시설 용량"> + <Input + className={'w-full !text-xl'} + placeholder="0" + type="number" + value={field.value ?? ""} + onChange={(e)=>{field.onChange(Number(e.target.value))} } + readOnly={true} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-4"> + <FormField + control={form.control} + name="reuseBuilding.0.rswtrObj.orgwtKndCd" + render={({ field }) => ( + <FormUi label="원수 종류"> + <SelectBox + className={""} + label={""} + selectClass={selectClass} + selectArray={code.orgwtKndCd} + onSelectValue={field.onChange} + value={field.value ?? code.orgwtKndCd[0]?.val} + readOnly={true} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-4"> + <FormField + control={form.control} + name="reuseBuilding.0.rswtrObj.filtrTrtmfctProcs" + render={({ field }) => ( + <FormUi label="주요처리공정"> + <Input + className={'w-full !text-xl'} + placeholder="주요처리공정" + type="" + value={field.value ?? ""} + onChange={field.onChange} + readOnly={true} + /> + </FormUi> + )} + /> + </div> + </div> + + <div className=" my-2"> + <h1 className={formH1Class}>인센티브 정보</h1> + </div> + <div> + <h2 className={formH2Class}>설치비 지원</h2> + </div> + <div className="grid grid-cols-2 gap-2 mt-4"> + <div className="col-span-1"> + <FormField + control={form.control} + name="reuseBuilding.0.instlCstSprtYn" + render={({ field }) => ( + <FormUi label="설치비 지원여부"> + <SelectBox + className={""} + selectClass={selectClass} + label={""} + selectArray={code.yn} + onSelectValue={field.onChange} + value={field.value ?? code.yn[0]?.val} + readOnly={true} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-1"> + <FormField + control={form.control} + name="reuseBuilding.0.instlCstSprtAmt" + render={({ field }) => ( + <FormUi label="지원금액(원)"> + <Input + className={'w-full !text-xl'} + placeholder="1,000,000" + type="text" + onChange={(e) => { + handleCommaNumberChange(e, field.onChange); + }} + value={formatCommaNumber(field.value)} + readOnly={instlCstSprtYn !== "Y"} + /> + </FormUi> + )} + /> + </div> + </div> + + <div> + <h2 className={formH2Class}>녹색건축물 인증</h2> + </div> + <div className="grid grid-cols-6 gap-2 mt-4"> + <div className="col-span-3"> + <FormField + control={form.control} + name="reuseBuilding.0.grntchBdstCertYn" + render={({ field }) => ( + <FormUi label="인증여부"> + <SelectBox + className={""} + selectClass={selectClass} + label={""} + selectArray={code.yn} + onSelectValue={field.onChange} + value={field.value ?? code.yn[0]?.val} + readOnly={true} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-3"> + <FormField + control={form.control} + name="reuseBuilding.0.grntchBdstCertYr" + render={({ field }) => ( + <FormUi label="인증년도"> + <Input + className={'w-full !text-xl'} + placeholder="2024" + type="text" + {...field} + value={field.value ?? ""} + onChange={field.onChange} + readOnly={true} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-3"> + <FormField + control={form.control} + name="reuseBuilding.0.grntchBdstCertNo" + render={({ field }) => ( + <FormUi label="인증번호"> + <Input + className={'w-full !text-xl'} + placeholder="ab1234678" + type="" + value={field.value ?? ""} + onChange={field.onChange} + readOnly={true} + /> + </FormUi> + )} + /> + </div> + <div className="col-span-3"> + <FormField + control={form.control} + name="reuseBuilding.0.grntchBdstCertGrd" + render={({ field }) => ( + <FormUi label="인증등급"> + <Input + className={'w-full !text-xl'} + placeholder="A" + type="" + value={field.value ?? ""} + onChange={field.onChange} + readOnly={true} + /> + </FormUi> + )} + /> + </div> + </div> + </form> + </Form> + </div> + ); +} diff --git a/apps/wr/app/page.tsx b/apps/wr/app/page.tsx index 960971d6b..70261c4c6 100644 --- a/apps/wr/app/page.tsx +++ b/apps/wr/app/page.tsx @@ -160,7 +160,7 @@ export default function Home() { <div className="grid grid-cols-12 gap-4"> <div className="col-span-2"> <Button className="w-full"> - <Link href="map/htdflSttn">온배수시설 현황(지도)</Link> + <Link href="map/htdflSttn">시설 현황(지도)</Link> </Button> </div> </div> diff --git a/apps/wr/app/ruseFcltMng/fcltSttn/rainSttn/page.tsx b/apps/wr/app/ruseFcltMng/fcltSttn/rainSttn/page.tsx index 88b3c8649..0af6072de 100644 --- a/apps/wr/app/ruseFcltMng/fcltSttn/rainSttn/page.tsx +++ b/apps/wr/app/ruseFcltMng/fcltSttn/rainSttn/page.tsx @@ -37,10 +37,7 @@ export default function RainSttn() { const [pageNation, setPageNation] = useState(1); const [isChHead, setIsChHead] = useState(false); const [bdstTable, setBdstTable] = useState<customRainBdst[]>(); - const [detailModal, setDetailModal] = useState<{ - isOpen: boolean; - data: customRainBdst | undefined; - }>({ data: undefined, isOpen: false }); + const [detailModal, setDetailModal] = useState<{ isOpen: boolean, data: customRainBdst | undefined }>({ data: undefined, isOpen: false }) const [bdstChgProps, setBdstChgProps] = useState<{ isOpen: boolean; chBuilding: chgBuilding[]; @@ -49,25 +46,12 @@ export default function RainSttn() { 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, - }); + + 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 }); } } }; @@ -168,9 +152,7 @@ export default function RainSttn() { headlist={rainSttn.rainSttHeadList} pageSize={20} total={100000} - cellClick={(index: number | undefined, headId: string) => { - cellClick(index ?? undefined, headId); - }} + cellClick={(index: number | undefined, headId: string) => { cellClick(index ?? undefined, headId) }} onPageChange={setPageNation} ></UiTable> </ListDiv> diff --git a/apps/wr/components/ui/modal/sttn/pbnstSttnModal/pbnstSttnModal.tsx b/apps/wr/components/ui/modal/sttn/pbnstSttnModal/pbnstSttnModal.tsx index 438ab439c..0d3c2ca63 100644 --- a/apps/wr/components/ui/modal/sttn/pbnstSttnModal/pbnstSttnModal.tsx +++ b/apps/wr/components/ui/modal/sttn/pbnstSttnModal/pbnstSttnModal.tsx @@ -28,7 +28,7 @@ import { useEffect, useState } from "react"; import { set, useForm } from "react-hook-form"; import { z } from "zod"; -export function PbnstSttnModal({ data }: { data: customPbnst }) { +export function PbnstSttnModal({ data, titleVisible }: { data: customPbnst; titleVisible?: boolean }) { const { code, pbnstSttn, prfmnc } = useWrStore((state) => state); const [fcltTab, setFcltTab] = useState<string>("fclt"); const [ruseTab, setRuseTab] = useState<string>("inoutTotal"); @@ -233,20 +233,24 @@ export function PbnstSttnModal({ data }: { data: customPbnst }) { } }, [pbnstSttn.pbnstSttnRes, data]); - useEffect(() => {}, [ruseBizData?.head]); + useEffect(() => { }, [ruseBizData?.head]); - useEffect(()=>{ + useEffect(() => { - if(data){ - form.reset({items:[data]}) + if (data) { + form.reset({ items: [data] }) } - },[data, form]) + }, [data, form]) return ( <> <div> - <Title title={"하수재이용 시설 상세정보"} /> + {titleVisible && ( + <> + <Title title={"하수재이용 시설 상세정보"} /> + </> + )} <ScrollArea className={"h-[70vh]"}> <PbnstMngForm form={form} diff --git a/packages/common/business_components/src/ui/map/Gis.js b/packages/common/business_components/src/ui/map/Gis.js index bb169c3ff..c09247f2f 100644 --- a/packages/common/business_components/src/ui/map/Gis.js +++ b/packages/common/business_components/src/ui/map/Gis.js @@ -1,5 +1,6 @@ import { Search } from 'lucide-react'; import OlMap from 'ol/Map'; +import Overlay from 'ol/Overlay'; import View from 'ol/View'; import { defaults as defaultControls, OverviewMap } from 'ol/control'; import { defaults } from 'ol/interaction'; @@ -7,31 +8,35 @@ import * as olProj from 'ol/proj'; import { register } from 'ol/proj/proj4'; import proj4 from 'proj4'; import React, { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react'; +import { createRoot } from 'react-dom/client'; import GisLonLat from './GisLonLat'; import GisMapToggle from './GisMapToggle'; import GisSelect from './GisSelect'; import GisToolBar from './GisToolBar'; import GisTree from './GisTree'; +import FacilityInfoPopup from './facilityInfoPopup'; import './lib/css/gis.common.css'; import GisApp from './lib/gis.core'; export const GisContext = createContext({ gisCore: null, gisMap: null, treeData: null, checkedLayerIds: null }); export const useGisContext = () => useContext(GisContext); -function Gis({ - isOpen , - gisMaps, - isSearchModalOpen, - treeData = [], - isSearch = false, - isMiniMap = false, - setIsOpen, - setFeatureData, - featureData, - searchView : SearchView, - detailView: DetailView, - onFeatureSelect = null, - setIsSearchModalOpen }) { +function Gis({ + isOpen, + gisMaps, + isSearchModalOpen, + treeData = [], + isSearch = false, + isMiniMap = false, + setIsOpen, + setFeatureData, + featureData, + searchView: SearchView, + detailView: DetailView, + onFeatureSelect, + setIsSearchModalOpen, + onCheckedLayersChange +}) { const mapRef = useRef(); const [mapObject, setMapObject] = useState({}); const [tabVisible, setTabVisible] = useState(true); @@ -41,13 +46,21 @@ function Gis({ const gisCore = useMemo(() => new GisApp.core({ targetId: mapRef.current }), []); const mapInstanceRef = useRef(null); const checkedLayerIds = useRef([]); + + // 팝업 데이터를 저장할 상태와 오버레이 참조를 저장할 ref 추가 + const [facilityInfoPopupData, setFacilityInfoPopupData] = useState([]); + const facilityOverlaysRef = useRef([]); + // 체크된 레이어 상태 관리 + const [checkedLayers, setCheckedLayers] = useState([]); + const [checkedLayersData, setCheckedLayersData] = useState([]); + let gisMap = null; const closePopup = () => { setIsOpen(false); // 팝업 닫기 setFeatureData(null) }; - // SearchModal, GisContent 등에서 호출 + // Feature Click Event const handleFeatureSelect = useCallback((data) => { gisMap = mapInstanceRef.current; @@ -148,7 +161,7 @@ function Gis({ gisMaps.current = map // 미니맵 추가 - if(isMiniMap){ + if (isMiniMap) { const overviewMap = new OverviewMap({ layers: [GisApp.Module.core.baseMiniMapLayers['base']], view: new View({ @@ -213,15 +226,140 @@ function Gis({ } } + // 데이터 로드 및 팝업 생성 로직 + useEffect(() => { + if (!mapInstanceRef.current) return; // 맵이 생성되지 않았으면 실행 안 함 + + // 1. 팝업 데이터 가져오기 (feature.js의 getData와 유사) + const fetchPopupData = async () => { + // 실제 API 호출 로직으로 대체해야 합니다. + // 예시: const response = await yourApiService.getFcltyWtCnt(); + // setFacilityInfoPopupData(response.data); + + // 임시 목업 데이터 + const mockData = [ + { ftrIdn: 1, ftrCde: 'SA206', bplcNm: '처리장A', fcltyUseYn: 'Y', x: 127.005, y: 37.282, aCnt: 10, cCnt: 5, eCnt: 3, mCnt: 8, iCnt: 2 }, + { ftrIdn: 2, ftrCde: 'SA112', bplcNm: '처리장B', fcltyUseYn: 'N', x: 127.010, y: 37.285, aCnt: 8, cCnt: 4, eCnt: 2, mCnt: 6, iCnt: 1 } + ]; + setFacilityInfoPopupData(mockData); + }; + + fetchPopupData(); + + }, [mapInstanceRef.current]); // 맵 인스턴스가 생성된 후 한 번만 실행 + + // 데이터가 변경되면 오버레이를 생성/업데이트하는 로직 + useEffect(() => { + const map = mapInstanceRef.current; + if (!map || facilityInfoPopupData.length === 0) return; + + // 기존 오버레이 정리 + facilityOverlaysRef.current.forEach(overlay => map.removeOverlay(overlay)); + facilityOverlaysRef.current = []; + + facilityInfoPopupData.forEach(data => { + const popupId = `${data.ftrIdn}_${data.ftrCde}`; + + // 팝업을 담을 div 엘리먼트 생성 + const popupElement = document.createElement('div'); + popupElement.id = popupId; + + // OpenLayers Overlay 생성 + const position = olProj.transform([Number(data.x), Number(data.y)], GisApp.FROM_PROJ, GisApp.TO_PROJ); + const overlay = new Overlay({ + element: popupElement, + position: position, + offset: [-75, -210], // 팝업 너비의 절반, 높이만큼 오프셋 조정 + positioning: 'bottom-center', + className: 'facility-popup-overlay' // 필요시 공통 클래스 추가 + }); + + map.addOverlay(overlay); + facilityOverlaysRef.current.push(overlay); + + // React 컴포넌트를 popupElement에 렌더링 + const root = createRoot(popupElement); + root.render(<FacilityInfoPopup data={data} />); + }); + + // 초기 가시성 업데이트 + updatePopupVisibility(); + + }, [facilityInfoPopupData]); // 팝업 데이터가 변경될 때마다 실행 + + // 레이어 가시성에 따라 팝업 가시성을 제어하는 함수 + const updatePopupVisibility = useCallback(() => { + const map = mapInstanceRef.current; + if (!map) return; + + const visibleLayers = new Set(); + map.getLayers().forEach(layer => { + if (layer.getVisible()) { + // feature.js의 로직처럼 typeName이나 ftrCde를 사용해 필터링 + const typeName = layer.get('typeName'); + if (typeName === 'VW_WTL_FCLTY_AS') { // 예시 레이어 이름 + visibleLayers.add(layer.get('ftrCde')); + } + } + }); + + facilityOverlaysRef.current.forEach(overlay => { + const element = overlay.getElement(); + const ftrCde = element.querySelector('table')?.classList[0]; // FacilityPopup의 클래스에서 ftrCde 가져오기 + + if (visibleLayers.has(ftrCde)) { + element.style.display = ''; + } else { + element.style.display = 'none'; + } + }); + }, []); + + // GisTree에서 체크 상태가 변경될 때 이 함수를 호출하도록 연결해야 합니다. + // 예시: GisTree 컴포넌트에 onCheck 콜백을 추가하고, 그 콜백에서 이 함수를 호출 + useEffect(() => { + // 레이어 가시성이 변경될 때마다 팝업 가시성 업데이트 + const map = mapInstanceRef.current; + if (!map) return; + + const handleLayerVisibilityChange = () => { + updatePopupVisibility(); + }; + + map.getLayers().on('change:length', handleLayerVisibilityChange); + map.getLayers().forEach(layer => { + layer.on('change:visible', handleLayerVisibilityChange); + }); + + return () => { + // 클린업 + map.getLayers().un('change:length', handleLayerVisibilityChange); + map.getLayers().forEach(layer => { + layer.un('change:visible', handleLayerVisibilityChange); + }); + }; + }, [updatePopupVisibility]); + + // 체크된 레이어 정보를 처리하는 함수 + const handleCheckedLayersChange = useCallback((checkedIds, checkedNodesData) => { + setCheckedLayers(checkedIds); + setCheckedLayersData(checkedNodesData); + onCheckedLayersChange?.(checkedIds, checkedNodesData); + }, [onCheckedLayersChange]); + return ( <> <GisContext.Provider value={{ gisCore, gisMap: mapInstanceRef.current, treeData, checkedLayerIds }}> <div className="h-[calc(100vh-10rem)] flex relative"> <div ref={mapRef} className="gis-map flex-1 relative" style={{ height: '100%', padding: 0 }} /> - <GisTree visible={tabVisible} setVisible={setTabVisible} /> - + <GisTree + visible={tabVisible} + setVisible={setTabVisible} + onCheckedChange={handleCheckedLayersChange} + /> + {/* <GisContent onFeatureSelect={handleFeatureSelect} isSearch={isSearch} isSearchModalOpen={isSearchModalOpen} setIsSearchModalOpen={setIsSearchModalOpen} /> */} - + {/* isSearchModalOpen이 true면 visible=false로 팝업 숨김 */} {isOpen && featureData && !isSearchModalOpen && ( <DetailView @@ -234,9 +372,9 @@ function Gis({ onOpenChange={setIsSearchModalOpen} onFeatureSelect={handleFeatureSelect} /> - <GisLonLat/> - <GisToolBar/> - <GisMapToggle/> + <GisLonLat /> + <GisToolBar /> + <GisMapToggle /> <GisSelect onFeatureSelect={handleFeatureSelect} /> </div> diff --git a/packages/common/business_components/src/ui/map/GisTree.js b/packages/common/business_components/src/ui/map/GisTree.js index dec618926..e36569143 100644 --- a/packages/common/business_components/src/ui/map/GisTree.js +++ b/packages/common/business_components/src/ui/map/GisTree.js @@ -9,7 +9,7 @@ import { DndProvider } from "react-dnd"; import { useGisContext } from './Gis'; import GisApp from './lib/gis.core'; -export default React.memo(function GisTree({ checkedNodes = [], setCheckedNodes = () => { }, visible, setVisible }) { +export default React.memo(function GisTree({ checkedNodes = [], setCheckedNodes = () => { }, visible, setVisible, onCheckedChange }) { const { gisCore, treeData } = useGisContext(); const [checked, setChecked] = useState([]); const [expanded, setExpanded] = useState([]); @@ -102,6 +102,11 @@ export default React.memo(function GisTree({ checkedNodes = [], setCheckedNodes const unchecked = allIds.filter((nid) => !newChecked.includes(nid)); gisCore.setLayerOnMap(newChecked, true); gisCore.setLayerOnMap(unchecked, false); + + // 체크된 노드 정보와 함께 노드 데이터를 상위로 전달 + const checkedNodesData = nodes.filter(node => newChecked.includes(node.id)); + onCheckedChange?.(newChecked, checkedNodesData); + return newChecked; }); }; From cbb915a3e51ec4c9be042cabad2a40b02d2133f7 Mon Sep 17 00:00:00 2001 From: jyha00 <jyha@mind-one.co.kr> Date: Thu, 26 Jun 2025 11:27:17 +0900 Subject: [PATCH 008/108] =?UTF-8?q?=EC=A7=80=EB=8F=84=EC=A0=90=EA=B2=80?= =?UTF-8?q?=ED=98=84=ED=99=A9=20=EB=B9=97=EB=AC=BC=20init=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80,=20=EC=A4=91=EC=88=98=EB=8F=84=20=EC=A1=B0=ED=9A=8C?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wr/app/matMng/coMng/ldrmntCoSttn/page.tsx | 8 +- .../prfmncSttn/chkgdnSttn/page.tsx | 71 +++++++------- .../chkgdnSttn/rainChkgdnSttn/index.tsx | 9 +- .../chkgdnSttn/rswtChkgdnSttn/index.tsx | 92 +++++++++++-------- 4 files changed, 101 insertions(+), 79 deletions(-) diff --git a/apps/wr/app/matMng/coMng/ldrmntCoSttn/page.tsx b/apps/wr/app/matMng/coMng/ldrmntCoSttn/page.tsx index f5dffbf8b..c5daee29c 100644 --- a/apps/wr/app/matMng/coMng/ldrmntCoSttn/page.tsx +++ b/apps/wr/app/matMng/coMng/ldrmntCoSttn/page.tsx @@ -99,8 +99,8 @@ export default function LdrmntCoSttn() { const tbData = Array.isArray(data) ? data.map((item: customLdrmnt) => ({ ...item, - ctpvCd: item.fctryAddr.split(" ")[0] || "", - sggCd: item.fctryAddr.split(" ")[1] || "" + ctpvCd: item.fctryAddr?.split(" ")[0] || "", + sggCd: item.fctryAddr?.split(" ")[1] || "" })) : []; @@ -114,8 +114,8 @@ export default function LdrmntCoSttn() { const data = Array.isArray(exData) ? exData.map((item: customLdrmnt) => { return { - "시도": item.fctryAddr.split(" ")[0] || "", - "시군구": item.fctryAddr.split(" ")[1] || "", + "시도": item.fctryAddr?.split(" ")[0] || "", + "시군구": item.fctryAddr?.split(" ")[1] || "", "회사명": item.dmdCoNm, "공장주소": item.fctryAddr, "공장관리번호": item.fctryMngNo, diff --git a/apps/wr/app/ruseFcltMng/prfmncSttn/chkgdnSttn/page.tsx b/apps/wr/app/ruseFcltMng/prfmncSttn/chkgdnSttn/page.tsx index f9a799c24..f415bf187 100644 --- a/apps/wr/app/ruseFcltMng/prfmncSttn/chkgdnSttn/page.tsx +++ b/apps/wr/app/ruseFcltMng/prfmncSttn/chkgdnSttn/page.tsx @@ -33,10 +33,9 @@ export default function Page() { code, rainChkSttn, rswtChkSttn, - - rainChkSttnInit, rainChkSttnSearch, + rswtChkSttnSearch, rswtChkSttnInit, isRainSttn, isRswtSttn @@ -51,25 +50,31 @@ export default function Page() { }); const [searchYear, setSearchYear] = useState<string>(String(new Date().getFullYear())); const cellClick = () => { - if (searchYear) rainChkSttnSearch(searchYear, "0"); + if (searchYear) { + if(selFcltDiv === "RNB"){ + rainChkSttnSearch(searchYear, "0"); + }else{ + rswtChkSttnSearch(searchYear, "0"); + } + } }; useEffect(() => { if (!rainChkSttn.isInit) { isRainSttn(true) - rainChkSttnInit( "0"); + rainChkSttnInit(new Date().getFullYear().toString(), "0"); setIsModal({ data: {}, open: false }); } }, [rainChkSttn.isInit, rainChkSttnInit]); - useEffect(() => { + useEffect(() => { if (!rswtChkSttn.isInit) { setIsModal({ data: {}, open: false }); - rswtChkSttnInit( "0"); + rswtChkSttnInit(new Date().getFullYear().toString(), "0"); isRswtSttn(true) } - },[rswtChkSttn.isInit, rswtChkSttnInit]) + }, [rswtChkSttn.isInit, rswtChkSttnInit]) const handleClick = useCallback( @@ -78,25 +83,25 @@ export default function Page() { }, [isModal] ); -const excelDown=() =>{ - if(rainChkSttn.sttnExcelList && selFcltDiv === "RNB"){ - excelDownload( - rainChkSttn.sttnExcelList, - "빗물이용시설 지도점검 현황", - "sample_rainChkSttn.xlsx", - "A1:Q2", - 3 - ); - }else if(rswtChkSttn.sttnExcelList && selFcltDiv === "RST"){ - excelDownload( - rswtChkSttn.sttnExcelList, - "중수도시설 지도점검 현황", - "sample_rswtChkSttn.xlsx", - "A1:X2", - 3 - ); + const excelDown = () => { + if (rainChkSttn.sttnExcelList && selFcltDiv === "RNB") { + excelDownload( + rainChkSttn.sttnExcelList, + "빗물이용시설 지도점검 현황", + "sample_rainChkSttn.xlsx", + "A1:Q2", + 3 + ); + } else if (rswtChkSttn.sttnExcelList && selFcltDiv === "RST") { + excelDownload( + rswtChkSttn.sttnExcelList, + "중수도시설 지도점검 현황", + "sample_rswtChkSttn.xlsx", + "A1:X2", + 3 + ); + } } -} return ( <ContentsDiv> @@ -126,9 +131,9 @@ const excelDown=() =>{ selectClass={""} selectArray={rainChkSttn.fcltDiv} onSelectValue={(val: string | number) => - setSelFcltDiv(String(val)) - } - value={selFcltDiv ?? rainChkSttn.fcltDiv[0]?.val} + setSelFcltDiv(String(val)) + } + value={selFcltDiv ?? rainChkSttn.fcltDiv[0]?.val} /> </div> </SearchFormLeft> @@ -143,7 +148,7 @@ const excelDown=() =>{ > 조회 </Button> - <Button color="green" size={"sm"} onClick={(e) =>{excelDown()}}> + <Button color="green" size={"sm"} onClick={(e) => { excelDown() }}> 다운로드 </Button> </div> @@ -157,15 +162,15 @@ const excelDown=() =>{ <ListDiv> {selFcltDiv === "RNB" ? ( <RainChkgdTable - deleteData={() => {}} - detailUp={() => {}} + deleteData={() => { }} + detailUp={() => { }} handleClick={handleClick} rainChkData={rainChkSttn.rainChkRes} /> ) : ( <RswtChkgdTable - detaiRswtlUpdate={() => {}} - deleteRswtData={() => {}} + detaiRswtlUpdate={() => { }} + deleteRswtData={() => { }} handleRswtClick={handleClick} rswtChkData={rswtChkSttn.rswtChkRes} /> diff --git a/apps/wr/app/store/prfmncSttn/chkgdnSttn/rainChkgdnSttn/index.tsx b/apps/wr/app/store/prfmncSttn/chkgdnSttn/rainChkgdnSttn/index.tsx index 9fd948856..ea3f58a40 100644 --- a/apps/wr/app/store/prfmncSttn/chkgdnSttn/rainChkgdnSttn/index.tsx +++ b/apps/wr/app/store/prfmncSttn/chkgdnSttn/rainChkgdnSttn/index.tsx @@ -35,7 +35,7 @@ export type rainChkSttnState = { }; type rainChkSttnActions = { - rainChkSttnInit: ( sggCd: string) => void; + rainChkSttnInit: (searchTxt: string, sggCd: string) => void; rainChkSttnSearch: (searchTxt: string, sggCd: string) => void; isRainSttn:(isSttn:boolean) => void; getRainSttnExcel:(excelList:rainChkZodType[]) => void; @@ -58,9 +58,10 @@ export const rainChkSttnStateExport: rainChkSttnState = { }; const initFetch = async (txt: string | undefined, sgg: string | undefined) => { + const currentYear = new Date().getFullYear().toString(); const rainRes = await getFetch( "wr", - `/reuseChk/rainy/` + (txt ? `${txt}` : "2024") + (sgg ? `/${sgg}` : "/0") + `/reuseChk/rainy/` + (txt ? `${txt}` : currentYear) + (sgg ? `/${sgg}` : "/0") ); return rainRes; }; @@ -68,9 +69,9 @@ export const rainChkSttnActionsExport: (set: any) => rainChkSttnActions = ( set: any ) => { return { - rainChkSttnInit: async ( sggCd: string) => { + rainChkSttnInit: async (searchTxt: string, sggCd: string) => { try { - const rainChkRes = await initFetch(undefined, sggCd); + const rainChkRes = await initFetch(searchTxt, sggCd); return set((state: rainChkSttnState) => ({ ...state, diff --git a/apps/wr/app/store/prfmncSttn/chkgdnSttn/rswtChkgdnSttn/index.tsx b/apps/wr/app/store/prfmncSttn/chkgdnSttn/rswtChkgdnSttn/index.tsx index b985cd944..cd95ef1fc 100644 --- a/apps/wr/app/store/prfmncSttn/chkgdnSttn/rswtChkgdnSttn/index.tsx +++ b/apps/wr/app/store/prfmncSttn/chkgdnSttn/rswtChkgdnSttn/index.tsx @@ -24,10 +24,10 @@ export type rswtChkSttnState = { }; type rswtChkSttnActions = { - rswtChkSttnInit: ( sggCd: string) => void; - isRswtSttn: (isSttn:boolean) => void; - getRswtSttnExcel:(excelList:rswtChkZodType[]) => void; - + rswtChkSttnInit: (searchTxt: string, sggCd: string) => void; + isRswtSttn: (isSttn: boolean) => void; + getRswtSttnExcel: (excelList: rswtChkZodType[]) => void; + rswtChkSttnSearch: (searchTxt: string, sggCd: string) => void; }; export type rswtChkSttnType = rswtChkSttnState & rswtChkSttnActions; @@ -40,16 +40,17 @@ export const rswtChkStateExport: rswtChkSttnState = { sttnExcelList: [], rswtChkRes: [], isInit: false, - isSttn:false, + isSttn: false, }, }; const initFetch = async (year: string | undefined, sgg: string | undefined) => { + const currentYear = new Date().getFullYear().toString(); const rswtChkRes = await getFetch( "wr", `/reuseChk/reuseWater/` + - (year ? `${year}` : "2024") + - (sgg ? `/${sgg}` : "/0") + (year ? `${year}` : currentYear) + + (sgg ? `/${sgg}` : "/0") ); return rswtChkRes; }; @@ -59,9 +60,9 @@ export const rswtChkSttnActionsExport: (set: any) => rswtChkSttnActions = ( set: any ) => { return { - rswtChkSttnInit: async ( sggCd: string) => { + rswtChkSttnInit: async (searchTxt: string, sggCd: string) => { try { - const rswtChkRes = await initFetch("2024", sggCd); + const rswtChkRes = await initFetch(searchTxt, sggCd); return set((state: rswtChkSttnState) => ({ ...state, rswtChkSttn: { @@ -73,35 +74,50 @@ export const rswtChkSttnActionsExport: (set: any) => rswtChkSttnActions = ( } catch (error) { console.log(error); } - },isRswtSttn :(isSttn:boolean) =>{ - try { - return set((state: rswtChkSttnState) => ({ - ...state, - rswtChkSttn: { - ...state.rswtChkSttn, - isSttn:isSttn - }, - })); - } catch (error) { - console.log(error); - } - },getRswtSttnExcel: async (excelList:rswtChkZodType []) => { - try { - - const excelData = buildingExcelRswtSttn(excelList) - - return set((state: rswtChkSttnState) => ({ - ...state, - rswtChkSttn: { - ...state.rswtChkSttn, - sttnExcelList:excelData - }, - })); - } catch (error) { - console.log(error); - } - } + }, + isRswtSttn: (isSttn: boolean) => { + try { + return set((state: rswtChkSttnState) => ({ + ...state, + rswtChkSttn: { + ...state.rswtChkSttn, + isSttn: isSttn + }, + })); + } catch (error) { + console.log(error); + } + }, + getRswtSttnExcel: async (excelList: rswtChkZodType[]) => { + try { - + const excelData = buildingExcelRswtSttn(excelList) + + return set((state: rswtChkSttnState) => ({ + ...state, + rswtChkSttn: { + ...state.rswtChkSttn, + sttnExcelList: excelData + }, + })); + } catch (error) { + console.log(error); + } + }, + rswtChkSttnSearch: async (searchTxt: string, sggCd: string) => { + try { + const rswtChkRes = await initFetch(searchTxt, sggCd); + + return set((state: rswtChkSttnState) => ({ + ...state, + rswtChkSttn: { + ...state.rswtChkSttn, + rswtChkRes: rswtChkRes + }, + })); + } catch (error) { + console.log(error); + } + }, }; }; From cd6568f4dfb2e0b86d5047cebd1e8c3878c57496 Mon Sep 17 00:00:00 2001 From: leedj312 <dlehd467@gmail.com> Date: Thu, 26 Jun 2025 16:08:00 +0900 Subject: [PATCH 009/108] =?UTF-8?q?=ED=95=98=EC=88=98=EC=9E=AC=EC=9D=B4?= =?UTF-8?q?=EC=9A=A9=20=EC=8B=9C=EC=84=A4=20=EB=B0=B8=EB=A5=98=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=EC=85=98=20=EC=B2=B4=ED=81=AC=20=EC=9E=91=EC=97=85?= =?UTF-8?q?=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/ruseFcltMng/fcltReg/pbnstReg/page.tsx | 59 +++++++++++++++++-- .../app/ruseFcltMng/fcltReg/rainReg/page.tsx | 1 - .../prfmncSttn/chkgdnSttn/page.tsx | 1 - .../ruseFcltMng/fcltReg/pbnstReg/type.tsx | 25 ++++---- .../ruseFcltMng/fcltReg/rainReg/index.tsx | 4 +- .../fcltReg/pbnstReg/buseReg/bugRegForm.tsx | 6 +- .../ui/form/fcltReg/rainReg/rainRegForm.tsx | 10 ++-- 7 files changed, 75 insertions(+), 31 deletions(-) diff --git a/apps/wr/app/ruseFcltMng/fcltReg/pbnstReg/page.tsx b/apps/wr/app/ruseFcltMng/fcltReg/pbnstReg/page.tsx index 441eabf25..acc72ea0f 100644 --- a/apps/wr/app/ruseFcltMng/fcltReg/pbnstReg/page.tsx +++ b/apps/wr/app/ruseFcltMng/fcltReg/pbnstReg/page.tsx @@ -29,9 +29,9 @@ import { import { PbnstMngForm } from "@/components/ui/form/fcltReg/pbnstReg/mngReg/mngRegForm"; import { PbnstBuseForm } from "@/components/ui/form/fcltReg/pbnstReg/buseReg/bugRegForm"; import { PbnstRegModal } from "@/components/ui/modal/fcltReg/pbnstRegModal/pbnstRegModal"; -import toast, { Toaster } from "react-hot-toast"; +import toast, { Toaster } from "react-hot-toast"; // toast 이미 import 되어 있음 import { writeRgtrAndMdfr } from "@/app/store/ruseFcltMng/fcltReg/pbnstReg"; -import { useFieldArray, useForm } from "react-hook-form"; +import { useFieldArray, useForm, type FieldErrors } from "react-hook-form"; // FieldErrors 타입 추가 import { zodResolver } from "@hookform/resolvers/zod"; import { z } from "zod"; @@ -105,6 +105,53 @@ export default function PublicReuseFaciltyInsert() { await pbnstRuseBizUpdate(bizs); }; + // 폼 유효성 검사 실패 시 호출될 함수 + const onInvalid = (errors: FieldErrors<{ items: z.infer<typeof pbnstRegZod>[]; }>) => { + // 재귀 함수를 사용해 중첩된 에러 객체에서 모든 에러 메시지와 경로를 찾습니다. + const findAllErrors = ( + errorObject: any, + path: (string | number)[] = [] + ): { message: string; path: (string | number)[] }[] => { + if (!errorObject) return []; + + return Object.keys(errorObject).flatMap((key) => { + const value = errorObject[key]; + const currentKey = !isNaN(parseInt(key)) ? parseInt(key) : key; + const newPath = [...path, currentKey]; + + if (value && typeof value === "object") { + if (typeof value.message === "string") { + return [{ message: value.message, path: newPath }]; + } + return findAllErrors(value, newPath); + } + return []; + }); + }; + + const errorList = findAllErrors(errors); + + if (errorList.length > 0) { + errorList.forEach((errorInfo) => { + const { message, path } = errorInfo; + let finalMessage = message; + + const itemsIndex = path.indexOf("items"); + const fcBizsIndex = path.indexOf("fcBizs"); + + if (itemsIndex > -1 && fcBizsIndex > -1 && path.length > fcBizsIndex + 1) { + const itemIdx = path[itemsIndex + 1] as number; + const bizIdx = path[fcBizsIndex + 1] as number; + const cycle = form.getValues(`items.${itemIdx}.fcBizs.${bizIdx}.cycl`); + if (cycle) { + finalMessage = `[${cycle}차수] ${message}`; + } + } + toast.error(finalMessage); + }); + } + }; + useEffect(() => { if (code.sggCd.length) { if (searchFclts) { @@ -250,7 +297,7 @@ useEffect(() =>{ <div className="grid grid-cols-6 gap-4 h-full"> <div className="col-span-4 h-full min-h-0"> <ListDiv className="min-h-0"> - <ScrollArea className="h-[60vh]"> + <ScrollArea className="h-[60vh]"> {/* 높이 조정 */} <UiTable tableData={bdstTable || []} headlist={pubFacHeadList} @@ -276,8 +323,8 @@ useEffect(() =>{ </TabsTrigger> <Button size={"sm"} - onClick={form.handleSubmit(onSubmit)} - className="!justify-end" + onClick={form.handleSubmit(onSubmit,onInvalid)} + className="!justify-end" // 저장 버튼 위치 조정 > 저장 </Button> @@ -310,7 +357,7 @@ useEffect(() =>{ <UiModal body={ <PbnstRegModal - form={form} + form={form} // form prop 전달 data={isRuseReg.data || ({} as customPbnst)} ></PbnstRegModal> } diff --git a/apps/wr/app/ruseFcltMng/fcltReg/rainReg/page.tsx b/apps/wr/app/ruseFcltMng/fcltReg/rainReg/page.tsx index 31743aaa9..221524918 100644 --- a/apps/wr/app/ruseFcltMng/fcltReg/rainReg/page.tsx +++ b/apps/wr/app/ruseFcltMng/fcltReg/rainReg/page.tsx @@ -80,7 +80,6 @@ export default function Home() { }; useEffect(() => { - console.log("getPageNation", pageNation); getRainData(pageNation, "41"); }, [pageNation]); diff --git a/apps/wr/app/ruseFcltMng/prfmncSttn/chkgdnSttn/page.tsx b/apps/wr/app/ruseFcltMng/prfmncSttn/chkgdnSttn/page.tsx index f415bf187..a5480a8b2 100644 --- a/apps/wr/app/ruseFcltMng/prfmncSttn/chkgdnSttn/page.tsx +++ b/apps/wr/app/ruseFcltMng/prfmncSttn/chkgdnSttn/page.tsx @@ -17,7 +17,6 @@ import { SearchDiv, SearchFormLeft, SearchFormRight, - SearchInput, SelectBox, Title, UiModal, diff --git a/apps/wr/app/store/ruseFcltMng/fcltReg/pbnstReg/type.tsx b/apps/wr/app/store/ruseFcltMng/fcltReg/pbnstReg/type.tsx index a34985810..9c390d076 100644 --- a/apps/wr/app/store/ruseFcltMng/fcltReg/pbnstReg/type.tsx +++ b/apps/wr/app/store/ruseFcltMng/fcltReg/pbnstReg/type.tsx @@ -148,7 +148,7 @@ export const fcBizSchema = z.object({ pbnstRuseFcltFk: z.string().nullish(), superkey: z.string().default("").nullish(), cycl: zNum.default(1), - seCd: z.string().default(""), + seCd: z.string().max(10, "구분은 10자를 초과할 수 없습니다.").default(""), bizMthdCd: z.string().default(""), instlAprvSncnDt: zDateDefaultNow, bizCmcnDay: zDateDefaultNow, @@ -171,17 +171,17 @@ export const fcBizSchema = z.object({ wwtEmsnFcltPrmsnYn: z.string().default("Y"), tmsInstlYn: z.string().default("Y"), pbnstDrngInflwExclYn: z.string().default("Y"), - bod: zNumDefaultZero, - grssClfg: zNumDefaultZero, - combResdch: zNumDefaultZero, - trbt: zNumDefaultZero, - clIonQnt: zNumDefaultZero, - smll: zNumDefaultZero, - crmty: zNumDefaultZero, - tph: zNumDefaultZero, - tn: zNumDefaultZero, - ph: zNumDefaultZero, - cdtnv: zNumDefaultZero, + bod: z.preprocess((v) => (v !== null && v !== undefined ? Number(v) : v), z.number().max(9999, "BOD 값은 5자리 미만이어야 합니다.")).nullish(), + grssClfg: z.preprocess((v) => (v !== null && v !== undefined ? Number(v) : v), z.number().max(9999, "총대장균군 값은 5자리 미만이어야 합니다.")).nullish(), + combResdch: z.preprocess((v) => (v !== null && v !== undefined ? Number(v) : v), z.number().max(9999, "결합잔류염소 값은 5자리 미만이어야 합니다.")).nullish(), + trbt: z.preprocess((v) => (v !== null && v !== undefined ? Number(v) : v), z.number().max(9999, "탁도 값은 5자리 미만이어야 합니다.")).nullish(), + clIonQnt: z.preprocess((v) => (v !== null && v !== undefined ? Number(v) : v), z.number().max(9999, "염소이온량 값은 5자리 미만이어야 합니다.")).nullish(), + smll: z.preprocess((v) => (v !== null && v !== undefined ? Number(v) : v), z.number().max(9999, "냄새 값은 5자리 미만이어야 합니다.")).nullish(), + crmty: z.preprocess((v) => (v !== null && v !== undefined ? Number(v) : v), z.number().max(9999, "색도 값은 5자리 미만이어야 합니다.")).nullish(), + tph: z.preprocess((v) => (v !== null && v !== undefined ? Number(v) : v), z.number().max(9999, "총인 값은 5자리 미만이어야 합니다.")).nullish(), + tn: z.preprocess((v) => (v !== null && v !== undefined ? Number(v) : v), z.number().max(9999, "총질소 값은 5자리 미만이어야 합니다.")).nullish(), + ph: z.preprocess((v) => (v !== null && v !== undefined ? Number(v) : v), z.number().max(9999, "수소이온농도 값은 5자리 미만이어야 합니다.")).nullish(), + cdtnv: z.preprocess((v) => (v !== null && v !== undefined ? Number(v) : v), z.number().max(9999, "전기전도도 값은 5자리 미만이어야 합니다.")).nullish(), designCst: zNumNullish, cstrnSpvsCst: zNumNullish, fcltSbsdCst: zNumNullish, @@ -241,4 +241,3 @@ export type fcOrgwtType = z.infer<typeof fcOrgwtSchema>; export type ppInstlType = z.infer<typeof pplInstlSchema>; export type operType = z.infer<typeof fcOperSchema>; export type fcFileType = z.infer<typeof fcFileSchema>; - diff --git a/apps/wr/app/store/ruseFcltMng/fcltReg/rainReg/index.tsx b/apps/wr/app/store/ruseFcltMng/fcltReg/rainReg/index.tsx index 5b3ad6813..4681f7250 100644 --- a/apps/wr/app/store/ruseFcltMng/fcltReg/rainReg/index.tsx +++ b/apps/wr/app/store/ruseFcltMng/fcltReg/rainReg/index.tsx @@ -16,7 +16,7 @@ const rainyObjSchema = z.object({ pntfColKnd: z.string().default(""), pntfColArea: z.number().default(0), dtfcltCpct: z.number().default(0), - filtrTrtmfctYn: z.string().default("Y").nullish(), + filtrTrtmfctYn: z.string().default("Y"), filtrTrtmfctCpct: z.number().nullish().default(0), filtrTrtmfctPrs: z.string().default(""), annRnwtQty: z.number().default(0), @@ -59,7 +59,7 @@ const reuseBuildingSchema = z.object({ }).refine((data) => data.bdstUsgCd > 0, { message: validationMessages.requiredSelect("건축물용도"), path: ["bdstUsgCd"], -}); +}) ; const chgBuildingSchema = z.object({ bdstInfoFk: z.number().nullish(), diff --git a/apps/wr/components/ui/form/fcltReg/pbnstReg/buseReg/bugRegForm.tsx b/apps/wr/components/ui/form/fcltReg/pbnstReg/buseReg/bugRegForm.tsx index df63b3520..a729b2533 100644 --- a/apps/wr/components/ui/form/fcltReg/pbnstReg/buseReg/bugRegForm.tsx +++ b/apps/wr/components/ui/form/fcltReg/pbnstReg/buseReg/bugRegForm.tsx @@ -55,8 +55,6 @@ export function PbnstBuseForm({ code, pbnst, demandCompList, - getpbnstInsertIndex, - appendRuseFclt, getDemandCompList, regDemandCompany, } = useWrStore((state) => state); @@ -65,7 +63,6 @@ export function PbnstBuseForm({ control: form.control, name: `items.${indexMng}.fcBizs`, }); - const [bizCycle, setBizCycle] = useState("1"); const [ppiSelectList, setPpiSelectList] = useState<[{ name: string; val: SelectDataType[] }]>(); @@ -499,7 +496,7 @@ export function PbnstBuseForm({ ) || [] ).map((field, index) => ( <div - key={`fcDemands-${indexMng}-${indexBiz}`} // 이게 핵심! + key={`fcDemands-${indexMng}-${indexBiz}=${index}`} // 이게 핵심! className="grid grid-cols-12 gap-4 mt-3" > <div key={index + "keySon1"} className="col-span-4"> @@ -664,6 +661,7 @@ export function PbnstBuseForm({ )} /> </div> + <br></br> </div> ))} </div> diff --git a/apps/wr/components/ui/form/fcltReg/rainReg/rainRegForm.tsx b/apps/wr/components/ui/form/fcltReg/rainReg/rainRegForm.tsx index 9ec4fc5c0..40a9bb436 100644 --- a/apps/wr/components/ui/form/fcltReg/rainReg/rainRegForm.tsx +++ b/apps/wr/components/ui/form/fcltReg/rainReg/rainRegForm.tsx @@ -60,10 +60,12 @@ export default function RainRegForm({ typeof item?.grntchBdstCertYr === "string" ? item?.grntchBdstCertYr : "", // null → "" + instlCstSprtYn: + item?.instlCstSprtYn ?? "Y", // null → "N" // Ensure enum fields are null if empty string to trigger Zod default rainyObj: { ...item?.rainyObj, - filtrTrtmfctYn: item?.rainyObj?.filtrTrtmfctYn === "" ? null : item?.rainyObj?.filtrTrtmfctYn, + filtrTrtmfctYn: item?.rainyObj?.filtrTrtmfctYn ??"Y" }, })), }; @@ -266,7 +268,7 @@ export default function RainRegForm({ if (!readonly) handleCommaNumberChange(e, field.onChange); }} value={formatCommaNumber(field.value)} - readOnly={filtrTrtmfctYn !== "Y"} + readOnly={filtrTrtmfctYn === "Y"} /> </FormUi> )} @@ -284,7 +286,7 @@ export default function RainRegForm({ type="text" value={field.value ?? ""} onChange={field.onChange} - readOnly={filtrTrtmfctYn !== "Y"} + readOnly={filtrTrtmfctYn === "Y" } /> </FormUi> )} @@ -333,7 +335,7 @@ export default function RainRegForm({ if (!readonly) handleCommaNumberChange(e, field.onChange); }} value={formatCommaNumber(field.value)} - readOnly={instlCstSprtYn !== "Y"} + readOnly={instlCstSprtYn === "Y"} /> </FormUi> )} From a83cb2709cdf42ccbc9831b6bb8649184fa3b9f0 Mon Sep 17 00:00:00 2001 From: leedj312 <dlehd467@gmail.com> Date: Thu, 26 Jun 2025 16:18:52 +0900 Subject: [PATCH 010/108] =?UTF-8?q?=EC=9E=85=EB=A0=A5=20=EB=B0=B8=EB=A5=98?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=EC=85=98=EC=9D=98=20=EC=9E=AC=EC=9D=B4?= =?UTF-8?q?=EC=9A=A9=20=EC=8B=9C=EC=84=A4=EB=AA=85=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/ruseFcltMng/fcltReg/pbnstReg/page.tsx | 31 ++++++++++--------- .../store/prfmncReg/pbnstPrfmncReg/index.tsx | 1 - 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/apps/wr/app/ruseFcltMng/fcltReg/pbnstReg/page.tsx b/apps/wr/app/ruseFcltMng/fcltReg/pbnstReg/page.tsx index acc72ea0f..6b45dacc9 100644 --- a/apps/wr/app/ruseFcltMng/fcltReg/pbnstReg/page.tsx +++ b/apps/wr/app/ruseFcltMng/fcltReg/pbnstReg/page.tsx @@ -111,43 +111,46 @@ export default function PublicReuseFaciltyInsert() { const findAllErrors = ( errorObject: any, path: (string | number)[] = [] - ): { message: string; path: (string | number)[] }[] => { + ): { message: string; path: (string | number)[]; }[] => { if (!errorObject) return []; return Object.keys(errorObject).flatMap((key) => { const value = errorObject[key]; const currentKey = !isNaN(parseInt(key)) ? parseInt(key) : key; const newPath = [...path, currentKey]; - if (value && typeof value === "object") { if (typeof value.message === "string") { - return [{ message: value.message, path: newPath }]; + return [{ message: value.message, path: newPath }]; // 에러 메시지와 경로 반환 } return findAllErrors(value, newPath); } return []; }); }; - const errorList = findAllErrors(errors); - if (errorList.length > 0) { errorList.forEach((errorInfo) => { const { message, path } = errorInfo; - let finalMessage = message; - + let prefix = ""; const itemsIndex = path.indexOf("items"); - const fcBizsIndex = path.indexOf("fcBizs"); - if (itemsIndex > -1 && fcBizsIndex > -1 && path.length > fcBizsIndex + 1) { + if (itemsIndex > -1 && path.length > itemsIndex + 1) { const itemIdx = path[itemsIndex + 1] as number; - const bizIdx = path[fcBizsIndex + 1] as number; - const cycle = form.getValues(`items.${itemIdx}.fcBizs.${bizIdx}.cycl`); - if (cycle) { - finalMessage = `[${cycle}차수] ${message}`; + const facilityName = form.getValues(`items.${itemIdx}.ruseUtztnFcltNm`); + if (facilityName) { + prefix += `[${facilityName}] `; + } + + const fcBizsIndex = path.indexOf("fcBizs"); + if (fcBizsIndex > -1 && path.length > fcBizsIndex + 1) { + const bizIdx = path[fcBizsIndex + 1] as number; + const cycle = form.getValues(`items.${itemIdx}.fcBizs.${bizIdx}.cycl`); + if (cycle) { + prefix += `[${cycle}차수] `; + } } } - toast.error(finalMessage); + toast.error(`${prefix}${message}`); }); } }; diff --git a/apps/wr/app/store/prfmncReg/pbnstPrfmncReg/index.tsx b/apps/wr/app/store/prfmncReg/pbnstPrfmncReg/index.tsx index 87e827f60..82f2ed59c 100644 --- a/apps/wr/app/store/prfmncReg/pbnstPrfmncReg/index.tsx +++ b/apps/wr/app/store/prfmncReg/pbnstPrfmncReg/index.tsx @@ -77,7 +77,6 @@ export const pbnstPrfmncActionsExport: (set: any) => pbnstPrfmncActions = ( prfmncHasuInit: async (page: string, sggOrCity: string) => { try { const prfmncRes = await hasuListFetch(page, sggOrCity); - console.log("prfmncRes", prfmncRes); return set((state: pbnstPrfmncState) => ({ ...state, prfmnc: { From fd2dab7a7784fa960880191b0c9ef020f320ffa2 Mon Sep 17 00:00:00 2001 From: jyha00 <jyha@mind-one.co.kr> Date: Thu, 26 Jun 2025 16:32:02 +0900 Subject: [PATCH 011/108] =?UTF-8?q?=EC=A7=80=EB=8F=84=EC=A0=90=EA=B2=80=20?= =?UTF-8?q?=ED=98=84=ED=99=A9=20=EB=AA=A8=EB=8B=AC=20=EC=98=A4=EB=A5=98=20?= =?UTF-8?q?=EC=88=98=EC=A0=95,=20=ED=8F=89=EA=B7=A0=EA=B0=92=20=EC=86=8C?= =?UTF-8?q?=EC=88=98=EC=A0=90=20=EC=9E=90=EB=A6=AC=EC=88=98=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95,=20=EC=97=91=EC=85=80=20=EB=8B=A4=EC=9A=B4=EB=A1=9C?= =?UTF-8?q?=EB=93=9C=20=EC=8B=9C=20=EB=88=84=EB=9D=BD=EB=90=9C=20=EA=B0=92?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80,=20=ED=85=8C=EC=9D=B4=EB=B8=94=20merge=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ruseFcltMng/prfmncReg/chkgdnReg/page.tsx | 18 +++++---- .../prfmncSttn/chkgdnSttn/page.tsx | 21 +++++----- .../prfmncReg/chkgdnReg/rainChkgdn/index.tsx | 39 +++++++++++-------- .../prfmncReg/chkgdnReg/rswrtChkgdn/index.tsx | 22 +++++++---- .../chkgdnSttn/rswtChkgdnSttn/excel.tsx | 1 + .../chkgdnSttn/rswtChkgdnSttn/headList.tsx | 18 ++++----- .../ui/modal/chkgd/chkgdModal/chkgdModal.tsx | 20 +++++----- 7 files changed, 77 insertions(+), 62 deletions(-) diff --git a/apps/wr/app/ruseFcltMng/prfmncReg/chkgdnReg/page.tsx b/apps/wr/app/ruseFcltMng/prfmncReg/chkgdnReg/page.tsx index 18dbc7943..fd8e2a6d6 100644 --- a/apps/wr/app/ruseFcltMng/prfmncReg/chkgdnReg/page.tsx +++ b/apps/wr/app/ruseFcltMng/prfmncReg/chkgdnReg/page.tsx @@ -68,7 +68,12 @@ export default function Page() { data: {}, }); const cellClick = () => { - if (searchYear) rainChkInit(searchYear, "0"); + if (searchYear) + if (selFcltDiv === "RNB") { + rainChkInit(searchYear, "0"); + } else { + rswtChkgdnInit(searchYear, "0"); + } }; const [searchYear, setSearchYear] = useState<string>( @@ -91,8 +96,7 @@ export default function Page() { }, [rainChkgdn.isInit, rainChkInit]); useEffect(() => { - if (rswtChkgdn.excelList[0]&&rswtChkgdn.excelList[0]?.data.length > 1) { - + if (rswtChkgdn.excelList[0] && rswtChkgdn.excelList[0]?.data.length > 1) { excelArrayDownload( "중수도시설 지도점검", "sample_rs_chk.xlsx", @@ -189,10 +193,10 @@ export default function Page() { label={""} selectClass={""} selectArray={rainChkgdn.fcltDiv} - onSelectValue={(val: string | number) =>{ - if(val === "RST") rswtChkPolInit(); - setSelFcltDiv(String(val)) - } + onSelectValue={(val: string | number) => { + if (val === "RST") rswtChkPolInit(); + setSelFcltDiv(String(val)) + } } value={selFcltDiv ?? rainChkgdn.fcltDiv[0]?.val} /> diff --git a/apps/wr/app/ruseFcltMng/prfmncSttn/chkgdnSttn/page.tsx b/apps/wr/app/ruseFcltMng/prfmncSttn/chkgdnSttn/page.tsx index f415bf187..8960f468d 100644 --- a/apps/wr/app/ruseFcltMng/prfmncSttn/chkgdnSttn/page.tsx +++ b/apps/wr/app/ruseFcltMng/prfmncSttn/chkgdnSttn/page.tsx @@ -1,14 +1,10 @@ "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 { - excelArrayDownload, excelDownload, - yearOptions, + yearOptions } 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"; import RswtChkgdTable from "@/components/ui/table/chkgdTable/rswtChkgd/rswtChkgdTable"; @@ -17,17 +13,15 @@ import { SearchDiv, SearchFormLeft, SearchFormRight, - SearchInput, SelectBox, Title, - UiModal, + UiModal } from "@common/business_components/ui"; import ListDiv from "@common/business_components/ui/list_div"; import { Button, Label } from "@common/components/ui"; import { useCallback, useEffect, useState } from "react"; import { Toaster } from "react-hot-toast"; -//----------------------- export default function Page() { const { code, @@ -38,7 +32,8 @@ export default function Page() { rswtChkSttnSearch, rswtChkSttnInit, isRainSttn, - isRswtSttn + isRswtSttn, + rswtChkPolInit, } = useWrStore((state) => { return state; }); @@ -51,9 +46,9 @@ export default function Page() { const [searchYear, setSearchYear] = useState<string>(String(new Date().getFullYear())); const cellClick = () => { if (searchYear) { - if(selFcltDiv === "RNB"){ + if (selFcltDiv === "RNB") { rainChkSttnSearch(searchYear, "0"); - }else{ + } else { rswtChkSttnSearch(searchYear, "0"); } } @@ -130,9 +125,11 @@ export default function Page() { label={""} selectClass={""} selectArray={rainChkSttn.fcltDiv} - onSelectValue={(val: string | number) => + onSelectValue={(val: string | number) => { + if (val === "RST") rswtChkPolInit(); setSelFcltDiv(String(val)) } + } value={selFcltDiv ?? rainChkSttn.fcltDiv[0]?.val} /> </div> diff --git a/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/index.tsx b/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/index.tsx index ff612a0fb..3bd10169f 100644 --- a/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/index.tsx +++ b/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/index.tsx @@ -61,7 +61,7 @@ type rainChkgdnActions = { ): Promise<void>; rainChkExcelDataUPload(excelData: rainChkZodType[]): Promise<void>; rainChkDelete(chkgdnPks: number[]): Promise<void>; - rainChkHeadChange: (rainChkgdn: rainChkgdnState, isEdit :boolean) => void; + rainChkHeadChange: (rainChkgdn: rainChkgdnState, isEdit: boolean) => void; }; export type rainChkgdnType = rainChkgdnState & rainChkgdnActions; @@ -99,9 +99,10 @@ const makeMdfrChk = (data: rainChkZodType[], type: string, toDo: string) => { }; const initFetch = async (txt: string | undefined, sgg: string | undefined) => { + const currentYear = new Date().getFullYear().toString(); const rainRes = await getFetch( "wr", - `/reuseChk/rainy` + (txt ? `/${txt}` : "2024") + (sgg ? `/${sgg}` : "/0") + `/reuseChk/rainy` + (txt ? `/${txt}` : currentYear) + (sgg ? `/${sgg}` : "/0") ); return rainRes; }; @@ -130,13 +131,16 @@ export const rainChkgdnActionsExport: (set: any) => rainChkgdnActions = ( rainChkExcelDataUPload: async (excelData: rainChkZodType[]) => { try { const res = await postFectch("/reuseChk/rainy", excelData); + console.log("res", res); + console.log("res.ok", res.ok) if (res.ok) { ToastFormUi({ body: "지도점검 등록이 완료되었습니다. ", title: "지도점검 등록", }); } - const rainChkRes = await initFetch("2024", "0"); + const currentYear = new Date().getFullYear().toString(); + const rainChkRes = await initFetch(currentYear, "0"); return set((state: rainChkgdnState) => ({ ...state, rainChkgdn: { @@ -178,7 +182,8 @@ export const rainChkgdnActionsExport: (set: any) => rainChkgdnActions = ( title: "지도점검 수정", }); } - const rainChkRes = await initFetch("2024", "0"); + const currentYear = new Date().getFullYear().toString(); + const rainChkRes = await initFetch(currentYear, "0"); return set((state: rainChkgdnState) => ({ ...state, rainChkgdn: { @@ -201,7 +206,8 @@ export const rainChkgdnActionsExport: (set: any) => rainChkgdnActions = ( title: "지도점검 수정", }); } - const rainChkRes = await initFetch("2024", "0"); + const currentYear = new Date().getFullYear().toString(); + const rainChkRes = await initFetch(currentYear, "0"); return set((state: rainChkgdnState) => ({ ...state, rainChkgdn: { @@ -223,7 +229,8 @@ export const rainChkgdnActionsExport: (set: any) => rainChkgdnActions = ( title: "지도점검 삭제", }); } - const rainChkRes = await initFetch("2024", "0"); + const currentYear = new Date().getFullYear().toString(); + const rainChkRes = await initFetch(currentYear, "0"); return set((state: rainChkgdnState) => ({ ...state, rainChkgdn: { @@ -235,23 +242,23 @@ export const rainChkgdnActionsExport: (set: any) => rainChkgdnActions = ( ToastFormUi({ body: "삭제를 실패했습니다.. ", title: "지도점검 삭제" }); } }, - rainChkHeadChange: async (rainChkgdn: rainChkgdnState , isEdit :boolean) => { + rainChkHeadChange: async (rainChkgdn: rainChkgdnState, isEdit: boolean) => { try { const rainChkgdnHeadList = rainChkgdn.rainChkgdn.rainChkgdnHeadList; const splHeadList = rainChkgdn.rainChkgdn.splHeadList; const ruseHeadList = rainChkgdn.rainChkgdn.ruseHeadList; - rainChkgdnHeadList.forEach(( head) => { - if(head.edit!== undefined) - head.edit=isEdit; + rainChkgdnHeadList.forEach((head) => { + if (head.edit !== undefined) + head.edit = isEdit; }); splHeadList.forEach((head) => { - if(head.edit!== undefined) - head.edit=isEdit; + if (head.edit !== undefined) + head.edit = isEdit; }); ruseHeadList.forEach((head) => { - if(head.edit!== undefined) - head.edit=isEdit; + if (head.edit !== undefined) + head.edit = isEdit; }); return set((state: rainChkgdnState) => ({ @@ -259,8 +266,8 @@ export const rainChkgdnActionsExport: (set: any) => rainChkgdnActions = ( rainChkgdn: { ...state.rainChkgdn, rainChkgdnHeadList: rainChkgdnHeadList, - splHeadList:splHeadList, - ruseHeadList:ruseHeadList + splHeadList: splHeadList, + ruseHeadList: ruseHeadList }, })); } catch (error) { diff --git a/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/index.tsx b/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/index.tsx index eb0010047..1cc7bd766 100644 --- a/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/index.tsx +++ b/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/index.tsx @@ -80,10 +80,11 @@ export const rswtChkgdnStateExport: rswtChkgdnState = { }; const initFetch = async (year: string | undefined, sgg: string | undefined) => { + const currentYear = new Date().getFullYear().toString(); const rswtChkRes = await getFetch( "wr", `/reuseChk/reuseWater/` + - (year ? `${year}` : "2024") + + (year ? `${year}` : currentYear) + (sgg ? `/${sgg}` : "/0") ); return rswtChkRes; @@ -121,12 +122,12 @@ export const rswtChkgdnActionsExport: (set: any) => rswtChkgdnActions = ( excelList.push({ data: exceldata.guideDetail, headerNum: 0, - headerRange: "A1:AI1", + headerRange: "A1:AJ1", }); excelList.push({ data: exceldata.polutions, headerNum: 0, - headerRange: "A1:AZ1", + headerRange: "A1:BB1", }); return set((state: rswtChkgdnState) => ({ @@ -138,7 +139,7 @@ export const rswtChkgdnActionsExport: (set: any) => rswtChkgdnActions = ( }, rswtChkgdnInit: async (year: string, sggCd: string) => { try { - const rswtChkRes = await initFetch("2024", sggCd); + const rswtChkRes = await initFetch(year, sggCd); return set((state: rswtChkgdnState) => ({ ...state, @@ -161,11 +162,13 @@ export const rswtChkgdnActionsExport: (set: any) => rswtChkgdnActions = ( title: "지도점검 등록", }); } - // const rainChkRes = await initFetch('2024', '0'); + const currentYear = new Date().getFullYear().toString(); + const rswtChkRes = await initFetch(currentYear, "0"); return set((state: rswtChkgdnState) => ({ ...state, rswtChkgdn: { ...state.rswtChkgdn, + rswtChkRes: rswtChkRes }, })); } catch (error) { @@ -184,7 +187,8 @@ export const rswtChkgdnActionsExport: (set: any) => rswtChkgdnActions = ( title: "지도점검 수정", }); } - const rswtChkRes = await initFetch("2024", "0"); + const currentYear = new Date().getFullYear().toString(); + const rswtChkRes = await initFetch(currentYear, "0"); return set((state: rswtChkgdnState) => ({ ...state, rswtChkgdn: { @@ -207,7 +211,8 @@ export const rswtChkgdnActionsExport: (set: any) => rswtChkgdnActions = ( title: "지도점검 삭제", }); } - const rswtChkRes = await initFetch("2024", "0"); + const currentYear = new Date().getFullYear().toString(); + const rswtChkRes = await initFetch(currentYear, "0"); return set((state: rswtChkgdnState) => ({ ...state, rswtChkgdn: { @@ -259,7 +264,8 @@ export const rswtChkgdnActionsExport: (set: any) => rswtChkgdnActions = ( title: "지도점검 수정", }); } - const rswtChkRes = await initFetch("2024", "0"); + const currentYear = new Date().getFullYear().toString(); + const rswtChkRes = await initFetch(currentYear, "0"); return set((state: rswtChkgdnState) => ({ ...state, rswtChkgdn: { diff --git a/apps/wr/app/store/prfmncSttn/chkgdnSttn/rswtChkgdnSttn/excel.tsx b/apps/wr/app/store/prfmncSttn/chkgdnSttn/rswtChkgdnSttn/excel.tsx index ff104bc91..6e6b5730c 100644 --- a/apps/wr/app/store/prfmncSttn/chkgdnSttn/rswtChkgdnSttn/excel.tsx +++ b/apps/wr/app/store/prfmncSttn/chkgdnSttn/rswtChkgdnSttn/excel.tsx @@ -19,6 +19,7 @@ export const buildingExcelRswtSttn = (excelList: any[]) => { String(item.trbt) ?? "", String(item.bod) ?? "", String(item.smll) ?? "", + String(item.crmty) ?? "", String(item.tn) ?? "", String(item.tph) ?? "", String(item.ph) ?? "", diff --git a/apps/wr/app/store/prfmncSttn/chkgdnSttn/rswtChkgdnSttn/headList.tsx b/apps/wr/app/store/prfmncSttn/chkgdnSttn/rswtChkgdnSttn/headList.tsx index f67e98101..91f6bf718 100644 --- a/apps/wr/app/store/prfmncSttn/chkgdnSttn/rswtChkgdnSttn/headList.tsx +++ b/apps/wr/app/store/prfmncSttn/chkgdnSttn/rswtChkgdnSttn/headList.tsx @@ -1,15 +1,15 @@ import type { TableUpperProps } from "@/app/store/common/type/header"; export const rswtChkSttnHeadListExport: 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: "anlOperCst", title: "연간운영비(원/년)", rowSpan: true }, - { id: "prcsCpct", title: "처리용량(m³)", rowSpan: true }, - { id: "spwtPositn", title: "채수지점", rowSpan: true }, - { id: "chckYmd", title: "점검일", rowSpan: true }, + { id: "order", title: "순번", mergeCell: true }, + { id: "cityText", title: "시도", mergeCell: true }, + { id: "sggText", title: "시군구", mergeCell: true }, + { id: "bdstNm", title: "시설명", mergeCell: true }, + { id: "operMngCd", title: "운영관리형태", mergeCell: true }, + { id: "anlOperCst", title: "연간운영비(원/년)", mergeCell: true }, + { id: "prcsCpct", title: "처리용량(m³)", mergeCell: true }, + { id: "spwtPositn", title: "채수지점", mergeCell: true }, + { id: "chckYmd", title: "점검일", mergeCell: true }, { id: "utztnUsgCd", title: "이용용도" }, { id: "splyUswtrUtztnQnt", title: "연간보급수이용량(m³)", type: "number" }, { id: "ruseFcltUtztnQnt", title: "연간중수도이용량(m³)", type: "number" }, diff --git a/apps/wr/components/ui/modal/chkgd/chkgdModal/chkgdModal.tsx b/apps/wr/components/ui/modal/chkgd/chkgdModal/chkgdModal.tsx index 9572d1228..4e3d61008 100644 --- a/apps/wr/components/ui/modal/chkgd/chkgdModal/chkgdModal.tsx +++ b/apps/wr/components/ui/modal/chkgd/chkgdModal/chkgdModal.tsx @@ -144,16 +144,16 @@ export default function ChkgdModal({ type, obj }: { type: string; obj: any }) { const average = { utztnUsgCd: "평균", edit: false, - grssClfgUswtr: total.grssClfgUswtr / onlyData.length, - combResdch: total.combResdch / onlyData.length, - trbt: total.trbt / onlyData.length, - bod: total.bod / onlyData.length, - smll: total.smll / onlyData.length, - crmty: total.crmty / onlyData.length, - tph: total.tph / onlyData.length, - tn: total.tn / onlyData.length, - ph: total.ph / onlyData.length, - clIonQnt: total.clIonQnt / onlyData.length, + grssClfgUswtr: (total.grssClfgUswtr / onlyData.length).toFixed(1), + combResdch: (total.combResdch / onlyData.length).toFixed(1), + trbt: (total.trbt / onlyData.length).toFixed(1), + bod: (total.bod / onlyData.length).toFixed(1), + smll: (total.smll / onlyData.length).toFixed(1), + crmty: (total.crmty / onlyData.length).toFixed(1), + tph: (total.tph / onlyData.length).toFixed(1), + tn: (total.tn / onlyData.length).toFixed(1), + ph: (total.ph / onlyData.length).toFixed(1), + clIonQnt: (total.clIonQnt / onlyData.length).toFixed(1), }; if (!resultPolution[nodeKey]) { From e1fcaad73635457e0cb7fc782ed8dae6874731fb Mon Sep 17 00:00:00 2001 From: jyha00 <jyha@mind-one.co.kr> Date: Thu, 26 Jun 2025 19:35:43 +0900 Subject: [PATCH 012/108] =?UTF-8?q?=EC=9E=A0=EC=9E=AC=EC=88=98=EC=9A=94?= =?UTF-8?q?=EC=B2=98=20=EC=A2=85=EC=97=85=EC=9B=90=20=EC=9E=90=EB=8F=99?= =?UTF-8?q?=ED=95=A9=EA=B3=84=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sttn/sttnDetailModal/coDatailModal.tsx | 26 +++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/apps/wr/components/ui/modal/sttn/sttnDetailModal/coDatailModal.tsx b/apps/wr/components/ui/modal/sttn/sttnDetailModal/coDatailModal.tsx index edffcc71d..b7229a559 100644 --- a/apps/wr/components/ui/modal/sttn/sttnDetailModal/coDatailModal.tsx +++ b/apps/wr/components/ui/modal/sttn/sttnDetailModal/coDatailModal.tsx @@ -7,7 +7,7 @@ import { handleCommaNumberChange } from "@/components/lib/func/commom"; import { DatePicker, Form, FormField, FormUi, ScrollArea } from "@common/business_components/ui"; import { Button, Input } from "@common/components/ui"; import { zodResolver } from "@hookform/resolvers/zod"; -import { useEffect } from "react"; +import { useEffect, useState } from "react"; import { useForm } from "react-hook-form"; import type { z } from "zod"; @@ -33,8 +33,24 @@ export default function SttnDetailModal({ useEffect(() => { if (ldrmntZod) form.reset(formObj); - }, [formObj]); + + useEffect(() => { + const male = Number(form.watch("maleEmpCnt") ?? 0); + const female = Number(form.watch("fmaleEmpCnt") ?? 0); + const frgnrMale = Number(form.watch("frgnrMaleEmpCnt") ?? 0); + const frgnrFemale = Number(form.watch("frgnrFmaleEmpCnt") ?? 0); + + const total = male + female + frgnrMale + frgnrFemale; + + form.setValue("totalEmpCnt", total); + }, [ + form.watch("maleEmpCnt"), + form.watch("fmaleEmpCnt"), + form.watch("frgnrMaleEmpCnt"), + form.watch("frgnrFmaleEmpCnt"), + ]); + return ( <div> <ScrollArea className="h-[85vh]"> @@ -324,6 +340,7 @@ export default function SttnDetailModal({ handleCommaNumberChange(e, field.onChange); }} value={field.value ?? 0} + min={0} /> </FormUi> )} @@ -343,6 +360,7 @@ export default function SttnDetailModal({ field.onChange(e.target.value); }} value={field.value ?? 0} + min={0} /> </FormUi> )} @@ -362,6 +380,7 @@ export default function SttnDetailModal({ field.onChange(e.target.value); }} value={field.value ?? 0} + min={0} /> </FormUi> )} @@ -381,6 +400,7 @@ export default function SttnDetailModal({ field.onChange(e.target.value); }} value={field.value ?? 0} + min={0} /> </FormUi> )} @@ -400,6 +420,8 @@ export default function SttnDetailModal({ field.onChange(e.target.value); }} value={field.value ?? 0} + min={0} + readOnly={true} /> </FormUi> )} From aae94a83ec6171699bd7d622b3a926fd87beb62b Mon Sep 17 00:00:00 2001 From: leedj312 <dlehd467@gmail.com> Date: Thu, 26 Jun 2025 20:19:23 +0900 Subject: [PATCH 013/108] =?UTF-8?q?=EB=B9=97=EB=AC=BC=EC=A7=80=EB=8F=84?= =?UTF-8?q?=EC=A0=90=EA=B2=80=20=EB=93=B1=EB=A1=9D=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/ruseFcltMng/fcltReg/rainReg/page.tsx | 2 + apps/wr/app/store/common/searchCode/index.ts | 48 +++++++++++- .../ruseFcltMng/fcltReg/rainReg/index.tsx | 77 +++++-------------- .../ruseFcltMng/fcltReg/rainReg/type.tsx | 45 +++++++++++ apps/wr/app/store/store.tsx | 12 +-- .../ui/form/fcltReg/bdstReg/bdstReg.tsx | 10 +-- .../ui/form/fcltReg/rainReg/rainRegForm.tsx | 40 ++++++---- .../fcltReg/bdstChgModal/bdstChgModal.tsx | 1 - 8 files changed, 148 insertions(+), 87 deletions(-) create mode 100644 apps/wr/app/store/ruseFcltMng/fcltReg/rainReg/type.tsx diff --git a/apps/wr/app/ruseFcltMng/fcltReg/rainReg/page.tsx b/apps/wr/app/ruseFcltMng/fcltReg/rainReg/page.tsx index 221524918..2fa8b12b8 100644 --- a/apps/wr/app/ruseFcltMng/fcltReg/rainReg/page.tsx +++ b/apps/wr/app/ruseFcltMng/fcltReg/rainReg/page.tsx @@ -38,8 +38,10 @@ export default function Home() { }>({ isOpen: false, chgBuilding: [] }); useEffect(() => { + setIsNewReg( false) if (!rain.isInit) { initRain('41'); + } }, [rain.isInit, initRain]); diff --git a/apps/wr/app/store/common/searchCode/index.ts b/apps/wr/app/store/common/searchCode/index.ts index 8f05eca09..34c2c86c5 100644 --- a/apps/wr/app/store/common/searchCode/index.ts +++ b/apps/wr/app/store/common/searchCode/index.ts @@ -7,8 +7,50 @@ import { import type { pubHasuType } from "../../ruseFcltMng/fcltReg/pbnstReg/type"; import type { DemandCompType } from "@common/business_components/ui/sdmInsertForm/form"; import type { conCostFeeType, rateFeeType } from "./type"; + + +export const comFcltStateExport: comFcltState = { + searchFclts: [] as pubHasuType[], + demandCompList: [], + dmdIsSucees: '', + checkDemand: { + index: 0, + dmdInfoPk: "", + dmdInfoFk: "", + bzmnNo: "", + dmdCoNm: "", + daddr: "", + telno: "", + rgtr: "", + useUsgCd: "", + ctpvCd: "", + sggCd: "", + dmdAplyPswd: "", + wntUntprc: 0, + dmndTmng: "", + dmndQty: 0, + grssClfg: 0, + tn: 0, + combResdch: 0, + tph: 0, + trbt: 0, + ph: 0, + blgcOxygDmndQnt: 0, + clIonQnt: 0, + smll: 0, + cdtnv: 0, + crmty: 0, + rmrk: "", + reqDt: "" + }, + constCostRes:[], + rateFeeRes:[], +}; + +export type comFcltType = comFcltState & comFcltActions; + export type comFcltState = { - searchFclts: pubHasuType; + searchFclts: pubHasuType[]; demandCompList: DemandCompType[]; dmdIsSucees: string; checkDemand: DemandCompType; @@ -16,7 +58,7 @@ export type comFcltState = { rateFeeRes:rateFeeType[], }; -export type comFcltActions = { + type comFcltActions = { getSwtrmtFcltNm: (searchNm: string) => void; getDemandCompList: (searchNm: string, cnt: number) => void; regDemandCompany: (data: DemandCompType) => void; @@ -25,7 +67,7 @@ export type comFcltActions = { getRateFee: (year: string) => void; }; -export const searchActions: (set: any) => comFcltActions = (set: any) => { +export const comFcltSearchAction: (set: any) => comFcltActions = (set: any) => { return { getSwtrmtFcltNm: async (searchNm: string) => { const fcltNm = await getFetch( diff --git a/apps/wr/app/store/ruseFcltMng/fcltReg/rainReg/index.tsx b/apps/wr/app/store/ruseFcltMng/fcltReg/rainReg/index.tsx index 4681f7250..a029bd869 100644 --- a/apps/wr/app/store/ruseFcltMng/fcltReg/rainReg/index.tsx +++ b/apps/wr/app/store/ruseFcltMng/fcltReg/rainReg/index.tsx @@ -2,19 +2,15 @@ import type { TableUpperProps } from "@/app/store/common/type/header"; import { getFetch, postFectch, putFectch } from "@/components/lib/api"; import { z } from "zod"; import { makeMdfr, makeRstr } from "../bdstReg"; - -const validationMessages = { - required: (field: string) => `${field}을(를) 입력해주세요.`, - requiredSelect: (field: string) => `${field}을(를) 선택해주세요.`, - yearFormat: '4자리 연도만 입력하세요', -}; +import toast from "react-hot-toast"; +import { baseReuseBuildingSchema, chgBuildingSchema, validationMessages } from "./type"; export const TEST_UUID= '2d4b151d-1828-41e0-91d9-4cd28ea1cc7e' const rainyObjSchema = z.object({ bdstReuseFcltFk: z.number().nullish(), - pntfColKnd: z.string().default(""), - pntfColArea: z.number().default(0), + pntfColKnd: z.string().min(1, { message: validationMessages.requiredSelect("종류") }), + pntfColArea: z.number().positive({ message: validationMessages.required("면적(m³)") }), dtfcltCpct: z.number().default(0), filtrTrtmfctYn: z.string().default("Y"), filtrTrtmfctCpct: z.number().nullish().default(0), @@ -23,55 +19,15 @@ const rainyObjSchema = z.object({ oilqMeasMthd: z.string().default("").nullish(), rgtr:z.string().nullish(), mdfr:z.string().nullish(), -}).refine((data) => data.pntfColKnd.length > 0, { - message: validationMessages.requiredSelect("종류"), - path: ["pntfColKnd"], -}).refine((data) => data.pntfColArea > 0, { - message: validationMessages.required("면적(m³)"), - path: ["pntfColArea"], }); -const reuseBuildingSchema = z.object({ - bdstRuseFcltPk: z.number().nullish(), - bdstInfoFk:z.number().nullish(), - instlCmptnDt: z.coerce.date().default(() => new Date()), - instlCst: z.number().nullish(), - operMngCd: z.string().optional().default(""), - anlOperCst: z.number().default(0), - instlCstSprtYn: z.string().default("Y"), - instlCstSprtAmt: z.number().nullish(), - grntchBdstCertYn: z.string().default("Y"), - grntchBdstCertYr: z.string().default("") - .refine((year)=> year.length===0 || /^\d{4}$/.test(year),{ - message: validationMessages.yearFormat - }), - grntchBdstCertNo: z.string().default(""), - grntchBdstCertGrd: z.string().default(""), - crgRdctYn: z.string().default("N").nullish(), /// 안쓰는 값 - crgRdctBss: z.string().default("").nullish(), /// 안쓰는 값 - crgRdctAmt: z.number().default(0), // 안쓰는 값 - ruseFcltFlg: z.string().default(""), - bdstUsgCd: z.number().default(0), - lawTrgtYn: z.string().default("Y"), - rgtr:z.string().nullish(), - mdfr:z.string().nullish(), - rainyObj: rainyObjSchema.default({}) -}).refine((data) => data.bdstUsgCd > 0, { - message: validationMessages.requiredSelect("건축물용도"), - path: ["bdstUsgCd"], -}) ; -const chgBuildingSchema = z.object({ - bdstInfoFk: z.number().nullish(), - archChgPrmsnDt: z.coerce.date({required_error: validationMessages.required("허가일")}), - archChgArea: z.string().default("-"), - bdstChgNm: z.string().default(""), - rgtr:z.string().nullish(), - mdfr:z.string().nullish(), -}).refine(data => data.bdstChgNm.length > 0, { - message: validationMessages.requiredSelect("구분"), - path: ["bdstChgNm"] -}); + + + + const rainBuildingSchema = baseReuseBuildingSchema.extend({ + rainyObj: rainyObjSchema, +}) export const rainZod = z.object({ bdstInfoPk: z.number().optional(), @@ -85,7 +41,7 @@ export const rainZod = z.object({ archChgArea: z.number().default(0), rgtr:z.string().nullish(), mdfr:z.string().nullish(), - reuseBuilding: z.array(reuseBuildingSchema).default([]), + reuseBuilding: z.array(rainBuildingSchema).default([]), chgBuilding: z.array(chgBuildingSchema).default([]) }) @@ -219,15 +175,18 @@ export const rainFacActionsExport: (set: any) => rainFacSActions = ( bdstInfoFk: chPK, }) ); - const chch= postFectch("/reuseFc/chgBuilding", makeCh); + const chch= postFectch("/reuseFc/chgBuilding", makeCh); return chch; }).then((chch: any) => { - if(chch.ok) { - alert("등록이 완료되었습니다."); + + console.log('chch ', chch) + if(chch.status ===200) { + toast.success("등록이 완료되었습니다."); + } }) }else{ - alert("등록이 완료되었습니다."); + toast.success("등록이 완료되었습니다."); } } const rainRes = await initFetch(code); diff --git a/apps/wr/app/store/ruseFcltMng/fcltReg/rainReg/type.tsx b/apps/wr/app/store/ruseFcltMng/fcltReg/rainReg/type.tsx new file mode 100644 index 000000000..6e28c3e76 --- /dev/null +++ b/apps/wr/app/store/ruseFcltMng/fcltReg/rainReg/type.tsx @@ -0,0 +1,45 @@ +import { z } from "zod"; + + +export const validationMessages = { + required: (field: string) => `${field}을(를) 입력해주세요.`, + requiredSelect: (field: string) => `${field}을(를) 선택해주세요.`, + yearFormat: '4자리 연도만 입력하세요', + }; + +export const baseReuseBuildingSchema = z.object({ + bdstRuseFcltPk: z.number().nullish(), + bdstInfoFk: z.number().nullish(), + instlCmptnDt: z.coerce.date().default(() => new Date()), + instlCst: z.number().nullish(), + operMngCd: z.string().optional().default(""), + anlOperCst: z.number().default(0), + instlCstSprtYn: z.string().default("Y"), + instlCstSprtAmt: z.number().nullish(), + grntchBdstCertYn: z.string().default("Y"), + grntchBdstCertYr: z.string().default("").refine( + (year) => year.length === 0 || /^\d{4}$/.test(year), + { + message: validationMessages.yearFormat, + } + ), + grntchBdstCertNo: z.string().default(""), + grntchBdstCertGrd: z.string().default(""), + crgRdctYn: z.string().default("N").nullish(), /// 안쓰는 값 + crgRdctBss: z.string().default("").nullish(), /// 안쓰는 값 + crgRdctAmt: z.number().default(0), // 안쓰는 값 + ruseFcltFlg: z.string().default(""), + bdstUsgCd: z.number().min(1, { message: validationMessages.requiredSelect("건축물용도") }).default(0), + lawTrgtYn: z.string().default("Y"), + rgtr: z.string().nullish(), + mdfr: z.string().nullish(), +}); + +export const chgBuildingSchema = z.object({ + bdstInfoFk: z.number().nullish(), + archChgPrmsnDt: z.coerce.date({required_error: validationMessages.required("허가일")}), + archChgArea: z.string().default("-"), + bdstChgNm: z.string().min(1, { message: validationMessages.requiredSelect("구분") }), + rgtr:z.string().nullish(), + mdfr:z.string().nullish(), +}); diff --git a/apps/wr/app/store/store.tsx b/apps/wr/app/store/store.tsx index 4557662f8..7520f7998 100644 --- a/apps/wr/app/store/store.tsx +++ b/apps/wr/app/store/store.tsx @@ -86,7 +86,7 @@ import { import { rswtChkStateExport, rswtChkSttnActionsExport, type rswtChkSttnState, type rswtChkSttnType } from "./prfmncSttn/chkgdnSttn/rswtChkgdnSttn"; import { pbnstSttnActionsExport, pbnstSttnStateExport, type pbnstSttnState, type pbnstSttnType } from "./ruseFcltMng/fcltSttn/pbnstSttn"; import { coSttnActionsExport, coSttnStateExport, type coSttnState, type coSttnType } from "./matDmd/coSttn"; // 옮겨야함 -import { searchActions, type comFcltActions, type comFcltState } from "./common/searchCode"; +import { comFcltStateExport, comFcltSearchAction, type comFcltState, type comFcltType } from "./common/searchCode"; import { pbnstPrfmncActionsExport, pbnstPrfmncStateExport, type pbnstPrfmncState, type pbnstPrfmncType } from "./prfmncReg/pbnstPrfmncReg"; import { coRvwActionsExport, coRvwStateExport, type coRvwState, type coRvwType } from "./matDmd/coMatRvw"; import { ruseHstryActionsExport, ruseHstryStateExport, type ruseHstryState, type ruseHstryType } from "./matDmd/ruseRvwHstry"; @@ -113,7 +113,6 @@ export type WrStore = comCodeType & rainChkSttnType& rswtChkSttnType& pbnstSttnType& - comFcltActions&comFcltState& coSttnType& // 옮겨야함 ldrmntType& // 옮겨야함 pbnstPrfmncType& @@ -124,7 +123,8 @@ export type WrStore = comCodeType & ruseCaseType& rainStatType& rswtStatType& - matStatType + matStatType& + comFcltType ; export const StoreZus = ( @@ -141,6 +141,7 @@ export const StoreZus = ( rswtSttnState & htdflSttnState & rainChkSttnState& + comFcltState& rswtChkSttnState & pbnstSttnState& coSttnState& // 옮겨야함 @@ -180,7 +181,8 @@ export const StoreZus = ( ...ruseCaseStateExport, ...rainStatExport, ...rswtStatExport, - ...matStatExport + ...matStatExport, + ...comFcltStateExport } ) => { return createStore<WrStore>()((set) => { @@ -192,7 +194,7 @@ export const StoreZus = ( getEnvPartCd(set); return { ...initState, - ...searchActions(set), + ...comFcltSearchAction(set), ...pubFacActionsExport(set), ...rainFacActionsExport(set), ...rswtFacActionsExport(set), diff --git a/apps/wr/components/ui/form/fcltReg/bdstReg/bdstReg.tsx b/apps/wr/components/ui/form/fcltReg/bdstReg/bdstReg.tsx index 5ab5edf98..0d00454e2 100644 --- a/apps/wr/components/ui/form/fcltReg/bdstReg/bdstReg.tsx +++ b/apps/wr/components/ui/form/fcltReg/bdstReg/bdstReg.tsx @@ -171,7 +171,7 @@ export default function BdstReg({ form }: { form: any }) { <FormUi label="위치"> <Input className={selectClass} - placeholder="1,000,000" + placeholder="" type="search" {...field} /> @@ -193,7 +193,7 @@ export default function BdstReg({ form }: { form: any }) { onSelectValue={(val) => { field.onChange(Number(val)); }} - value={field.value} + value={field.value??code.bdstUsgCd[0]?.val} /> </FormUi> )} @@ -208,7 +208,7 @@ export default function BdstReg({ form }: { form: any }) { <DatePicker mode={"single"} field={{ - value: field.value || new Date(), + value: field.value?? new Date(), onChange: (date: Date | undefined) => field.onChange(date), }} selectClass={'w-full'} @@ -226,7 +226,7 @@ export default function BdstReg({ form }: { form: any }) { <Input className={selectClass} placeholder="1,000,000" - type="number" + type="text" onChange={(e) => { handleCommaNumberChange(e, field.onChange); }} @@ -250,7 +250,7 @@ export default function BdstReg({ form }: { form: any }) { onSelectValue={(val) => { field.onChange(val); }} - value={field.value} + value={field.value??code.yn[0]?.val } /> </FormUi> )} diff --git a/apps/wr/components/ui/form/fcltReg/rainReg/rainRegForm.tsx b/apps/wr/components/ui/form/fcltReg/rainReg/rainRegForm.tsx index 40a9bb436..fc4ed566e 100644 --- a/apps/wr/components/ui/form/fcltReg/rainReg/rainRegForm.tsx +++ b/apps/wr/components/ui/form/fcltReg/rainReg/rainRegForm.tsx @@ -46,32 +46,40 @@ export default function RainRegForm({ ]); const cleanRainData = (raw: any): z.infer<typeof rainZod> => { + const buildings = + raw?.reuseBuilding && raw.reuseBuilding.length > 0 + ? raw.reuseBuilding + : [{}]; return { ...raw, - frstArchPrmsnDt: new Date(raw?.frstArchPrmsnDt || null), + frstArchPrmsnDt: raw?.frstArchPrmsnDt + ? new Date(raw.frstArchPrmsnDt) + : new Date(), // raw?.frstArchPrmsnDt가 유효하지 않으면 현재 날짜로 초기화 archChgPrmsnDt: raw?.archChgPrmsnDt ? new Date(raw?.archChgPrmsnDt) : null, - reuseBuilding: (raw?.reuseBuilding || []).map((item: any) => ({ + reuseBuilding: buildings.map((item: any) => ({ ...item, - instlCmptnDt: new Date(item?.instlCmptnDt), + instlCmptnDt: item?.instlCmptnDt + ? new Date(item.instlCmptnDt) + : new Date(), // item?.instlCmptnDt가 유효하지 않으면 현재 날짜로 초기화 operMngCd: item?.operMngCd ?? "", // null → "" grntchBdstCertYr: typeof item?.grntchBdstCertYr === "string" ? item?.grntchBdstCertYr : "", // null → "" - instlCstSprtYn: - item?.instlCstSprtYn ?? "Y", // null → "N" - // Ensure enum fields are null if empty string to trigger Zod default + instlCstSprtYn: item?.instlCstSprtYn ?? "Y", // null → "Y" + grntchBdstCertYn:item?.grntchBdstCertYn??"Y", rainyObj: { ...item?.rainyObj, - filtrTrtmfctYn: item?.rainyObj?.filtrTrtmfctYn ??"Y" + filtrTrtmfctYn: item?.rainyObj?.filtrTrtmfctYn ?? "Y", }, })), }; }; useEffect(() => { + if (grntchBdstCertYn === "N") { form.setValue("reuseBuilding.0.grntchBdstCertYr", ""); form.setValue("reuseBuilding.0.grntchBdstCertNo", ""); @@ -84,6 +92,9 @@ export default function RainRegForm({ } }, [grntchBdstCertYn, instlCstSprtYn, filtrTrtmfctYn]); + + + // form 값이 변경될 때마다 로그 출력 useEffect(() => { const subscription = form.watch((value) => { @@ -148,9 +159,8 @@ export default function RainRegForm({ value: field.value && !isNaN(new Date(field.value).getTime()) ? new Date(field.value) - : new Date(), // Provide a default Date value - onChange: (date) => field.onChange(date), - + : new Date(), + onChange: (date: Date | undefined) => field.onChange(date), }} selectClass={"w-full"} readOnly={readonly} @@ -247,7 +257,7 @@ export default function RainRegForm({ label={""} selectArray={code.yn} onSelectValue={readonly ? () => { } : field.onChange} - value={field.value || code.yn[0]?.val} + value={field.value ?? code.yn[0]?.val} readOnly={readonly} /> </FormUi> @@ -268,7 +278,8 @@ export default function RainRegForm({ if (!readonly) handleCommaNumberChange(e, field.onChange); }} value={formatCommaNumber(field.value)} - readOnly={filtrTrtmfctYn === "Y"} + readOnly={readonly } + disabled={filtrTrtmfctYn !== "Y"} /> </FormUi> )} @@ -286,7 +297,8 @@ export default function RainRegForm({ type="text" value={field.value ?? ""} onChange={field.onChange} - readOnly={filtrTrtmfctYn === "Y" } + readOnly={readonly } + disabled={filtrTrtmfctYn !== "Y"} /> </FormUi> )} @@ -335,7 +347,7 @@ export default function RainRegForm({ if (!readonly) handleCommaNumberChange(e, field.onChange); }} value={formatCommaNumber(field.value)} - readOnly={instlCstSprtYn === "Y"} + readOnly={instlCstSprtYn !== "Y"} /> </FormUi> )} diff --git a/apps/wr/components/ui/modal/fcltReg/bdstChgModal/bdstChgModal.tsx b/apps/wr/components/ui/modal/fcltReg/bdstChgModal/bdstChgModal.tsx index 03c257c59..0b071aa91 100644 --- a/apps/wr/components/ui/modal/fcltReg/bdstChgModal/bdstChgModal.tsx +++ b/apps/wr/components/ui/modal/fcltReg/bdstChgModal/bdstChgModal.tsx @@ -14,7 +14,6 @@ const {bdst,rain} = useWrStore((state) => { return state; }); const { bdstChgHeadList } = bdst; - console.log("chgBuilding", chgBuilding); return ( From 7053966007354362c37c69c7b5c775933fe4efb8 Mon Sep 17 00:00:00 2001 From: YangJinSoo94 <jschrome782@gmail.com> Date: Fri, 27 Jun 2025 09:28:57 +0900 Subject: [PATCH 014/108] =?UTF-8?q?=EB=AC=BC=EC=9E=AC=EC=9D=B4=EC=9A=A9?= =?UTF-8?q?=EC=82=AC=EB=A1=80=EA=B4=80=EB=A6=AC=20=EC=97=91=EC=85=80?= =?UTF-8?q?=EB=93=B1=EB=A1=9D=ED=8C=9D=EC=97=85=20=EB=93=B1=EB=A1=9D=20?= =?UTF-8?q?=EB=B0=8F=20=ED=8C=9D=EC=97=85=EB=93=B1=EB=A1=9D=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wr/app/matMng/coMat/ruseCaseMng/page.tsx | 86 +++++---- .../wr/app/store/matDmd/ruseCaseMng/excel.tsx | 109 +++++++++++ .../wr/app/store/matDmd/ruseCaseMng/index.tsx | 110 ++++++++++- .../ruseCaseMng/ruseCaseMngExcelModal.tsx | 172 ++++++++++++++++++ 4 files changed, 432 insertions(+), 45 deletions(-) create mode 100644 apps/wr/app/store/matDmd/ruseCaseMng/excel.tsx create mode 100644 apps/wr/components/ui/modal/coMat/ruseCaseMng/ruseCaseMngExcelModal.tsx diff --git a/apps/wr/app/matMng/coMat/ruseCaseMng/page.tsx b/apps/wr/app/matMng/coMat/ruseCaseMng/page.tsx index 4950ab0da..ad5a4f751 100644 --- a/apps/wr/app/matMng/coMat/ruseCaseMng/page.tsx +++ b/apps/wr/app/matMng/coMat/ruseCaseMng/page.tsx @@ -1,30 +1,48 @@ "use client"; + import { useWrStore } from "@/app/store"; import { ruseUtzType } from "@/app/store/matDmd/ruseCaseMng/headList"; import { getCity } from "@/components/lib/func/commom"; +import RuseCaseMngExcel from "@/components/ui/modal/coMat/ruseCaseMng/ruseCaseMngExcelModal"; import type { objSearchType } from "@/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal"; -import { ContentsDiv, SearchDiv, SearchFormLeft, SearchFormRight, SearchInput, SelectBox, Title, UiTable, type SelectDataType } from "@common/business_components/ui"; +import { + ContentsDiv, + SearchDiv, + SearchFormLeft, + SearchFormRight, + SearchInput, + SelectBox, + Title, + UiModal, + UiTable, + type SelectDataType, +} from "@common/business_components/ui"; import ListDiv from "@common/business_components/ui/list_div"; import { Button } from "@common/components/ui"; import { useState } from "react"; export default function RuseCaseMng() { - const { code,ruseCase} = useWrStore((state) => state); - const [selectSgg, setSelectSgg] = useState<SelectDataType[] | []>() + const { code, ruseCaseMng } = useWrStore((state) => state); + const [selectSgg, setSelectSgg] = useState<SelectDataType[] | []>(); const [objSearch, setObjSearch] = useState<objSearchType>({ sgg: "", city: "" }); - const [selectUtz, setSelectUtz] = useState<string[] | []>() - const [searchTxt, setSearchTxt] = useState<string>() + const [selectUtz, setSelectUtz] = useState<string[] | []>(); + const [searchTxt, setSearchTxt] = useState<string>(); const getSelCity = (val: string | number) => { const cityValue = String(val); const data = getCity(cityValue, code.sggCd); setSelectSgg(data); - setObjSearch({ city: cityValue, sgg: '' }); + setObjSearch({ city: cityValue, sgg: "" }); }; const getSgg = (val: string | number) => { - setObjSearch({ ...objSearch, sgg: String(val) }) + setObjSearch({ ...objSearch, sgg: String(val) }); }; + + const [isExcelOpen, setIsExcelOpen] = useState<{ + isModal: boolean; + }>({ isModal: false }); + return ( <> <ContentsDiv> @@ -54,47 +72,32 @@ export default function RuseCaseMng() { selectClass={""} selectArray={ruseUtzType} onSelectValue={(val) => { - if(val) - setSelectUtz([String(val)])}} + if (val) setSelectUtz([String(val)]); + }} /> - <SearchInput className={"text-2xl"} label="수요처명" textValue={""} setText={(e) => { - setSearchTxt(e) + setSearchTxt(e); }} /> </div> - </SearchFormLeft> - <SearchFormRight - className="w-2/12 mx-5 flex flex-col items-center justify-center space-x-2 "> + <SearchFormRight className="w-2/12 mx-5 flex flex-col items-center justify-center space-x-2"> <div className="space-y"> <div className="flex flex-row space-x-2"> - <Button - size={"sm"} - onClick={(e) => { }} - > + <Button size={"sm"} onClick={() => {}}> 조회 </Button> - <Button - size={"sm"} - onClick={(e) => { }} - > + <Button size={"sm"} onClick={() => { setIsExcelOpen({ isModal: true }); }}> 엑셀등록 </Button> - <Button - size={"sm"} - onClick={(e) => { }} - > + <Button size={"sm"} onClick={() => {}}> 다운로드 </Button> - <Button - size={"sm"} - onClick={(e) => { }} - > + <Button size={"sm"} onClick={() => {}}> 양식받기 </Button> </div> @@ -103,24 +106,33 @@ export default function RuseCaseMng() { </SearchDiv> </div> </div> - <div className="grid grid-cols-6 gap-4 "> + <div className="grid grid-cols-6 gap-4"> <div className="col-span-6"> <ListDiv> <UiTable headName={"물재이용사례 현황"} tableData={[]} children={undefined} - headlist={ruseCase.ruseHeadList} + headlist={ruseCaseMng.ruseHeadList} pageSize={20} total={100000} - cellClick={(index: number | undefined, headId: string) => { - }} - onPageChange={() => { }} - ></UiTable> + cellClick={(index: number | undefined, headId: string) => {}} + onPageChange={() => {}} + /> </ListDiv> </div> </div> </ContentsDiv> + + {/* 엑셀 등록 모달 */} + <UiModal + open={isExcelOpen.isModal} + onOpenChange={() => { + setIsExcelOpen({ isModal: !isExcelOpen.isModal }) + }} + bodyClass={"w-[40vw] max-w-none"} + body={<RuseCaseMngExcel />} + /> </> - ) + ); } \ No newline at end of file diff --git a/apps/wr/app/store/matDmd/ruseCaseMng/excel.tsx b/apps/wr/app/store/matDmd/ruseCaseMng/excel.tsx new file mode 100644 index 000000000..ebbaa8b7a --- /dev/null +++ b/apps/wr/app/store/matDmd/ruseCaseMng/excel.tsx @@ -0,0 +1,109 @@ +import { z } from "zod"; + +export const reUseCaseExcel = z.object({ + file: z.any() +}); + +type nameMap = { + [key: string]: string; +} + +export const reUseCaseNames: nameMap = { + "사업명": "ruseBizNm", + "사업시작일": "bizBgngDate", + "사업종료일": "bizEndtDate", + "공급처_시설종류": "splyFcltKnd", + "공급처명": "splyFcltNm", + "공급처_시설용량(㎥/일)": "splyFcltCpct", + "수요처명": "dmdFcltNm", + "수요처_주소": "dmdFcltAddr", + "재이용시설_시설용량(㎥/일)": "ruseFcltCpct", + "재이용시설_관로(km)": "ruseFcltPplLen", + "재이용시설_용도": "ruseFcltUsg", + "재이용시설_공급량(㎥/일)": "ruseFcltSplyCpct", + "재이용시설_처리공법": "ruseFcltPrcsMsrc", + "보증수질_용도별": "grnteWqUsg", + "보증수질_총대장균군수": "grnteWqGrssClfg", + "보증수질_결합잔류염소": "grnteWqCombResdch", + "보증수질_탁도": "grnteWqTrbt", + "보증수질_생물학적산소요구량(BOD)(mg/L)": "grnteWqBod", + "보증수질_냄새": "grnteWqSmll", + "보증수질_색도(도)": "grnteWqCrmty", + "보증수질_총질소(T-N)(mg/L)": "grnteWqTn", + "보증수질_총인(T-P)(mg/L)": "grnteWqTp", + "보증수질_수소이온농도(pH)": "grnteWqPh", + "보증수질_염화물": "grnteWqClIonQnt", + "보증수질_전기전도도": "grnteWqCndctStat", + "보증수질_비고": "grnteWqRmrk", + "소요사업비_총(백만원)": "reqBizGrssCst", + "소요사업비_토목_재이용시설(백만원)": "reqBizReuseFcltCst", + "소요사업비_토목_관로(백만원)": "reqBizPplCst", + "소요사업비_건축(백만원)": "reqBizArchCst", + "소요사업비_기계(백만원)": "reqBizMchnCst", + "소요사업비_전기.계측(백만원)": "reqBizMtrngCst", + "소요사업비_GIS DB(백만원)": "reqBizGisCst", + "소요사업비_CCTV(백만원)": "reqBizCctvCst", + "소요사업비_시공 및 준공(백만원)": "reqBizCmcnCst", + "소요사업비_폐기물(백만원)": "reqBizWstCst", + "소요사업비_설계비(백만원)": "reqBizDesignCst", + "소요사업비_감리비(백만원)": "reqBizSpvsCst", + "소요사업비_시설부대비(백만원)": "reqBizSbsdCst", + "소요사업비_기타(백만원)": "reqBizEtc", + "재원조달계획_총(백만원)": "prcmPlanGrssCst", + "재원조달계획_국비(백만원)": "prcmPlanNlep", + "재원조달계획_지방비(백만원)": "prcmPlanLcep", + "재원조달계획_원인자부담(백만원)": "prcmPlanBrdnCst", + "재원조달계획_국고비율(백만원)": "prcmPlanEcqrRt", + "사업비 정산_총(백만원)": "clclnGrssCst", + "사업비 정산_국비(백만원)": "clclnNlep", + "사업비 정산_지방비(백만원)": "clclnLcep", + "사업비 정산_원인자부담(백만원)": "clclnBrdnCst", + "사업비 정산_국고비율(백만원)": "clclnEcqrRt" +} + +interface PbnstSttnItem { + rvbsn: string; + lgbsn: number; + mdmbsn?: string; + rgnNm?: string; + sido?: number; + gugun?: number; + bscFctNm?: string; + efflWqltStndrdNm?: string; + fctCpc?: number; + tel?: string; + ruseUtztnFcltNm?: string; + fcltCpct?: number; + splyExtraCpct?: number; + ruseCpct?: number; + usageTotal?: number; + inUsage?: number; + outUsage?: number; +} + +export const excelPbnstSttnParsing = (data: PbnstSttnItem[]): string[][] => { + const excelExportData: string[][] = []; + + data.forEach((item) => { + excelExportData.push([ + item.rvbsn, + String(item.lgbsn), + item.mdmbsn ?? "", + item.rgnNm ?? "", + String(item.sido ?? ""), + String(item.gugun ?? ""), + String(item.bscFctNm ?? ""), + String(item.efflWqltStndrdNm ?? ""), + String(item.fctCpc ?? ""), + String(item.tel ?? ""), + String(item.ruseUtztnFcltNm ?? ""), + String(item.fcltCpct ?? ""), + String(item.splyExtraCpct ?? ""), + String(item.ruseCpct ?? ""), + String(item.usageTotal ?? ""), + String(item.inUsage ?? ""), + String(item.outUsage ?? ""), + ]) + }) + return excelExportData; +}; \ No newline at end of file diff --git a/apps/wr/app/store/matDmd/ruseCaseMng/index.tsx b/apps/wr/app/store/matDmd/ruseCaseMng/index.tsx index c54b0db1a..274af07af 100644 --- a/apps/wr/app/store/matDmd/ruseCaseMng/index.tsx +++ b/apps/wr/app/store/matDmd/ruseCaseMng/index.tsx @@ -1,24 +1,118 @@ +import { fetchType, postFectch } from "@/components/lib/api"; import type { TableUpperProps } from "../../common/type/header"; import { ruseCaseHeadListExport } from "./headList"; +import { ToastFormUi } from "@/components/ui/toast/toastFromUi"; export type ruseCaseState = { - ruseCase: { - ruseHeadList:TableUpperProps[]; - }; + ruseCaseMng: { + ruseHeadList:TableUpperProps[]; + ruseCaseMngList:TableUpperProps[]; }; -type ruseCaseActions = { }; + +export interface ruseCaseMngExcelProps { + ruseBizNm: string, + bizBgngDate: string, + bizEndDate: string, + splyFcltKnd: string, + splyFcltNm: string, + splyFcltCpct: number | 0 |'', + dmdFcltNm: string, + dmdFcltAddr: string, + ruseFcltCpct: number | 0 |'', + ruseFcltPplLen: string, + ruseFcltUsg: string, + ruseFcltSplyCpct: number | 0 |'', + ruseFcltPrcsMsrc: string, + grnteWqUsg: string, + grnteWqGrssClfg: number | 0 |'', + grnteWqCombResdch: number | 0 |'', + grnteWqTrbt: string, + grnteWqBod: string, + grnteWqSmll: string, + grnteWqCrmty: string, + grnteWqTn: string, + grnteWqTp: string, + grnteWqPh: string, + grnteWqClIonQnt: string, + grnteWqCndctStat: string, + grnteWqRmrk: string, + reqBizReuseFcltCst: number | 0 |'', + reqBizPplCst: number | 0 |'', + reqBizArchCst: number | 0 |'', + reqBizMchnCst: number | 0 |'', + reqBizMtrngCst: number | 0 |'', + reqBizGisCst: number | 0 |'', + reqBizCctvCst: number | 0 |'', + reqBizCmcnCst: number | 0 |'', + reqBizWstCst: number | 0 |'', + reqBizDesignCst: number | 0 |'', + reqBizSpvsCst: number | 0 |'', + reqBizSbsdCst: number | 0 |'', + reqBizEtc: string, + reqBizGrssCst: number | 0 |'', + prcmPlanNlep: number | 0 |'', + prcmPlanLcep: number | 0 |'', + prcmPlanBrdnCst: number | 0 |'', + prcmPlanEcqrRt: number | 0 |'', + prcmPlanGrssCst: number | 0 |'', + clclnNlep: number | 0 |'', + clclnLcep: number | 0 |'', + clclnBrdnCst: number | 0 |'', + clclnEcqrRt: number | 0 |'', + clclnGrssCst: number | 0 |'', +} + +type ruseCaseActions = { + selectRuseCaseMng: (sidoCd: string, sigunguCd: string, usageCd : string, name: string) => Promise<void>; + insertRuseCaseMng: (data: any) => Promise<void>; +}; + +export const fetchSearchRuseCaseMng = async (sidoCd: string, sigunguCd: string, usageCd : string, name: string) => { + const tailUrl = (sidoCd && !sigunguCd ? `/${sigunguCd}` : sigunguCd ? `/${sigunguCd}` : '/0') + (usageCd ? `/${usageCd}` : '') + (name ? `/${name}` : ''); + console.log('tailUrl : ', tailUrl); + const response = await postFectch(`/reuseFc/pubFcRuseCase${tailUrl}`, {usageCd, name}, fetchType.WR); + return []; +}; + +export const fetchInsertRuseCaseMng = async (data: any) => { + console.log('data : ', data) + const response = await postFectch('/reuseFc/pubFcRuseCase', data, fetchType.WR); + return response.data; +}; + export type ruseCaseType = ruseCaseState & ruseCaseActions; export const ruseCaseStateExport: ruseCaseState = { - ruseCase: { - ruseHeadList:ruseCaseHeadListExport, + ruseCaseMng: { + ruseHeadList:ruseCaseHeadListExport, + ruseCaseMngList:[], }, }; -export const ruseCaseActionsExport: (set: any) => ruseCaseActions = ( + +export const ruseCaseActionsExport: (set: any) => ruseCaseActions = ( set: any ) => { return { - + selectRuseCaseMng: async (sidoCd: string, sigunguCd: string, usageCd : string, name: string) => { + const res = await fetchSearchRuseCaseMng(sidoCd, sigunguCd, usageCd, name); + + set((state: ruseCaseState) => ({ + ruseCaseMng: { + ...state.ruseCaseMng, + ruseCaseMngList: data, + }, + })); + }, + + insertRuseCaseMng: async (data: any) => { + const response = await fetchInsertRuseCaseMng(data); + if(response.status === 200){ + ToastFormUi({ + body: "잠재수요처 등록이 완료되었습니다. ", + title: "잠재수요처 등록", + }); + } + }, }; }; diff --git a/apps/wr/components/ui/modal/coMat/ruseCaseMng/ruseCaseMngExcelModal.tsx b/apps/wr/components/ui/modal/coMat/ruseCaseMng/ruseCaseMngExcelModal.tsx new file mode 100644 index 000000000..57424996e --- /dev/null +++ b/apps/wr/components/ui/modal/coMat/ruseCaseMng/ruseCaseMngExcelModal.tsx @@ -0,0 +1,172 @@ +import { coRegExcel } from "@/app/store/matDmd/coSttn/headList"; +import type { ruseCaseMngExcelProps } from "@/app/store/matDmd/ruseCaseMng/index"; +import { reUseCaseNames } from "@/app/store/matDmd/ruseCaseMng/excel"; +import { filePicked, FileUpload, FormUi, Title } from "@common/business_components/ui"; +import { Form, FormField } from "@common/business_components/ui/form"; +import { Button } from "@common/components/ui"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { useState } from "react"; +import { useForm } from "react-hook-form"; +import type { z } from "zod"; +import { useWrStore } from "@/app/store"; + +export default function RuseCaseMngExcel() { + const { insertRuseCaseMng } = useWrStore((state) => state); + const form = useForm<z.infer<typeof coRegExcel>>({ + resolver: zodResolver(coRegExcel), + }); + + const [file, setFiles] = useState<File | undefined>(); + + const onSubmit = () => { + if (file) { + filePicked(file, 0).then((res: unknown) => { + try { + const newArray: ruseCaseMngExcelProps[] = [] + const dataArray = Array.isArray(res) ? res : []; + dataArray.forEach((item: Record<string, unknown>) => { + const object: ruseCaseMngExcelProps = { + ruseBizNm: '', + bizBgngDate: '', + bizEndDate: '', + splyFcltKnd: '', + splyFcltNm: '', + splyFcltCpct: 0, + dmdFcltNm: '', + dmdFcltAddr: '', + ruseFcltCpct: 0, + ruseFcltPplLen: '', + ruseFcltUsg: '', + ruseFcltSplyCpct: 0, + ruseFcltPrcsMsrc: '', + grnteWqUsg: '', + grnteWqGrssClfg: 0, + grnteWqCombResdch: 0, + grnteWqTrbt: '', + grnteWqBod: '', + grnteWqSmll: '', + grnteWqCrmty: '', + grnteWqTn: '', + grnteWqTp: '', + grnteWqPh: '', + grnteWqClIonQnt: '', + grnteWqCndctStat: '', + grnteWqRmrk: '', + reqBizReuseFcltCst: 0, + reqBizPplCst: 0, + reqBizArchCst: 0, + reqBizMchnCst: 0, + reqBizMtrngCst: 0, + reqBizGisCst: 0, + reqBizCctvCst: 0, + reqBizCmcnCst: 0, + reqBizWstCst: 0, + reqBizDesignCst: 0, + reqBizSpvsCst: 0, + reqBizSbsdCst: 0, + reqBizEtc: '', + reqBizGrssCst: 0, + prcmPlanNlep: 0, + prcmPlanLcep: 0, + prcmPlanBrdnCst: 0, + prcmPlanEcqrRt: 0, + prcmPlanGrssCst: 0, + clclnNlep: 0, + clclnLcep: 0, + clclnBrdnCst: 0, + clclnEcqrRt: 0, + clclnGrssCst: 0, + }; + + Object.keys(reUseCaseNames).forEach((key: string) => { + if (item && reUseCaseNames[key] !== undefined) { + const mappedKey = reUseCaseNames[key]; + if (mappedKey && object.hasOwnProperty(mappedKey)) { + let value; + + if(value === "-"){ + value = ''; + } + if(key === "사업시작일" || key === "사업종료일"){ + if(item[key]?.toString().split(".")[1]?.length === 1){ + value = `${item[key]?.toString().split(".")[0] || ""}${item[key]?.toString().split(".")[1]?.length === 1 ? "0" : ""}${item[key]?.toString().split(".")[1] || ""}` + }else{ + value = `${item[key]?.toString().split(".")[0] || ""}${item[key]?.toString().split(".")[1] || ""}` + } + }else if(key === "재이용시설_관로(km)" || key?.toString().includes("보증수질_")){ + value = item[key]?.toString().trim() || ''; + }else{ + if(item[key]?.toString().includes("%") && Number(item[key]?.toString().replaceAll("%", ""))){ + value = item[key]?.toString().replaceAll("%", ""); + }else{ + value = item[key]; + } + } + + if(value === null || value?.toString().trim() === "-" || value?.toString().trim() === ""){ + if (typeof object[mappedKey as keyof ruseCaseMngExcelProps] === 'number') { + (object as unknown as Record<string, unknown>)[mappedKey] = ''; + } else { + (object as unknown as Record<string, unknown>)[mappedKey] = ''; + } + }else if (typeof object[mappedKey as keyof ruseCaseMngExcelProps] === 'number' && Number(value?.toString().replaceAll("%", "").replaceAll(",", ""))) { + (object as unknown as Record<string, unknown>)[mappedKey] = Number(value?.toString().replaceAll("%", "").replaceAll(",", "")); + }else{ + (object as unknown as Record<string, unknown>)[mappedKey] = value?.toString().trim() || ''; + } + } + } + }); + newArray.push(object); + }); + console.log("newArray : ", newArray); + insertRuseCaseMng(newArray); + } catch (e) { + console.log(e) + } + }); + }; + } + + return ( + <> + <div> + <Form {...form}> + <form onSubmit={form.handleSubmit(onSubmit)}> + <div> + <Title title={"물재이용 사례 등록"}> +
+
+

+ {"물재이용 사례 파일"} +

+ +
+ +
+ ( + + { + console.log("files", files) + if (files && files?.length === 1) setFiles(files[0]); + }} + fileTypes="" + maxFiles={1} + > + + )} + /> +
+ + +
+ + ); +} \ No newline at end of file From f949319db3a30920eff8cf4a57cced7ef52ed4ca Mon Sep 17 00:00:00 2001 From: YangJinSoo94 Date: Fri, 27 Jun 2025 09:31:56 +0900 Subject: [PATCH 015/108] =?UTF-8?q?=EC=A1=B0=ED=9A=8C=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B0=9C=EB=B0=9C=EC=A7=84=ED=96=89=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wr/app/matMng/coMat/ruseCaseMng/page.tsx | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/apps/wr/app/matMng/coMat/ruseCaseMng/page.tsx b/apps/wr/app/matMng/coMat/ruseCaseMng/page.tsx index ad5a4f751..fecbb4ac6 100644 --- a/apps/wr/app/matMng/coMat/ruseCaseMng/page.tsx +++ b/apps/wr/app/matMng/coMat/ruseCaseMng/page.tsx @@ -22,12 +22,22 @@ import { Button } from "@common/components/ui"; import { useState } from "react"; export default function RuseCaseMng() { - const { code, ruseCaseMng } = useWrStore((state) => state); + const { code, ruseCaseMng, selectRuseCaseMng } = useWrStore((state) => state); const [selectSgg, setSelectSgg] = useState(); const [objSearch, setObjSearch] = useState({ sgg: "", city: "" }); const [selectUtz, setSelectUtz] = useState(); const [searchTxt, setSearchTxt] = useState(); + const [sidoCd, setSidoCd] = useState(""); + const [sigunguCd, setSigunguCd] = useState(""); + const [usageCd, setUsageCd] = useState(""); + const [name, setName] = useState(""); + + const searchRuseCaseMng = async () => { + const res = await selectRuseCaseMng(sidoCd, sigunguCd, usageCd, name); + console.log('res : ', res); + } + const getSelCity = (val: string | number) => { const cityValue = String(val); const data = getCity(cityValue, code.sggCd); From 401b68fe24ad83c0ef2adc94c7f81f19e4a8b2bf Mon Sep 17 00:00:00 2001 From: YangJinSoo94 Date: Fri, 27 Jun 2025 11:21:03 +0900 Subject: [PATCH 016/108] =?UTF-8?q?=EB=AC=BC=EC=9E=AC=EC=9D=B4=EC=9A=A9?= =?UTF-8?q?=EC=82=AC=EB=A1=80=EA=B4=80=EB=A6=AC=20=EC=A1=B0=ED=9A=8C?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=20=ED=99=95=EC=9D=B8=EC=A4=91,=20=EC=83=98?= =?UTF-8?q?=ED=94=8C=EC=96=91=EC=8B=9D=20=EB=8B=A4=EC=9A=B4=EB=A1=9C?= =?UTF-8?q?=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wr/app/matMng/coMat/ruseCaseMng/page.tsx | 69 ++++++++++++------ .../wr/app/store/matDmd/ruseCaseMng/index.tsx | 9 ++- apps/wr/public/sample_ruseCaseMng.xlsx | Bin 0 -> 19429 bytes 3 files changed, 53 insertions(+), 25 deletions(-) create mode 100644 apps/wr/public/sample_ruseCaseMng.xlsx diff --git a/apps/wr/app/matMng/coMat/ruseCaseMng/page.tsx b/apps/wr/app/matMng/coMat/ruseCaseMng/page.tsx index ac68c4a66..5e4bddaad 100644 --- a/apps/wr/app/matMng/coMat/ruseCaseMng/page.tsx +++ b/apps/wr/app/matMng/coMat/ruseCaseMng/page.tsx @@ -2,7 +2,7 @@ import { useWrStore } from "@/app/store"; import { ruseUtzType } from "@/app/store/matDmd/ruseCaseMng/headList"; -import { getCity } from "@/components/lib/func/commom"; +import { excelDownload, getCity } from "@/components/lib/func/commom"; import RuseCaseMngExcel from "@/components/ui/modal/coMat/ruseCaseMng/ruseCaseMngExcelModal"; import type { objSearchType } from "@/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal"; import { @@ -23,36 +23,61 @@ import { useState } from "react"; export default function RuseCaseMng() { const { code, ruseCaseMng, selectRuseCaseMng } = useWrStore((state) => state); - const [selectSgg, setSelectSgg] = useState(); + const [selectSgg, setSelectSgg] = useState([{val: "0", text: "전체"}]); const [objSearch, setObjSearch] = useState({ sgg: "", city: "" }); - const [selectUtz, setSelectUtz] = useState(); - const [searchTxt, setSearchTxt] = useState(); - + const [sidoCd, setSidoCd] = useState(""); const [sigunguCd, setSigunguCd] = useState(""); - const [usageCd, setUsageCd] = useState(""); + const [selectUtz, setSelectUtz] = useState(["조경용수"]); const [name, setName] = useState(""); + // 물재이용사례 조회 const searchRuseCaseMng = async () => { - const res = await selectRuseCaseMng(sidoCd, sigunguCd, usageCd, name); + console.log(selectUtz); + const res = await selectRuseCaseMng(sidoCd, sigunguCd, selectUtz, name); console.log('res : ', res); } + // 시도 선택 const getSelCity = (val: string | number) => { const cityValue = String(val); + const cityText = code.cityCd.find((item: any) => item.cityCd === cityValue)?.text; + setSidoCd(cityText || ""); + const data = getCity(cityValue, code.sggCd); setSelectSgg(data); setObjSearch({ city: cityValue, sgg: "" }); }; + // 시군구 선택 const getSgg = (val: string | number) => { + const sggValue = String(val); + const sggText = selectSgg?.find((item: any) => item.sggCd === sggValue)?.sggNm; + setSigunguCd(sggText || ""); setObjSearch({ ...objSearch, sgg: String(val) }); }; + // 엑셀 등록 모달 const [isExcelOpen, setIsExcelOpen] = useState<{ isModal: boolean; }>({ isModal: false }); + // 다운로드 + const download = () => { + console.log("다운로드"); + } + + // 양식받기 + const sampleDownLoad = () => { + excelDownload( + [[]], + "물재이용사례 관리", + "sample_ruseCaseMng.xlsx", + "A1:AX1", + 2 + ); + } + return ( <> @@ -88,9 +113,9 @@ export default function RuseCaseMng() { { - setSearchTxt(e); + setName(e); }} />
@@ -98,7 +123,7 @@ export default function RuseCaseMng() {
- -
@@ -119,16 +144,18 @@ export default function RuseCaseMng() {
- {}} - onPageChange={() => {}} - /> +
+ {}} + onPageChange={() => {}} + /> +
diff --git a/apps/wr/app/store/matDmd/ruseCaseMng/index.tsx b/apps/wr/app/store/matDmd/ruseCaseMng/index.tsx index 274af07af..0b40c1826 100644 --- a/apps/wr/app/store/matDmd/ruseCaseMng/index.tsx +++ b/apps/wr/app/store/matDmd/ruseCaseMng/index.tsx @@ -1,4 +1,4 @@ -import { fetchType, postFectch } from "@/components/lib/api"; +import { fetchType, getFetch, postFectch } from "@/components/lib/api"; import type { TableUpperProps } from "../../common/type/header"; import { ruseCaseHeadListExport } from "./headList"; import { ToastFormUi } from "@/components/ui/toast/toastFromUi"; @@ -71,8 +71,9 @@ type ruseCaseActions = { export const fetchSearchRuseCaseMng = async (sidoCd: string, sigunguCd: string, usageCd : string, name: string) => { const tailUrl = (sidoCd && !sigunguCd ? `/${sigunguCd}` : sigunguCd ? `/${sigunguCd}` : '/0') + (usageCd ? `/${usageCd}` : '') + (name ? `/${name}` : ''); console.log('tailUrl : ', tailUrl); - const response = await postFectch(`/reuseFc/pubFcRuseCase${tailUrl}`, {usageCd, name}, fetchType.WR); - return []; + const resp = await getFetch(fetchType.WR,`/reuseFc/pubFcRuseCase${tailUrl}`); + console.log('resp : ', resp); + return resp; }; export const fetchInsertRuseCaseMng = async (data: any) => { @@ -96,7 +97,7 @@ export const ruseCaseActionsExport: (set: any) => ruseCaseActions = ( return { selectRuseCaseMng: async (sidoCd: string, sigunguCd: string, usageCd : string, name: string) => { const res = await fetchSearchRuseCaseMng(sidoCd, sigunguCd, usageCd, name); - + console.log('res : ', res); set((state: ruseCaseState) => ({ ruseCaseMng: { ...state.ruseCaseMng, diff --git a/apps/wr/public/sample_ruseCaseMng.xlsx b/apps/wr/public/sample_ruseCaseMng.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..3a88e0623e067d483927ce6dfdc452c2dbb2ee19 GIT binary patch literal 19429 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 VpMfz503d?DD8Mi-mUw#f{{gi`Sz7=A literal 0 HcmV?d00001 From 1493d0c6968e38d5945bc2bef76f59d2495c2da9 Mon Sep 17 00:00:00 2001 From: leedj312 Date: Fri, 27 Jun 2025 15:51:33 +0900 Subject: [PATCH 017/108] =?UTF-8?q?=ED=85=8C=EC=9D=B4=EB=B8=94=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/ruseFcltMng/fcltReg/pbnstReg/page.tsx | 126 ++++++++++-------- 1 file changed, 74 insertions(+), 52 deletions(-) diff --git a/apps/wr/app/ruseFcltMng/fcltReg/pbnstReg/page.tsx b/apps/wr/app/ruseFcltMng/fcltReg/pbnstReg/page.tsx index 6b45dacc9..e5c9241d5 100644 --- a/apps/wr/app/ruseFcltMng/fcltReg/pbnstReg/page.tsx +++ b/apps/wr/app/ruseFcltMng/fcltReg/pbnstReg/page.tsx @@ -1,6 +1,7 @@ "use client"; import { ContentsDiv, + DateMonthPicker, ScrollArea, SearchDiv, SearchFormLeft, @@ -31,7 +32,7 @@ import { PbnstBuseForm } from "@/components/ui/form/fcltReg/pbnstReg/buseReg/bug import { PbnstRegModal } from "@/components/ui/modal/fcltReg/pbnstRegModal/pbnstRegModal"; import toast, { Toaster } from "react-hot-toast"; // toast 이미 import 되어 있음 import { writeRgtrAndMdfr } from "@/app/store/ruseFcltMng/fcltReg/pbnstReg"; -import { useFieldArray, useForm, type FieldErrors } from "react-hook-form"; // FieldErrors 타입 추가 +import { set, useFieldArray, useForm, type FieldErrors } from "react-hook-form"; // FieldErrors 타입 추가 import { zodResolver } from "@hookform/resolvers/zod"; import { z } from "zod"; @@ -41,6 +42,7 @@ export default function PublicReuseFaciltyInsert() { pbnst, searchFclts, pubHasuInit, + pbnstInsert, pbnstRegClick, pbnstRuseFcltUpdate, pubHasuSearch, @@ -50,8 +52,7 @@ export default function PublicReuseFaciltyInsert() { const [currentTab, setCurrentTab] = useState("mng"); const [isRuseReg, setIsRuseReg] = useState<{ isModal: boolean; - data: pubHasuType; - }>({ isModal: false, data: {} as pubHasuType }); + }>({ isModal: false }); const [indexMng, setIndexMng] = useState(0); const [indexBiz, setIndexBiz] = useState(0); @@ -60,7 +61,6 @@ export default function PublicReuseFaciltyInsert() { const [selectRow, setSelectRow] = useState(); const [pageNation, setPageNation] = useState(1); - const form = useForm<{ items: z.infer[]; }>({ @@ -88,30 +88,32 @@ export default function PublicReuseFaciltyInsert() { data.forEach((fclt) => { bizs = [...bizs, ...fclt.fcBizs]; }); - const pbnst =data.map((fclt) => { - return({ - pbnstRuseFcltPk: fclt.pbnstRuseFcltPk, - fcltCpct: fclt.fcltCpct, - ruseCpct: fclt.ruseCpct, - splyExtraCpct: fclt.splyExtraCpct, - rgtr: fclt.rgtr, - mdfr: fclt.mdfr, - fcFiles: fclt.fcFiles, - fcOpers: fclt.fcOpers, - }) - }) + const pbnst = data.map((fclt) => { + return { + pbnstRuseFcltPk: fclt.pbnstRuseFcltPk, + fcltCpct: fclt.fcltCpct, + ruseCpct: fclt.ruseCpct, + splyExtraCpct: fclt.splyExtraCpct, + rgtr: fclt.rgtr, + mdfr: fclt.mdfr, + fcFiles: fclt.fcFiles, + fcOpers: fclt.fcOpers, + }; + }); await pbnstRuseFcltUpdate(pbnst as customPbnst[]); await pbnstRuseBizUpdate(bizs); }; // 폼 유효성 검사 실패 시 호출될 함수 - const onInvalid = (errors: FieldErrors<{ items: z.infer[]; }>) => { + const onInvalid = ( + errors: FieldErrors<{ items: z.infer[] }> + ) => { // 재귀 함수를 사용해 중첩된 에러 객체에서 모든 에러 메시지와 경로를 찾습니다. const findAllErrors = ( errorObject: any, path: (string | number)[] = [] - ): { message: string; path: (string | number)[]; }[] => { + ): { message: string; path: (string | number)[] }[] => { if (!errorObject) return []; return Object.keys(errorObject).flatMap((key) => { @@ -136,7 +138,9 @@ export default function PublicReuseFaciltyInsert() { if (itemsIndex > -1 && path.length > itemsIndex + 1) { const itemIdx = path[itemsIndex + 1] as number; - const facilityName = form.getValues(`items.${itemIdx}.ruseUtztnFcltNm`); + const facilityName = form.getValues( + `items.${itemIdx}.ruseUtztnFcltNm` + ); if (facilityName) { prefix += `[${facilityName}] `; } @@ -144,7 +148,9 @@ export default function PublicReuseFaciltyInsert() { const fcBizsIndex = path.indexOf("fcBizs"); if (fcBizsIndex > -1 && path.length > fcBizsIndex + 1) { const bizIdx = path[fcBizsIndex + 1] as number; - const cycle = form.getValues(`items.${itemIdx}.fcBizs.${bizIdx}.cycl`); + const cycle = form.getValues( + `items.${itemIdx}.fcBizs.${bizIdx}.cycl` + ); if (cycle) { prefix += `[${cycle}차수] `; } @@ -156,12 +162,18 @@ export default function PublicReuseFaciltyInsert() { }; useEffect(() => { - if (code.sggCd.length) { - if (searchFclts) { - setbdstTable(tableData(searchFclts, { code })); - } else if (code.sggCd.length > 0 && pbnstHasuRes.length > 0) { - setbdstTable(tableData(pbnstHasuRes, { code })); - } + console.log("bdstTable ", bdstTable); + }, [bdstTable]); + + + useEffect(() => { + + if (code.sggCd.length &&pbnstHasuRes.length > 0) { + setbdstTable(tableData(pbnstHasuRes, { code })); + // if (searchFclts) { + // setbdstTable(tableData(searchFclts, { code })); + // } else if (code.sggCd.length > 0 && pbnstHasuRes.length > 0) { + // } } }, [code, pbnstHasuRes, searchFclts]); @@ -179,11 +191,10 @@ export default function PublicReuseFaciltyInsert() { }; useEffect(() => { - if (pbnst.pbnstRes&& pbnst.pbnstRes?.length>0) { + if (pbnst.pbnstRes && pbnst.pbnstRes?.length > 0) { console.log("pbnst.pbnstRes", pbnstRes); form.reset({ items: pbnstRegArrayZod.parse(pbnst.pbnstRes) }); - }else{ - + } else { console.log("pbnst.pbnstRes is null", pbnstRes); form.reset({ items: [] }); } @@ -200,11 +211,9 @@ export default function PublicReuseFaciltyInsert() { const appendPbnstRuseFclt = useCallback(() => { const selecTableRow = selectRow; - if (pbnstRes && pbnstRes.length > 0) { const lastPbnst = pbnstRes[pbnstRes.length - 1]; if (lastPbnst && selecTableRow) { - append( pbnstRegZod.parse({ pbnstRuseFcltPk: @@ -220,32 +229,37 @@ export default function PublicReuseFaciltyInsert() { ] as fcBizType[], }) ); - setIsRuseReg({ ...isRuseReg, isModal: true }); + setIsRuseReg({ isModal: true }); } } else { if (selecTableRow) { append( - pbnstRegZod.parse({ - pbnstRuseFcltPk: String(selecTableRow.bscFctCd)+'1', - ruseUtztnFcltNm: "", - regDt: null, - mdfcnDt: null, - fcBizs: [fcBizSchema.parse({pbnstRuseFcltFk:String(selecTableRow.bscFctCd)+'1',cycl:1})] as fcBizType[], - }) - ) - setIsRuseReg({ ...isRuseReg, isModal: true }); + pbnstRegZod.parse({ + pbnstRuseFcltPk: String(selecTableRow.bscFctCd) + "1", + ruseUtztnFcltNm: "", + regDt: null, + mdfcnDt: null, + fcBizs: [ + fcBizSchema.parse({ + pbnstRuseFcltFk: String(selecTableRow.bscFctCd) + "1", + cycl: 1, + }), + ] as fcBizType[], + }) + ); + setIsRuseReg({ isModal: true }); } } }, [pbnstRegZod, pbnstRes, append, selectRow, form]); useEffect(() => { - // console.log(" page getgetvaslue ", form.getValues()); + // console.log(" page getgetvaslue ", form.getValues()); }, [form.getValues()]); -useEffect(() =>{ - pubHasuInit(String(pageNation), "41"); -},[pageNation,pubHasuInit]) - + useEffect(() => { + pubHasuInit(String(pageNation), "41"); + }, [pageNation, pubHasuInit]); + return ( <> @@ -278,7 +292,7 @@ useEffect(() =>{ size={"sm"} className="" onClick={() => { - // pbnstMngData([] as customPbnst[]); + // pbnstMngData([] as customPbnst[]); if (!selectRow) { toast.error( "하수리시시설을 테이블을 선택하고 등록버튼을 눌러주세요" @@ -291,6 +305,7 @@ useEffect(() =>{ > 신규등록 +
@@ -300,7 +315,9 @@ useEffect(() =>{
- {/* 높이 조정 */} + + {" "} + {/* 높이 조정 */} {
From 1c603efc6232a5e05742443195970fb00c901d5d Mon Sep 17 00:00:00 2001 From: YangJinSoo94 Date: Fri, 27 Jun 2025 16:51:10 +0900 Subject: [PATCH 018/108] =?UTF-8?q?=EB=AC=BC=EC=9E=AC=EC=9D=B4=EC=9A=A9?= =?UTF-8?q?=EC=82=AC=EB=A1=80=EA=B4=80=EB=A6=AC=20=EC=A1=B0=ED=9A=8C?= =?UTF-8?q?=EA=B9=8C=EC=A7=80=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wr/app/matMng/coMat/ruseCaseMng/page.tsx | 3 +- .../app/store/matDmd/ruseCaseMng/headList.tsx | 121 ++++++++++-------- .../wr/app/store/matDmd/ruseCaseMng/index.tsx | 2 +- 3 files changed, 70 insertions(+), 56 deletions(-) diff --git a/apps/wr/app/matMng/coMat/ruseCaseMng/page.tsx b/apps/wr/app/matMng/coMat/ruseCaseMng/page.tsx index 5e4bddaad..63d0f662e 100644 --- a/apps/wr/app/matMng/coMat/ruseCaseMng/page.tsx +++ b/apps/wr/app/matMng/coMat/ruseCaseMng/page.tsx @@ -23,6 +23,7 @@ import { useState } from "react"; export default function RuseCaseMng() { const { code, ruseCaseMng, selectRuseCaseMng } = useWrStore((state) => state); + const { ruseCaseMngList } = ruseCaseMng; const [selectSgg, setSelectSgg] = useState([{val: "0", text: "전체"}]); const [objSearch, setObjSearch] = useState({ sgg: "", city: "" }); @@ -147,7 +148,7 @@ export default function RuseCaseMng() {
ruseCaseActions = ( set((state: ruseCaseState) => ({ ruseCaseMng: { ...state.ruseCaseMng, - ruseCaseMngList: data, + ruseCaseMngList: res, }, })); }, From 7c811df864ea4b8ca47fcd501f6a71e91f81ac6c Mon Sep 17 00:00:00 2001 From: leedj312 Date: Sat, 28 Jun 2025 14:59:17 +0900 Subject: [PATCH 019/108] =?UTF-8?q?=EB=AC=BC=EC=9E=AC=EC=95=84=EC=9A=A9=20?= =?UTF-8?q?=EA=B1=B4=EC=B6=95=EB=AC=BC=20=EB=93=B1=EB=A1=9D=20=EB=B9=97?= =?UTF-8?q?=EB=AC=BC/=EC=A4=91=EC=88=98=EB=8F=84/=EC=98=A8=EB=B0=B0?= =?UTF-8?q?=EC=88=98=20=EC=88=98=EC=A0=95=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 --- .../app/ruseFcltMng/fcltReg/pbnstReg/page.tsx | 20 +- .../app/ruseFcltMng/fcltReg/rswtReg/page.tsx | 8 +- .../ruseFcltMng/fcltSttn/rswtSttn/page.tsx | 2 +- apps/wr/app/store/common/searchCode/index.ts | 1 + .../chkgdnReg/rainChkgdn/headList.tsx | 2 +- .../ruseFcltMng/fcltReg/htdflReg/index.tsx | 176 ++++++++---------- .../ruseFcltMng/fcltReg/pbnstReg/index.tsx | 3 +- .../ruseFcltMng/fcltReg/rainReg/index.tsx | 3 - .../ruseFcltMng/fcltReg/rswtReg/index.tsx | 93 +++------ .../ui/form/fcltReg/rswtrReg/rswtrRegForm.tsx | 26 +-- .../fcltReg/bdstRegModal/bdstRegModal.tsx | 5 +- .../fcltReg/pbnstRegModal/pbnstRegModal.tsx | 52 +++--- 12 files changed, 158 insertions(+), 233 deletions(-) diff --git a/apps/wr/app/ruseFcltMng/fcltReg/pbnstReg/page.tsx b/apps/wr/app/ruseFcltMng/fcltReg/pbnstReg/page.tsx index e5c9241d5..d61478a75 100644 --- a/apps/wr/app/ruseFcltMng/fcltReg/pbnstReg/page.tsx +++ b/apps/wr/app/ruseFcltMng/fcltReg/pbnstReg/page.tsx @@ -1,7 +1,6 @@ "use client"; import { ContentsDiv, - DateMonthPicker, ScrollArea, SearchDiv, SearchFormLeft, @@ -40,7 +39,6 @@ export default function PublicReuseFaciltyInsert() { const { code, pbnst, - searchFclts, pubHasuInit, pbnstInsert, pbnstRegClick, @@ -60,7 +58,7 @@ export default function PublicReuseFaciltyInsert() { const [bdstTable, setbdstTable] = useState(); const [selectRow, setSelectRow] = useState(); const [pageNation, setPageNation] = useState(1); - + const [testMonth, setTestMonth] = useState("202406"); const form = useForm<{ items: z.infer[]; }>({ @@ -165,17 +163,16 @@ export default function PublicReuseFaciltyInsert() { console.log("bdstTable ", bdstTable); }, [bdstTable]); - useEffect(() => { - - if (code.sggCd.length &&pbnstHasuRes.length > 0) { + if (code.sggCd.length && pbnstHasuRes.length > 0) { setbdstTable(tableData(pbnstHasuRes, { code })); - // if (searchFclts) { + // if (searchFclts) { // 왜 오류가 발생할까? + // setbdstTable(tableData(searchFclts, { code })); // } else if (code.sggCd.length > 0 && pbnstHasuRes.length > 0) { // } } - }, [code, pbnstHasuRes, searchFclts]); + }, [code, pbnstHasuRes]); const cellClick = (val: number | undefined) => { if (val === undefined) return; @@ -192,10 +189,8 @@ export default function PublicReuseFaciltyInsert() { useEffect(() => { if (pbnst.pbnstRes && pbnst.pbnstRes?.length > 0) { - console.log("pbnst.pbnstRes", pbnstRes); form.reset({ items: pbnstRegArrayZod.parse(pbnst.pbnstRes) }); } else { - console.log("pbnst.pbnstRes is null", pbnstRes); form.reset({ items: [] }); } }, [pbnst.pbnstRes]); @@ -252,10 +247,6 @@ export default function PublicReuseFaciltyInsert() { } }, [pbnstRegZod, pbnstRes, append, selectRow, form]); - useEffect(() => { - // console.log(" page getgetvaslue ", form.getValues()); - }, [form.getValues()]); - useEffect(() => { pubHasuInit(String(pageNation), "41"); }, [pageNation, pubHasuInit]); @@ -305,7 +296,6 @@ export default function PublicReuseFaciltyInsert() { > 신규등록 -
diff --git a/apps/wr/app/ruseFcltMng/fcltReg/rswtReg/page.tsx b/apps/wr/app/ruseFcltMng/fcltReg/rswtReg/page.tsx index 2931690b9..d57c730ce 100644 --- a/apps/wr/app/ruseFcltMng/fcltReg/rswtReg/page.tsx +++ b/apps/wr/app/ruseFcltMng/fcltReg/rswtReg/page.tsx @@ -42,8 +42,8 @@ export default function Home() { } }, [rswt.isInit, initRswt]); useEffect(() => { - if (code.sggCd.length > 0) { - const rswtData = tableData(rswtRes, { code }).map( + if (code.sggCd.length > 0 && rswtRes?.length > 0) { + const rswtData = tableData(rswtRes, { code })?.map( (item: customRswtBdst) => { return { ...item, @@ -151,7 +151,7 @@ export default function Home() {
} + body={} bodyClass="w-[68vw] max-w-none h-[90vh]" open={issNewReg} onOpenChange={(open) => { @@ -162,7 +162,7 @@ export default function Home() { bodyClass={""} open={bdstChgProps.isOpen} body={ - + } onOpenChange={(open) => { setBdstChgProps({ ...bdstChgProps, isOpen: open }); diff --git a/apps/wr/app/ruseFcltMng/fcltSttn/rswtSttn/page.tsx b/apps/wr/app/ruseFcltMng/fcltSttn/rswtSttn/page.tsx index 7a3444a9b..f9b3c6187 100644 --- a/apps/wr/app/ruseFcltMng/fcltSttn/rswtSttn/page.tsx +++ b/apps/wr/app/ruseFcltMng/fcltSttn/rswtSttn/page.tsx @@ -65,7 +65,7 @@ export default function RswtSttn() { }, [rswtSttn.isInit, rswtSttnInit]); useEffect(() => { - if (rswtSttnRes.length > 0 && code.cityCd.length > 0) { + if (rswtSttnRes?.length > 0 && code.cityCd.length > 0) { const data = tableData(rswtSttnRes, { code }); const chData = data .map((item: customRswtBdst) => ({ diff --git a/apps/wr/app/store/common/searchCode/index.ts b/apps/wr/app/store/common/searchCode/index.ts index 34c2c86c5..51ebda8a5 100644 --- a/apps/wr/app/store/common/searchCode/index.ts +++ b/apps/wr/app/store/common/searchCode/index.ts @@ -82,6 +82,7 @@ export const comFcltSearchAction: (set: any) => comFcltActions = (set: any) => { addr: item.addr, fctCpc: item.fctCpc, })); + set((state: comFcltState) => ({ ...state, searchFclts: searchFclts, diff --git a/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList.tsx b/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList.tsx index 3cc75d70c..d8057cad6 100644 --- a/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList.tsx +++ b/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList.tsx @@ -17,7 +17,7 @@ export const chkgdRegExcel = z.object({ }, { message: "현재 년도보다 높은 수를 입력할 수 없습니다." } ), - file: z.instanceof(File).optional(), + file: z.instanceof(File).nullish(), }); export const guideDetail = z.object({ diff --git a/apps/wr/app/store/ruseFcltMng/fcltReg/htdflReg/index.tsx b/apps/wr/app/store/ruseFcltMng/fcltReg/htdflReg/index.tsx index 0a51ebb5d..314d5762b 100644 --- a/apps/wr/app/store/ruseFcltMng/fcltReg/htdflReg/index.tsx +++ b/apps/wr/app/store/ruseFcltMng/fcltReg/htdflReg/index.tsx @@ -1,10 +1,63 @@ import { getFetch, postFectch, putFectch } from "@/components/lib/api"; -import { upHeadList, type chgBuilding} from "../rainReg"; +import { upHeadList, type chgBuilding } from "../rainReg"; import { z } from "zod"; import { makeMdfr, makeRstr } from "../bdstReg"; +import { + baseReuseBuildingSchema, + chgBuildingSchema, + validationMessages, +} from "../rainReg/type"; // Import baseReuseBuildingSchema, chgBuildingSchema, validationMessages import type { TableUpperProps } from "@/app/store/common/type/header"; +const hdtflObjSchema = z.object({ + htdflFcltPk: z.number().nullish(), + bdstRuseFcltFk: z.number().nullish(), + etgtType: z + .string() + .min(1, { message: validationMessages.required("에너지원 종류") }), + fclCpct: z + .number() + .positive({ message: validationMessages.required("시설 용량") }), + plwtYn: z.enum(["Y", "N"]).default("Y"), + orgwtWtritSpt: z + .number() + .positive({ message: validationMessages.required("원수 취수량") }), + orgwtOcrnProcs: z + .string() + .min(1, { message: validationMessages.required("원수 발생 공정") }), + wtritEntrcTp: z + .number() + .max(100, { message: "온도는 100이상이 될 수 없습니다." }) + .default(0), // Changed max to 100 based on message + amtgnr: z.number().default(0), + prcsProcsYn: z.enum(["Y", "N"]).default("Y"), + prcsFcltCpct: z + .number() + .positive({ message: validationMessages.required("처리시설 용량") }), + fmYn: z.string().default(""), + utztnCpct: z + .number() + .positive({ message: validationMessages.required("이용 용량") }), + utztnUsg: z + .string() + .min(1, { message: validationMessages.required("이용 용도") }), + grssClfg: z.number().default(0), // Measurements, 0 is fine as default + combResdch: z.number().default(0), // Measurements, 0 is fine as default + trbt: z.number().default(0), // Measurements, 0 is fine as default + bod: z.number().default(0), // Measurements, 0 is fine as default + smll: z.number().default(0), // Measurements, 0 is fine as default + crmty: z.number().default(0), // Measurements, 0 is fine as default + tn: z.number().default(0), // Measurements, 0 is fine as default + tph: z.number().default(0), // Measurements, 0 is fine as default + ph: z.number().default(0), // Measurements, 0 is fine as default + clIonQnt: z.number().default(0), // Measurements, 0 is fine as default + cdtnv: z.number().default(0), // Measurements, 0 is fine as default + emsnExitTp: z.number().default(0), // Measurements, 0 is fine as default + rgtr: z.string().nullish().default(null), + mdfr: z.string().nullish().default(null), +}); + export const hdtflZod = z.object({ bdstInfoPk: z.number().nullish(), ctpvCd: z.number().default(0), @@ -14,85 +67,25 @@ export const hdtflZod = z.object({ daddr: z.string().default(" "), bdstNm: z.string().default(" "), frstArchPrmsnDt: z.date().default(() => new Date()), - archChgPrmsnDt: z.date().default(() => new Date()).nullish() , + archChgPrmsnDt: z + .date() + .default(() => new Date()) + .nullish(), archArea: z.number().default(0), archChgArea: z.number().nullish().default(0), - rgtr:z.string().nullish().default(null), - mdfr:z.string().nullish().default(null), + rgtr: z.string().nullish().default(null), + mdfr: z.string().nullish().default(null), reuseBuilding: z .array( - z.object({ - bdstRuseFcltPk: z.number().nullish(), - bdstInfoFk: z.number().nullish(), - instlCmptnDt: z.coerce.date().default(() => new Date()), - instlCst: z.number().nullish().default(0), - operMngCd: z.string().nullish().default(""), - anlOperCst: z.number().default(0), - instlCstSprtYn: z.enum(["Y", "N"]).nullish(), - instlCstSprtAmt: z.number().nullish().default(0), - grntchBdstCertNo: z.string().nullish().default(""), - grntchBdstCertGrd: z.string().nullish().default(""), - ruseFcltFlg: z.string().nullish().default(""), - bdstUsgCd: z.number().default(0), - lawTrgtYn: z.enum(["Y", "N"]).nullish(), - rgtr:z.string().nullish().default(null), - mdfr:z.string().nullish().default(null), - // htdflObj 매핑 - htdflObj: z - .object({ - htdflFcltPk: z.number().nullish(), - bdstRuseFcltFk: z.number().nullish(), - etgtType: z.string().default(""), - fclCpct: z.number().default(0), - plwtYn: z.enum(["Y", "N"]), - orgwtWtritSpt: z.number().default(0), - orgwtOcrnProcs: z.string().default(""), - wtritEntrcTp: z.number().max(4, "온도는 100이상이 될 수 없습니다.") - .default(0), - amtgnr: z.number().default(0), - prcsProcsYn: z.enum(["Y", "N"]), - prcsFcltCpct: z.number().default(0), - fmYn: z.string().default(""), - utztnCpct: z.number().default(0), - utztnUsg: z.string().default(""), - grssClfg: z.number().default(0), - combResdch: z.number().default(0), - trbt: z.number().default(0), - bod: z.number().default(0), - smll: z.number().default(0), - crmty: z.number().default(0), - tn: z.number().default(0), - tph: z.number().default(0), - ph: z.number().default(0), - clIonQnt: z.number().default(0), - cdtnv: z.number().default(0), - emsnExitTp: z.number().default(0), - rgtr:z.string().nullish().default(null), - mdfr:z.string().nullish().default(null), - }) - .default({ - plwtYn: "N", - prcsProcsYn: "N", - }), - }) - ) - .default([]), - chgBuilding: z - .array( - z.object({ - bdstInfoFk: z.number().nullish(), - archChgPrmsnDt: z.coerce.date() , - archChgArea: z.string().default("-").nullish(), - bdstChgNm: z.string().default(""), - rgtr:z.string().nullish().default(null), - mdfr:z.string().nullish().default(null), + baseReuseBuildingSchema.extend({ + htdflObj: hdtflObjSchema, }) ) .default([]), + chgBuilding: z.array(chgBuildingSchema).default([]), // Use imported chgBuildingSchema }); - export type customHtdflBdst = z.infer - +export type customHtdflBdst = z.infer; export type htdflFacState = { htdfl: { @@ -102,11 +95,10 @@ export type htdflFacState = { }; }; type htdflFacSActions = { - initHtdfl: (code: string ) => void; - htdflBdstUpdate:(data: customHtdflBdst, code:string|number) => void; - htdflBdstReg:(data: customHtdflBdst, code:string|number) => void; - getHtdflBdstData:(pageNation:number, code:string|number) =>void; - + initHtdfl: (code: string) => void; + htdflBdstUpdate: (data: customHtdflBdst, code: string | number) => void; + htdflBdstReg: (data: customHtdflBdst, code: string | number) => void; + getHtdflBdstData: (pageNation: number, code: string | number) => void; }; const htdflRes: customHtdflBdst[] = []; @@ -121,25 +113,22 @@ export const htdflFacStateExport: htdflFacState = { }, }; +const initFetch = async (code: string | number) => { + const rswtRes = await getFetch("wr", `/facilityList/1/20/${code}`); -const initFetch = async (code:string|number) => { - const rswtRes = await getFetch( - "wr", - `/facilityList/1/20/${code}`) - return rswtRes; }; export const htdflFacActionsExport: (set: any) => htdflFacSActions = ( set: any ) => { return { - getHtdflBdstData: async (pageNation: number, code: string | number) => { + getHtdflBdstData: async (pageNation: number, code: string | number) => { try { const rswtRes = await getFetch( "wr", `/drain/facilityList/${pageNation}/20/${code}` ); - console.log(pageNation) + console.log(pageNation); // 여기서 상태 업데이트 return set((state: htdflFacState) => ({ ...state, @@ -154,8 +143,7 @@ export const htdflFacActionsExport: (set: any) => htdflFacSActions = ( } }, - - initHtdfl: async (code: string ) => { + initHtdfl: async (code: string) => { try { const htdflRes = await initFetch(code); set((state: htdflFacState) => { @@ -172,9 +160,9 @@ export const htdflFacActionsExport: (set: any) => htdflFacSActions = ( console.log(error); } }, - htdflBdstUpdate: async (data: customHtdflBdst, code:string|number) => { + htdflBdstUpdate: async (data: customHtdflBdst, code: string | number) => { try { - const chData=await makeMdfr(data) + const chData = await makeMdfr(data); const res = await putFectch("/drain/facility", data); if (res.ok) { alert(" 수정이 완료되었습니다."); @@ -196,18 +184,17 @@ export const htdflFacActionsExport: (set: any) => htdflFacSActions = ( ); } }, - htdflBdstReg: async (data: customHtdflBdst , code:string|number) => { + htdflBdstReg: async (data: customHtdflBdst, code: string | number) => { try { - const chData=await makeRstr(data) + const chData = await makeRstr(data); const res = await postFectch("/drain/facility", data); if (res.ok) { - - if(data.chgBuilding.length > 0){ + if (data.chgBuilding.length > 0) { await res .json() .then((resData: any) => { const chPK = resData?.data?.bdstInfoPk; - + const makeCh = resData.data.chgBuilding.map( (item: chgBuilding) => ({ ...item, @@ -222,9 +209,8 @@ export const htdflFacActionsExport: (set: any) => htdflFacSActions = ( alert("등록이 완료되었습니다."); } }); - }else{ + } else { alert("등록이 완료되었습니다."); - } } const htdflRes = await initFetch(code); @@ -238,7 +224,7 @@ export const htdflFacActionsExport: (set: any) => htdflFacSActions = ( })); } catch (errorReg) { alert("등록 오류입니다."); - } + } }, }; }; diff --git a/apps/wr/app/store/ruseFcltMng/fcltReg/pbnstReg/index.tsx b/apps/wr/app/store/ruseFcltMng/fcltReg/pbnstReg/index.tsx index fb799c26c..67a9f36f4 100644 --- a/apps/wr/app/store/ruseFcltMng/fcltReg/pbnstReg/index.tsx +++ b/apps/wr/app/store/ruseFcltMng/fcltReg/pbnstReg/index.tsx @@ -102,12 +102,10 @@ const checkWrRegList = async () => { const hasuListFetch = async (page: string, sggOrCity: string, searchTxt?:string) => { const checkWrRegListRes = await checkWrRegList(); - console.log(' checkWrRegListRes ', checkWrRegListRes) let url = `/prcsFclt/ruseInfo/${page ? page : 1}/10/A/${sggOrCity? sggOrCity : "11"}/0/1/SW`; const data = checkWrRegListRes.filter((item:string) => item !== "") - console.log(' checkWrRegListRes ' ) if(searchTxt){ url= url +`/${searchTxt}` @@ -177,6 +175,7 @@ export const pubFacActionsExport: (set: any) => pubFacActions = (set: any) => { try { const pbnstHasuRes = await hasuListFetch(page, sggOrCity); + return set((state: pubFacState) => ({ ...state, pbnst: { diff --git a/apps/wr/app/store/ruseFcltMng/fcltReg/rainReg/index.tsx b/apps/wr/app/store/ruseFcltMng/fcltReg/rainReg/index.tsx index a029bd869..372a4bd9a 100644 --- a/apps/wr/app/store/ruseFcltMng/fcltReg/rainReg/index.tsx +++ b/apps/wr/app/store/ruseFcltMng/fcltReg/rainReg/index.tsx @@ -22,9 +22,6 @@ const rainyObjSchema = z.object({ }); - - - const rainBuildingSchema = baseReuseBuildingSchema.extend({ rainyObj: rainyObjSchema, }) diff --git a/apps/wr/app/store/ruseFcltMng/fcltReg/rswtReg/index.tsx b/apps/wr/app/store/ruseFcltMng/fcltReg/rswtReg/index.tsx index 3c2504b9a..178badd3b 100644 --- a/apps/wr/app/store/ruseFcltMng/fcltReg/rswtReg/index.tsx +++ b/apps/wr/app/store/ruseFcltMng/fcltReg/rswtReg/index.tsx @@ -1,10 +1,32 @@ import { getFetch, postFectch, putFectch } from "@/components/lib/api"; -import { upHeadList, type chgBuilding } from "../rainReg"; +import { upHeadList, type chgBuilding } from "../rainReg"; import type { TableUpperProps } from "@/app/store/common/type/header"; import { z } from "zod"; +import { baseReuseBuildingSchema, chgBuildingSchema, validationMessages } from "../rainReg/type"; import { makeMdfr, makeRstr } from "../bdstReg"; + const rswtrObjSchema = z.object({ + bdstRuseFcltFk: z.number().nullish(), + wtrUsqty: z.number({ required_error: validationMessages.required("물사용량(m³)") }).positive({ message: validationMessages.required("물사용량(m³)") }), + oblgTrgtWtrUsqty: z.number().nullish().default(0), + orgwtKndCd: z.string({ required_error: validationMessages.requiredSelect("원수 종류") }).min(1, { message: validationMessages.requiredSelect("원수 종류") }), + filtrTrtmfctCpct: z.number({ required_error: validationMessages.required("처리시설 용량") }).positive({ message: validationMessages.required("처리시설 용량") }), + filtrTrtmfctProcs: z.string({ required_error: validationMessages.required("주요처리공정") }).min(1, { message: validationMessages.required("주요처리공정") }), + filtrTrtmfctUsgCd: z.string().default("").nullish(), + rswtrUsqty: z.number().default(0), + altUswtrUtztnYn: z.enum(["Y", "N"]).nullish(), //안쓰는값 + altUswtrKndCd: z.string().default("").nullish(), //안쓰는값 + altUswtrUtztnQnt: z.number().default(0), //안쓰는값 + altUswtrIdntyMthdCd: z.string().default("").nullish(), //안쓰는값 + rgtr: z.string().nullish().default(null), + mdfr: z.string().nullish().default(null), +}); + + const rswtBuildingSchema = baseReuseBuildingSchema.extend({ + rswtrObj: rswtrObjSchema, +}) + export const rswtZod = z.object({ bdstInfoPk: z.number().nullish(), ctpvCd: z.number().default(0), @@ -21,73 +43,8 @@ export const rswtZod = z.object({ bdstUsgText: z.string().default(""), cityText: z.string().default(""), sggText: z.string().default(""), - - reuseBuilding: z - .array( - z.object({ - bdstRuseFcltPk: z.number().nullish(), - bdstInfoFk: z.number().nullish(), - instlCmptnDt: z.coerce.date().default(() => new Date()), - instlCst: z.number().default(0), - operMngCd: z.string().nullish().default(""), - - anlOperCst: z.number().default(0), - instlCstSprtYn: z.enum(["Y", "N"]), - instlCstSprtAmt: z.number().default(0), - - grntchBdstCertYn: z.enum(["Y", "N"]), - grntchBdstCertYr: z - .string() - .default("") - .refine((year) => year.length === 0 || /^\d{4}$/.test(year), { - message: "4자리 연도만 입력하세요", - }), - grntchBdstCertNo: z.string().default(""), - grntchBdstCertGrd: z.string().default(""), - - crgRdctYn: z.string().default("N").nullish(), // 안쓰는 값 - crgRdctBss: z.string().default("").nullish(), /// 안쓰는 값 - crgRdctAmt: z.number().default(0), // 안쓰는 값 - ruseFcltFlg: z.string().default(""), - bdstUsgCd: z.number().default(0), - lawTrgtYn: z.string().default(""), - rgtr: z.string().nullish().default(null), - mdfr: z.string().nullish().default(null), - - rswtrObj: z - .object({ - bdstRuseFcltFk: z.number().nullish(), - wtrUsqty: z.number().default(0), - oblgTrgtWtrUsqty: z.number().nullish().default(0), - orgwtKndCd: z.string().default(""), - filtrTrtmfctCpct: z.number().default(0), - filtrTrtmfctProcs: z.string().default(""), - filtrTrtmfctUsgCd: z.string().default("").nullish(), - rswtrUsqty: z.number().default(0), - altUswtrUtztnYn: z.enum(["Y", "N"]).nullish(), //안쓰는값 - altUswtrKndCd: z.string().default("").nullish(), //안쓰는값 - altUswtrUtztnQnt: z.number().default(0), //안쓰는값 - altUswtrIdntyMthdCd: z.string().default("").nullish(), //안쓰는값 - rgtr: z.string().nullish().default(null), - mdfr: z.string().nullish().default(null), - }) - .default({}), - }) - ) - .default([]), - - chgBuilding: z - .array( - z.object({ - bdstInfoFk: z.number().nullish(), - archChgPrmsnDt: z.coerce.date(), - archChgArea: z.string().default("-"), - bdstChgNm: z.string().default(""), - rgtr: z.string().nullish().default(null), - mdfr: z.string().nullish().default(null), - }) - ) - .default([]), + reuseBuilding: z.array(rswtBuildingSchema).default([]), + chgBuilding: z.array(chgBuildingSchema).default([]), }); export type customRswtBdst = z.infer; diff --git a/apps/wr/components/ui/form/fcltReg/rswtrReg/rswtrRegForm.tsx b/apps/wr/components/ui/form/fcltReg/rswtrReg/rswtrRegForm.tsx index a4f29477a..d820bddc1 100644 --- a/apps/wr/components/ui/form/fcltReg/rswtrReg/rswtrRegForm.tsx +++ b/apps/wr/components/ui/form/fcltReg/rswtrReg/rswtrRegForm.tsx @@ -28,10 +28,12 @@ import BdstReg from "../bdstReg/bdstReg"; export default function RswtrRegForm({ rswtForm, isModal, + setIsModal, readonly = false }: { rswtForm: customRswtBdst; isModal: boolean; + setIsModal?: (isModal: boolean) => void; readonly?: boolean; }) { const { rswt, code, rswtBdstUpdate, rswtBdstReg } = useWrStore( @@ -54,6 +56,7 @@ export default function RswtrRegForm({ const onSubmit = (values: z.infer) => { if (isModal) { rswtBdstReg(values, "41"); + if(setIsModal) setIsModal(false) } else { rswtBdstUpdate(values, "41"); } @@ -61,28 +64,29 @@ export default function RswtrRegForm({ const cleanRswtData = (raw: any): z.infer => { return { ...raw, - frstArchPrmsnDt: new Date(raw?.frstArchPrmsnDt || null), + frstArchPrmsnDt: raw?.frstArchPrmsnDt + ? new Date(raw.frstArchPrmsnDt) + : new Date(), // raw?.frstArchPrmsnDt가 유효하지 않으면 현재 날짜로 초기화 archChgPrmsnDt: raw?.archChgPrmsnDt ? new Date(raw?.archChgPrmsnDt) : null, reuseBuilding: (raw?.reuseBuilding || []).map((item: any) => ({ ...item, - instlCmptnDt: new Date(item?.instlCmptnDt), - operMngCd: item?.operMngCd ?? "", // null → "" - grntchBdstCertYr: - typeof item?.grntchBdstCertYr === "string" - ? item?.grntchBdstCertYr - : "", // null → "" - rswtrObj: { + instlCmptnDt: item?.instlCmptnDt + ? new Date(item.instlCmptnDt) + : new Date(), // item?.instlCmptnDt가 유효하지 않으면 현재 날짜로 초기화 + operMngCd: item?.operMngCd ?? "", // null → "" + instlCstSprtYn: item?.instlCstSprtYn ?? "Y", // null → "Y" + grntchBdstCertYn:item?.grntchBdstCertYn??"Y", + rswtrObj: { ...item?.rswtrObj, + filtrTrtmfctYn: item?.rainyObj?.filtrTrtmfctYn ?? "Y", + }, })), }; }; - useEffect(()=>{ - console.log("rswtForm:", rswtForm) - }, [rswtForm]) useEffect(() => { console.log("form errors:", form.formState.errors); diff --git a/apps/wr/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal.tsx b/apps/wr/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal.tsx index 315f9397f..205846bb4 100644 --- a/apps/wr/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal.tsx +++ b/apps/wr/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal.tsx @@ -29,9 +29,10 @@ export type objSearchType = { interface BdstRegProps { moType: T; + setIsModal?: (open: boolean) => void; } -export function BdstReg({ moType }: BdstRegProps) { +export function BdstReg({ moType,setIsModal }: BdstRegProps) { const { rain, code, bdst,findBdst } = useWrStore((state) => { return state; }); @@ -143,7 +144,7 @@ export function BdstReg({ moType }: BdstRegProps) { { moType === 'RNB'? :moType==='RST' - ? + ? {setIsModal}}> : } diff --git a/apps/wr/components/ui/modal/fcltReg/pbnstRegModal/pbnstRegModal.tsx b/apps/wr/components/ui/modal/fcltReg/pbnstRegModal/pbnstRegModal.tsx index b930a36bb..cc0eb80bf 100644 --- a/apps/wr/components/ui/modal/fcltReg/pbnstRegModal/pbnstRegModal.tsx +++ b/apps/wr/components/ui/modal/fcltReg/pbnstRegModal/pbnstRegModal.tsx @@ -20,13 +20,13 @@ import { useFieldArray, type UseFormReturn } from "react-hook-form"; import { writeRgtrAndMdfr } from "@/app/store/ruseFcltMng/fcltReg/pbnstReg"; export function PbnstRegModal({ - data, form, + setPbnstInsert }: { - data: pubHasuType; form: UseFormReturn<{ items: PbnstRegArrrayType }>; + setPbnstInsert: (data: customPbnst) => void }) { - const { pbnst, pbnstInsert } = useWrStore((state) => { + const { pbnstInsert } = useWrStore((state) => { return state; }); const [pbnstBdstForm, setPbnstBdstForm] = useState( @@ -35,40 +35,30 @@ export function PbnstRegModal({ const [currentTab, setCurrentTab] = useState("mng"); const [indexMng, setIndexMng] = useState(0); const [indexBiz, setIndexBiz] = useState(0); - const onSubmit = async (valuse: { items: PbnstRegArrrayType }) => { - - const item = valuse.items[indexMng]; - const data = item ? writeRgtrAndMdfr([item]) : []; + const onSubmit = async (valuse: { items: PbnstRegArrrayType }) => { + const item = valuse.items[indexMng]; + const data = item ? writeRgtrAndMdfr([item]) : []; - if(data && data[0]){ - pbnstInsert(data[0]) - } - }; + if (data && data[0]) { + // pbnstInsert(data[0]); + setPbnstInsert(data[0]) + } + }; const handleTabChange = (value: string) => { setCurrentTab(value); - }; - - useEffect(() => { - console.log("form errors:", form.formState.errors); - }, [form.formState.errors]); - - + }; useEffect(() => { - if (data) { - - const firstForm = form.getValues("items"); - setIndexMng(firstForm.length - 1) } - }, [data, form]); - - + console.log("form errors:", form.formState.errors); + }, [form.formState.errors]); useEffect(() => { - console.log(' form.getValues() ' ,form.getValues()) - - }, [form.getValues()]); - + const firstForm = form.getValues("items"); + if (firstForm) { + setIndexMng(firstForm.length - 1); + } + }, [form]); return ( <>

@@ -86,7 +76,7 @@ export function PbnstRegModal({ 차수별정보 - //

- // ); - // } const pbnstBiz = form.watch(`items.${indexMng}.fcBizs`); return ( @@ -672,7 +655,7 @@ export function PbnstBuseForm({ setIsFcOrgwts(open)} - bodyClass={"w-[50vw] max-w-none h-[90vh]"} + bodyClass={"w-[70vw] max-w-none max-h-[90vh] overflow-y-auto"} body={ { diff --git a/apps/wr/components/ui/modal/fcltReg/fcOrgwtsRegModal/fcOrgwtsRegModal.tsx b/apps/wr/components/ui/modal/fcltReg/fcOrgwtsRegModal/fcOrgwtsRegModal.tsx index 92b40e146..9c8f2cdd6 100644 --- a/apps/wr/components/ui/modal/fcltReg/fcOrgwtsRegModal/fcOrgwtsRegModal.tsx +++ b/apps/wr/components/ui/modal/fcltReg/fcOrgwtsRegModal/fcOrgwtsRegModal.tsx @@ -1,6 +1,10 @@ "use client"; import { useWrStore } from "@/app/store"; -import type { pubHasuType, fcBizType, fcOrgwtType,} from "@/app/store/ruseFcltMng/fcltReg/pbnstReg/type"; +import type { + pubHasuType, + fcBizType, + fcOrgwtType, +} from "@/app/store/ruseFcltMng/fcltReg/pbnstReg/type"; import type { TableUpperProps } from "@/app/store/common/type/header"; import { tableData } from "@/components/lib/func/commom"; import RuseMngTable from "@/components/ui/table/pbnstReg/ruseMngTable/ruseMngTable"; @@ -11,138 +15,161 @@ import { Input } from "@common/components/ui"; import { ArrowLeft, ArrowRight } from "lucide-react"; import { useEffect, useRef, useState } from "react"; -export default function FcOrgwtsRegModal({ onRightSet, closeModal }: { onRightSet?: (rightData:fcOrgwtType[]) => void,closeModal?:(val:boolean) =>void }) { - const { code, pbnst, getSwtrmtFcltNm, searchFclts } = useWrStore((state) => state); +export default function FcOrgwtsRegModal({ + onRightSet, + closeModal, +}: { + onRightSet?: (rightData: fcOrgwtType[]) => void; + closeModal?: (val: boolean) => void; +}) { + const { code, pbnst, getSwtrmtFcltNm, searchFclts } = useWrStore( + (state) => state + ); const { fcOrgwtsHeadList, pbnstHasuRes } = pbnst; const [bdstTable, setbdstTable] = useState(); const rightRef = useRef([] as pubHasuType[]); const leftRef = useRef([] as pubHasuType[]); const [rightData, setRightData] = useState(); const [inputVal, setInputVal] = useState(""); - - useEffect(()=>{ - if(Array.isArray(searchFclts)){ + + useEffect(() => { + if (Array.isArray(searchFclts)) { const filteredData = searchFclts.filter((item) => { - if(rightData){ - if(rightData.find(rightItem =>rightItem.bscFctCd===item.bscFctCd)) + if (rightData) { + if ( + rightData.find((rightItem) => rightItem.bscFctCd === item.bscFctCd) + ) return false; } - return true - }) - setbdstTable(Array.isArray(filteredData) ? [...filteredData] : []) + return true; + }); + setbdstTable(Array.isArray(filteredData) ? [...filteredData] : []); } - - }, [searchFclts]) + }, [searchFclts]); useEffect(() => { if (code.sggCd.length > 0 && pbnstHasuRes.length > 0) { setbdstTable(tableData(pbnstHasuRes, { code })); - console.log('bdstTable ', bdstTable) + console.log("bdstTable ", bdstTable); } }, [code, pbnstHasuRes]); useEffect(() => { - console.log('rightData ', rightData) - if(onRightSet) - onRightSet( - (rightData as pubHasuType[])?.map(item => ({ - swtrmtFcltFk: item.bscFctCd, // 임시값, 실제 데이터에 맞게 수정 - orgwtWtritQnt: Number(item.fctCpc), // 임시값, 실제 데이터에 맞게 수정 - tmsYn: "N", // 임시값, 실제 데이터에 맞게 수정 - dschgFmYn: "N", // 임시값, 실제 데이터에 맞게 수정 - dtlPstnExpln: item.addr, // 임시값, 실제 데이터에 맞게 수정 - choose: false, // 임시값, 실제 데이터에 맞게 수정 - // 필요한 경우 다른 속성들도 추가 - })) as unknown as fcOrgwtType[] - ); - }, [rightData]) + console.log("rightData ", rightData); + if (onRightSet) + onRightSet( + (rightData as pubHasuType[])?.map((item) => ({ + swtrmtFcltFk: item.bscFctCd, // 임시값, 실제 데이터에 맞게 수정 + orgwtWtritQnt: Number(item.fctCpc), // 임시값, 실제 데이터에 맞게 수정 + tmsYn: "N", // 임시값, 실제 데이터에 맞게 수정 + dschgFmYn: "N", // 임시값, 실제 데이터에 맞게 수정 + dtlPstnExpln: item.addr, // 임시값, 실제 데이터에 맞게 수정 + choose: false, // 임시값, 실제 데이터에 맞게 수정 + // 필요한 경우 다른 속성들도 추가 + })) as unknown as fcOrgwtType[] + ); + }, [rightData]); return ( - <> -
-
- - <Button className="m-2" size={"sm"} - onClick={() => {closeModal && closeModal(false) }}>저장</Button> - </div> + <div className="flex flex-col h-[80vh]"> + <div className="shrink-0 p-4 border-b z-10 bg-white"> + <div className="flex items-center"> + <Title title="취수하수처리장 추가" /> + {/* <Button size="sm" onClick={() => closeModal?.(false)}>저장</Button> */} + <div className="flex items-center gap-2 mt-8"> - <div className="flex flex-row flex-grow gap-4 mt-4"> - <div className="w-3/7"> - <div className="flex items-center justify-between"> - <Label className="w-full text-2xl text-gray-600 text-left">공공하수처리시설 목록</Label> - <div className="flex items-center"> - <Label className="w-full text-xl text-gray-600 text-right">시설명</Label> - <Input className="w-1/2 !text-xl ml-2" type="text" value={inputVal} onChange={(e) => {setInputVal(e.target.value)}} /> - <Button className="m-2" size={"sm"} onClick={() => getSwtrmtFcltNm(inputVal)}>조회</Button> - </div> - </div> - <div className="border-t border-gray-300 my-4"></div> - <RuseMngTable - tableData={bdstTable || []} - headList={fcOrgwtsHeadList} - upHeadList={[]} // TODO: Replace with actual upHeadList data if available - ref={leftRef} - /> - </div> - <div className="flex flex-col items-center justify-center w-1/7 gap-2"> - <ArrowRight - type="button" - onClick={() => { - if (leftRef && typeof leftRef === "object" && "current" in leftRef) { - const checked = leftRef.current?.filter((item) => ((item as unknown as pubHasuType)?.choose)) - const unchecked = leftRef.current?.filter((item) => !(item as unknown as pubHasuType)?.choose) - if (checked && checked.length > 0) { - checked.forEach(item=>(item as unknown as pubHasuType).choose = false) - setRightData([...(checked as unknown as pubHasuType[]), - ...((rightRef.current ?? []) as unknown as pubHasuType[]) - ]) - - } - if (unchecked && unchecked.length > 0) { - setbdstTable([...(unchecked as unknown as pubHasuType[])]) - }else{ - setbdstTable([]) - } - } - } - } - className="w-12 h-12 text-gray-500 cursor-pointer" - /> - <ArrowLeft - type="button" - onClick={() => { - if (rightRef && typeof rightRef === "object" && "current" in rightRef) { - const checked = rightRef.current?.filter((item) => ((item as unknown as pubHasuType)?.choose)) - const unchecked = rightRef.current?.filter((item) => !(item as unknown as pubHasuType)?.choose) - if (checked && checked.length > 0) { - checked.forEach(item=>(item as unknown as pubHasuType).choose = false) - setbdstTable([ - ...(checked as unknown as pubHasuType[]), - ...((leftRef.current ?? []) as unknown as pubHasuType[]) - ]) - } - if (unchecked && unchecked.length > 0) { - setRightData([...(unchecked as unknown as pubHasuType[])]) - }else{ - setRightData([]) - } - } - }} - className="w-12 h-12 text-gray-500 cursor-pointer mt-5" /> - </div> - {/* Right Table container: Takes 2/5 of the width */} - <div className="w-3/7"> - <Label className="w-full text-2xl text-gray-600 text-right">취수하수처리장 목록</Label> - <div className="border-t border-gray-300 my-4"></div> - <RuseMngTable - tableData={rightData || []} - headList={fcOrgwtsHeadList} - upHeadList={[]} // TODO: Replace with actual upHeadList data if available - ref={rightRef} - /> + <Label className="text-xl text-gray-600">시설명</Label> + <Input + className="w-48 text-xl" + type="text" + value={inputVal} + onChange={(e) => setInputVal(e.target.value)} + /> + <Button size="sm" onClick={() => getSwtrmtFcltNm(inputVal)}> + 조회 + </Button> </div> </div> </div> - </> + {/* <div className="flex items-center justify-between"> + <div className="flex items-center gap-2"> + <Label className="text-xl text-gray-600">시설명</Label> + <Input + className="w-48 text-xl" + type="text" + value={inputVal} + onChange={(e) => setInputVal(e.target.value)} + /> + <Button size="sm" onClick={() => getSwtrmtFcltNm(inputVal)}>조회</Button> + </div> + </div> + </div> */} + + {/* 테이블 영역 */} + <div className="flex flex-row gap-4 flex-grow p-4 overflow-x-auto"> + {/* 왼쪽 테이블 */} + + <div className="w-5/12 min-w-[400px]"> + <Label className="text-2xl text-gray-600 mb-2 block text-right"> + 공공하수처리시설 목록 + </Label> + <RuseMngTable + tableData={bdstTable || []} + headList={fcOrgwtsHeadList} + upHeadList={[]} + ref={leftRef} + /> + </div> + + {/* 중간 이동 버튼 */} + <div className="flex flex-col justify-center items-center gap-4 w-[200px] min-w-[120px]"> + <ArrowRight + onClick={() => { + const checked = leftRef.current?.filter((item) => item.choose); + const unchecked = leftRef.current?.filter((item) => !item.choose); + if (checked?.length) { + checked.forEach((item) => (item.choose = false)); + setRightData([ + ...(checked as pubHasuType[]), + ...(rightRef.current ?? []), + ]); + } + setbdstTable(unchecked ?? []); + }} + className="w-8 h-8 text-gray-500 cursor-pointer" + /> + <ArrowLeft + onClick={() => { + const checked = rightRef.current?.filter((item) => item.choose); + const unchecked = rightRef.current?.filter( + (item) => !item.choose + ); + if (checked?.length) { + checked.forEach((item) => (item.choose = false)); + setbdstTable([ + ...(checked as pubHasuType[]), + ...(leftRef.current ?? []), + ]); + } + setRightData(unchecked ?? []); + }} + className="w-8 h-8 text-gray-500 cursor-pointer" + /> + </div> + + {/* 오른쪽 테이블 */} + <div className="w-5/12 min-w-[400px]"> + <Label className="text-2xl text-gray-600 mb-2 block text-right"> + 취수하수처리장 목록 + </Label> + <RuseMngTable + tableData={rightData || []} + headList={fcOrgwtsHeadList} + upHeadList={[]} + ref={rightRef} + /> + </div> + </div> + </div> ); } From 16bcaf89473364fb4859f6947d2f46874f19093c Mon Sep 17 00:00:00 2001 From: YangJinSoo94 <jschrome782@gmail.com> Date: Mon, 30 Jun 2025 14:21:52 +0900 Subject: [PATCH 021/108] =?UTF-8?q?=EB=AC=BC=EC=9E=AC=EC=9D=B4=EC=9A=A9=20?= =?UTF-8?q?=EC=82=AC=EB=A1=80=EA=B4=80=EB=A6=AC=20=EC=A1=B0=ED=9A=8C?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wr/app/matMng/coMat/ruseCaseMng/page.tsx | 96 +++++++++++++++++-- .../app/store/matDmd/ruseCaseMng/headList.tsx | 4 +- .../wr/app/store/matDmd/ruseCaseMng/index.tsx | 8 +- 3 files changed, 92 insertions(+), 16 deletions(-) diff --git a/apps/wr/app/matMng/coMat/ruseCaseMng/page.tsx b/apps/wr/app/matMng/coMat/ruseCaseMng/page.tsx index 63d0f662e..436b77736 100644 --- a/apps/wr/app/matMng/coMat/ruseCaseMng/page.tsx +++ b/apps/wr/app/matMng/coMat/ruseCaseMng/page.tsx @@ -7,6 +7,7 @@ import RuseCaseMngExcel from "@/components/ui/modal/coMat/ruseCaseMng/ruseCaseMn import type { objSearchType } from "@/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal"; import { ContentsDiv, + filePickedBlobArray, SearchDiv, SearchFormLeft, SearchFormRight, @@ -24,7 +25,7 @@ import { useState } from "react"; export default function RuseCaseMng() { const { code, ruseCaseMng, selectRuseCaseMng } = useWrStore((state) => state); const { ruseCaseMngList } = ruseCaseMng; - const [selectSgg, setSelectSgg] = useState<SelectDataType[] | []>([{val: "0", text: "전체"}]); + const [selectSgg, setSelectSgg] = useState<SelectDataType[] | []>([]); const [objSearch, setObjSearch] = useState<objSearchType>({ sgg: "", city: "" }); const [sidoCd, setSidoCd] = useState<string>(""); @@ -34,17 +35,19 @@ export default function RuseCaseMng() { // 물재이용사례 조회 const searchRuseCaseMng = async () => { - console.log(selectUtz); - const res = await selectRuseCaseMng(sidoCd, sigunguCd, selectUtz, name); - console.log('res : ', res); + const res = await selectRuseCaseMng( + sidoCd ?? "", + sigunguCd ?? "", + selectUtz[0] ?? "", + name ?? "" + ); } // 시도 선택 const getSelCity = (val: string | number) => { const cityValue = String(val); - const cityText = code.cityCd.find((item: any) => item.cityCd === cityValue)?.text; + const cityText = code.cityCd.find((item: { val: string; text: string }) => item.val === cityValue)?.text; setSidoCd(cityText || ""); - const data = getCity(cityValue, code.sggCd); setSelectSgg(data); setObjSearch({ city: cityValue, sgg: "" }); @@ -53,7 +56,7 @@ export default function RuseCaseMng() { // 시군구 선택 const getSgg = (val: string | number) => { const sggValue = String(val); - const sggText = selectSgg?.find((item: any) => item.sggCd === sggValue)?.sggNm; + const sggText = selectSgg?.find((item: { val: string; text: string }) => item.val === sggValue)?.text; setSigunguCd(sggText || ""); setObjSearch({ ...objSearch, sgg: String(val) }); }; @@ -65,7 +68,82 @@ export default function RuseCaseMng() { // 다운로드 const download = () => { - console.log("다운로드"); + searchRuseCaseMng(); + + fetch(`/sample_ruseCaseMng.xlsx`, {}).then(async (response) => { + if (!response.ok) { + throw Error(response.statusText); + } + // 받은 파일 바인너리 처리 + const blobRet = await response.arrayBuffer(); + + const excelData1 = ruseCaseMngList.map((item) => { + return [ + item.ruseBizNm, + item.bizBgngDate, + item.bizEndDate, + item.splyFcltKnd, + item.splyFcltNm, + item.splyFcltCpct, + item.dmdFcltNm, + item.dmdFcltAddr, + item.ruseFcltCpct, + item.ruseFcltPplLen, + item.ruseFcltUsg, + item.ruseFcltSplyCpct, + item.ruseFcltPrcsMsrc, + item.grnteWqUsg, + item.grnteWqGrssClfg, + item.grnteWqCombResdch, + item.grnteWqTrbt, + item.grnteWqBod, + item.grnteWqSmll, + item.grnteWqCrmty, + item.grnteWqTn, + item.grnteWqTp, + item.grnteWqPh, + item.grnteWqClIonQnt, + item.grnteWqCndctStat, + item.grnteWqRmrk, + item.reqBizGrssCst, + item.reqBizReuseFcltCst, + item.reqBizPplCst, + item.reqBizArchCst, + item.reqBizMchnCst, + item.reqBizMtrngCst, + item.reqBizGisCst, + item.reqBizCctvCst, + item.reqBizCmcnCst, + item.reqBizWstCst, + item.reqBizDesignCst, + item.reqBizSpvsCst, + item.reqBizSbsdCst, + item.reqBizEtc, + item.prcmPlanGrssCst, + item.prcmPlanNlep, + item.prcmPlanLcep, + item.prcmPlanBrdnCst, + item.prcmPlanEcqrRt, + item.clclnGrssCst, + item.clclnNlep, + item.clclnLcep, + item.clclnBrdnCst, + item.clclnEcqrRt, + ]; + }); + + const sheetParameter = [ + { + data: excelData1, + headerNum: 2, + headerRange: 'A1:AX1', + sheetName: '물재이용사례', + startCol: 'A' + } + ]; + // 버퍼에 있는 엑셀데이터 전달 후 json으로 변환 + await filePickedBlobArray(blobRet, '시설정보및운영현황황', sheetParameter); + }); } // 양식받기 @@ -130,7 +208,7 @@ export default function RuseCaseMng() { <Button size={"sm"} onClick={() => { setIsExcelOpen({ isModal: true }); }}> 엑셀등록 </Button> - <Button size={"sm"} onClick={() => {}}> + <Button size={"sm"} onClick={() => { download() }}> 다운로드 </Button> <Button size={"sm"} onClick={() => { sampleDownLoad() }}> diff --git a/apps/wr/app/store/matDmd/ruseCaseMng/headList.tsx b/apps/wr/app/store/matDmd/ruseCaseMng/headList.tsx index 0a722e6c4..8c3a232e7 100644 --- a/apps/wr/app/store/matDmd/ruseCaseMng/headList.tsx +++ b/apps/wr/app/store/matDmd/ruseCaseMng/headList.tsx @@ -110,7 +110,7 @@ export const ruseCaseHeadListExport: TableUpperProps[] = [ title: "보증수질", visible: true, row: 0, - colSpan: 14, + colSpan: 13, }, { id: "grnteWqUsg", @@ -205,7 +205,7 @@ export const ruseCaseHeadListExport: TableUpperProps[] = [ }, { id: "reqBizReuseFcltCst", - title: "토목재이용시설", + title: "토목재이용시설", visible: true, row: 1, }, diff --git a/apps/wr/app/store/matDmd/ruseCaseMng/index.tsx b/apps/wr/app/store/matDmd/ruseCaseMng/index.tsx index 7ef350565..915740f79 100644 --- a/apps/wr/app/store/matDmd/ruseCaseMng/index.tsx +++ b/apps/wr/app/store/matDmd/ruseCaseMng/index.tsx @@ -6,7 +6,7 @@ import { ToastFormUi } from "@/components/ui/toast/toastFromUi"; export type ruseCaseState = { ruseCaseMng: { ruseHeadList:TableUpperProps[]; - ruseCaseMngList:TableUpperProps[]; + ruseCaseMngList:ruseCaseMngExcelProps[]; }; }; @@ -69,15 +69,13 @@ type ruseCaseActions = { }; export const fetchSearchRuseCaseMng = async (sidoCd: string, sigunguCd: string, usageCd : string, name: string) => { - const tailUrl = (sidoCd && !sigunguCd ? `/${sigunguCd}` : sigunguCd ? `/${sigunguCd}` : '/0') + (usageCd ? `/${usageCd}` : '') + (name ? `/${name}` : ''); - console.log('tailUrl : ', tailUrl); + + const tailUrl = ((sigunguCd !== '전체' && sigunguCd !=='') ? `/${sigunguCd}` : (sidoCd != '전체' && sidoCd != '') ? `/${sidoCd}` : `/0`) + (usageCd? `/${usageCd}` : '/0') + (name? `/${name}` : ''); const resp = await getFetch(fetchType.WR,`/reuseFc/pubFcRuseCase${tailUrl}`); - console.log('resp : ', resp); return resp; }; export const fetchInsertRuseCaseMng = async (data: any) => { - console.log('data : ', data) const response = await postFectch('/reuseFc/pubFcRuseCase', data, fetchType.WR); return response.data; }; From b6e847a55a97b41ab81af6b5306cf9df8489244e Mon Sep 17 00:00:00 2001 From: jyha00 <jyha@mind-one.co.kr> Date: Mon, 30 Jun 2025 19:27:37 +0900 Subject: [PATCH 022/108] =?UTF-8?q?=EC=9A=B0=EB=A6=AC=EB=8F=99=EB=84=A4?= =?UTF-8?q?=EA=B0=80=EB=AD=84=20=EC=A1=B0=ED=9A=8C,=20=EC=83=81=EC=84=B8?= =?UTF-8?q?=EC=A0=95=EB=B3=B4=20=EC=A1=B0=ED=9A=8C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wr/app/matMng/coMng/drgtSttn/page.tsx | 140 +++++++++++++++--- .../wr/app/store/matDmd/drgtSttn/headList.tsx | 33 ++--- apps/wr/app/store/matDmd/drgtSttn/index.tsx | 138 +++++++++++++++-- .../ui/modal/coMat/drgtDetailModal.tsx | 80 +++++----- 4 files changed, 310 insertions(+), 81 deletions(-) diff --git a/apps/wr/app/matMng/coMng/drgtSttn/page.tsx b/apps/wr/app/matMng/coMng/drgtSttn/page.tsx index 77bbe4a5a..d7c5170b2 100644 --- a/apps/wr/app/matMng/coMng/drgtSttn/page.tsx +++ b/apps/wr/app/matMng/coMng/drgtSttn/page.tsx @@ -1,32 +1,68 @@ "use client"; import { useWrStore } from "@/app/store"; import { btnClass } from "@/app/store/common/code"; -import type { customCoSttnBdst } from "@/app/store/matDmd/coSttn"; +import type { customDrgt } from "@/app/store/matDmd/drgtSttn"; import { drgtHeadListExport } from "@/app/store/matDmd/drgtSttn/headList"; import { excelDownload, getCity } from "@/components/lib/func/commom"; +import DrgtDetailModal from "@/components/ui/modal/coMat/drgtDetailModal"; import type { objSearchType } from "@/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal"; -import { ContentsDiv, SearchDiv, SearchFormLeft, SearchFormRight, SelectBox, Title, UiTable, type SelectDataType } from "@common/business_components/ui"; +import { ContentsDiv, DateMonthPicker, SearchDiv, SearchFormLeft, SearchFormRight, SelectBox, Title, UiModal, UiTable, type SelectDataType } from "@common/business_components/ui"; +import { MonthPicker } from "@common/business_components/ui/date_month_picker/mothPicker"; import ListDiv from "@common/business_components/ui/list_div"; import { Button, DatePicker, Label } from "@common/components/ui"; -import { useCallback, useState } from "react"; +import { useCallback, useEffect, useState } from "react"; +import { toast, Toaster } from "react-hot-toast"; export default function DrgtSttn() { - const { code, } = useWrStore((state) => state); - const [fromDate, setFromDate] = useState<Date>(new Date()); - const [toDate, setToDate] = useState<Date>(new Date()); + const { code, drgt, drgtSearch, drgtModalSearch } = useWrStore((state) => state); + const { drgtRes, drgtMoRes } = drgt; + const [fromDate, setFromDate] = useState<string>(); + const [toDate, setToDate] = useState<string>(); const [selectSgg, setSelectSgg] = useState<SelectDataType[] | []>(); const [objSearch, setObjSearch] = useState<objSearchType>({ sgg: "", city: "" }); + const [tableData, setTableData] = useState<customDrgt[]>(); + const [selectedSgg, setSelectedSgg] = useState<string>(''); + const [drgtModal, setDrgtModal] = useState<{ + isOpen: boolean; + data: customDrgt | undefined; + }>({ data: undefined, isOpen: false }); - const getSelCity = (val: string | number) => { - const cityValue = String(val); // Ensure val is treated as a string - const data = getCity(cityValue, code.sggCd); - setSelectSgg(data); - setObjSearch({ city: cityValue, sgg: '' }); + const cellClick = (index: number | undefined, headId: string) => { + if (fromDate && toDate) { + drgtModalSearch(fromDate, toDate, drgtRes[index].sggNm); + } else { + toast.error("조회기간을 선택해주세요."); + } + } + + useEffect(() => { + if (drgtMoRes && drgtMoRes.length > 0) { + setDrgtModal({ isOpen: true, data: drgtMoRes }); + } + }, [drgtMoRes]); + + const getCityVal = (val: string, sggCd: SelectDataType[]) => { + const data = sggCd + .filter((item) => String(item.val).substring(0, 2) === val) + .map((item) => ({ + val: item.val, + text: item.text, // 한국어 텍스트 + })); + return data; }; + const getSelCity = (val: string | number) => { + const cityValue = String(val); + const data = getCityVal(cityValue, code.sggCd); + setSelectSgg(data); + + const sggVal = data.length > 0 ? data[0].val : ''; + setSelectedSgg(sggVal); // 시군구 value 세팅 + setObjSearch({ city: cityValue, sgg: sggVal }); + }; const getSgg = (val: string | number) => { - console.log(val); - setObjSearch({ ...objSearch, sgg: String(val) }) + setSelectedSgg(String(val)); + setObjSearch({ ...objSearch, sgg: String(val) }); }; const excelClick = useCallback(() => { @@ -37,6 +73,17 @@ export default function DrgtSttn() { "A1:H1" ); }, []); + + useEffect(() => { + if (fromDate === undefined && toDate === undefined) { + const today = new Date(); + const year = today.getFullYear(); + const month = String(today.getMonth() + 1).padStart(2, '0'); + const formatted = `${year}${month}`; + setFromDate(formatted); + setToDate(formatted); + } + }, [fromDate, toDate]) return ( <> <ContentsDiv> @@ -59,18 +106,41 @@ export default function DrgtSttn() { selectClass={""} selectArray={selectSgg || []} onSelectValue={getSgg} + value={selectedSgg} /> <div className="col-span-5 w-auto"> <div className="flex flex-row items-center"> <Label className="text-xl text-gray-600">조회기간</Label> <div className="flex flex-row w-1/4"> - <DatePicker mode={'single'} - selected={fromDate} - onSelect={(date: Date | undefined) => { if (date) setFromDate(date) }} /> + <DateMonthPicker + field={{ + value: fromDate, + onChange: (date: string) => { + const year = date.slice(0, 4); + let month = date.slice(4); + if (month.length < 2) { + month = month.padStart(2, "0"); + } + const formatted = `${year}${month}`; + setFromDate(formatted); + } + }} + /> <span>~</span> - <DatePicker mode={'single'} - selected={toDate} - onSelect={(date: Date | undefined) => { if (date) setToDate(date) }} /> + <DateMonthPicker + field={{ + value: toDate, + onChange: (date: string) => { + const year = date.slice(0, 4); + let month = date.slice(4); + if (month.length < 2) { + month = month.padStart(2, "0"); + } + const formatted = `${year}${month}`; + setToDate(formatted); + } + }} + /> </div> </div> </div> @@ -81,7 +151,22 @@ export default function DrgtSttn() { <div className="flex flex-row space-x-2"> <Button size={"sm"} - onClick={(e) => { }} + onClick={(e) => { + let sidoText = ''; + let sggText = ''; + + if (objSearch?.city.toString().length > 0) { + sidoText = code.cityCd.find((item) => item.val === objSearch?.city)?.text ?? ''; + } + if (objSearch?.sgg.toString().length > 1) { + sggText = code.sggCd.find((item) => item.val === objSearch?.sgg)?.text ?? ''; + } + if (sidoText === '세종특별자치시') { + sidoText = '세종시'; + sggText = sidoText; + } + drgtSearch(fromDate ?? "", toDate ?? "", sidoText, sggText) + }} > 조회 </Button> @@ -116,16 +201,27 @@ export default function DrgtSttn() { <ListDiv> <UiTable headName={[]} - tableData={[]} + tableData={drgtRes || []} headlist={drgtHeadListExport} pageSize={20} total={10000} - cellClick={(index: number | undefined, headId: string) => { }} + cellClick={(index: number | undefined, headId: string) => { + cellClick(index ?? undefined, headId); + }} onPageChange={() => { }} ></UiTable> </ListDiv> </div> </div> + <UiModal + open={drgtModal.isOpen} + onOpenChange={() => { + setDrgtModal({ ...drgtModal, isOpen: !drgtModal.isOpen }); + }} + bodyClass={"w-[50vw] max-w-none h-[80vh]"} + body={<DrgtDetailModal data={drgtModal.data} /*formObj={drgtModal.data}*/ />} + /> + <Toaster position="top-center" /> </ContentsDiv> </> ) diff --git a/apps/wr/app/store/matDmd/drgtSttn/headList.tsx b/apps/wr/app/store/matDmd/drgtSttn/headList.tsx index 40eae47e3..1741b7456 100644 --- a/apps/wr/app/store/matDmd/drgtSttn/headList.tsx +++ b/apps/wr/app/store/matDmd/drgtSttn/headList.tsx @@ -1,5 +1,19 @@ +import { z } from "zod"; import type { CommonTableProps } from "../../common/type/header"; +export const drgtZod = z.object({ + ctpvCd: z.string().default(''), + sggNm: z.string().default(''), + normal: z.string().default(''), + attention: z.string().default(''), + caution: z.string().default(''), + warning: z.string().default(''), + ranking: z.string().default(''), + year: z.string().default(''), + month: z.string().default(''), + drgtState: z.string().default('') +}) + export const drgtHeadListExport: CommonTableProps[] = [ { id: "ctpvCd", @@ -10,21 +24,13 @@ export const drgtHeadListExport: CommonTableProps[] = [ colSpan: 1, }, { - id: "sggCd", + id: "sggNm", title: "시군구", visible: true, row: 1, rowSpan: 1, colSpan: 1, }, - { - id: "haengjung", - title: "행정동", - visible: true, - row: 1, - rowSpan: 1, - colSpan: 1, - }, { id: "normal", title: "정상(횟수)", @@ -83,16 +89,9 @@ export const drgtDetailHeadListExport: CommonTableProps[] = [ row: 1, rowSpan: 1, colSpan: 1, - },{ - id: "day", - title: "일", - visible: true, - row: 1, - rowSpan: 1, - colSpan: 1, }, { - id: "state", + id: "drgtState", title: "가뭄상태", visible: true, row: 1, diff --git a/apps/wr/app/store/matDmd/drgtSttn/index.tsx b/apps/wr/app/store/matDmd/drgtSttn/index.tsx index edbbc2f28..698ba5984 100644 --- a/apps/wr/app/store/matDmd/drgtSttn/index.tsx +++ b/apps/wr/app/store/matDmd/drgtSttn/index.tsx @@ -1,27 +1,147 @@ +import { fetchType, getFetch } from "@/components/lib/api"; import type { CommonTableProps } from "../../common/type/header"; -import { drgtHeadListExport } from "./headList"; +import { drgtHeadListExport, drgtZod } from "./headList"; +import type { z } from "zod"; const drgtHeadList = drgtHeadListExport; +export type customDrgt = z.infer<typeof drgtZod>; + +const drgtRes: customDrgt[] = []; + export type drgtState = { - drgt:{ - drgtHeadList: CommonTableProps[]; - } + drgt: { + drgtHeadList: CommonTableProps[]; + drgtRes: customDrgt[]; + drgtMoRes: customDrgt[]; + } } type drgtActions = { + drgtSearch: (fromDate: string, toDate: string, sido: string, sgg: string) => void + drgtModalSearch: (fromDate: string, toDate: string, sgg: string) => void }; export type drgtType = drgtState & drgtActions; export const drgtStateExport: drgtState = { - drgt: { - drgtHeadList: drgtHeadList, - } + drgt: { + drgtHeadList: drgtHeadList, + drgtRes: drgtRes, + drgtMoRes: drgtRes, + } } export const drgtActionsExport: (set: any) => drgtActions = ( set: any ) => { - return {}; -}; \ No newline at end of file + return { + drgtSearch: async (fromDate: string, toDate: string, sido: string, sgg: string) => { + try { + let searchUrl = ""; + + if (sido === "" || sido === "전체") { + searchUrl = `/il/getDataCount/${fromDate}/${toDate}`; + } else { + searchUrl = `/il/getDataCount/${fromDate}/${toDate}/${sido}/${sgg}`; + } + + console.log("searchUrl", searchUrl); + + const drgtData = await getFetch( + fetchType.WR, + searchUrl + ); + + console.log("drgtRes", drgtData) + + //주의, 관심, 정상 추출 + const statusData = Array.from( + new Set(drgtData.map((e) => e.lvngMfgUswtrDrgtInfo)) + ); + + const statusMap = drgtData.reduce((acc: any, { sggNm, lvngMfgUswtrDrgtInfo: status, countDrgt }: { sggNm: string; lvngMfgUswtrDrgtInfo: string; countDrgt: number }) => { + if (!acc[sggNm]) { + acc[sggNm] = { sggNm, ...Object.fromEntries(statusData.map((e) => [e, 0])) }; + } + acc[sggNm][status] = Number(countDrgt); + return acc; + }, {}); + const data = Object.values(statusMap); + + const formatStatus = data.map((item) => { + const attention = item['관심'] || 0; + const caution = item['주의'] || 0; + return { + sggNm: item.sggNm, + normal: String(item['정상'] || 0), + caution: String(caution), + attention: String(attention), + warning: String(attention + caution), + }; + }); + + const ranking = Array.from( + new Set(formatStatus.map(e => Number(e.warning))) + ).sort((a, b) => b - a); + + const rankData = ranking.reduce((acc, val, idx) => { + acc[val] = idx + 1; + return acc; + }, {}); + + const drgtRes = formatStatus.map(item => ({ + ...item, + warning: String(item.warning), + ranking: rankData[Number(item.warning)], + })); + set((state: drgtState) => { + return { + ...state, + drgt: { + ...state.drgt, + drgtRes: drgtRes, + }, + } + }); + } catch (e) { + console.log(e) + } + }, + drgtModalSearch: async (fromDate: string, toDate: string, sgg: string) => { + try { + const drgtMoData = await getFetch( + fetchType.WR, + `/il/getData/${fromDate}/${toDate}/${sgg}/${sgg}` + ); + + console.log("drgtMoData", drgtMoData) + + const drgtMoRes = drgtMoData.map((item) => { + console.log("item", item) + const year = item.anlsYmd.slice(0, 4); + const month = item.anlsYmd.slice(4, 6); + const drgtState = item.lvngMfgUswtrDrgtInfo; + const sggNm = item.sggNm; + return { + year: year, + month: month, + drgtState: drgtState, + sggNm: sggNm + } + }); + set((state: drgtState) => { + return { + ...state, + drgt: { + ...state.drgt, + drgtMoRes: drgtMoRes, + }, + } + }); + } catch (e) { + console.log(e) + } + }, + } +} \ No newline at end of file diff --git a/apps/wr/components/ui/modal/coMat/drgtDetailModal.tsx b/apps/wr/components/ui/modal/coMat/drgtDetailModal.tsx index a6b05ca77..1be95fae6 100644 --- a/apps/wr/components/ui/modal/coMat/drgtDetailModal.tsx +++ b/apps/wr/components/ui/modal/coMat/drgtDetailModal.tsx @@ -1,48 +1,62 @@ "use client" import { btnClass } from "@/app/store/common/code" +import type { customDrgt } from "@/app/store/matDmd/drgtSttn" import { drgtDetailHeadListExport } from "@/app/store/matDmd/drgtSttn/headList" -import { ContentsDiv, Title, UiTable } from "@common/business_components/ui" +import { excelDownload } from "@/components/lib/func/commom" +import { Title, UiTable } from "@common/business_components/ui" import ListDiv from "@common/business_components/ui/list_div" import { Button } from "@common/components/ui" +import { useCallback, useEffect } from "react" + +export default function DrgtDetailModal({ + data, +}: { + data: customDrgt; +}) { + const excelClick = useCallback(() => { + excelDownload( + [[]], + "우리동네가뭄", + "sample_drgtSttn.xlsx", + "A1:H1" + ); + }, []); -export default function DrgtDetailModal() { return ( <> - <ContentsDiv> - <div className="grid grid-cols-4 gap-4 "> - <div className="col-span-4"> - <Title title={"우리동네가뭄 상세내용"} /> - <div className="w-2/12 mx-5 flex flex-col items-center justify-center space-x-2 "> - <div className="flex flex-row space-x-2"> - <Button - size={"sm"} - className={`${btnClass} green`} - onClick={() => { }} - > - 다운로드 - </Button> - </div> - <div className="grid grid-cols-6 gap-4 "> - <div className="col-span-6"> - <ListDiv> - <UiTable - headName={[]} - tableData={[]} - headlist={drgtDetailHeadListExport} - pageSize={10} - total={10000} - cellClick={(index: number | undefined, headId: string) => { }} - onPageChange={() => { }} - ></UiTable> - </ListDiv> - </div> - </div> + <div className="grid grid-cols-4 gap-4 h-full"> + <div className="col-span-4"> + <div className="flex items-center justify-between"> + <Title title={`우리동네가뭄 상세내용 - ${data[0].sggNm}`} /> + <div className="flex flex-row space-x-2"> + <Button + size={"sm"} + className={`${btnClass} green mt-12`} + onClick={excelClick} + > + 다운로드 + </Button> + </div> + </div> + <hr className="mt-2 mb-2" /> + <div className="grid grid-cols-6 gap-4 "> + <div className="col-span-6"> + <ListDiv> + <UiTable + headName={[]} + tableData={data} + headlist={drgtDetailHeadListExport} + pageSize={10} + total={10000} + cellClick={(index: number | undefined, headId: string) => { }} + onPageChange={() => { }} + ></UiTable> + </ListDiv> </div> </div> </div> - - </ContentsDiv> + </div> </> ) } \ No newline at end of file From 19621c17b43c3872c350fc0cf646a4dce9b1a215 Mon Sep 17 00:00:00 2001 From: jyha00 <jyha@mind-one.co.kr> Date: Mon, 30 Jun 2025 20:13:28 +0900 Subject: [PATCH 023/108] =?UTF-8?q?=EC=9E=A0=EC=9E=AC=EC=88=98=EC=9A=94?= =?UTF-8?q?=EC=B2=98=20=EC=97=91=EC=85=80=20=EB=93=B1=EB=A1=9D=20=EC=A0=84?= =?UTF-8?q?=ED=99=94=EB=B2=88=ED=98=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../coReg/coRegExcel/coRegExcelModal.tsx | 114 +++++++++--------- 1 file changed, 60 insertions(+), 54 deletions(-) diff --git a/apps/wr/components/ui/modal/coReg/coRegExcel/coRegExcelModal.tsx b/apps/wr/components/ui/modal/coReg/coRegExcel/coRegExcelModal.tsx index 1fa8fa8f3..34d46043f 100644 --- a/apps/wr/components/ui/modal/coReg/coRegExcel/coRegExcelModal.tsx +++ b/apps/wr/components/ui/modal/coReg/coRegExcel/coRegExcelModal.tsx @@ -1,12 +1,12 @@ import { useWrStore } from "@/app/store"; import { coRegExcel } from "@/app/store/matDmd/coSttn/headList"; import type { customLdrmnt } from "@/app/store/matDmd/ldrmntCoSttn"; -import { excelToLdrmntData, names } from "@/app/store/matDmd/ldrmntCoSttn/excel"; +import { names } from "@/app/store/matDmd/ldrmntCoSttn/excel"; import { filePicked, FileUpload, FormUi, Title } from "@common/business_components/ui"; import { Form, FormField } from "@common/business_components/ui/form"; -import { Button, Input } from "@common/components/ui"; +import { Button } from "@common/components/ui"; import { zodResolver } from "@hookform/resolvers/zod"; -import { useEffect, useState } from "react"; +import { useState } from "react"; import { useForm } from "react-hook-form"; import type { z } from "zod"; @@ -26,34 +26,41 @@ export default function CoRegExcel() { ldrmntDelete(); if (file) { filePicked(file, 0).then((res) => { + //TODO:loading try { - console.log("res", res) let array: { [key: string]: string | number }[] = [] - if (Array.isArray(res)) - res.forEach((item, index) => { + let isError = false; + if (Array.isArray(res)){ + res.some((item, index) => { let object: { [key: string]: string | number } = {} - Object.keys(names).forEach((key) => { + Object.keys(names).some((key) => { if (item && names[key] !== undefined) { - if (key === "공장관리번호") + if (key === "공장관리번호") { item[key] = String(item[key]) - object[names[key] as string] = item[key]; + } else { + object[names[key] as string] = item[key]; + } } - + return false; }) + if(isError === true) + return true; array.push(object) if (index != 0 && index % 5000 == 0) { ldrmntRegData(array); - console.log("array", array) array = [] array.length = 0; - // throw new Error("error") } + return false; }) + } + if(isError === false) ldrmntRegData(array); - console.log("array끝", array) } catch (e) { console.log(e) + alert(e) + return true; } }); @@ -61,47 +68,46 @@ export default function CoRegExcel() { } - return ( - <> - <div> - <Form {...form}> - <form onSubmit={form.handleSubmit(onSubmit)}> - <div> - <Title title={"잠재수요처 등록"}> -
-
-

- {"잠재수요처 파일"} -

+ return ( + <> +
+
+ +
+ +
+
+

+ {"잠재수요처 파일"} +

- -
+ +
-
+
- ( - - { - console.log("files", files) - if (files && files?.length === 1) setFiles(files[0]); - }} - fileTypes="" - maxFiles={1} - > - - )} - /> -
- - -
- - ); - } + ( + + { + if (files && files?.length === 1) setFiles(files[0]); + }} + fileTypes="" + maxFiles={1} + > + + )} + /> +
+ + +
+ + ); +} From 31e2e3dea14e298ce1a49e9c8f5dec3ac4ac8fae Mon Sep 17 00:00:00 2001 From: leedj312 Date: Mon, 30 Jun 2025 20:18:18 +0900 Subject: [PATCH 024/108] =?UTF-8?q?=ED=95=98=EC=88=98=EC=9E=AC=EC=9D=B4?= =?UTF-8?q?=EC=9A=A9=20=EC=8B=9C=EC=84=A4=20=EB=93=B1=EB=A1=9D=20=EA=B2=B0?= =?UTF-8?q?=ED=95=A8=EB=8C=80=EC=9E=A5=20=EC=88=98=EC=A0=95=2011=EB=B2=88?= =?UTF-8?q?=1C=20=EA=B9=8C=EC=A7=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/ruseFcltMng/fcltReg/pbnstReg/page.tsx | 122 +++++++++++------- apps/wr/app/store/common/code/index.ts | 34 +++++ .../ruseFcltMng/fcltReg/pbnstReg/headList.tsx | 45 +++---- .../ruseFcltMng/fcltReg/pbnstReg/index.tsx | 22 ++++ .../ruseFcltMng/fcltReg/pbnstReg/type.tsx | 105 ++++++++------- .../fcltReg/pbnstReg/buseReg/bugRegForm.tsx | 57 +++++++- .../fcltReg/pbnstReg/mngReg/mngRegForm.tsx | 55 ++++---- .../mngRegFormDetail/mngRegFormDetail.tsx | 60 +++++---- 8 files changed, 329 insertions(+), 171 deletions(-) diff --git a/apps/wr/app/ruseFcltMng/fcltReg/pbnstReg/page.tsx b/apps/wr/app/ruseFcltMng/fcltReg/pbnstReg/page.tsx index d61478a75..174f268d0 100644 --- a/apps/wr/app/ruseFcltMng/fcltReg/pbnstReg/page.tsx +++ b/apps/wr/app/ruseFcltMng/fcltReg/pbnstReg/page.tsx @@ -31,7 +31,7 @@ import { PbnstBuseForm } from "@/components/ui/form/fcltReg/pbnstReg/buseReg/bug import { PbnstRegModal } from "@/components/ui/modal/fcltReg/pbnstRegModal/pbnstRegModal"; import toast, { Toaster } from "react-hot-toast"; // toast 이미 import 되어 있음 import { writeRgtrAndMdfr } from "@/app/store/ruseFcltMng/fcltReg/pbnstReg"; -import { set, useFieldArray, useForm, type FieldErrors } from "react-hook-form"; // FieldErrors 타입 추가 +import { useFieldArray, useForm, type FieldErrors } from "react-hook-form"; // FieldErrors 타입 추가 import { zodResolver } from "@hookform/resolvers/zod"; import { z } from "zod"; @@ -57,8 +57,8 @@ export default function PublicReuseFaciltyInsert() { const [searchTxt, setSearchTxt] = useState(); const [bdstTable, setbdstTable] = useState(); const [selectRow, setSelectRow] = useState(); + const [pageNation, setPageNation] = useState(1); - const [testMonth, setTestMonth] = useState("202406"); const form = useForm<{ items: z.infer[]; }>({ @@ -74,6 +74,8 @@ export default function PublicReuseFaciltyInsert() { control: form.control, name: `items`, }); + + const formWatch = form.watch("items"); useEffect(() => { if (!pbnst.isInit) { pubHasuInit("1", "41"); @@ -159,18 +161,9 @@ export default function PublicReuseFaciltyInsert() { } }; - useEffect(() => { - console.log("bdstTable ", bdstTable); - }, [bdstTable]); - useEffect(() => { if (code.sggCd.length && pbnstHasuRes.length > 0) { setbdstTable(tableData(pbnstHasuRes, { code })); - // if (searchFclts) { // 왜 오류가 발생할까? - - // setbdstTable(tableData(searchFclts, { code })); - // } else if (code.sggCd.length > 0 && pbnstHasuRes.length > 0) { - // } } }, [code, pbnstHasuRes]); @@ -180,6 +173,8 @@ export default function PublicReuseFaciltyInsert() { setSelectRow(pbnstHasuRes?.[val]); if (selectRow) { pbnstRegClick(selectRow); + } else { + console.error("선택된 행이 없습니다."); } }; @@ -261,7 +256,7 @@ export default function PublicReuseFaciltyInsert() { { setSearchTxt(e); @@ -323,45 +318,76 @@ export default function PublicReuseFaciltyInsert() {
- - - - 시설정보 - - - 차수별정보 - - + + + { + console.log("setIndexMng", index); + setIndexMng(index); + }} + indexMng={indexMng} + /> + + + { + setIndexBiz(index); + }} + indexMng={indexMng} + indexBiz={indexBiz} + /> + + + ) : ( + <> +
+ 등록된 재이용시설 정보가 없습니다. +
+
+

+ 신규등록 버튼을 클릭하여 재이용시설을 등록해주세요. +

+ - - - { - console.log("setIndexMng", index); - setIndexMng(index); - }} - indexMng={indexMng} - /> - - - { - setIndexBiz(index); - }} - indexMng={indexMng} - indexBiz={indexBiz} - /> - - +
+ + + )}
void; pbnstRuseBizUpdate: (ruseRes: fcBizType[]) => void; pubHasuSearch :(page: string, sggOrCity: string , searchTxt:string) => void + getFcOrgwts: (fcltPkList: string[], size:number) => void; }; export type pubFacType = pubFacState & pubFacActions; @@ -90,6 +92,7 @@ export const pubFacStateExport: pubFacState = { pbnstBizCycle: [{ text: "없음", val: "1" }], pbnstMngRegData: [], pbnsUpdateIndex: {} as pbnstInsertIndex, + fcOrgwtsNmList:[], dmdComList: [], }, }; @@ -398,6 +401,25 @@ export const pubFacActionsExport: (set: any) => pubFacActions = (set: any) => { } catch (e) { console.log(e); } + },getFcOrgwts : async (fcltPkList: string[] ,size:number) => { + let url = `/prcsFclt/ruseInfo/1/${size}/A/0/0/1/SW`; + + const fcOrgwtsNmList = await postFectch( + url, + fcltPkList, + fetchType.COMMON + ); + + return set((state: pubFacState) => ({ + ...state, + pbnst: { + ...state.pbnst, + fcOrgwtsNmList:fcOrgwtsNmList.data??[] + }, + })); + + + } }; }; diff --git a/apps/wr/app/store/ruseFcltMng/fcltReg/pbnstReg/type.tsx b/apps/wr/app/store/ruseFcltMng/fcltReg/pbnstReg/type.tsx index 9c390d076..b908750fd 100644 --- a/apps/wr/app/store/ruseFcltMng/fcltReg/pbnstReg/type.tsx +++ b/apps/wr/app/store/ruseFcltMng/fcltReg/pbnstReg/type.tsx @@ -14,10 +14,10 @@ export type selectLabel = { val: string; }; -export type pbnstInsertIndex ={ - mng:number, - biz:number -} +export type pbnstInsertIndex = { + mng: number; + biz: number; +}; export type publicReuseFacilityType = { sido: string; @@ -45,20 +45,20 @@ export type ruseFcltType = { }; export type PbnstDemandComType = { - dmdInfoPk : string | ''; - bzmnNo : string | ''; - dmdCoNm : string | ''; - daddr : string | ''; - telno : string | ''; - rgtr : string | ''; - useUsgCd : string | ''; - ctpvCd : string | ''; - sggCd : string | ''; - dmdAplyPswd: string | ''; + dmdInfoPk: string | ""; + bzmnNo: string | ""; + dmdCoNm: string | ""; + daddr: string | ""; + telno: string | ""; + rgtr: string | ""; + useUsgCd: string | ""; + ctpvCd: string | ""; + sggCd: string | ""; + dmdAplyPswd: string | ""; splyCpct: number | 0; - splyFmYn:string|'Y' -// dmdInfoFk:string|'' -} + splyFmYn: string | "Y"; + // dmdInfoFk:string|'' +}; const pubHasuZod = z.object({ choose: z.boolean().default(false), @@ -68,23 +68,27 @@ const pubHasuZod = z.object({ bscFctNm: z.string().default(""), efflWqltStndrdNm: z.string().default(""), fctCpc: z.number().default(0), - addr:z.string().nullish() + addr: z.string().nullish(), }); - export type pubHasuType = z.infer; -const zDate = z.preprocess((v) => (v ? new Date(v as string) : undefined), z.date()).nullish() ; +const zDate = z + .preprocess((v) => (v ? new Date(v as string) : undefined), z.date()) + .nullish(); const zDateNullish = zDate.nullish(); const zDateDefaultNow = zDate.default(new Date()); -const zNum = z.preprocess((v) => (v !== null && v !== undefined ? Number(v) : v), z.number()); +const zNum = z.preprocess( + (v) => (v !== null && v !== undefined ? Number(v) : v), + z.number() +); const zNumNullish = zNum.nullish(); const zNumDefaultZero = zNum.default(0).nullish(); const pplInstlSchema = z.object({ ruseFcltBizFk: zNumNullish, choose: z.boolean().default(false), - pdmt: z.string().default(''), + pdmt: z.string().default(""), ppMqlt: z.string().default(""), cpct: zNumDefaultZero, }); @@ -115,22 +119,23 @@ export const fcOperSchema = z.object({ }); const fcOrgwtSchema = z.object({ - pbnstRuseFcltBizFk:zNumNullish, + pbnstRuseFcltBizFk: zNumNullish, swtrmtFcltFk: z.string().nullish(), orgwtWtritQnt: zNum, tmsYn: z.enum(["Y", "N"]).default("N"), dschgFmYn: z.enum(["Y", "N"]).default("N"), - dtlPstnExpln: z.string().default(''), + dtlPstnExpln: z.string().default(""), regDt: zDateNullish, - rgtr: z.string().default(''), + rgtr: z.string().default(""), mdfcnDt: zDateNullish, - mdfr: z.string().default(''), + mdfr: z.string().default(""), choose: z.boolean().default(false), + ruseUtztnFcltNm:z.string().nullish() }); const dmdColist = z.object({ - dmdInfoPk: z.string().default(''), - dmdCoNm: z.string().default(''), + dmdInfoPk: z.string().default(""), + dmdCoNm: z.string().default(""), daddr: z.string(), useUsgCd: z.string().nullish(), splyBgngDay: zDateDefaultNow, @@ -155,11 +160,11 @@ export const fcBizSchema = z.object({ gvsbs: zNumDefaultZero, lcep: zNumDefaultZero, cusrAlotm: zNumDefaultZero, - etc: z.string().default(''), + etc: z.string().default(""), totalMoney: zNumNullish.default(0), rprcsFcltCpct: zNumDefaultZero, majorPrcsMsrcCd: z.string().default(""), - addr: z.string().default(''), + addr: z.string().default(""), rprcsMthdCd: z.string().default(""), inoutYn: z.enum(["Y", "N"]).default("Y"), mainSplyPumpCpct: zNumNullish, @@ -171,17 +176,29 @@ export const fcBizSchema = z.object({ wwtEmsnFcltPrmsnYn: z.string().default("Y"), tmsInstlYn: z.string().default("Y"), pbnstDrngInflwExclYn: z.string().default("Y"), - bod: z.preprocess((v) => (v !== null && v !== undefined ? Number(v) : v), z.number().max(9999, "BOD 값은 5자리 미만이어야 합니다.")).nullish(), - grssClfg: z.preprocess((v) => (v !== null && v !== undefined ? Number(v) : v), z.number().max(9999, "총대장균군 값은 5자리 미만이어야 합니다.")).nullish(), - combResdch: z.preprocess((v) => (v !== null && v !== undefined ? Number(v) : v), z.number().max(9999, "결합잔류염소 값은 5자리 미만이어야 합니다.")).nullish(), - trbt: z.preprocess((v) => (v !== null && v !== undefined ? Number(v) : v), z.number().max(9999, "탁도 값은 5자리 미만이어야 합니다.")).nullish(), - clIonQnt: z.preprocess((v) => (v !== null && v !== undefined ? Number(v) : v), z.number().max(9999, "염소이온량 값은 5자리 미만이어야 합니다.")).nullish(), - smll: z.preprocess((v) => (v !== null && v !== undefined ? Number(v) : v), z.number().max(9999, "냄새 값은 5자리 미만이어야 합니다.")).nullish(), - crmty: z.preprocess((v) => (v !== null && v !== undefined ? Number(v) : v), z.number().max(9999, "색도 값은 5자리 미만이어야 합니다.")).nullish(), - tph: z.preprocess((v) => (v !== null && v !== undefined ? Number(v) : v), z.number().max(9999, "총인 값은 5자리 미만이어야 합니다.")).nullish(), - tn: z.preprocess((v) => (v !== null && v !== undefined ? Number(v) : v), z.number().max(9999, "총질소 값은 5자리 미만이어야 합니다.")).nullish(), - ph: z.preprocess((v) => (v !== null && v !== undefined ? Number(v) : v), z.number().max(9999, "수소이온농도 값은 5자리 미만이어야 합니다.")).nullish(), - cdtnv: z.preprocess((v) => (v !== null && v !== undefined ? Number(v) : v), z.number().max(9999, "전기전도도 값은 5자리 미만이어야 합니다.")).nullish(), + // bod: z.preprocess((v) => (v !== null && v !== undefined ? Number(v) : v), z.number().max(9999, "BOD 값은 5자리 미만이어야 합니다.")).nullish(), + // grssClfg: z.preprocess((v) => (v !== null && v !== undefined ? Number(v) : v), z.number().max(9999, "총대장균군 값은 5자리 미만이어야 합니다.")).nullish(), + // combResdch: z.preprocess((v) => (v !== null && v !== undefined ? Number(v) : v), z.number().max(9999, "결합잔류염소 값은 5자리 미만이어야 합니다.")).nullish(), + // trbt: z.preprocess((v) => (v !== null && v !== undefined ? Number(v) : v), z.number().max(9999, "탁도 값은 5자리 미만이어야 합니다.")).nullish(), + // clIonQnt: z.preprocess((v) => (v !== null && v !== undefined ? Number(v) : v), z.number().max(9999, "염소이온량 값은 5자리 미만이어야 합니다.")).nullish(), + // smll: z.preprocess((v) => (v !== null && v !== undefined ? Number(v) : v), z.number().max(9999, "냄새 값은 5자리 미만이어야 합니다.")).nullish(), + // crmty: z.preprocess((v) => (v !== null && v !== undefined ? Number(v) : v), z.number().max(9999, "색도 값은 5자리 미만이어야 합니다.")).nullish(), + // tph: z.preprocess((v) => (v !== null && v !== undefined ? Number(v) : v), z.number().max(9999, "총인 값은 5자리 미만이어야 합니다.")).nullish(), + // tn: z.preprocess((v) => (v !== null && v !== undefined ? Number(v) : v), z.number().max(9999, "총질소 값은 5자리 미만이어야 합니다.")).nullish(), + // ph: z.preprocess((v) => (v !== null && v !== undefined ? Number(v) : v), z.number().max(9999, "수소이온농도 값은 5자리 미만이어야 합니다.")).nullish(), + // cdtnv: z.preprocess((v) => (v !== null && v !== undefined ? Number(v) : v), z.number().max(9999, "전기전도도 값은 5자리 미만이어야 합니다.")).nullish(), + bod: z.string().nullish(), + grssClfg: z.string().nullish(), + combResdch: z.string().nullish(), + trbt: z.string().nullish(), + clIonQnt: z.string().nullish(), + smll: z.string().nullish(), + crmty: z.string().nullish(), + tph: z.string().nullish(), + tn: z.string().nullish(), + ph: z.string().nullish(), + cdtnv: z.string().nullish(), + designCst: zNumNullish, cstrnSpvsCst: zNumNullish, fcltSbsdCst: zNumNullish, @@ -217,7 +234,7 @@ export const fcBizSchema = z.object({ }); export const pbnstRegZod = z.object({ - pbnstRuseFcltPk: z.string().default(''), + pbnstRuseFcltPk: z.string().default(""), swtrmtFcltFk: z.string().default(""), ruseUtztnFcltNm: z.string().default(""), fcltCpct: zNumDefaultZero, @@ -232,9 +249,9 @@ export const pbnstRegZod = z.object({ fcOpers: z.array(fcOperSchema).default([]).nullish(), }); -export const pbnstRegArrayZod = z.array(pbnstRegZod) +export const pbnstRegArrayZod = z.array(pbnstRegZod); -export type PbnstRegArrrayType = z.infer +export type PbnstRegArrrayType = z.infer; export type fcBizType = z.infer; export type fcOrgwtType = z.infer; diff --git a/apps/wr/components/ui/form/fcltReg/pbnstReg/buseReg/bugRegForm.tsx b/apps/wr/components/ui/form/fcltReg/pbnstReg/buseReg/bugRegForm.tsx index be1459947..152441c70 100644 --- a/apps/wr/components/ui/form/fcltReg/pbnstReg/buseReg/bugRegForm.tsx +++ b/apps/wr/components/ui/form/fcltReg/pbnstReg/buseReg/bugRegForm.tsx @@ -56,14 +56,14 @@ export function PbnstBuseForm({ pbnst, demandCompList, getDemandCompList, + getFcOrgwts, regDemandCompany, } = useWrStore((state) => state); - const { pbnstBizCycle, pbnstRes } = pbnst; + const { fcOrgwtsNmList } = pbnst; const { fields: fcBizs, append: appendBiz } = useFieldArray({ control: form.control, name: `items.${indexMng}.fcBizs`, }); - const [bizCycle, setBizCycle] = useState("1"); const [ppiSelectList, setPpiSelectList] = useState<[{ name: string; val: SelectDataType[] }]>(); const [ppiYn, setPpiYn] = useState("Y"); @@ -76,25 +76,72 @@ export function PbnstBuseForm({ >([{ text: "없음", val: 0 }]); const [isFcOrgwts, setIsFcOrgwts] = useState(false); const [sumCpct, setSumCpct] = useState(0); - const [getDmdList, setGetFmdList] = useState(0); const [isDmdModal, setIsDmdModal] = useState<{ isMoal: boolean; data: DemandCompType; }>({ isMoal: false, data: {} as DemandCompType }); useEffect(() => { - const total = + const etc= isNaN(Number(form.getValues(`items.${indexMng}.fcBizs.${indexBiz}.etc`))) + let total = (form.getValues(`items.${indexMng}.fcBizs.${indexBiz}.gvsbs`) ?? 0) + (form.getValues(`items.${indexMng}.fcBizs.${indexBiz}.lcep`) ?? 0) + - (form.getValues(`items.${indexMng}.fcBizs.${indexBiz}.cusrAlotm`) ?? 0); + (form.getValues(`items.${indexMng}.fcBizs.${indexBiz}.cusrAlotm`) ?? 0) + + if( !etc ) { + total+=Number(form.getValues(`items.${indexMng}.fcBizs.${indexBiz}.etc`)) + } form.setValue(`items.${indexMng}.fcBizs.${indexBiz}.totalMoney`, total); }, [ form.getValues(`items.${indexMng}.fcBizs.${indexBiz}.gvsbs`), form.getValues(`items.${indexMng}.fcBizs.${indexBiz}.lcep`), form.getValues(`items.${indexMng}.fcBizs.${indexBiz}.cusrAlotm`), + form.getValues(`items.${indexMng}.fcBizs.${indexBiz}.etc`) ]); + + const fcOrgwts = form.watch( `items.${indexMng}.fcBizs.${indexBiz}.fcOrgwts`) + + useEffect(() =>{ + + console.log('fcOrgwts ', fcOrgwts) + if(fcOrgwts&&fcOrgwts?.length>0){ + const swtrmtFcltFkList = fcOrgwts.map((item) => item.swtrmtFcltFk); + + console.log('swtrmtFcltFkList ', swtrmtFcltFkList) + + if(swtrmtFcltFkList.length > 0){ + getFcOrgwts(swtrmtFcltFkList.filter((item): item is string => item != null), fcOrgwts?.length) + } + } + },[fcOrgwts]) + + useEffect(() =>{ + + if (fcOrgwtsNmList && fcOrgwtsNmList.length > 0) { + + console.log('fcOrgwtsNmList ', fcOrgwtsNmList) + console.log('fcOrgwts ', fcOrgwts) + + fcOrgwts?.forEach((item, index) => { + const orgwtNm = fcOrgwtsNmList.find( + (orgwt) => orgwt.bscFctCd === item.swtrmtFcltFk + )?.bscFctNm; + + console.log('orgwtNm ', orgwtNm) + if (orgwtNm) { + form.setValue( + `items.${indexMng}.fcBizs.${indexBiz}.fcOrgwts.${index}.ruseUtztnFcltNm`, + orgwtNm + ); + } + }); + } + + },[fcOrgwtsNmList,fcOrgwts]) + + // fcBizs 데이터가 변경될 때마다 차수 선택 목록을 업데이트합니다. useEffect(() => { const cycleLength = fcBizs.length; diff --git a/apps/wr/components/ui/form/fcltReg/pbnstReg/mngReg/mngRegForm.tsx b/apps/wr/components/ui/form/fcltReg/pbnstReg/mngReg/mngRegForm.tsx index d07d24a23..e1454342b 100644 --- a/apps/wr/components/ui/form/fcltReg/pbnstReg/mngReg/mngRegForm.tsx +++ b/apps/wr/components/ui/form/fcltReg/pbnstReg/mngReg/mngRegForm.tsx @@ -29,6 +29,10 @@ import { TEST_UUID } from "@/app/store/ruseFcltMng/fcltReg/rainReg"; import { Label } from "@common/business_components/ui/form/lib/label"; import React from "react"; import RuseMngTableForm from "@/components/ui/table/pbnstReg/ruseMngTableForm/ruseMngTableForm"; +import { + handleCommaNumberChange, + formatCommaNumber, +} from "@/components/lib/func/commom"; export type FilesType = { atflCd: string; file: File; @@ -46,13 +50,9 @@ export function PbnstMngForm({ indexMng: number; setIndexMng: (index: number) => void; }) { - const { - code, - pbnst, - pbnstInsert, - fileDelete, - fileDownLoad, - } = useWrStore((state) => state); + const { code, pbnst, pbnstInsert, fileDelete, fileDownLoad } = useWrStore( + (state) => state + ); const { pbnstRes } = pbnst; const [regFileList, setRegFileList] = useState(null); @@ -215,16 +215,14 @@ export function PbnstMngForm({ useEffect(() => { if (pbnst.pbnstRes && pbnst.pbnstRes?.length >= 0) { - const fcltNmList = pbnst.pbnstRes?.map((item, index) => ({ text: item.ruseUtztnFcltNm, val: String(index), })); - - console.log(' fcltNmList ', fcltNmList) + console.log(" fcltNmList ", fcltNmList); if (fcltNmList) setFcltNm(fcltNmList); - }else{ + } else { setFcltNm(null); } }, [pbnst.pbnstRes]); @@ -253,16 +251,14 @@ export function PbnstMngForm({ } }, [code]); - - useEffect(() =>{ - // console.log(' fcltNm ', watchedItem?.ruseUtztnFcltNm) - },[watchedItem]) - + useEffect(() => { + // console.log(' fcltNm ', watchedItem?.ruseUtztnFcltNm) + }, [watchedItem]); useEffect(() => { console.log(" form indexMng ", indexMng); - }, [indexMng]); - + }, [indexMng]); + return (
@@ -297,8 +293,6 @@ export function PbnstMngForm({ render={({ field }) => ( { - field.onChange(Number(e.target.value)); + if (formType !== "sttn") + handleCommaNumberChange(e, field.onChange); }} - value={field.value ?? 0} + value={formatCommaNumber(field.value)} readOnly={formType === "sttn" ? true : false} /> @@ -401,11 +396,12 @@ export function PbnstMngForm({ { - field.onChange(Number(e.target.value)); + if (formType !== "sttn") + handleCommaNumberChange(e, field.onChange); }} - value={field.value ?? 0} + value={formatCommaNumber(field.value)} readOnly={formType === "sttn" ? true : false} /> @@ -422,11 +418,12 @@ export function PbnstMngForm({ { - field.onChange(Number(e.target.value)); + if (formType !== "sttn") + handleCommaNumberChange(e, field.onChange); }} - value={field.value ?? 0} + value={formatCommaNumber(field.value)} readOnly={formType === "sttn" ? true : false} /> diff --git a/apps/wr/components/ui/form/fcltReg/pbnstReg/mngReg/mngRegFormDetail/mngRegFormDetail.tsx b/apps/wr/components/ui/form/fcltReg/pbnstReg/mngReg/mngRegFormDetail/mngRegFormDetail.tsx index 971888e10..6e13058ab 100644 --- a/apps/wr/components/ui/form/fcltReg/pbnstReg/mngReg/mngRegFormDetail/mngRegFormDetail.tsx +++ b/apps/wr/components/ui/form/fcltReg/pbnstReg/mngReg/mngRegFormDetail/mngRegFormDetail.tsx @@ -2,17 +2,14 @@ import { useWrStore } from "@/app/store"; import type { formDetailType } from "@/app/store/ruseFcltMng/fcltReg/pbnstReg/headList"; import { formH1Class } from "@/components/lib/class/comClass"; -import { safeDate } from "@/components/lib/func/commom"; +import { formatCommaNumber, handleCommaNumberChange, safeDate } from "@/components/lib/func/commom"; import { DatePicker, FormField, FormUi, - SearchInputSelect, SelectBox, - type SelectDataType, } from "@common/business_components/ui"; import { Input } from "@common/components/ui"; -import { useState } from "react"; export default function MngRegFormDetail({ formList, @@ -55,9 +52,7 @@ export default function MngRegFormDetail({ name={fieldId} render={({ field }) => ( - {formHead.type === "select" ? ( - - ) : formHead.type === "date" ? ( + {formHead.type === "date" ? ( ) : formHead.type === "number" ? ( { - field.onChange(Number(e.target.value)); + handleCommaNumberChange( + e, + field.onChange + ); }} - readOnly={formHead.option === "readOnly"?true:false} - /> - ) : formHead.type === "selectYn" ? ( - - field.onChange(val) - } - value={field.value ?? "Y"} + value={formatCommaNumber(field.value)} + readOnly={formHead.option === "readOnly" ? true : false} + disabled={formHead.option === "disabled" ? true : false} + /> + ) : formHead.type === "select" ? ( + (() => { + const potentialOptions = formHead.selectData + ? code[formHead.selectData as keyof typeof code] + : []; + + return ( + + ); + })() ) : ( )} From c5830e0326f7804533abda9d51e86317928b0425 Mon Sep 17 00:00:00 2001 From: leedj312 Date: Tue, 1 Jul 2025 10:26:37 +0900 Subject: [PATCH 025/108] =?UTF-8?q?=EC=88=98=EC=9A=94=EC=B2=98=20=EB=B2=84?= =?UTF-8?q?=ED=8A=BC=20=EB=AA=85=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/form/fcltReg/pbnstReg/buseReg/bugRegForm.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/wr/components/ui/form/fcltReg/pbnstReg/buseReg/bugRegForm.tsx b/apps/wr/components/ui/form/fcltReg/pbnstReg/buseReg/bugRegForm.tsx index 152441c70..17983edf2 100644 --- a/apps/wr/components/ui/form/fcltReg/pbnstReg/buseReg/bugRegForm.tsx +++ b/apps/wr/components/ui/form/fcltReg/pbnstReg/buseReg/bugRegForm.tsx @@ -504,7 +504,7 @@ export function PbnstBuseForm({ size={commonButonSize} className="mt-6 mr-3" > - 수요항목 추가 + 수요처 추가
{( From 5579ab677d6a352c65ab5118882d4fa3aed09fed Mon Sep 17 00:00:00 2001 From: swcho Date: Tue, 1 Jul 2025 10:26:40 +0900 Subject: [PATCH 026/108] =?UTF-8?q?feat:=20=EC=9E=AC=EC=9D=B4=EC=9A=A9?= =?UTF-8?q?=EC=8B=9C=EC=84=A4=ED=98=84=ED=99=A9=20=EC=83=81=EB=8B=A8=20?= =?UTF-8?q?=EA=B2=80=EC=83=89=EB=B0=94=20=EC=8B=9C=EC=84=A4=EB=AA=85=20?= =?UTF-8?q?=EC=9E=90=EB=8F=99=EC=99=84=EC=84=B1=20=EA=B2=80=EC=83=89=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20(=EC=A1=B0=ED=9A=8C=20=EB=B2=84=ED=8A=BC,?= =?UTF-8?q?=20=EC=B4=88=EA=B8=B0=ED=99=94=20=EB=B2=84=ED=8A=BC=20=EB=AF=B8?= =?UTF-8?q?=EA=B5=AC=ED=98=84)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/sn/app/map/page.tsx | 1 + apps/wr/app/map/htdflSttn/page.tsx | 31 ++-- apps/wr/app/map/mapSearchBar.tsx | 157 +++++++----------- apps/wr/app/page.tsx | 4 +- .../business_components/src/ui/map/GisTree.js | 6 +- 5 files changed, 88 insertions(+), 111 deletions(-) diff --git a/apps/sn/app/map/page.tsx b/apps/sn/app/map/page.tsx index e1a90d5f1..d8bab06bb 100644 --- a/apps/sn/app/map/page.tsx +++ b/apps/sn/app/map/page.tsx @@ -106,6 +106,7 @@ const Map = () => { setIsSearchModalOpen={setIsSearchModalOpen} isOpen={isOpen} isSearchModalOpen={isSearchModalOpen} + onCheckedLayersChange={undefined} />
); diff --git a/apps/wr/app/map/htdflSttn/page.tsx b/apps/wr/app/map/htdflSttn/page.tsx index 5ba199c8c..937490fe2 100644 --- a/apps/wr/app/map/htdflSttn/page.tsx +++ b/apps/wr/app/map/htdflSttn/page.tsx @@ -108,15 +108,15 @@ const Map = () => { }; // MapSearchBar 래퍼 컴포넌트 - const CustomMapSearchBar = (props: any) => { - return ( - - ); - }; + // const CustomMapSearchBar = (props: any) => { + // return ( + // + // ); + // }; return (
@@ -125,10 +125,21 @@ const Map = () => { isSearch={false} gisMaps={gisMaps} isMiniMap={true} - searchView={CustomMapSearchBar} + searchView={(props: any) => ( + + ) + } detailView={(props: any) => ( )} diff --git a/apps/wr/app/map/mapSearchBar.tsx b/apps/wr/app/map/mapSearchBar.tsx index f5d693ee3..eabc6b892 100644 --- a/apps/wr/app/map/mapSearchBar.tsx +++ b/apps/wr/app/map/mapSearchBar.tsx @@ -1,16 +1,12 @@ "use client" -import { Button, Input, Label } from "@common/components/ui"; -import React, { useCallback, useEffect, useRef, useState, useMemo } from "react"; -import { useForm } from "react-hook-form"; -import { MapSearchFormLeft } from "./mapSearchForm"; import { useWrStore } from "@/app/store"; -import { btnClass } from "@/app/store/common/code"; -import { SearchBaseForm } from "@common/business_components"; -import { FormUi, SelectBox, SearchFormLeft, SearchFormRight, Form, FormField } from "@common/business_components/ui"; -import type { ReturnType, SelectDataType, EnvPartSelectDataType } from "@common/business_components/ui"; import SearchInputText from "@/components/ui/searchInput/searchInput"; -import { zodResolver } from "@hookform/resolvers/zod"; +import type { EnvPartSelectDataType, ReturnType, SelectDataType } from "@common/business_components/ui"; +import { SelectBox } from "@common/business_components/ui"; +import { Button, Input, Label } from "@common/components/ui"; +import React, { useEffect, useMemo, useRef, useState } from "react"; +import { MapSearchFormLeft } from "./mapSearchForm"; // 건물 검색 타입 정의 @@ -49,6 +45,8 @@ type SearchBaseType = { showQueryButton?: boolean; showInitButton?: boolean; showApplyButton?: boolean; + checkedLayers?: string[]; + checkedLayersData?: any[]; } const API_URL = `${process.env.NEXT_PUBLIC_SEWERAGE_API_URL}`; @@ -82,17 +80,29 @@ export function MapSearchBar({ rightClassName, showQueryButton = true, showInitButton = true, - showApplyButton = true + showApplyButton = true, + checkedLayers = [], + checkedLayersData = [], }: SearchBaseType) { // 로딩바 const [isLoading, setIsLoading] = useState(false); // 상세 정보 조회를 위한 상태 추가 const [popupFeature, setPopupFeature] = useState(null); - const { code } = useWrStore((state) => state); const [isChHead, setIsChHead] = useState(false); const ref = useRef({} as ReturnType); + // zustand store에서 건물 검색 함수와 데이터 가져오기 + const { + code, + bdst, + findBdst, + wrMap, + // buildingSearchRes, + buildingNmSearch, + buildingNmSearchMultiple + } = useWrStore((state) => state); + const [selectedBuilding, setSelectedBuilding] = useState(null); const [buildingSearchText, setBuildingSearchText] = useState(""); const [buildingList, setBuildingList] = useState([]); @@ -141,77 +151,16 @@ export function MapSearchBar({ ]; }, [selectedSigun]); - // 건물 검색 API 호출 함수 - const searchBuildings = async (searchText: string) => { - if (!searchText.trim()) { - setBuildingList([]); - return; - } - - try { - const flg = "41"; // 적절한 플래그 값으로 설정 - const sigunCode = selectedSigun.val !== '0' ? selectedSigun.val : ''; - const apiEndpoint = `${API_URL}/v1/reuseFc/building/${flg}/${sigunCode}/${encodeURIComponent(searchText)}`; - - const response = await fetch(apiEndpoint, { - method: "GET", - headers: { - accept: "application/json", - "Content-Type": "application/json" - } - }); - - if (!response.ok) { - throw new Error(`API 호출 실패: ${response.status}`); - } - - const result = await response.json(); - - if (result.status === 200 && Array.isArray(result.data)) { - // 건물 검색 결과를 demandCompList 형태로 변환 - const convertedList = result.data.map((building: BuildingInfo) => ({ - dmdInfoPk: String(building.bdstInfoPk), - dmdCoNm: building.bdstNm, - daddr: building.daddr, - useUsgCd: "", - splyCpct: 0, - splyFmYn: "Y", - // 추가 필드들... - })); - setBuildingList(result.data); - // 글로벌 상태에도 업데이트 (SearchInputText가 사용할 수 있도록) - // getDemandCompList를 직접 호출하는 대신 상태를 직접 설정 - const { setBuildingSearchResults } = useWrStore.getState(); - if (setBuildingSearchResults) { - setBuildingSearchResults(convertedList); - } - } else { - setBuildingList([]); - } - } catch (error) { - console.error("건물 검색 중 오류 발생:", error); - setBuildingList([]); - } - }; - // 조회 함수 const queryVal = () => { - console.log("조회버튼 클릭"); - if (searchType == '공급처 후보지 조회') { - // queryClick({ - // partValue: partValue, - // sidoValue: sidoValue, - // sigunValue: sigunValue, - // regionValue: regionValue, - // unitWaterShedValue: untitWaterShedValue, - // middleRegionValue: middleRegionValue, - // envPartValue: envPartValue, - // sidoText: selectedSido.text, - // sigunText: selectedSigun.text, - // }); - } else if (searchType == '우리동네가뭄지도보기') { - } else if (searchType == '지도보기') { - + // 46 : 하수처리시설 + // 47 : 하수재이용시설 + // 48 : 빗물이용시설 + // 49 : 중수도시설 + // 50 : 온배수시설 + console.log("조회버튼 클릭: ", checkedLayers); + if (checkedLayers && checkedLayers.includes('48')) { + // searchBuildings } } @@ -308,6 +257,27 @@ export function MapSearchBar({ } }, [partValue, sidoValue, sigunValue, regionValue, untitWaterShedValue, middleRegionValue, envPartValue, ref]); + // buildingSearchText가 변경될 때 API 호출 + useEffect(() => { + if (buildingSearchText && buildingSearchText.length > 1) { + if (checkedLayers && checkedLayers.length > 0) { + // 다중 레이어 검색 + // buildingNmSearchMultiple(checkedLayers, selectedSigun.val || "", buildingSearchText); + buildingNmSearch("RNB", selectedSigun.val, buildingSearchText); + } else { + // 단일 레이어 검색 + // buildingNmSearch(checkedLayers[0], selectedSigun.val, buildingSearchText); + } + } else { + // 검색 텍스트가 짧으면 리스트 초기화 + if (checkedLayers && checkedLayers.length > 0) { + // buildingNmSearchMultiple(checkedLayers, selectedSigun.val || "", ""); + } else { + buildingNmSearch("RNB", selectedSigun.val || "", ""); + } + } + }, [buildingSearchText]); + return (
@@ -338,32 +308,23 @@ export function MapSearchBar({ value={selectedSigun.val} selectClass={'w-32'} /> - {/* -
- -
*/}
{ - setBuildingSearchText(searchText || ""); - if (searchText) { - searchBuildings(searchText); - } + setValue={(searchNm?: string) => { + setBuildingSearchText(searchNm || ""); }} - setRtnData={(buildingData) => { - if (buildingData && buildingData.length > 0 && buildingData[0]) { - // 선택된 건물 정보를 찾아서 설정 - const selectedBuildingInfo = buildingList.find(building => - building.bdstNm === buildingData[0].dmdCoNm - ); - setSelectedBuilding(selectedBuildingInfo || null); - console.log("선택된 시설:", selectedBuildingInfo); - } + setRtnData={function (rtnData: any): void { + // 선택된 건물 데이터 처리 + // if (rtnData && rtnData.bdstInfoPk) { + // setSelectedBuilding(rtnData); + // setBuildingSearchText(rtnData.bdstNm || ""); + // } }} + searchList={wrMap.buildingSearchRes || []} />
diff --git a/apps/wr/app/page.tsx b/apps/wr/app/page.tsx index 70261c4c6..c2be341cf 100644 --- a/apps/wr/app/page.tsx +++ b/apps/wr/app/page.tsx @@ -159,8 +159,8 @@ export default function Home() {

시설 현황(지도)

-
diff --git a/packages/common/business_components/src/ui/map/GisTree.js b/packages/common/business_components/src/ui/map/GisTree.js index e36569143..3c2cb2819 100644 --- a/packages/common/business_components/src/ui/map/GisTree.js +++ b/packages/common/business_components/src/ui/map/GisTree.js @@ -41,8 +41,12 @@ export default React.memo(function GisTree({ checkedNodes = [], setCheckedNodes // 기본 체크된 레이어들을 지도에 표시 gisCore.setLayerOnMap(defaultChecked, true); + + // 초기 체크된 레이어 정보를 상위로 전달 + const defaultCheckedNodesData = nodes.filter(node => defaultChecked.includes(node.id)); + onCheckedChange?.(defaultChecked, defaultCheckedNodesData); } - }, [nodes]); + }, [nodes]); // onCheckedChange를 의존성에 추가 // 초기 표시 여부가 'Y'인 레이어들 찾기 const getDefaultCheckedNodes = (nodes, values = []) => { From ecc11720acbf2795439c46d69dad7af262213c61 Mon Sep 17 00:00:00 2001 From: swcho Date: Tue, 1 Jul 2025 10:30:38 +0900 Subject: [PATCH 027/108] =?UTF-8?q?fix:=20Gis.js=20import=20=EC=97=90?= =?UTF-8?q?=EB=9F=AC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/common/business_components/src/ui/map/Gis.js | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/common/business_components/src/ui/map/Gis.js b/packages/common/business_components/src/ui/map/Gis.js index 39cfdbe44..e85b8d162 100644 --- a/packages/common/business_components/src/ui/map/Gis.js +++ b/packages/common/business_components/src/ui/map/Gis.js @@ -14,7 +14,6 @@ import GisMapToggle from './GisMapToggle'; import GisSelect from './GisSelect'; import GisToolBar from './GisToolBar'; import GisTree from './GisTree'; -import FacilityInfoPopup from './facilityInfoPopup'; import './lib/css/gis.common.css'; import GisApp from './lib/gis.core'; From f4b0af5edea5013b3b9204e69c4637b04be913f6 Mon Sep 17 00:00:00 2001 From: swcho Date: Tue, 1 Jul 2025 11:01:20 +0900 Subject: [PATCH 028/108] =?UTF-8?q?fix:=20mapSearchBar=20=EC=A3=BC?= =?UTF-8?q?=EC=84=9D=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wr/app/map/htdflSttn/page.tsx | 21 +- apps/wr/app/map/mapSearchBar.tsx | 771 +++++++++--------- apps/wr/app/map/newWindowPopup.tsx | 8 +- .../business_components/src/ui/map/Gis.js | 200 ++--- 4 files changed, 503 insertions(+), 497 deletions(-) diff --git a/apps/wr/app/map/htdflSttn/page.tsx b/apps/wr/app/map/htdflSttn/page.tsx index 937490fe2..e74d055d8 100644 --- a/apps/wr/app/map/htdflSttn/page.tsx +++ b/apps/wr/app/map/htdflSttn/page.tsx @@ -125,16 +125,17 @@ const Map = () => { isSearch={false} gisMaps={gisMaps} isMiniMap={true} - searchView={(props: any) => ( - - ) - } + searchView={undefined} + // searchView={(props: any) => ( + // + // ) + // } detailView={(props: any) => ( void; - btnClass?: string; - ref?: React.RefObject; - isAdmin?: boolean; - leftClassName?: string; - rightClassName?: string; - showQueryButton?: boolean; - showInitButton?: boolean; - showApplyButton?: boolean; - checkedLayers?: string[]; - checkedLayersData?: any[]; -} +// type SearchBaseType = { +// searchType: string; +// selectSidoData?: SelectDataType[] | []; +// selectSigunData?: SelectDataType[] | []; +// selectRegionData?: SelectDataType[] | []; +// selectUnitWaterShedData?: SelectDataType[] | []; +// selectMiddleRegionData?: SelectDataType[] | []; +// selectEnvPartData?: EnvPartSelectDataType[] | []; +// onFeatureSelect: () => void; +// btnClass?: string; +// ref?: React.RefObject; +// isAdmin?: boolean; +// leftClassName?: string; +// rightClassName?: string; +// showQueryButton?: boolean; +// showInitButton?: boolean; +// showApplyButton?: boolean; +// checkedLayers?: string[]; +// checkedLayersData?: any[]; +// } -const API_URL = `${process.env.NEXT_PUBLIC_SEWERAGE_API_URL}`; +// const API_URL = `${process.env.NEXT_PUBLIC_SEWERAGE_API_URL}`; -const YEARS = Array.from({ length: 100 }, (_, i) => `${2025 - i}`); +// const YEARS = Array.from({ length: 100 }, (_, i) => `${2025 - i}`); -interface SearchModalProps { - open: boolean; - onOpenChange: (open: boolean) => void; - onFeatureSelect: (row: any) => void; -} +// interface SearchModalProps { +// open: boolean; +// onOpenChange: (open: boolean) => void; +// onFeatureSelect: (row: any) => void; +// } -interface SelectedDataTypeWithSggNm { - text: string; - val: string; - sggNm: string; -} +// interface SelectedDataTypeWithSggNm { +// text: string; +// val: string; +// sggNm: string; +// } -export function MapSearchBar({ - searchType, - selectSidoData, - selectSigunData, - selectRegionData, - selectUnitWaterShedData, - selectMiddleRegionData, - selectEnvPartData, - btnClass, - onFeatureSelect, - isAdmin = false, - leftClassName, - rightClassName, - showQueryButton = true, - showInitButton = true, - showApplyButton = true, - checkedLayers = [], - checkedLayersData = [], -}: SearchBaseType) { +// export function MapSearchBar({ +// searchType, +// selectSidoData, +// selectSigunData, +// selectRegionData, +// selectUnitWaterShedData, +// selectMiddleRegionData, +// selectEnvPartData, +// btnClass, +// onFeatureSelect, +// isAdmin = false, +// leftClassName, +// rightClassName, +// showQueryButton = true, +// showInitButton = true, +// showApplyButton = true, +// checkedLayers = [], +// checkedLayersData = [], +// }: SearchBaseType) { - // 로딩바 - const [isLoading, setIsLoading] = useState(false); - // 상세 정보 조회를 위한 상태 추가 - const [popupFeature, setPopupFeature] = useState(null); - const [isChHead, setIsChHead] = useState(false); - const ref = useRef({} as ReturnType); +// // 로딩바 +// const [isLoading, setIsLoading] = useState(false); +// // 상세 정보 조회를 위한 상태 추가 +// const [popupFeature, setPopupFeature] = useState(null); +// const [isChHead, setIsChHead] = useState(false); +// const ref = useRef({} as ReturnType); - // zustand store에서 건물 검색 함수와 데이터 가져오기 - const { - code, - bdst, - findBdst, - wrMap, - // buildingSearchRes, - buildingNmSearch, - buildingNmSearchMultiple - } = useWrStore((state) => state); +// // zustand store에서 건물 검색 함수와 데이터 가져오기 +// const { +// code, +// bdst, +// findBdst, +// wrMap, +// buildingSearchRes, +// buildingNmSearch, +// buildingNmSearchMultiple +// } = useWrStore((state) => state); - const [selectedBuilding, setSelectedBuilding] = useState(null); - const [buildingSearchText, setBuildingSearchText] = useState(""); - const [buildingList, setBuildingList] = useState([]); +// const [selectedBuilding, setSelectedBuilding] = useState(null); +// const [buildingSearchText, setBuildingSearchText] = useState(""); +// const [buildingList, setBuildingList] = useState([]); - // 전체 목록 - const [partValue, setPartValue] = useState("A"); - const [sidoValue, setSidoValue] = useState("0"); - const [sigunValue, setSigunValue] = useState("0"); - const [regionValue, setRegionValue] = useState("0"); - const [untitWaterShedValue, setUntitWaterShedValue] = useState("0"); - const [middleRegionValue, setMiddleRegionValue] = useState("0"); - const [envPartValue, setEnvPartValue] = useState("0"); - const [resetTrigger, setResetTrigger] = useState(false); - const [sidoText, setSidoText] = useState("0"); - const [sigunText, setSigunText] = useState("0"); - // 공급처 후보지 조회 필터 목록 - const [selectedSido, setSelectedSido] = useState({ text: "전체", val: '0' }); - const [selectedSigun, setSelectedSigun] = useState({ text: "전체", val: '0' }); - const [selectedFclt, setSelectedFclt] = useState({ text: "전체", val: '0', sggNm: '전체' }); // 시설명 선택 상태 - // 전체 시설 - let allFclt: SelectedDataTypeWithSggNm[] = [{ text: "전체", val: '0', sggNm: '전체' }]; - // 우리동네가뭄지도 필터 목록 - const [selectedDong, setSelectedDong] = useState({ text: "전체", val: '0', sggNm: '전체' }); // 시설명 선택 상태 // 행정동 +// // 전체 목록 +// const [partValue, setPartValue] = useState("A"); +// const [sidoValue, setSidoValue] = useState("0"); +// const [sigunValue, setSigunValue] = useState("0"); +// const [regionValue, setRegionValue] = useState("0"); +// const [untitWaterShedValue, setUntitWaterShedValue] = useState("0"); +// const [middleRegionValue, setMiddleRegionValue] = useState("0"); +// const [envPartValue, setEnvPartValue] = useState("0"); +// const [resetTrigger, setResetTrigger] = useState(false); +// const [sidoText, setSidoText] = useState("0"); +// const [sigunText, setSigunText] = useState("0"); +// // 공급처 후보지 조회 필터 목록 +// const [selectedSido, setSelectedSido] = useState({ text: "전체", val: '0' }); +// const [selectedSigun, setSelectedSigun] = useState({ text: "전체", val: '0' }); +// const [selectedFclt, setSelectedFclt] = useState({ text: "전체", val: '0', sggNm: '전체' }); // 시설명 선택 상태 +// // 전체 시설 +// let allFclt: SelectedDataTypeWithSggNm[] = [{ text: "전체", val: '0', sggNm: '전체' }]; +// const [selectedDong, setSelectedDong] = useState({ text: "전체", val: '0', sggNm: '전체' }); // 시설명 선택 상태 // 행정동 - const filteredSigunData = useMemo(() => { - if (!selectedSido || selectedSido.val === '0' || !selectedSido.val) return [{ text: "전체", val: '0' }]; - return [ - { text: "전체", val: '0' }, - ...code.sggCd.filter(item => item.val.substring(0, 2) === selectedSido.val.substring(0, 2)) - ]; - }, [selectedSido]); +// const filteredSigunData = useMemo(() => { +// if (!selectedSido || selectedSido.val === '0' || !selectedSido.val) return [{ text: "전체", val: '0' }]; +// return [ +// { text: "전체", val: '0' }, +// ...code.sggCd.filter((item: { val: string; }) => item.val.substring(0, 2) === selectedSido.val.substring(0, 2)) +// ]; +// }, [selectedSido]); - const filteredFcltData = useMemo(() => { - if (!selectedSigun || selectedSigun.val === '0' || !selectedSigun.val) return [{ text: "전체", val: '0', sggNm: '전체' }]; - return [ - { text: "전체", val: '0', sggNm: '전체' }, - ...allFclt.filter(item => item.sggNm === selectedSido.text) - ]; - }, [selectedSigun]); +// const filteredFcltData = useMemo(() => { +// if (!selectedSigun || selectedSigun.val === '0' || !selectedSigun.val) return [{ text: "전체", val: '0', sggNm: '전체' }]; +// return [ +// { text: "전체", val: '0', sggNm: '전체' }, +// ...allFclt.filter(item => item.sggNm === selectedSido.text) +// ]; +// }, [selectedSigun]); - const filteredDong = useMemo(() => { - if (!selectedSigun || selectedSigun.val === '0' || !selectedSigun.val) return [{ text: "전체", val: '0', sggNm: '전체' }]; - return [ - { text: "전체", val: '0', sggNm: '전체' }, - // ...code.dong.filter(item => item.sggNm === selectedSido.text) - ]; - }, [selectedSigun]); +// const filteredDong = useMemo(() => { +// if (!selectedSigun || selectedSigun.val === '0' || !selectedSigun.val) return [{ text: "전체", val: '0', sggNm: '전체' }]; +// return [ +// { text: "전체", val: '0', sggNm: '전체' }, +// // ...code.dong.filter(item => item.sggNm === selectedSido.text) +// ]; +// }, [selectedSigun]); - // 조회 함수 - const queryVal = () => { - // 46 : 하수처리시설 - // 47 : 하수재이용시설 - // 48 : 빗물이용시설 - // 49 : 중수도시설 - // 50 : 온배수시설 - console.log("조회버튼 클릭: ", checkedLayers); - if (checkedLayers && checkedLayers.includes('48')) { - // searchBuildings - } - } +// // 조회 함수 +// const queryVal = () => { +// // 46 : 하수처리시설 +// // 47 : 하수재이용시설 +// // 48 : 빗물이용시설 +// // 49 : 중수도시설 +// // 50 : 온배수시설 +// console.log("조회버튼 클릭: ", checkedLayers); +// if (checkedLayers && checkedLayers.includes('48')) { +// // searchBuildings +// } +// } - // 초기화 함수 - const initVal = () => { - setPartValue("A"); - setSidoValue("0"); - setSigunValue("0"); - setRegionValue("0"); - setUntitWaterShedValue("0"); - setMiddleRegionValue("0"); - setEnvPartValue("0"); - setSidoText("전체"); - setSigunText("전체"); - setSelectedSido({ text: "전체", val: '0' }); - setSelectedSigun({ text: "전체", val: '0' }); - setSelectedFclt({ text: "전체", val: '0', sggNm: '전체' }); - setSelectedDong({ text: "전체", val: '0', sggNm: '전체' }); - setResetTrigger(true); - }; +// // 초기화 함수 +// const initVal = () => { +// setPartValue("A"); +// setSidoValue("0"); +// setSigunValue("0"); +// setRegionValue("0"); +// setUntitWaterShedValue("0"); +// setMiddleRegionValue("0"); +// setEnvPartValue("0"); +// setSidoText("전체"); +// setSigunText("전체"); +// setSelectedSido({ text: "전체", val: '0' }); +// setSelectedSigun({ text: "전체", val: '0' }); +// setSelectedFclt({ text: "전체", val: '0', sggNm: '전체' }); +// setSelectedDong({ text: "전체", val: '0', sggNm: '전체' }); +// setResetTrigger(true); +// }; - // 적용 버튼 - const applyVal = () => { - onFeatureSelect(); - }; +// // 적용 버튼 +// const applyVal = () => { +// onFeatureSelect(); +// }; - useEffect(() => { - if (resetTrigger) { - setResetTrigger(false); - } - }, [resetTrigger]); +// useEffect(() => { +// if (resetTrigger) { +// setResetTrigger(false); +// } +// }, [resetTrigger]); - // 시설 조회 - useEffect(() => { - const fetchData = async () => { - setIsLoading(true); - if (!selectedSigun || selectedSigun.val === '0' || !selectedSigun.val) { - allFclt = ([{ text: "전체", val: '0', sggNm: '전체' }]); - setIsLoading(false); - return; - } +// // 시설 조회 +// useEffect(() => { +// const fetchData = async () => { +// setIsLoading(true); +// if (!selectedSigun || selectedSigun.val === '0' || !selectedSigun.val) { +// allFclt = ([{ text: "전체", val: '0', sggNm: '전체' }]); +// setIsLoading(false); +// return; +// } - try { - const queryParamsFacility = new URLSearchParams({ - lyrTypeNm: 'tb_il_swage_prcs_fclt', - page: '0', - size: '100000', - sort: 'string', - }); - const cqlFiltersFacility: string[] = []; - if (selectedSigun.text && selectedSigun.val !== '0') { - cqlFiltersFacility.push(`sgg_nm ILIKE '%${selectedSigun.text}%'`); - } - if (cqlFiltersFacility.length > 0) { - queryParamsFacility.append('cqlFiter', cqlFiltersFacility.join(' and ')); - } - console.log("BaseLayout API URL for Facilities:", `v1/fclts?${queryParamsFacility.toString()}`); +// try { +// const queryParamsFacility = new URLSearchParams({ +// lyrTypeNm: 'tb_il_swage_prcs_fclt', +// page: '0', +// size: '100000', +// sort: 'string', +// }); +// const cqlFiltersFacility: string[] = []; +// if (selectedSigun.text && selectedSigun.val !== '0') { +// cqlFiltersFacility.push(`sgg_nm ILIKE '%${selectedSigun.text}%'`); +// } +// if (cqlFiltersFacility.length > 0) { +// queryParamsFacility.append('cqlFiter', cqlFiltersFacility.join(' and ')); +// } +// console.log("BaseLayout API URL for Facilities:", `v1/fclts?${queryParamsFacility.toString()}`); - const facilityRes = await fetch(`${API_URL}/v1/fclts?${queryParamsFacility.toString()}`, { - method: "GET", - headers: { accept: "application/json" } - }).then(res => res.json()); +// const facilityRes = await fetch(`${API_URL}/v1/fclts?${queryParamsFacility.toString()}`, { +// method: "GET", +// headers: { accept: "application/json" } +// }).then(res => res.json()); - const facilities = (facilityRes.data || []).map((item: any) => ({ - text: item.swtrmtFcltNm, - val: item.fid, - sggNm: item.sggNm - })); - allFclt = (facilities); +// const facilities = (facilityRes.data || []).map((item: any) => ({ +// text: item.swtrmtFcltNm, +// val: item.fid, +// sggNm: item.sggNm +// })); +// allFclt = (facilities); - } catch (error) { - console.error("BaseLayout: 지역/시설 데이터 조회 중 오류 발생:", error); - allFclt = ([{ text: "전체", val: '0', sggNm: '전체' }]); - } finally { - setIsLoading(false); - } - }; +// } catch (error) { +// console.error("BaseLayout: 지역/시설 데이터 조회 중 오류 발생:", error); +// allFclt = ([{ text: "전체", val: '0', sggNm: '전체' }]); +// } finally { +// setIsLoading(false); +// } +// }; - fetchData(); - }, [selectedSigun]); +// fetchData(); +// }, [selectedSigun]); - useEffect(() => { - if (ref && ref.current) { - const customRef = ref.current; - customRef.partValue = partValue; - customRef.sidoValue = sidoValue; - customRef.sigunValue = sigunValue; - customRef.regionValue = regionValue; - customRef.unitWaterShedValue = untitWaterShedValue; - customRef.middleRegionValue = middleRegionValue; - customRef.envPartValue = envPartValue; - customRef.sidoText = selectedSido.text; - customRef.sigunText = selectedSigun.text; - } - }, [partValue, sidoValue, sigunValue, regionValue, untitWaterShedValue, middleRegionValue, envPartValue, ref]); +// useEffect(() => { +// if (ref && ref.current) { +// const customRef = ref.current; +// customRef.partValue = partValue; +// customRef.sidoValue = sidoValue; +// customRef.sigunValue = sigunValue; +// customRef.regionValue = regionValue; +// customRef.unitWaterShedValue = untitWaterShedValue; +// customRef.middleRegionValue = middleRegionValue; +// customRef.envPartValue = envPartValue; +// customRef.sidoText = selectedSido.text; +// customRef.sigunText = selectedSigun.text; +// } +// }, [partValue, sidoValue, sigunValue, regionValue, untitWaterShedValue, middleRegionValue, envPartValue, ref]); - // buildingSearchText가 변경될 때 API 호출 - useEffect(() => { - if (buildingSearchText && buildingSearchText.length > 1) { - if (checkedLayers && checkedLayers.length > 0) { - // 다중 레이어 검색 - // buildingNmSearchMultiple(checkedLayers, selectedSigun.val || "", buildingSearchText); - buildingNmSearch("RNB", selectedSigun.val, buildingSearchText); - } else { - // 단일 레이어 검색 - // buildingNmSearch(checkedLayers[0], selectedSigun.val, buildingSearchText); - } - } else { - // 검색 텍스트가 짧으면 리스트 초기화 - if (checkedLayers && checkedLayers.length > 0) { - // buildingNmSearchMultiple(checkedLayers, selectedSigun.val || "", ""); - } else { - buildingNmSearch("RNB", selectedSigun.val || "", ""); - } - } - }, [buildingSearchText]); +// // buildingSearchText가 변경될 때 API 호출 +// useEffect(() => { +// if (buildingSearchText && buildingSearchText.length > 1) { +// if (checkedLayers && checkedLayers.length > 0) { +// // 다중 레이어 검색 +// // buildingNmSearchMultiple(checkedLayers, selectedSigun.val || "", buildingSearchText); +// buildingNmSearch("RNB", selectedSigun.val, buildingSearchText); +// } else { +// // 단일 레이어 검색 +// // buildingNmSearch(checkedLayers[0], selectedSigun.val, buildingSearchText); +// } +// } else { +// // 검색 텍스트가 짧으면 리스트 초기화 +// if (checkedLayers && checkedLayers.length > 0) { +// // buildingNmSearchMultiple(checkedLayers, selectedSigun.val || "", ""); +// } else { +// buildingNmSearch("RNB", selectedSigun.val || "", ""); +// } +// } +// }, [buildingSearchText]); - return ( -
-
-
- {searchType == "공급처 후보지 조회" && ( - <> - { - const selected = code.cityCd.find(item => (item.val === val)); - setSelectedSido(selected || { text: "전체", val: '0' }); - setSelectedSigun({ text: "전체", val: '0' }); - }} - value={selectedSido.val} - selectClass={'w-32'} - /> - { - const selected = filteredSigunData.find(item => (item.val === val)); - setSelectedSigun(selected || { text: "전체", val: '0' }); - setSelectedFclt({ text: "전체", val: '0', sggNm: '전체' }); - }} - value={selectedSigun.val} - selectClass={'w-32'} - /> -
- -
- { - setBuildingSearchText(searchNm || ""); - }} - setRtnData={function (rtnData: any): void { - // 선택된 건물 데이터 처리 - // if (rtnData && rtnData.bdstInfoPk) { - // setSelectedBuilding(rtnData); - // setBuildingSearchText(rtnData.bdstNm || ""); - // } - }} - searchList={wrMap.buildingSearchRes || []} - /> -
-
- - )} - {searchType == "우리동네가뭄지도보기" && ( - <> - { - const selected = code.cityCd.find(item => (item.val === val)); - setSelectedSido(selected || { text: "전체", val: '0' }); - setSelectedSigun({ text: "전체", val: '0' }); - }} - value={selectedSido.val} - selectClass={'w-44'} - /> - { - const selected = filteredSigunData.find(item => (item.val === val)); - setSelectedSigun(selected || { text: "전체", val: '0' }); - setSelectedDong({ text: "전체", val: '0', sggNm: '전체' }); - }} - value={selectedSigun.val} - selectClass={'w-32'} - /> - { - const selected = filteredDong.find(item => (item.val === val)); - setSelectedDong(selected || { text: "전체", val: '0', sggNm: '전체' }); - }} - value={selectedDong.val} - selectClass={'w-32'} - /> - - )} - {searchType == "지도보기" && ( -
-
{/* SearchFormLeft 래퍼 */} - -
- -
- -
-
- )} -
- {/* 버튼 부분 */} -
- {showQueryButton && } - {showInitButton && } - {showApplyButton && } -
-
-
- ) -} +// return ( +//
+//
+//
+// {searchType == "공급처 후보지 조회" && ( +// <> +// { +// const selected = code.cityCd.find((item: { val: string | number; }) => (item.val === val)); +// setSelectedSido(selected || { text: "전체", val: '0' }); +// setSelectedSigun({ text: "전체", val: '0' }); +// }} +// value={selectedSido.val} +// selectClass={'w-32'} +// /> +// { +// const selected = filteredSigunData.find(item => (item.val === val)); +// setSelectedSigun(selected || { text: "전체", val: '0' }); +// setSelectedFclt({ text: "전체", val: '0', sggNm: '전체' }); +// }} +// value={selectedSigun.val} +// selectClass={'w-32'} +// /> +//
+// +//
+// { +// setBuildingSearchText(searchNm || ""); +// }} +// setRtnData={function (rtnData: any): void { +// // 선택된 건물 데이터 처리 +// // if (rtnData && rtnData.bdstInfoPk) { +// // setSelectedBuilding(rtnData); +// // setBuildingSearchText(rtnData.bdstNm || ""); +// // } +// }} +// searchList={buildingSearchRes || []} +// /> +//
+//
+// +// )} +// {searchType == "우리동네가뭄지도보기" && ( +// <> +// { +// const selected = code.cityCd.find((item: { val: string | number; }) => (item.val === val)); +// setSelectedSido(selected || { text: "전체", val: '0' }); +// setSelectedSigun({ text: "전체", val: '0' }); +// }} +// value={selectedSido.val} +// selectClass={'w-44'} +// /> +// { +// const selected = filteredSigunData.find(item => (item.val === val)); +// setSelectedSigun(selected || { text: "전체", val: '0' }); +// setSelectedDong({ text: "전체", val: '0', sggNm: '전체' }); +// }} +// value={selectedSigun.val} +// selectClass={'w-32'} +// /> +// { +// const selected = filteredDong.find(item => (item.val === val)); +// setSelectedDong(selected || { text: "전체", val: '0', sggNm: '전체' }); +// }} +// value={selectedDong.val} +// selectClass={'w-32'} +// /> +// +// )} +// {searchType == "지도보기" && ( +//
+//
{/* SearchFormLeft 래퍼 */} +// +//
+// +//
+// +//
+//
+// )} +//
+// {/* 버튼 부분 */} +//
+// {showQueryButton && } +// {showInitButton && } +// {showApplyButton && } +//
+//
+//
+// ) +// } diff --git a/apps/wr/app/map/newWindowPopup.tsx b/apps/wr/app/map/newWindowPopup.tsx index e67b2b0fb..dfbb1dc24 100644 --- a/apps/wr/app/map/newWindowPopup.tsx +++ b/apps/wr/app/map/newWindowPopup.tsx @@ -48,7 +48,8 @@ const NewWindowPopup: React.FC = ({ onClose }) => { const [activeTab, setActiveTab] = useState(TABS[0]?.val ?? "0"); - const [showDetailView, setShowDetailView] = useState(false); // 상세정보 화면 표시 상태 추가 + // swcho : useState의 true, false 값 변경하여 목록, 상태 테스트 하였음 + const [showDetailView, setShowDetailView] = useState(true); // 상세정보 화면 표시 상태 추가 // onClose 함수를 useCallback으로 메모이제이션 const memoizedOnClose = useCallback(() => { @@ -183,14 +184,15 @@ const NewWindowPopup: React.FC = ({ layer.data?.lyrNm?.includes('하수처리') || layer.data?.lyrNm?.includes('재이용시설') ); + // swcho : true, false 값 변경하여 시설 변경하여 테스트하였음 // 재이용시설 const isPbnst = false; // 빗물이용시설 const isRain = false; // 중수도시설 - const isRswt = false; + const isRswt = true; // 온배수시설 - const isHtdfl = true; + const isHtdfl = false; return ( diff --git a/packages/common/business_components/src/ui/map/Gis.js b/packages/common/business_components/src/ui/map/Gis.js index e85b8d162..5ac98f27f 100644 --- a/packages/common/business_components/src/ui/map/Gis.js +++ b/packages/common/business_components/src/ui/map/Gis.js @@ -16,6 +16,7 @@ import GisToolBar from './GisToolBar'; import GisTree from './GisTree'; import './lib/css/gis.common.css'; import GisApp from './lib/gis.core'; +// import FacilityInfoPopup from './FacilityInfoPopup'; export const GisContext = createContext({ gisCore: null, gisMap: null, treeData: null, checkedLayerIds: null }); export const useGisContext = () => useContext(GisContext); @@ -47,8 +48,8 @@ function Gis({ const checkedLayerIds = useRef([]); // 팝업 데이터를 저장할 상태와 오버레이 참조를 저장할 ref 추가 - const [facilityInfoPopupData, setFacilityInfoPopupData] = useState([]); - const facilityOverlaysRef = useRef([]); + // const [facilityInfoPopupData, setFacilityInfoPopupData] = useState([]); + // const facilityOverlaysRef = useRef([]); // 체크된 레이어 상태 관리 const [checkedLayers, setCheckedLayers] = useState([]); const [checkedLayersData, setCheckedLayersData] = useState([]); @@ -225,119 +226,119 @@ function Gis({ } } - // 데이터 로드 및 팝업 생성 로직 - useEffect(() => { - if (!mapInstanceRef.current) return; // 맵이 생성되지 않았으면 실행 안 함 + // // 데이터 로드 및 팝업 생성 로직 + // useEffect(() => { + // if (!mapInstanceRef.current) return; // 맵이 생성되지 않았으면 실행 안 함 - // 1. 팝업 데이터 가져오기 (feature.js의 getData와 유사) - const fetchPopupData = async () => { - // 실제 API 호출 로직으로 대체해야 합니다. - // 예시: const response = await yourApiService.getFcltyWtCnt(); - // setFacilityInfoPopupData(response.data); + // // 1. 팝업 데이터 가져오기 (feature.js의 getData와 유사) + // const fetchPopupData = async () => { + // // 실제 API 호출 로직으로 대체해야 합니다. + // // 예시: const response = await yourApiService.getFcltyWtCnt(); + // // setFacilityInfoPopupData(response.data); - // 임시 목업 데이터 - const mockData = [ - { ftrIdn: 1, ftrCde: 'SA206', bplcNm: '처리장A', fcltyUseYn: 'Y', x: 127.005, y: 37.282, aCnt: 10, cCnt: 5, eCnt: 3, mCnt: 8, iCnt: 2 }, - { ftrIdn: 2, ftrCde: 'SA112', bplcNm: '처리장B', fcltyUseYn: 'N', x: 127.010, y: 37.285, aCnt: 8, cCnt: 4, eCnt: 2, mCnt: 6, iCnt: 1 } - ]; - setFacilityInfoPopupData(mockData); - }; + // // 임시 목업 데이터 + // const mockData = [ + // { ftrIdn: 1, ftrCde: 'SA206', bplcNm: '처리장A', fcltyUseYn: 'Y', x: 127.005, y: 37.282, aCnt: 10, cCnt: 5, eCnt: 3, mCnt: 8, iCnt: 2 }, + // { ftrIdn: 2, ftrCde: 'SA112', bplcNm: '처리장B', fcltyUseYn: 'N', x: 127.010, y: 37.285, aCnt: 8, cCnt: 4, eCnt: 2, mCnt: 6, iCnt: 1 } + // ]; + // setFacilityInfoPopupData(mockData); + // }; - fetchPopupData(); + // fetchPopupData(); - }, [mapInstanceRef.current]); // 맵 인스턴스가 생성된 후 한 번만 실행 + // }, [mapInstanceRef.current]); // 맵 인스턴스가 생성된 후 한 번만 실행 - // 데이터가 변경되면 오버레이를 생성/업데이트하는 로직 - useEffect(() => { - const map = mapInstanceRef.current; - if (!map || facilityInfoPopupData.length === 0) return; + // // 데이터가 변경되면 오버레이를 생성/업데이트하는 로직 + // useEffect(() => { + // const map = mapInstanceRef.current; + // if (!map || facilityInfoPopupData.length === 0) return; - // 기존 오버레이 정리 - facilityOverlaysRef.current.forEach(overlay => map.removeOverlay(overlay)); - facilityOverlaysRef.current = []; + // // 기존 오버레이 정리 + // facilityOverlaysRef.current.forEach(overlay => map.removeOverlay(overlay)); + // facilityOverlaysRef.current = []; - facilityInfoPopupData.forEach(data => { - const popupId = `${data.ftrIdn}_${data.ftrCde}`; + // facilityInfoPopupData.forEach(data => { + // const popupId = `${data.ftrIdn}_${data.ftrCde}`; - // 팝업을 담을 div 엘리먼트 생성 - const popupElement = document.createElement('div'); - popupElement.id = popupId; + // // 팝업을 담을 div 엘리먼트 생성 + // const popupElement = document.createElement('div'); + // popupElement.id = popupId; - // OpenLayers Overlay 생성 - const position = olProj.transform([Number(data.x), Number(data.y)], GisApp.FROM_PROJ, GisApp.TO_PROJ); - const overlay = new Overlay({ - element: popupElement, - position: position, - offset: [-75, -210], // 팝업 너비의 절반, 높이만큼 오프셋 조정 - positioning: 'bottom-center', - className: 'facility-popup-overlay' // 필요시 공통 클래스 추가 - }); + // // OpenLayers Overlay 생성 + // const position = olProj.transform([Number(data.x), Number(data.y)], GisApp.FROM_PROJ, GisApp.TO_PROJ); + // const overlay = new Overlay({ + // element: popupElement, + // position: position, + // offset: [-75, -210], // 팝업 너비의 절반, 높이만큼 오프셋 조정 + // positioning: 'bottom-center', + // className: 'facility-popup-overlay' // 필요시 공통 클래스 추가 + // }); - map.addOverlay(overlay); - facilityOverlaysRef.current.push(overlay); + // map.addOverlay(overlay); + // facilityOverlaysRef.current.push(overlay); - // React 컴포넌트를 popupElement에 렌더링 - const root = createRoot(popupElement); - root.render(); - }); + // // React 컴포넌트를 popupElement에 렌더링 + // const root = createRoot(popupElement); + // root.render(); + // }); - // 초기 가시성 업데이트 - updatePopupVisibility(); + // // 초기 가시성 업데이트 + // updatePopupVisibility(); - }, [facilityInfoPopupData]); // 팝업 데이터가 변경될 때마다 실행 + // }, [facilityInfoPopupData]); // 팝업 데이터가 변경될 때마다 실행 - // 레이어 가시성에 따라 팝업 가시성을 제어하는 함수 - const updatePopupVisibility = useCallback(() => { - const map = mapInstanceRef.current; - if (!map) return; + // // 레이어 가시성에 따라 팝업 가시성을 제어하는 함수 + // const updatePopupVisibility = useCallback(() => { + // const map = mapInstanceRef.current; + // if (!map) return; - const visibleLayers = new Set(); - map.getLayers().forEach(layer => { - if (layer.getVisible()) { - // feature.js의 로직처럼 typeName이나 ftrCde를 사용해 필터링 - const typeName = layer.get('typeName'); - if (typeName === 'VW_WTL_FCLTY_AS') { // 예시 레이어 이름 - visibleLayers.add(layer.get('ftrCde')); - } - } - }); + // const visibleLayers = new Set(); + // map.getLayers().forEach(layer => { + // if (layer.getVisible()) { + // // feature.js의 로직처럼 typeName이나 ftrCde를 사용해 필터링 + // const typeName = layer.get('typeName'); + // if (typeName === 'VW_WTL_FCLTY_AS') { // 예시 레이어 이름 + // visibleLayers.add(layer.get('ftrCde')); + // } + // } + // }); - facilityOverlaysRef.current.forEach(overlay => { - const element = overlay.getElement(); - const ftrCde = element.querySelector('table')?.classList[0]; // FacilityPopup의 클래스에서 ftrCde 가져오기 + // facilityOverlaysRef.current.forEach(overlay => { + // const element = overlay.getElement(); + // const ftrCde = element.querySelector('table')?.classList[0]; // FacilityPopup의 클래스에서 ftrCde 가져오기 - if (visibleLayers.has(ftrCde)) { - element.style.display = ''; - } else { - element.style.display = 'none'; - } - }); - }, []); + // if (visibleLayers.has(ftrCde)) { + // element.style.display = ''; + // } else { + // element.style.display = 'none'; + // } + // }); + // }, []); // GisTree에서 체크 상태가 변경될 때 이 함수를 호출하도록 연결해야 합니다. // 예시: GisTree 컴포넌트에 onCheck 콜백을 추가하고, 그 콜백에서 이 함수를 호출 - useEffect(() => { - // 레이어 가시성이 변경될 때마다 팝업 가시성 업데이트 - const map = mapInstanceRef.current; - if (!map) return; + // useEffect(() => { + // // 레이어 가시성이 변경될 때마다 팝업 가시성 업데이트 + // const map = mapInstanceRef.current; + // if (!map) return; - const handleLayerVisibilityChange = () => { - updatePopupVisibility(); - }; + // const handleLayerVisibilityChange = () => { + // updatePopupVisibility(); + // }; - map.getLayers().on('change:length', handleLayerVisibilityChange); - map.getLayers().forEach(layer => { - layer.on('change:visible', handleLayerVisibilityChange); - }); + // map.getLayers().on('change:length', handleLayerVisibilityChange); + // map.getLayers().forEach(layer => { + // layer.on('change:visible', handleLayerVisibilityChange); + // }); - return () => { - // 클린업 - map.getLayers().un('change:length', handleLayerVisibilityChange); - map.getLayers().forEach(layer => { - layer.un('change:visible', handleLayerVisibilityChange); - }); - }; - }, [updatePopupVisibility]); + // return () => { + // // 클린업 + // map.getLayers().un('change:length', handleLayerVisibilityChange); + // map.getLayers().forEach(layer => { + // layer.un('change:visible', handleLayerVisibilityChange); + // }); + // }; + // }, [updatePopupVisibility]); // 체크된 레이어 정보를 처리하는 함수 const handleCheckedLayersChange = useCallback((checkedIds, checkedNodesData) => { @@ -359,18 +360,21 @@ function Gis({ {/* */} - {/* isSearchModalOpen이 true면 visible=false로 팝업 숨김 */} + {/* DetailView가 존재할 때만 렌더링 isSearchModalOpen이 true면 visible=false로 팝업 숨김 */} {isOpen && featureData && !isSearchModalOpen && ( )} - + {/* SearchView가 존재할 때만 렌더링 */} + {SearchView && ( + + )} From ea20ceacb3de830ec0748ff008d9e65f443251d7 Mon Sep 17 00:00:00 2001 From: swcho Date: Tue, 1 Jul 2025 11:01:22 +0900 Subject: [PATCH 029/108] =?UTF-8?q?fix:=20mapSearchBar=20=EC=A3=BC?= =?UTF-8?q?=EC=84=9D=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wr/app/store/map/index.tsx | 185 ++++++++++++++++++++++++++++++++ 1 file changed, 185 insertions(+) create mode 100644 apps/wr/app/store/map/index.tsx diff --git a/apps/wr/app/store/map/index.tsx b/apps/wr/app/store/map/index.tsx new file mode 100644 index 000000000..6a1e76c9b --- /dev/null +++ b/apps/wr/app/store/map/index.tsx @@ -0,0 +1,185 @@ +import { getFetch } from "@/components/lib/api"; + +export type wrMapType = wrMapState & wrMapActions; + +export type wrMapState = { + wrMap: { + buildingSearchRes: any[] + } // 건물 검색 결과 데이터 +}; + +type wrMapActions = { + buildingNmSearch: ( + flg: string, + sigunCode: string, + searchNm: string | undefined + ) => void; + buildingNmSearchMultiple: ( + checkedLayers: string[], + sigunCode: string, + searchNm: string | undefined + ) => void; // 여러 레이어 동시 검색 함수 추가 +}; + +export const wrMapStateExport: wrMapState = { + wrMap: { + buildingSearchRes: [] + } +}; + +// 레이어 타입과 플래그 매핑 +const LAYER_FLAG_MAP: { [key: string]: string } = { + // '46': '???', // 하수처리시설 + // '47': '???', // 하수재이용시설 + '48': 'RNB', // 빗물이용시설 + '49': 'RST', // 중수도시설 + '50': 'HTB', // 온배수시설 +}; + +const findBdst = async (flg: string, sggCd: string, searchTxt: string | undefined) => { + return await getFetch('wr', `/reuseFc/building/${flg}/${sggCd}` + (searchTxt ? `/${searchTxt}` : '')) +} + +export const wrMapActionsExport: (set: any) => wrMapActions = ( + set: any +) => { + return { + buildingNmSearch: async ( + flg: string, + sigunCode: string, + searchNm: string | undefined + ) => { + try { + // if (!searchNm || searchNm.length < 2) { + // set((state: wrMapState) => ({ + // ...state, + // buildingSearchData: [] + // })); + // return; + // } + console.log("건축물 코드, 시군구 코드, 검색어: ", flg, sigunCode, searchNm); + + const response = await findBdst(flg, sigunCode, searchNm); + console.log("response: ", response); + + // API 응답을 SearchInputText 형식으로 변환 + const searchListFormatted = (response || []).map((building: any) => ({ + searchNm: building.bdstNm, + searchPk: String(building.bdstInfoPk), + dmdCoNm: building.bdstNm, + layerType: flg, + ...building + })); + + // findBdst와 유사한 API 호출 + // findBdst(flg, sigunCode || "", searchNm).then(() => { + // console.log("건물 검색 API 호출: ", sigunCode, searchNm, flg); + // // bdst.bdstBase를 SearchInputText 형식으로 변환 + // const searchListFormatted = (state.bdst.bdstBase || []).map((building: any) => ({ + // searchNm: building.bdstNm, + // searchPk: String(building.bdstInfoPk), + // dmdCoNm: building.bdstNm, + // layerType: flg, + // ...building + // })); + + // set((state: any) => ({ + // ...state, + // wrMap: { + // ...state.wrMap, + // buildingSearchData: searchListFormatted + // } + // })); + // }); + set((state: wrMapState) => ({ + ...state, + wrMap: { + ...state.wrMap, + buildingSearchRes: searchListFormatted + } + })); + } catch (error) { + console.error("건물 검색 중 오류:", error); + } + }, + + buildingNmSearchMultiple: async ( + checkedLayers: string[], + sigunCode: string, + searchNm: string | undefined + ) => { + try { + if (!searchNm || searchNm.length < 2) { + set((state: any) => ({ + ...state.wrMap, + buildingSearchData: [] + })); + return; + } + + // 체크된 레이어들에 대해 병렬로 API 호출 + const searchPromises = checkedLayers + .filter(layer => LAYER_FLAG_MAP[layer]) // 매핑된 레이어만 필터링 + .map(layer => { + const flg = LAYER_FLAG_MAP[layer]; + + return findBdst(flg, sigunCode, searchNm) + .then(response => { + console.log(`레이어 ${layer}(${flg}) API 응답:`, response); + + const searchListFormatted = (state.bdst.bdstBase || []).map((building: any) => ({ + searchNm: building.bdstNm, + searchPk: String(building.bdstInfoPk), + dmdCoNm: building.bdstNm, + layerType: flg, + ...building + })); + + return { + layerType: layer, + flg: flg, + data: bdstNames + }; + }) + .catch(error => { + console.error(`레이어 ${layer}(${flg}) 검색 중 오류:`, error); + return { layerType: layer, flg: flg, data: [] }; + }); + }); + + const results = await Promise.all(searchPromises); + console.log("다중 레이어 검색 결과:", results); + + // 모든 결과를 하나의 배열로 합치기 + const allSearchResults = results.reduce((acc, result) => { + const formatted = result.data.map((building: any) => ({ + searchNm: building.bdstNm, + searchPk: String(building.bdstInfoPk), + dmdCoNm: building.bdstNm, + layerType: result.layerType, + flg: result.flg, + ...building.originalData + })); + return [...acc, ...formatted]; + }, [] as any[]); + + console.log("최종 합쳐진 검색 결과:", { + totalCount: allSearchResults.length, + sampleData: allSearchResults.slice(0, 3) + }); + + set((state: any) => ({ + ...state.wrMap, + buildingSearchData: allSearchResults + })); + + } catch (error) { + console.error("다중 레이어 검색 중 오류:", error); + set((state: any) => ({ + ...state, + buildingSearchData: [] + })); + } + } + }; +}; \ No newline at end of file From 547244872d6320772dd8237e15ada51852cd5557 Mon Sep 17 00:00:00 2001 From: swcho Date: Tue, 1 Jul 2025 11:06:15 +0900 Subject: [PATCH 030/108] =?UTF-8?q?etc:=20=ED=95=98=EC=88=98=EC=9E=AC?= =?UTF-8?q?=EC=9D=B4=EC=9A=A9=EC=8B=9C=EC=84=A4=20=EC=83=81=EC=84=B8?= =?UTF-8?q?=EC=A0=95=EB=B3=B4=EB=A1=9C=20=ED=8C=9D=EC=97=85=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wr/app/map/newWindowPopup.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/wr/app/map/newWindowPopup.tsx b/apps/wr/app/map/newWindowPopup.tsx index dfbb1dc24..ebfcac1ca 100644 --- a/apps/wr/app/map/newWindowPopup.tsx +++ b/apps/wr/app/map/newWindowPopup.tsx @@ -186,11 +186,11 @@ const NewWindowPopup: React.FC = ({ // swcho : true, false 값 변경하여 시설 변경하여 테스트하였음 // 재이용시설 - const isPbnst = false; + const isPbnst = true; // 빗물이용시설 const isRain = false; // 중수도시설 - const isRswt = true; + const isRswt = false; // 온배수시설 const isHtdfl = false; From b0dfec9825022ffc2ab85ea10b8c1f75171d916e Mon Sep 17 00:00:00 2001 From: jyha00 Date: Tue, 1 Jul 2025 11:27:09 +0900 Subject: [PATCH 031/108] =?UTF-8?q?=EC=9A=B0=EB=A6=AC=EB=8F=99=EB=84=A4?= =?UTF-8?q?=EA=B0=80=EB=AD=84=20=EC=97=91=EC=85=80=20=EB=8B=A4=EC=9A=B4?= =?UTF-8?q?=EB=A1=9C=EB=93=9C=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wr/app/matMng/coMng/drgtSttn/page.tsx | 84 ++++++---- apps/wr/app/store/matDmd/drgtSttn/index.tsx | 148 +++++++++++------- .../ui/modal/coMat/drgtDetailModal.tsx | 56 ++++--- 3 files changed, 183 insertions(+), 105 deletions(-) diff --git a/apps/wr/app/matMng/coMng/drgtSttn/page.tsx b/apps/wr/app/matMng/coMng/drgtSttn/page.tsx index d7c5170b2..2f07dd6de 100644 --- a/apps/wr/app/matMng/coMng/drgtSttn/page.tsx +++ b/apps/wr/app/matMng/coMng/drgtSttn/page.tsx @@ -6,15 +6,15 @@ import { drgtHeadListExport } from "@/app/store/matDmd/drgtSttn/headList"; import { excelDownload, getCity } from "@/components/lib/func/commom"; import DrgtDetailModal from "@/components/ui/modal/coMat/drgtDetailModal"; import type { objSearchType } from "@/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal"; -import { ContentsDiv, DateMonthPicker, SearchDiv, SearchFormLeft, SearchFormRight, SelectBox, Title, UiModal, UiTable, type SelectDataType } from "@common/business_components/ui"; +import { ContentsDiv, DateMonthPicker, downloadExcel, SearchDiv, SearchFormLeft, SearchFormRight, SelectBox, Title, UiModal, UiTable, type SelectDataType } from "@common/business_components/ui"; import { MonthPicker } from "@common/business_components/ui/date_month_picker/mothPicker"; import ListDiv from "@common/business_components/ui/list_div"; -import { Button, DatePicker, Label } from "@common/components/ui"; +import { Button, DatePicker, Label, ScrollArea } from "@common/components/ui"; import { useCallback, useEffect, useState } from "react"; import { toast, Toaster } from "react-hot-toast"; export default function DrgtSttn() { - const { code, drgt, drgtSearch, drgtModalSearch } = useWrStore((state) => state); + const { code, drgt, drgtSearch, drgtModalSearch, drgtInit } = useWrStore((state) => state); const { drgtRes, drgtMoRes } = drgt; const [fromDate, setFromDate] = useState(); const [toDate, setToDate] = useState(); @@ -22,6 +22,8 @@ export default function DrgtSttn() { const [objSearch, setObjSearch] = useState({ sgg: "", city: "" }); const [tableData, setTableData] = useState(); const [selectedSgg, setSelectedSgg] = useState(''); + const [excelData, setExcelData] = useState(); + const [num, setNum] = useState(); const [drgtModal, setDrgtModal] = useState<{ isOpen: boolean; data: customDrgt | undefined; @@ -35,6 +37,39 @@ export default function DrgtSttn() { } } + const excelClick = () => { + downloadExcel({ + data: excelData ?? [], + fileName: "우리동네가뭄 현황", + sheetNameArray: ["Sheet1"], + headerRange: "A1:G1" + }) + } + + useEffect(() => { + if (!drgt.isInit) { + drgtInit(); + } + }, [drgt.isInit, drgtInit]); + + useEffect(() => { + const resData = Array.isArray(drgtRes) ? drgtRes : [drgtRes]; + const data = Array.isArray(resData) + ? resData.map((item: customDrgt) => { + return { + "시도": item.ctpvCd, + "시군구": item.sggNm, + "정상(횟수)": item.normal, + "관심(횟수)": item.attention, + "주의(횟수)": item.caution, + "관심+주의(횟수)": item.warning, + "가뭄순위": item.ranking, + } + }) : []; + setExcelData(data) + setTableData(drgtRes) + }, [drgtRes]) + useEffect(() => { if (drgtMoRes && drgtMoRes.length > 0) { setDrgtModal({ isOpen: true, data: drgtMoRes }); @@ -65,15 +100,6 @@ export default function DrgtSttn() { setObjSearch({ ...objSearch, sgg: String(val) }); }; - const excelClick = useCallback(() => { - excelDownload( - [[]], - "우리동네가뭄", - "sample_drgtSttn.xlsx", - "A1:H1" - ); - }, []); - useEffect(() => { if (fromDate === undefined && toDate === undefined) { const today = new Date(); @@ -101,7 +127,7 @@ export default function DrgtSttn() { onSelectValue={getSelCity} />
- +
- ~ + ~
@@ -148,7 +176,7 @@ export default function DrgtSttn() { -
+
+

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

-
+
( field.onChange(val)} /> )} /> - - ( - - - - )} - /> - ( - - - - )} - /> - - ( - - { - if (files) setFiles(files); - }} - fileTypes="xlsx" - maxFiles={1} - > - - )} - /> +
+
+ ( + + + + )} + /> +
+
+ ( + + + + )} + /> +
+
+
+ ( + + { + if (files) setFiles(files); + }} + fileTypes="xlsx" + maxFiles={1} + > + + )} + /> +
diff --git a/apps/wr/public/sample_pbnstPrfmncReg.xlsx b/apps/wr/public/sample_pbnstPrfmncReg.xlsx index 67f0d559e096443366eeff0f951b5648e422a972..1f2ff81aa24e1dce6ca8e8bce7fd7d9f54ed91ad 100644 GIT binary patch delta 5703 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 Zg@FIlFR(-*Qlvky5+P>vNWTBX{udqJuh;+p delta 5698 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 Date: Tue, 1 Jul 2025 19:09:45 +0900 Subject: [PATCH 033/108] =?UTF-8?q?=ED=95=98=EC=88=98=EA=B4=80=EB=A1=9C=20?= =?UTF-8?q?=ED=94=84=EB=A1=9D=EC=8B=9C=20=EC=84=A4=EC=A0=95=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wr/middleware.ts | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/apps/wr/middleware.ts b/apps/wr/middleware.ts index 3fabc529d..5acc41863 100644 --- a/apps/wr/middleware.ts +++ b/apps/wr/middleware.ts @@ -6,6 +6,7 @@ const CLAIM_NAME = "Authorization"; const AUTH_USER_ID = "token-id"; const serverApiUrl = CS_URL; const GEOSERVER_URL = process.env.NEXT_PUBLIC_GEOSERVER_URL || ""; +const SEWERAGE_API_URL = process.env.NEXT_PUBLIC_SEWERAGE_API_URL || ""; export function middleware(req: NextRequest) { const pathname = req.nextUrl.pathname; @@ -55,9 +56,17 @@ export function middleware(req: NextRequest) { return NextResponse.rewrite(new URL(newUrl)); } + if (pathname.startsWith("/sewerage-service")) { + const queryParams = searchParams.toString(); + const newUrl = `${SEWERAGE_API_URL}${pathname.replace("/sewerage-service/api", "")}${queryParams ? `?${queryParams}` : "" + }`; + console.log(`[Middleware] Rewriting from '/geoserver' path to: ${newUrl}`); + return NextResponse.rewrite(new URL(newUrl)); + } + return NextResponse.next(); } export const config = { - matcher: ["/server/:path*", "/attach-service/:path*", "/geoserver/:path*"], + matcher: ["/server/:path*", "/attach-service/:path*", "/geoserver/:path*", "/sewerage-service/:path*"], }; From 9cf281db7e9c139a21a9383caa99f31621e99c1e Mon Sep 17 00:00:00 2001 From: yschoi Date: Tue, 1 Jul 2025 19:32:25 +0900 Subject: [PATCH 034/108] =?UTF-8?q?gis=20=EC=9B=90=EB=B3=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../business_components/src/ui/map/Gis.js | 67 ++++--------------- .../src/ui/map/lib/gis.core.js | 21 +++--- .../src/ui/map/lib/gis.layer.js | 10 ++- 3 files changed, 31 insertions(+), 67 deletions(-) diff --git a/packages/common/business_components/src/ui/map/Gis.js b/packages/common/business_components/src/ui/map/Gis.js index 8929a3124..6d46e77aa 100644 --- a/packages/common/business_components/src/ui/map/Gis.js +++ b/packages/common/business_components/src/ui/map/Gis.js @@ -1,6 +1,5 @@ import { Search } from 'lucide-react'; import OlMap from 'ol/Map'; -import Overlay from 'ol/Overlay'; import View from 'ol/View'; import { defaults as defaultControls, OverviewMap } from 'ol/control'; import { defaults } from 'ol/interaction'; @@ -19,22 +18,7 @@ import GisApp from './lib/gis.core'; export const GisContext = createContext({ gisCore: null, gisMap: null, treeData: null, checkedLayerIds: null }); export const useGisContext = () => useContext(GisContext); -function Gis({ - isOpen, - gisMaps, - isSearchModalOpen, - treeData = [], - isSearch = false, - isMiniMap = false, - setIsOpen, - setFeatureData, - featureData, - searchView: SearchView, - detailView: DetailView, - onFeatureSelect, - setIsSearchModalOpen, - onCheckedLayersChange -}) { +function Gis({ isOpen, gisMaps, isSearchModalOpen, treeData = [], isSearch = false, isMiniMap = false, setIsOpen, setFeatureData, featureData, searchView: SearchView, detailView: DetailView, onFeatureSelect, setIsSearchModalOpen }) { const mapRef = useRef(); const [mapObject, setMapObject] = useState({}); const [tabVisible, setTabVisible] = useState(true); @@ -44,17 +28,13 @@ function Gis({ const gisCore = useMemo(() => new GisApp.core({ targetId: mapRef.current }), []); const mapInstanceRef = useRef(null); const checkedLayerIds = useRef([]); - // 체크된 레이어 상태 관리 - const [checkedLayers, setCheckedLayers] = useState([]); - const [checkedLayersData, setCheckedLayersData] = useState([]); - let gisMap = null; const closePopup = () => { setIsOpen(false); // 팝업 닫기 setFeatureData(null) }; - // Feature Click Event + // SearchModal, GisContent 등에서 호출 const handleFeatureSelect = useCallback((data) => { gisMap = mapInstanceRef.current; @@ -109,10 +89,8 @@ function Gis({ setFeatureData(data); setIsOpen(true); setIsSearchModalOpen(false); - onFeatureSelect?.(data); // 선택된 피쳐 데이터 콜백 호출 }, [mapInstanceRef]); - // 지도 생성 및 초기화 useEffect(() => { if (!mapRef.current) return; if (mapInstanceRef.current) return; // 이미 생성된 맵이 있다면 중단 @@ -126,7 +104,7 @@ function Gis({ ]); register(proj4); - // OpenLayers 지도 객체 생성 + //맵 생성 const map = new OlMap({ layers: [gisCore.baseLayers['base'], gisCore.baseLayers['satellite']], renderer: 'webgl', @@ -154,7 +132,7 @@ function Gis({ gisMaps.current = map - // 미니맵 추가 + //미니맵 if (isMiniMap) { const overviewMap = new OverviewMap({ layers: [GisApp.Module.core.baseMiniMapLayers['base']], @@ -169,7 +147,6 @@ function Gis({ map.addControl(overviewMap); } - // 스케일 컨트롤 및 베이스 레이어 설정 gisCore.__proto__.map = map; gisCore.scaleControl(); gisCore.changeBaseLayer('base'); @@ -177,20 +154,16 @@ function Gis({ mapInstanceRef.current = map; // 생성 완료 플래그 설정 }, []); - // treeData 변경 시 레이어 코드 생성 useEffect(() => { if (!treeData || treeData.length === 0) return; - // 기존 오버레이 및 레이어 모두 삭제 gisCore.deleteAllOverlay(); gisCore.deleteAllLayer(); - // 트리 데이터를 레이어 코드 객체로 변환 const layerCode = gisCore.convertLayerInfosToLayerCode(treeData, 'lyrSn'); - GisApp.layerCode = layerCode; // 전역 레이어 코드 설정 + GisApp.layerCode = layerCode; //setTreeData(data); - // 편집 가능한 레이어 목록 생성 const layerItems = getLayerItems(layerCode); //setEditLayerItems(prev => [...layerItems]) }, [treeData]); @@ -220,45 +193,31 @@ function Gis({ } } - // 체크된 레이어 정보를 처리하는 함수 - const handleCheckedLayersChange = useCallback((checkedIds, checkedNodesData) => { - setCheckedLayers(checkedIds); - setCheckedLayersData(checkedNodesData); - onCheckedLayersChange?.(checkedIds, checkedNodesData); - }, [onCheckedLayersChange]); - return ( <>
- + {/* */} - {/* DetailView가 존재할 때만 렌더링 isSearchModalOpen이 true면 visible=false로 팝업 숨김 */} + {/* isSearchModalOpen이 true면 visible=false로 팝업 숨김 */} {isOpen && featureData && !isSearchModalOpen && ( )} - {/* SearchView가 존재할 때만 렌더링 */} - {SearchView && ( - - )} + - +
{isSearch && ( diff --git a/packages/common/business_components/src/ui/map/lib/gis.core.js b/packages/common/business_components/src/ui/map/lib/gis.core.js index 7488e4b72..ddfcec3a1 100644 --- a/packages/common/business_components/src/ui/map/lib/gis.core.js +++ b/packages/common/business_components/src/ui/map/lib/gis.core.js @@ -1,24 +1,26 @@ import MousePosition from 'ol/control/MousePosition'; import ScaleLine from 'ol/control/ScaleLine'; -import { getTopLeft } from 'ol/extent'; import Feature from 'ol/Feature'; -import GeoSJON from 'ol/format/GeoJSON'; import WKT from 'ol/format/WKT'; -import { Point } from 'ol/geom'; import LinearRing from 'ol/geom/LinearRing'; import Draw from 'ol/interaction/Draw'; import TileLayer from 'ol/layer/Tile'; -import VectorLayer from 'ol/layer/Vector'; import * as olProj from 'ol/proj'; import VectorSource from 'ol/source/Vector'; -import TileWMTS from 'ol/source/WMTS'; import XYZ from 'ol/source/XYZ'; +import GeoSJON from 'ol/format/GeoJSON'; import { Fill, Stroke, Style } from 'ol/style'; import CircleStyle from 'ol/style/Circle'; +import VectorLayer from 'ol/layer/Vector'; +import { Point } from 'ol/geom'; +import { getTopLeft } from 'ol/extent'; +import TileWMTS from 'ol/source/WMTS'; import WMTSTileGrid from 'ol/tilegrid/WMTS'; // import { get as getProjection } from 'ol/proj'; -import { register } from 'ol/proj/proj4'; import proj4 from 'proj4'; +import { register } from 'ol/proj/proj4'; +import olSourceImageWMS from 'ol/source/ImageWMS'; +import { GeoJSON } from 'ol/format'; import GisOlEditLayer from './gis.edit'; import GisOlLayer from './gis.layer'; @@ -560,7 +562,6 @@ GisApp.core.prototype = { * * Returns: */ - // 레이어를 지도에 표시/숨김 setLayerOnMap: function (layerIds, visible, addQurryFlter) { for (let i in layerIds) { let id = layerIds[i]; @@ -571,14 +572,10 @@ GisApp.core.prototype = { const layerCode = GisApp.layerCode[id]; const queryFlter = layerCode?.flterSql || '' + ' ' + (addQurryFlter || ''); layerCode['flterSql'] = queryFlter; - //layer = this.addWmsLayer(layerCode); - - // WFS 레이어 생성 및 지도에 추가 + //layer = this.addWmsLayer(layerCode); layer = this.addWfsLayer(layerCode); } } - - // 선택 인터랙션 및 스냅 기능 설정 if (GisApp.Module.select) { this.setSelectInteraction(); } diff --git a/packages/common/business_components/src/ui/map/lib/gis.layer.js b/packages/common/business_components/src/ui/map/lib/gis.layer.js index d9657c313..097c90ba5 100644 --- a/packages/common/business_components/src/ui/map/lib/gis.layer.js +++ b/packages/common/business_components/src/ui/map/lib/gis.layer.js @@ -77,7 +77,15 @@ GisOlLayer.prototype = { let lyrTyNm = options.lyrTyNm; let typename = this.storageNm + lyrTyNm; - if(options.lyrTyNm == "om_dt_untrt_swg_rain"){ //중점우수토실실 + if(options.lyrTyNm == "om_dt_untrt_swg_rain" //중점우수토실 + || options.lyrTyNm == "om_wqlt_station" //관측소 + || options.lyrTyNm == "tb_wr_bdst_ruse_fclt" //재이용시설 + || options.lyrTyNm == "tb_wr_rnwtr_utztn_fclt" //빗물이용시설 + || options.lyrTyNm == "tb_wr_rswtr_fclt" //중수도시설 + || options.lyrTyNm == "tb_wr_htdfl_fclt" //온배수시설 + || options.lyrTyNm == "tb_wr_tmpr_dmd_co" //잠재수요처 + ) + { url = GisApp.SN_GEOSERVER_URL; typename = lyrTyNm; } From 54140dbad5cbf7bc4cf9afc36ccc3d41fecee79e Mon Sep 17 00:00:00 2001 From: jyha00 Date: Tue, 1 Jul 2025 20:11:29 +0900 Subject: [PATCH 035/108] =?UTF-8?q?=EC=9A=B0=EB=A6=AC=EB=8F=99=EB=84=A4?= =?UTF-8?q?=EA=B0=80=EB=AD=84=20=EC=8B=9C=EB=8F=84,=20=EC=8B=9C=EA=B5=B0?= =?UTF-8?q?=EA=B5=AC=20=EC=A1=B0=ED=9A=8C=EC=A1=B0=EA=B1=B4=20=EC=88=98?= =?UTF-8?q?=EC=A0=95,=20=ED=85=8C=EC=9D=B4=EB=B8=94=EC=97=90=20=EC=8B=9C?= =?UTF-8?q?=EB=8F=84,=20=EC=8B=9C=EA=B5=B0=EA=B5=AC=20=ED=91=9C=EC=B6=9C?= =?UTF-8?q?=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wr/app/matMng/coMng/drgtSttn/page.tsx | 58 ++++++++++--------- .../wr/app/store/matDmd/drgtSttn/headList.tsx | 4 +- apps/wr/app/store/matDmd/drgtSttn/index.tsx | 36 +++++------- .../ui/modal/coMat/drgtDetailModal.tsx | 3 +- 4 files changed, 48 insertions(+), 53 deletions(-) diff --git a/apps/wr/app/matMng/coMng/drgtSttn/page.tsx b/apps/wr/app/matMng/coMng/drgtSttn/page.tsx index 2f07dd6de..af49f02b1 100644 --- a/apps/wr/app/matMng/coMng/drgtSttn/page.tsx +++ b/apps/wr/app/matMng/coMng/drgtSttn/page.tsx @@ -3,14 +3,13 @@ import { useWrStore } from "@/app/store"; import { btnClass } from "@/app/store/common/code"; import type { customDrgt } from "@/app/store/matDmd/drgtSttn"; import { drgtHeadListExport } from "@/app/store/matDmd/drgtSttn/headList"; -import { excelDownload, getCity } from "@/components/lib/func/commom"; +import { getCity } from "@/components/lib/func/commom"; import DrgtDetailModal from "@/components/ui/modal/coMat/drgtDetailModal"; import type { objSearchType } from "@/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal"; import { ContentsDiv, DateMonthPicker, downloadExcel, SearchDiv, SearchFormLeft, SearchFormRight, SelectBox, Title, UiModal, UiTable, type SelectDataType } from "@common/business_components/ui"; -import { MonthPicker } from "@common/business_components/ui/date_month_picker/mothPicker"; import ListDiv from "@common/business_components/ui/list_div"; -import { Button, DatePicker, Label, ScrollArea } from "@common/components/ui"; -import { useCallback, useEffect, useState } from "react"; +import { Button, Label, ScrollArea } from "@common/components/ui"; +import { useEffect, useState } from "react"; import { toast, Toaster } from "react-hot-toast"; export default function DrgtSttn() { @@ -23,7 +22,6 @@ export default function DrgtSttn() { const [tableData, setTableData] = useState(); const [selectedSgg, setSelectedSgg] = useState(''); const [excelData, setExcelData] = useState(); - const [num, setNum] = useState(); const [drgtModal, setDrgtModal] = useState<{ isOpen: boolean; data: customDrgt | undefined; @@ -53,7 +51,23 @@ export default function DrgtSttn() { }, [drgt.isInit, drgtInit]); useEffect(() => { - const resData = Array.isArray(drgtRes) ? drgtRes : [drgtRes]; + let resData = Array.isArray(drgtRes) ? drgtRes : [drgtRes]; + resData = resData.map((item) => { + console.log("item", item); + if(item.sggNm === '세종시'){ + return { + ...item, + ctpvCd: '세종특별자치시', + sggNm: '' + } + } + + return { + ...item, + ctpvCd: code.cityCd.find(ct => (ct.text == item.sggNm || (code.sggCd.find(sgg => sgg.text == item.sggNm)?.val.substring(0,2) == ct.val)))?.text, + sggNm: code.sggCd.find(sgg => sgg.text == item.sggNm)?.text + } + }) const data = Array.isArray(resData) ? resData.map((item: customDrgt) => { return { @@ -67,7 +81,7 @@ export default function DrgtSttn() { } }) : []; setExcelData(data) - setTableData(drgtRes) + setTableData(resData) }, [drgtRes]) useEffect(() => { @@ -76,27 +90,14 @@ export default function DrgtSttn() { } }, [drgtMoRes]); - const getCityVal = (val: string, sggCd: SelectDataType[]) => { - const data = sggCd - .filter((item) => String(item.val).substring(0, 2) === val) - .map((item) => ({ - val: item.val, - text: item.text, // 한국어 텍스트 - })); - return data; - }; - const getSelCity = (val: string | number) => { - const cityValue = String(val); - const data = getCityVal(cityValue, code.sggCd); + const cityValue = String(val); // Ensure val is treated as a string + const data = getCity(cityValue, code.sggCd); setSelectSgg(data); - - const sggVal = data.length > 0 ? data[0].val : ''; - setSelectedSgg(sggVal); // 시군구 value 세팅 - setObjSearch({ city: cityValue, sgg: sggVal }); + setObjSearch({ city: cityValue, sgg: "" }); }; + const getSgg = (val: string | number) => { - setSelectedSgg(String(val)); setObjSearch({ ...objSearch, sgg: String(val) }); }; @@ -181,19 +182,22 @@ export default function DrgtSttn() { size={"sm"} onClick={(e) => { let sidoText = ''; - let sggText = ''; + let sggText = []; if (objSearch?.city.toString().length > 0) { sidoText = code.cityCd.find((item) => item.val === objSearch?.city)?.text ?? ''; } if (objSearch?.sgg.toString().length > 1) { sggText = code.sggCd.find((item) => item.val === objSearch?.sgg)?.text ?? ''; + } else { + sggText = code.sggCd.filter((item) => item.val.substring(0, 2) === objSearch?.city).map((item) => item.text); + } if (sidoText === '세종특별자치시') { sidoText = '세종시'; sggText = sidoText; } - drgtSearch(fromDate ?? "", toDate ?? "", sidoText, sggText) + drgtSearch(fromDate ?? "", toDate ?? "", [sidoText, ...sggText]) }} > 조회 @@ -249,7 +253,7 @@ export default function DrgtSttn() { setDrgtModal({ ...drgtModal, isOpen: !drgtModal.isOpen }); }} bodyClass={"w-[50vw] max-w-none h-[80vh]"} - body={} + body={} /> diff --git a/apps/wr/app/store/matDmd/drgtSttn/headList.tsx b/apps/wr/app/store/matDmd/drgtSttn/headList.tsx index 1741b7456..dcdc655b5 100644 --- a/apps/wr/app/store/matDmd/drgtSttn/headList.tsx +++ b/apps/wr/app/store/matDmd/drgtSttn/headList.tsx @@ -2,8 +2,8 @@ import { z } from "zod"; import type { CommonTableProps } from "../../common/type/header"; export const drgtZod = z.object({ - ctpvCd: z.string().default(''), - sggNm: z.string().default(''), + ctpvCd: z.string().default('').nullish(), + sggNm: z.string().default('').nullish(), normal: z.string().default(''), attention: z.string().default(''), caution: z.string().default(''), diff --git a/apps/wr/app/store/matDmd/drgtSttn/index.tsx b/apps/wr/app/store/matDmd/drgtSttn/index.tsx index 053bb12fc..4211d2d08 100644 --- a/apps/wr/app/store/matDmd/drgtSttn/index.tsx +++ b/apps/wr/app/store/matDmd/drgtSttn/index.tsx @@ -1,4 +1,4 @@ -import { fetchType, getFetch } from "@/components/lib/api"; +import { fetchType, getFetch, postFectch } from "@/components/lib/api"; import type { CommonTableProps } from "../../common/type/header"; import { drgtHeadListExport, drgtZod } from "./headList"; import type { z } from "zod"; @@ -20,7 +20,7 @@ export type drgtState = { type drgtActions = { drgtInit: () => void - drgtSearch: (fromDate: string, toDate: string, sido: string, sgg: string) => void + drgtSearch: (fromDate: string, toDate: string, sido: string[]) => void drgtModalSearch: (fromDate: string, toDate: string, sgg: string) => void }; @@ -35,27 +35,20 @@ export const drgtStateExport: drgtState = { } } -const initFetch = async (fromDate: string, toDate: string, sido: string, sgg: string) => { - - let searchUrl = ""; - - if (sido === "" || sido === "전체") { - searchUrl = `/il/getDataCount/${fromDate}/${toDate}`; - } else { - searchUrl = `/il/getDataCount/${fromDate}/${toDate}/${sido}/${sgg}`; - } - - const drgtData = await getFetch( - fetchType.WR, - searchUrl - ); +const initFetch = async (fromDate: string, toDate: string, sido:string[]) => { +console.log("sido",sido) + const drgtData = await postFectch( + `/il/getDataCount/${fromDate}/${toDate}`, + sido, + fetchType.WR + ); //주의, 관심, 정상 추출 const statusData = Array.from( - new Set(drgtData.map((e) => e.lvngMfgUswtrDrgtInfo)) + new Set(drgtData.data.map((e) => e.lvngMfgUswtrDrgtInfo)) ); - const statusMap = drgtData.reduce((acc: any, { sggNm, lvngMfgUswtrDrgtInfo: status, countDrgt }: { sggNm: string; lvngMfgUswtrDrgtInfo: string; countDrgt: number }) => { + const statusMap = drgtData.data.reduce((acc: any, { sggNm, lvngMfgUswtrDrgtInfo: status, countDrgt }: { sggNm: string; lvngMfgUswtrDrgtInfo: string; countDrgt: number }) => { if (!acc[sggNm]) { acc[sggNm] = { sggNm, ...Object.fromEntries(statusData.map((e) => [e, 0])) }; } @@ -110,7 +103,7 @@ export const drgtActionsExport: (set: any) => drgtActions = ( const toDate = formatted; //FIXME: 페이지 처음 로딩될때 데이터 보려고 6월로 박아놓음 이거 fromDate로 고치기 - const drgtRes = await initFetch("202506", toDate, "", ""); + const drgtRes = await initFetch("202506", toDate, []); set((state: drgtState) => { return { ...state, @@ -125,10 +118,9 @@ export const drgtActionsExport: (set: any) => drgtActions = ( console.log(e) } }, - drgtSearch: async (fromDate: string, toDate: string, sido: string, sgg: string) => { + drgtSearch: async (fromDate: string, toDate: string, sido: string[]) => { try { - - const drgtRes = await initFetch(fromDate, toDate, sido, sgg); + const drgtRes = await initFetch(fromDate, toDate, sido); set((state: drgtState) => { return { diff --git a/apps/wr/components/ui/modal/coMat/drgtDetailModal.tsx b/apps/wr/components/ui/modal/coMat/drgtDetailModal.tsx index 102292ccf..880c789c3 100644 --- a/apps/wr/components/ui/modal/coMat/drgtDetailModal.tsx +++ b/apps/wr/components/ui/modal/coMat/drgtDetailModal.tsx @@ -3,11 +3,10 @@ import { btnClass } from "@/app/store/common/code" import type { customDrgt } from "@/app/store/matDmd/drgtSttn" import { drgtDetailHeadListExport } from "@/app/store/matDmd/drgtSttn/headList" -import { excelDownload } from "@/components/lib/func/commom" import { downloadExcel, Title, UiTable } from "@common/business_components/ui" import ListDiv from "@common/business_components/ui/list_div" import { Button, ScrollArea } from "@common/components/ui" -import { useCallback, useEffect, useState } from "react" +import { useEffect, useState } from "react" export default function DrgtDetailModal({ data, From 08adf380e678d3b2ec16a83a958a966d3ba93abf Mon Sep 17 00:00:00 2001 From: jyha00 Date: Tue, 1 Jul 2025 20:17:38 +0900 Subject: [PATCH 036/108] =?UTF-8?q?=EC=83=81=EC=84=B8=EB=B3=B4=EA=B8=B0=20?= =?UTF-8?q?=EB=B2=84=ED=8A=BC=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wr/app/matMng/coMng/drgtSttn/page.tsx | 7 ------- 1 file changed, 7 deletions(-) diff --git a/apps/wr/app/matMng/coMng/drgtSttn/page.tsx b/apps/wr/app/matMng/coMng/drgtSttn/page.tsx index af49f02b1..317e05dc6 100644 --- a/apps/wr/app/matMng/coMng/drgtSttn/page.tsx +++ b/apps/wr/app/matMng/coMng/drgtSttn/page.tsx @@ -202,13 +202,6 @@ export default function DrgtSttn() { > 조회 -
} + body={} bodyClass="w-[68vw] max-w-none h-[90vh]" open={issNewReg} onOpenChange={(open) => { diff --git a/apps/wr/app/store/common/code/index.ts b/apps/wr/app/store/common/code/index.ts index 83d87d738..17ea1f2e7 100644 --- a/apps/wr/app/store/common/code/index.ts +++ b/apps/wr/app/store/common/code/index.ts @@ -9,7 +9,26 @@ const yn: SelectDataType[] = [ { val: 'Y', text: 'Y' }, { val: 'N', text: 'N' }, ] +const bdstFilterYn: SelectDataType[] = [ + { val: 'Y', text: 'Y' }, + { val: 'N', text: 'N' }, + { val: 'D', text: '확인불가' }, + { val: 'F', text: '미가동' }, +] +// 최우수(그린1)/우수(그린2), 우량(그린3), 일반(그린4), 확인불가 체크 +const bdstGrntchClass: SelectDataType[] = [ + { val: '최우수(그린1)', text: '최우수(그린1)' }, + { val: '우수(그린2)', text: '우수(그린2)' }, + { val: '우량(그린3)', text: '우량(그린3)' }, + { val: '일반(그린4)', text: '일반(그린4)' }, + { val: '확인불가', text: '확인불가' }, +] +const bdstIncenYn: SelectDataType[] = [ + { val: 'Y', text: 'Y' }, + { val: 'N', text: 'N' }, + { val: 'D', text: '확인불가' }, +] //처리장연계/자체+연계/자체처리 const erctUswtrPrcsMeth : SelectDataType[]= [ { val: '처리장연계', text: '처리장연계' }, @@ -204,6 +223,9 @@ export type comCodeState = { ruseBizMthdCd: SelectDataType[]; rusePrcsMthdCd: SelectDataType[]; erctUswtrPrcsMeth: SelectDataType[]; + bdstFilterYn: SelectDataType[]; + bdstIncenYn: SelectDataType[]; + bdstGrntchClass:SelectDataType[]; }; }; @@ -217,7 +239,7 @@ export const getSggCd = async (set: any) => { })); */ const sggCd = [ - { text: "전체", val: '0' }, + // { text: "전체", val: '0' }, ...sgg .map((item: any) => ({ text: item.gugun || '', @@ -356,7 +378,10 @@ export const comCodeStateExport: comCodeState = { ruseSeCd:ruseSeCd, ruseBizMthdCd: ruseBizMthdCd, rusePrcsMthdCd:rusePrcsMthdCd, - erctUswtrPrcsMeth:erctUswtrPrcsMeth + erctUswtrPrcsMeth:erctUswtrPrcsMeth, + bdstFilterYn:bdstFilterYn, + bdstIncenYn:bdstIncenYn, + bdstGrntchClass:bdstGrntchClass }, }; diff --git a/apps/wr/app/store/ruseFcltMng/fcltReg/rainReg/index.tsx b/apps/wr/app/store/ruseFcltMng/fcltReg/rainReg/index.tsx index 372a4bd9a..9a6c73e4e 100644 --- a/apps/wr/app/store/ruseFcltMng/fcltReg/rainReg/index.tsx +++ b/apps/wr/app/store/ruseFcltMng/fcltReg/rainReg/index.tsx @@ -10,7 +10,7 @@ export const TEST_UUID= '2d4b151d-1828-41e0-91d9-4cd28ea1cc7e' const rainyObjSchema = z.object({ bdstReuseFcltFk: z.number().nullish(), pntfColKnd: z.string().min(1, { message: validationMessages.requiredSelect("종류") }), - pntfColArea: z.number().positive({ message: validationMessages.required("면적(m³)") }), + pntfColArea: z.number().nullish() , dtfcltCpct: z.number().default(0), filtrTrtmfctYn: z.string().default("Y"), filtrTrtmfctCpct: z.number().nullish().default(0), @@ -162,29 +162,27 @@ export const rainFacActionsExport: (set: any) => rainFacSActions = ( const test =await makeRstr(data); const res = await postFectch("/reuseFc/rainyInfo", test); - if(res.ok){ + if(res.status===200){ if(data.chgBuilding.length > 0){ - await res.json().then((resData: any) => { - const chPK = resData?.data?.bdstInfoPk - const makeCh= resData.data.chgBuilding.map((item: chgBuilding) => + + const chPK = res?.data?.bdstInfoPk + const makeCh= res.data.chgBuilding.map((item: chgBuilding) => ( { ...item, bdstInfoFk: chPK, }) ); - const chch= postFectch("/reuseFc/chgBuilding", makeCh); - return chch; - }).then((chch: any) => { - - console.log('chch ', chch) + const chch= await postFectch("/reuseFc/chgBuilding", makeCh); if(chch.status ===200) { - toast.success("등록이 완료되었습니다."); - + toast.success("건축물 등록이 완료되었습니다."); + }else{ + toast.error("건축물 증축/개축/중축 오류입니다."); } - }) }else{ - toast.success("등록이 완료되었습니다."); + toast.success("건축물 등록이 완료되었습니다."); } + }else{ + toast.error("건축물 등록이 완료되었습니다."); } const rainRes = await initFetch(code); set((state: rainFacState) => ({ @@ -197,7 +195,7 @@ export const rainFacActionsExport: (set: any) => rainFacSActions = ( })); } catch (e) { console.log("e", e); - alert("등록 오류입니다."); + toast.error("등록시 오류가 발생하였습니다."); } finally { } }, @@ -205,9 +203,9 @@ export const rainFacActionsExport: (set: any) => rainFacSActions = ( try { const chData =await makeMdfr(data); const res = await putFectch("/reuseFc/rainyInfo", chData); - console.log('rainUpdate', res); - if(res.ok){ - alert(" 수정이 완료되었습니다.") + if(res.status === 200){ + toast.success("수정이 완료되었습니다."); + } const rainRes = await initFetch(code); set((state: rainFacState) => ({ @@ -220,7 +218,7 @@ export const rainFacActionsExport: (set: any) => rainFacSActions = ( })); } catch (e) { console.log("e", e); - alert("업데이트 오류입니다."); + toast.error("수정시 오류가 발생하였습니다."); } finally { } } diff --git a/apps/wr/app/store/ruseFcltMng/fcltReg/rainReg/type.tsx b/apps/wr/app/store/ruseFcltMng/fcltReg/rainReg/type.tsx index 6e28c3e76..59d6633a2 100644 --- a/apps/wr/app/store/ruseFcltMng/fcltReg/rainReg/type.tsx +++ b/apps/wr/app/store/ruseFcltMng/fcltReg/rainReg/type.tsx @@ -17,19 +17,28 @@ export const baseReuseBuildingSchema = z.object({ instlCstSprtYn: z.string().default("Y"), instlCstSprtAmt: z.number().nullish(), grntchBdstCertYn: z.string().default("Y"), - grntchBdstCertYr: z.string().default("").refine( + grntchBdstCertYr: z + .string() + .max(4, { message: validationMessages.yearFormat }) + .refine( (year) => year.length === 0 || /^\d{4}$/.test(year), { message: validationMessages.yearFormat, } - ), + ) + .nullish(), grntchBdstCertNo: z.string().default(""), grntchBdstCertGrd: z.string().default(""), crgRdctYn: z.string().default("N").nullish(), /// 안쓰는 값 crgRdctBss: z.string().default("").nullish(), /// 안쓰는 값 crgRdctAmt: z.number().default(0), // 안쓰는 값 ruseFcltFlg: z.string().default(""), - bdstUsgCd: z.number().min(1, { message: validationMessages.requiredSelect("건축물용도") }).default(0), + bdstUsgCd: z + .number() + .default(0) + .refine((val) => val === -1 || val >= 1, { + message: validationMessages.requiredSelect("건축물용도"), + }), lawTrgtYn: z.string().default("Y"), rgtr: z.string().nullish(), mdfr: z.string().nullish(), diff --git a/apps/wr/components/lib/func/commom.tsx b/apps/wr/components/lib/func/commom.tsx index e5dfc8c0c..8569d4eb3 100644 --- a/apps/wr/components/lib/func/commom.tsx +++ b/apps/wr/components/lib/func/commom.tsx @@ -9,12 +9,22 @@ import { import type { RefObject } from "react"; export const getCity = (val: string, sggCd: SelectDataType[]) => { - const data = sggCd - .filter((item) => String(item.val).substring(0, 2) === val) + + let data:any + if(val !=='0'){ + data = sggCd + .filter((item) => String(item.val).substring(0, 2) === val) + .map((item) => ({ + val: item.val, + text: item.text, // 한국어 텍스트 + })); + }else{ + data = sggCd .map((item) => ({ val: item.val, text: item.text, // 한국어 텍스트 })); + } data.unshift({ val: "0", text: "전체" }); return data; }; @@ -103,7 +113,18 @@ export const handleCommaNumberChange = ( ) => { const raw = e.target.value.replace(/,/g, ""); if (!/^\d*$/.test(raw)) return; - onChange(raw === "" ? "" : Number(raw)); + onChange(raw === "" ? "" : Number(raw)); +}; + + +// onchange 텍스트를 숫자로 and ,제거 +export const handleMinusNumberChange = ( + e: React.ChangeEvent, + onChange: (value: number | "") => void +) => { + const raw = e.target.value.replace(/,/g, ""); + if (!/^\d*$/.test(raw)) return; + onChange(raw === "" ? -1 : Number(raw)); }; // 숫자입력시 , 삽입 diff --git a/apps/wr/components/ui/form/fcltReg/bdstReg/bdstReg.tsx b/apps/wr/components/ui/form/fcltReg/bdstReg/bdstReg.tsx index 0d00454e2..c1bf7e2ca 100644 --- a/apps/wr/components/ui/form/fcltReg/bdstReg/bdstReg.tsx +++ b/apps/wr/components/ui/form/fcltReg/bdstReg/bdstReg.tsx @@ -16,8 +16,7 @@ import { type SelectDataType, } from "@common/business_components"; import { Button, Checkbox, Input } from "@common/components"; -import { Check } from "lucide-react"; -import { useEffect, useRef, useState } from "react"; +import { useEffect, useState } from "react"; import { useFieldArray } from "react-hook-form"; export default function BdstReg({ form }: { form: any }) { @@ -37,12 +36,6 @@ export default function BdstReg({ form }: { form: any }) { bdstUsgCd: "", }); }; -// 잠깐 주석처리 - // const removeLastItem = () => { - // if (fields.length > 0) { - // remove(fields.length - 1); - // } - // }; const removeSelected = () => { const idxsToRemove = fields .map((item, idx) => selectedIds.includes(item.id) ? idx : -1) @@ -100,7 +93,7 @@ export default function BdstReg({ form }: { form: any }) { return ( <>

- 건축물 시설 정보 + 건축물 기본 정보

@@ -155,7 +148,7 @@ export default function BdstReg({ form }: { form: any }) { @@ -168,10 +161,10 @@ export default function BdstReg({ form }: { form: any }) { control={form.control} name="daddr" render={({ field }) => ( - + @@ -225,7 +218,7 @@ export default function BdstReg({ form }: { form: any }) { { handleCommaNumberChange(e, field.onChange); @@ -324,8 +317,8 @@ export default function BdstReg({ form }: { form: any }) { handleCommaNumberChange(e, f.onChange)} - value={formatCommaNumber(f.value)} + onChange={e => f.onChange} + value={f.value} className={selectClass} /> diff --git a/apps/wr/components/ui/form/fcltReg/pbnstReg/buseReg/bugRegForm.tsx b/apps/wr/components/ui/form/fcltReg/pbnstReg/buseReg/bugRegForm.tsx index 17983edf2..d2f9b79e6 100644 --- a/apps/wr/components/ui/form/fcltReg/pbnstReg/buseReg/bugRegForm.tsx +++ b/apps/wr/components/ui/form/fcltReg/pbnstReg/buseReg/bugRegForm.tsx @@ -30,7 +30,11 @@ import { Button, DataTable, Input, Label } from "@common/components"; import { useEffect, useRef, useState } from "react"; import { useFieldArray, useForm, type UseFormReturn } from "react-hook-form"; import MngRegFormDetail from "../mngReg/mngRegFormDetail/mngRegFormDetail"; -import { safeDate } from "@/components/lib/func/commom"; +import { + formatCommaNumber, + handleCommaNumberChange, + safeDate, +} from "@/components/lib/func/commom"; import SearchInputText from "@/components/ui/searchInput/searchInput"; import SdmInsertForm from "@common/business_components/ui/sdmInsertForm"; import type { DemandCompType } from "@common/business_components/ui/sdmInsertForm/form"; @@ -82,54 +86,57 @@ export function PbnstBuseForm({ }>({ isMoal: false, data: {} as DemandCompType }); useEffect(() => { - const etc= isNaN(Number(form.getValues(`items.${indexMng}.fcBizs.${indexBiz}.etc`))) + const etc = isNaN( + Number(form.getValues(`items.${indexMng}.fcBizs.${indexBiz}.etc`)) + ); let total = (form.getValues(`items.${indexMng}.fcBizs.${indexBiz}.gvsbs`) ?? 0) + (form.getValues(`items.${indexMng}.fcBizs.${indexBiz}.lcep`) ?? 0) + - (form.getValues(`items.${indexMng}.fcBizs.${indexBiz}.cusrAlotm`) ?? 0) - - if( !etc ) { - total+=Number(form.getValues(`items.${indexMng}.fcBizs.${indexBiz}.etc`)) - } + (form.getValues(`items.${indexMng}.fcBizs.${indexBiz}.cusrAlotm`) ?? 0); + + if (!etc) { + total += Number( + form.getValues(`items.${indexMng}.fcBizs.${indexBiz}.etc`) + ); + } form.setValue(`items.${indexMng}.fcBizs.${indexBiz}.totalMoney`, total); }, [ form.getValues(`items.${indexMng}.fcBizs.${indexBiz}.gvsbs`), form.getValues(`items.${indexMng}.fcBizs.${indexBiz}.lcep`), form.getValues(`items.${indexMng}.fcBizs.${indexBiz}.cusrAlotm`), - form.getValues(`items.${indexMng}.fcBizs.${indexBiz}.etc`) + form.getValues(`items.${indexMng}.fcBizs.${indexBiz}.etc`), ]); + const fcOrgwts = form.watch(`items.${indexMng}.fcBizs.${indexBiz}.fcOrgwts`); - const fcOrgwts = form.watch( `items.${indexMng}.fcBizs.${indexBiz}.fcOrgwts`) - - useEffect(() =>{ - - console.log('fcOrgwts ', fcOrgwts) - if(fcOrgwts&&fcOrgwts?.length>0){ + useEffect(() => { + console.log("fcOrgwts ", fcOrgwts); + if (fcOrgwts && fcOrgwts?.length > 0) { const swtrmtFcltFkList = fcOrgwts.map((item) => item.swtrmtFcltFk); - console.log('swtrmtFcltFkList ', swtrmtFcltFkList) + console.log("swtrmtFcltFkList ", swtrmtFcltFkList); - if(swtrmtFcltFkList.length > 0){ - getFcOrgwts(swtrmtFcltFkList.filter((item): item is string => item != null), fcOrgwts?.length) + if (swtrmtFcltFkList.length > 0) { + getFcOrgwts( + swtrmtFcltFkList.filter((item): item is string => item != null), + fcOrgwts?.length + ); } } - },[fcOrgwts]) - - useEffect(() =>{ + }, [fcOrgwts]); + useEffect(() => { if (fcOrgwtsNmList && fcOrgwtsNmList.length > 0) { - - console.log('fcOrgwtsNmList ', fcOrgwtsNmList) - console.log('fcOrgwts ', fcOrgwts) + console.log("fcOrgwtsNmList ", fcOrgwtsNmList); + console.log("fcOrgwts ", fcOrgwts); fcOrgwts?.forEach((item, index) => { const orgwtNm = fcOrgwtsNmList.find( (orgwt) => orgwt.bscFctCd === item.swtrmtFcltFk )?.bscFctNm; - console.log('orgwtNm ', orgwtNm) + console.log("orgwtNm ", orgwtNm); if (orgwtNm) { form.setValue( `items.${indexMng}.fcBizs.${indexBiz}.fcOrgwts.${index}.ruseUtztnFcltNm`, @@ -137,10 +144,8 @@ export function PbnstBuseForm({ ); } }); - } - - },[fcOrgwtsNmList,fcOrgwts]) - + } + }, [fcOrgwtsNmList, fcOrgwts]); // fcBizs 데이터가 변경될 때마다 차수 선택 목록을 업데이트합니다. useEffect(() => { @@ -294,7 +299,6 @@ export function PbnstBuseForm({ setSumCpct(sum); }, [form.formState, indexMng, indexBiz]); - const pbnstBiz = form.watch(`items.${indexMng}.fcBizs`); return ( <> @@ -527,7 +531,9 @@ export function PbnstBuseForm({ ).map((field, index) => (
{ - f.onChange(Number(e.target.value)); + handleCommaNumberChange(e, f.onChange); }} + value={formatCommaNumber(f.value)} /> )} @@ -691,7 +697,6 @@ export function PbnstBuseForm({ )} />
-

))}
diff --git a/apps/wr/components/ui/form/fcltReg/pbnstReg/mngReg/mngRegFormDetail/mngRegFormDetail.tsx b/apps/wr/components/ui/form/fcltReg/pbnstReg/mngReg/mngRegFormDetail/mngRegFormDetail.tsx index 6e13058ab..30fab689e 100644 --- a/apps/wr/components/ui/form/fcltReg/pbnstReg/mngReg/mngRegFormDetail/mngRegFormDetail.tsx +++ b/apps/wr/components/ui/form/fcltReg/pbnstReg/mngReg/mngRegFormDetail/mngRegFormDetail.tsx @@ -97,7 +97,7 @@ export default function MngRegFormDetail({ : [] } onSelectValue={field.onChange} - value={field.value.trim()} + value={field.value?.trim() ??""} readOnly={formHead.option === "readOnly"} /> ); diff --git a/apps/wr/components/ui/form/fcltReg/rainReg/rainRegForm.tsx b/apps/wr/components/ui/form/fcltReg/rainReg/rainRegForm.tsx index fc4ed566e..b893f9bf4 100644 --- a/apps/wr/components/ui/form/fcltReg/rainReg/rainRegForm.tsx +++ b/apps/wr/components/ui/form/fcltReg/rainReg/rainRegForm.tsx @@ -4,29 +4,38 @@ import { useWrStore } from "@/app/store"; import { rainZod, selectClass, - type customRainBdst + type customRainBdst, } from "@/app/store/ruseFcltMng/fcltReg/rainReg"; import { formH1Class, formH2Class } from "@/components/lib/class/comClass"; import { formatCommaNumber, handleCommaNumberChange, + handleMinusNumberChange, } from "@/components/lib/func/commom"; import { DatePicker, FormUi } from "@common/business_components"; -import { Form, FormField, SelectBox } from "@common/business_components/ui"; -import { Button, Input } from "@common/components/ui"; +import { + Form, + FormField, + FormLabel, + SelectBox, +} from "@common/business_components/ui"; +import { Button, Checkbox, Input, Label } from "@common/components/ui"; import { zodResolver } from "@hookform/resolvers/zod"; -import { useEffect } from "react"; +import { useEffect, useState } from "react"; import { useForm } from "react-hook-form"; import { z } from "zod"; import BdstReg from "../bdstReg/bdstReg"; +import toast from "react-hot-toast"; export default function RainRegForm({ rainForm, isModal, - readonly = false + readonly = false, + setIsModal }: { rainForm: customRainBdst; isModal: boolean; + setIsModal?:(modal:boolean) => void readonly?: boolean; }) { const { code, rainBdstReg, rainBdstUpdate, rain } = useWrStore( @@ -38,6 +47,7 @@ export default function RainRegForm({ resolver: zodResolver(rainZod), defaultValues: defaultValues, }); + const instlCst = form.watch(`reuseBuilding.0.instlCst`); const [grntchBdstCertYn, instlCstSprtYn, filtrTrtmfctYn] = form.watch([ "reuseBuilding.0.grntchBdstCertYn", @@ -69,7 +79,7 @@ export default function RainRegForm({ ? item?.grntchBdstCertYr : "", // null → "" instlCstSprtYn: item?.instlCstSprtYn ?? "Y", // null → "Y" - grntchBdstCertYn:item?.grntchBdstCertYn??"Y", + grntchBdstCertYn: item?.grntchBdstCertYn ?? "Y", rainyObj: { ...item?.rainyObj, filtrTrtmfctYn: item?.rainyObj?.filtrTrtmfctYn ?? "Y", @@ -79,7 +89,6 @@ export default function RainRegForm({ }; useEffect(() => { - if (grntchBdstCertYn === "N") { form.setValue("reuseBuilding.0.grntchBdstCertYr", ""); form.setValue("reuseBuilding.0.grntchBdstCertNo", ""); @@ -90,15 +99,25 @@ export default function RainRegForm({ form.setValue("reuseBuilding.0.rainyObj.filtrTrtmfctCpct", 0); form.setValue("reuseBuilding.0.rainyObj.filtrTrtmfctPrs", ""); } + + if(grntchBdstCertYn === "D") { + form.setValue("reuseBuilding.0.grntchBdstCertYr", "0000"); + form.setValue("reuseBuilding.0.grntchBdstCertNo", "확인불가"); + form.setValue("reuseBuilding.0.grntchBdstCertGrd", "확인불가"); + }else if (instlCstSprtYn === "D") { + form.setValue("reuseBuilding.0.instlCstSprtAmt", -1); + } else if (filtrTrtmfctYn === "D") { + form.setValue("reuseBuilding.0.rainyObj.filtrTrtmfctCpct", -1); + form.setValue("reuseBuilding.0.rainyObj.filtrTrtmfctPrs", "확인불가"); + } + }, [grntchBdstCertYn, instlCstSprtYn, filtrTrtmfctYn]); - - +const [regCtpvCd , regSggcd] = form.watch(['ctpvCd','sggCd']) // form 값이 변경될 때마다 로그 출력 useEffect(() => { - const subscription = form.watch((value) => { - }); + const subscription = form.watch((value) => {}); return () => subscription.unsubscribe(); }, [form]); @@ -108,7 +127,15 @@ export default function RainRegForm({ const onSubmit = (values: z.infer) => { if (isModal) { - rainBdstReg(values, "41"); + const data = regCtpvCd?regCtpvCd:regSggcd?regSggcd:'noCd' + + if(data ==='noCd'){ + toast.error("시도/시군구 코드를 선택해야합니다."); + return + } + + rainBdstReg(values, data); + setIsModal(false) } else { rainBdstUpdate(values, "41"); } @@ -125,10 +152,14 @@ export default function RainRegForm({

- {isModal ? "건축물 운영정보" : "빗물이용시설 정보"} + {isModal ? "건축물 정보" : "빗물이용시설 정보"}

{!readonly && ( - )} @@ -142,12 +173,9 @@ export default function RainRegForm({ ) : ( <> )} -

- 기본정보 -

- -
-
+

기본정보

+
+
field.onChange(date), + onChange: (date: Date | undefined) => + field.onChange(date), }} selectClass={"w-full"} readOnly={readonly} @@ -170,48 +199,95 @@ export default function RainRegForm({ />
-
+
( - - { - if (!readonly) handleCommaNumberChange(e, field.onChange); - }} - value={formatCommaNumber(field.value)} - readOnly={readonly} - /> - - )} + name={`reuseBuilding.0.instlCst`} + render={({ field }) => { + const value = field.value; + const isUnknown = value === null || value === -1; // 또는 Number(value) === -1 + return ( +
+ + { + // 입력 시 자동으로 확인불가 해제 + if (isUnknown && !readonly) { + field.onChange(""); // 초기화 + } + }} + onChange={(e) => { + if (readonly ) return; + handleMinusNumberChange(e, field.onChange); + }} + /> + +
+ ); + }} />
-

- 빗물이용시설 정보 -

+

빗물이용시설 정보

-

집수면

+

집수면 및 저류조

-
-
+
+
+ + + { + const isUnknown = field.value === null || field.value === -1; // 또는 Number(value) === -1 + return ( + + { + // 입력 시 자동으로 확인불가 해제 + if (isUnknown && !readonly) { + field.onChange(""); // 초기화 + } + }} + onChange={(e) => { + if (readonly ) return; + handleMinusNumberChange(e, field.onChange); + }} + readOnly={readonly} + /> + + ) + }} + /> +
+
( - + { } : field.onChange} + onSelectValue={readonly ? () => {} : field.onChange} value={field.value ?? rainFcltCd[0]?.val} readOnly={readonly} /> @@ -219,24 +295,40 @@ export default function RainRegForm({ )} />
-
+
( - - { - if (!readonly) handleCommaNumberChange(e, field.onChange); - }} - value={formatCommaNumber(field.value)} - readOnly={readonly} - /> - - )} + render={({ field }) => { + const value = field.value; + const isUnknown = value === null || value === -1; // 또는 Number(value) === -1 + return ( +
+ + { + // 입력 시 자동으로 확인불가 해제 + if (isUnknown && !readonly) { + field.onChange(""); // 초기화 + } + }} + onChange={(e) => { + if (readonly ) return; + handleMinusNumberChange(e, field.onChange); + }} + readOnly={readonly} + /> + + +
+ ); + }} />
@@ -255,8 +347,8 @@ export default function RainRegForm({ className={""} selectClass={selectClass} label={""} - selectArray={code.yn} - onSelectValue={readonly ? () => { } : field.onChange} + selectArray={code.bdstFilterYn} + onSelectValue={readonly ? () => {} : field.onChange} value={field.value ?? code.yn[0]?.val} readOnly={readonly} /> @@ -268,47 +360,68 @@ export default function RainRegForm({ ( + render={({ field }) => + + + { + const isUnknown = field.value === null || field.value === -1; // 또는 Number(value) === -1 + return( { - if (!readonly) handleCommaNumberChange(e, field.onChange); + value={ + isUnknown ? "확인불가" : formatCommaNumber(field.value) + } + onFocus={() => { + // 입력 시 자동으로 확인불가 해제 + if (isUnknown && !readonly) { + field.onChange(""); // 초기화 + } }} - value={formatCommaNumber(field.value)} - readOnly={readonly } + onChange={(e) => { + if (readonly ) return; + handleMinusNumberChange(e, field.onChange); + }} + readOnly={readonly} disabled={filtrTrtmfctYn !== "Y"} /> - )} + ) + }} />
( - - - - )} + render={({ field }) => { + const value = field.value; + const isUnknown = value === null || value === "-"; // 또는 Number(value) === -1 + + return ( +
+ + + + +
+ ); + }} />
-

- 인센티브 정보 -

+

인센티브 정보

설치비 지원

@@ -324,8 +437,8 @@ export default function RainRegForm({ className={""} selectClass={selectClass} label={""} - selectArray={code.yn} - onSelectValue={readonly ? () => { } : field.onChange} + selectArray={code.bdstIncenYn} + onSelectValue={readonly ? () => {} : field.onChange} value={field.value ?? code.yn[0]?.val} readOnly={readonly} /> @@ -337,24 +450,38 @@ export default function RainRegForm({ ( - - { - if (!readonly) handleCommaNumberChange(e, field.onChange); + render={({ field }) => + { + const value = field.value; + const isUnknown = value === null || value === -1; // 또는 Number(value) === -1 + return ( + + { + // 입력 시 자동으로 확인불가 해제 + if (isUnknown && !readonly) { + field.onChange(""); // 초기화 + } }} - value={formatCommaNumber(field.value)} - readOnly={instlCstSprtYn !== "Y"} - /> - - )} + onChange={(e) => { + if (readonly ) return; + handleMinusNumberChange(e, field.onChange); + }} + disabled={instlCstSprtYn !== "Y"} + /> + + )} + } />
-

녹색건축물 인증

@@ -369,8 +496,8 @@ export default function RainRegForm({ className={""} selectClass={selectClass} label={""} - selectArray={code.yn} - onSelectValue={readonly ? () => { } : field.onChange} + selectArray={code.bdstIncenYn} + onSelectValue={readonly ? () => {} : field.onChange} value={field.value ?? code.yn[0]?.val} /> @@ -381,20 +508,38 @@ export default function RainRegForm({ ( - - { - if (!readonly) field.onChange(e); - }} - readOnly={grntchBdstCertYn !== "Y"} - /> - - )} + render={({ field }) => + { + const isUnknown = field.value === null || field.value === "0000"; // 또는 Number(value) === -1 + return ( + + { + // 입력 시 자동으로 확인불가 해제 + if (isUnknown && !readonly) { + field.onChange(""); // 초기화 + } + }} + value={ + + isUnknown?'확인불가':field.value ?? ""} + onChange={(e) => { + if (readonly) return + + if (e.target.value === '확인불가') { + field.onChange('0000'); + }else{ + field.onChange(e.target.value); + } + }} + disabled={grntchBdstCertYn !== "Y"} + /> + + )} + } />
@@ -411,7 +556,7 @@ export default function RainRegForm({ onChange={(e) => { if (!readonly) field.onChange(e); }} - readOnly={grntchBdstCertYn !== "Y"} + disabled={grntchBdstCertYn !== "Y"} /> )} @@ -423,15 +568,14 @@ export default function RainRegForm({ name="reuseBuilding.0.grntchBdstCertGrd" render={({ field }) => ( - { - if (!readonly) field.onChange(e); - }} - readOnly={grntchBdstCertYn !== "Y"} + {} : field.onChange} + value={field.value ?? code.bdstGrntchClass[0]?.val} + readOnly={readonly} /> )} diff --git a/apps/wr/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal.tsx b/apps/wr/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal.tsx index 205846bb4..6ab1b25c4 100644 --- a/apps/wr/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal.tsx +++ b/apps/wr/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal.tsx @@ -29,10 +29,11 @@ export type objSearchType = { interface BdstRegProps { moType: T; + isModal:boolean; setIsModal?: (open: boolean) => void; } -export function BdstReg({ moType,setIsModal }: BdstRegProps) { +export function BdstReg({ moType,setIsModal,isModal }: BdstRegProps) { const { rain, code, bdst,findBdst } = useWrStore((state) => { return state; }); @@ -45,13 +46,13 @@ export function BdstReg({ moType,setIsModal }: BdstRegProps const getSelCity = (val: string | number) => { const cityValue = String(val); // Ensure val is treated as a string const data = getCity(cityValue, code.sggCd); + setSelectSgg(data); - getSgg(data[0]?.val || ""); + // getSgg(data[0]?.val || ""); setObjSearch({ city: cityValue, sgg: '' }); }; const getSgg = (val: string | number) => { - console.log(val); setObjSearch({...objSearch,sgg: String(val)}) }; @@ -85,10 +86,7 @@ export function BdstReg({ moType,setIsModal }: BdstRegProps if (data) { setRainBdstForm(data); } - - } - } useEffect(()=>{ @@ -122,7 +120,7 @@ export function BdstReg({ moType,setIsModal }: BdstRegProps
-

건축물 목록

+

건축물 목록 (유의 사항: 신규등록하려는 건축물이 목록에 없을 경우 하단 건축물 정보에 추가하여 주시기 바랍니다.)

({ moType,setIsModal }: BdstRegProps
{ moType === 'RNB'? - + :moType==='RST' - ? {setIsModal}}> + ? {setIsModal}}> : } diff --git a/apps/wr/components/ui/searchInput/searchInput.tsx b/apps/wr/components/ui/searchInput/searchInput.tsx index 796c8c298..be63b9685 100644 --- a/apps/wr/components/ui/searchInput/searchInput.tsx +++ b/apps/wr/components/ui/searchInput/searchInput.tsx @@ -62,9 +62,6 @@ export default function SearchInputText({ document.removeEventListener("mousedown", handleClickOutside); }; }, []); -useEffect(() =>{ - console.log('searchList ', searchList) -},[searchList]) return (
From 83b3fad3af3d0b752372296ce627a18583e4bddc Mon Sep 17 00:00:00 2001 From: leedj312 Date: Tue, 1 Jul 2025 20:38:00 +0900 Subject: [PATCH 038/108] =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20=ED=86=A0?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/ruseFcltMng/fcltReg/rainReg/page.tsx | 56 +++++++++++-------- .../ruseFcltMng/fcltReg/rainReg/index.tsx | 1 + 2 files changed, 33 insertions(+), 24 deletions(-) diff --git a/apps/wr/app/ruseFcltMng/fcltReg/rainReg/page.tsx b/apps/wr/app/ruseFcltMng/fcltReg/rainReg/page.tsx index 607b90886..b468fad03 100644 --- a/apps/wr/app/ruseFcltMng/fcltReg/rainReg/page.tsx +++ b/apps/wr/app/ruseFcltMng/fcltReg/rainReg/page.tsx @@ -21,11 +21,14 @@ import { import ListDiv from "@common/business_components/ui/list_div"; import { Button } from "@common/components"; import { useEffect, useState } from "react"; +import { Toaster } from "react-hot-toast"; export default function Home() { - const { rain, code, initRain, rainSearch, getRainData } = useWrStore((state) => { - return state; - }); + const { rain, code, initRain, rainSearch, getRainData } = useWrStore( + (state) => { + return state; + } + ); const { rainRes } = rain; const [rainForm, setRainForm] = useState(); const [issNewReg, setIsNewReg] = useState(false); @@ -38,10 +41,9 @@ export default function Home() { }>({ isOpen: false, chgBuilding: [] }); useEffect(() => { - setIsNewReg( false) + setIsNewReg(false); if (!rain.isInit) { - initRain('41'); - + initRain("41"); } }, [rain.isInit, initRain]); @@ -130,35 +132,39 @@ export default function Home() {
{/* */} - { - cellClick(val, headId); - }} - onPageChange={setPageNation} - > - + { + cellClick(val, headId); + }} + onPageChange={setPageNation} + > {/* */}
- - +
} + body={ + + } bodyClass="w-[68vw] max-w-none h-[90vh]" open={issNewReg} onOpenChange={(open) => { @@ -176,6 +182,8 @@ export default function Home() { setBdstChgProps({ ...bdstChgProps, isOpen: open }); }} /> + + ); diff --git a/apps/wr/app/store/ruseFcltMng/fcltReg/rainReg/index.tsx b/apps/wr/app/store/ruseFcltMng/fcltReg/rainReg/index.tsx index 9a6c73e4e..d4cfebfca 100644 --- a/apps/wr/app/store/ruseFcltMng/fcltReg/rainReg/index.tsx +++ b/apps/wr/app/store/ruseFcltMng/fcltReg/rainReg/index.tsx @@ -173,6 +173,7 @@ export const rainFacActionsExport: (set: any) => rainFacSActions = ( }) ); const chch= await postFectch("/reuseFc/chgBuilding", makeCh); + if(chch.status ===200) { toast.success("건축물 등록이 완료되었습니다."); }else{ From fe66bc4e881bac6f0aa8835a2d8bdbed9189201a Mon Sep 17 00:00:00 2001 From: JIeunhuh Date: Wed, 2 Jul 2025 11:22:40 +0900 Subject: [PATCH 039/108] =?UTF-8?q?=EC=9E=AC=EC=9D=B4=EC=9A=A9=EC=8B=9C?= =?UTF-8?q?=EC=84=A4=20=EC=83=81=EC=84=B8=EC=A0=95=EB=B3=B4=20=ED=8C=9D?= =?UTF-8?q?=EC=97=85=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20=ED=91=9C=EC=B6=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wr/app/map/newWindowPopup.tsx | 34 +++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/apps/wr/app/map/newWindowPopup.tsx b/apps/wr/app/map/newWindowPopup.tsx index ebfcac1ca..a9f1f0326 100644 --- a/apps/wr/app/map/newWindowPopup.tsx +++ b/apps/wr/app/map/newWindowPopup.tsx @@ -67,6 +67,7 @@ const NewWindowPopup: React.FC = ({ } = useWrStore((state) => state); const [bdstTable, setBdstTable] = useState(); const [pageNation, setPageNation] = useState(1); + const [pbnstRuseFcltPk, setPbnstRuseFcltPk] = useState(''); const [pbnDetaileData, setPbnDetaileData] = useState<{ isModal: boolean; data: customPbnst; @@ -108,8 +109,7 @@ const NewWindowPopup: React.FC = ({ } // 2. RuseMngTable에 필요한 tableData를 생성합니다. (단일 행 데이터) - const tableData = [featureData]; - console.log("tableData", tableData); + // 선택된 피쳐 데이터 속성값 // 물재이용시설 cellClick 이벤트 (상세정보) const pbnstCellClick = (index: number | undefined) => { @@ -118,20 +118,35 @@ const NewWindowPopup: React.FC = ({ pbnstSttn.pbnstSttnRes && pbnstSttn.pbnstSttnRes[index] ) { - const pbnstRuseFcltPk = pbnstSttn.pbnstSttnRes[index].pbnstRuseFcltPk; - getPbnStnDetail(pbnstRuseFcltPk); + setPbnstRuseFcltPk(pbnstSttn.pbnstSttnRes[index].pbnstRuseFcltPk); + getPbnStnDetail(pbnstSttn.pbnstSttnRes[index].pbnstRuseFcltPk); } }; - useEffect(() => { - setBdstTable(pbnstSttn.pbnstSttnRes); - }, [pbnstSttn.pbnstSttnRes]); + // useEffect(() => { + // setBdstTable(pbnstSttn.pbnstSttnRes); + // }, [pbnstSttn.pbnstSttnRes]); useEffect(() => { - if (pbnstSttn.pbnstDetail.pbnstRuseFcltPk) setShowDetailView(true); + if (featureData) { + const selectedVal = featureData.getProperties(); + const featureCd = selectedVal?.swtrmt_fclt_cd + '1'; + + if (featureCd && featureCd !== pbnstRuseFcltPk) { + setPbnstRuseFcltPk(featureCd); // 현재 호출된 featureCd 저장 + getPbnStnDetail(featureCd); + } + } + }, [featureData]); + useEffect(() => { + if (pbnstSttn.pbnstDetail) { + setPbnDetaileData({ + isModal: true, + data: pbnstSttn.pbnstDetail, + }); + } }, [pbnstSttn.pbnstDetail]); - // 빗물재이용시설 cellClick 이벤트 (상세정보) const rainCellClick = (index: number | undefined, headId: string) => { if ( @@ -221,7 +236,6 @@ const NewWindowPopup: React.FC = ({ data={pbnDetaileData.data || ({} as customPbnst)} titleVisible={false} /> - ) : ( // 재이용시설 목록 From f2167e69a3789dd922bc115ef9c5a53df788546c Mon Sep 17 00:00:00 2001 From: JIeunhuh Date: Wed, 2 Jul 2025 14:25:28 +0900 Subject: [PATCH 040/108] =?UTF-8?q?=EB=A0=88=EC=9D=B4=EC=96=B4=20=ED=94=BC?= =?UTF-8?q?=EC=B2=98=20=EC=84=A0=ED=83=9D=20=EC=8B=9C=20=ED=95=B4=EB=8B=B9?= =?UTF-8?q?=20=EC=8B=9C=EC=84=A4=EC=9D=98=20=EC=9E=AC=EC=9D=B4=EC=9A=A9?= =?UTF-8?q?=EC=8B=9C=EC=84=A4=20=ED=8C=9D=EC=97=85=20=ED=83=AD=EB=B3=84?= =?UTF-8?q?=EB=A1=9C=20=EB=82=98=EC=98=A4=EB=8F=84=EB=A1=9D=20=EC=9E=91?= =?UTF-8?q?=EC=97=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wr/app/map/newWindowPopup.tsx | 55 +++++++++++++++++------------- 1 file changed, 32 insertions(+), 23 deletions(-) diff --git a/apps/wr/app/map/newWindowPopup.tsx b/apps/wr/app/map/newWindowPopup.tsx index a9f1f0326..e6438656d 100644 --- a/apps/wr/app/map/newWindowPopup.tsx +++ b/apps/wr/app/map/newWindowPopup.tsx @@ -2,12 +2,11 @@ import { useWrStore } from "@/app/store"; import { btnClass } from "@/app/store/common/code"; import type { customPbnst } from "@/app/store/ruseFcltMng/fcltReg/pbnstReg/type"; import { excelDownload } from "@/components/lib/func/commom"; -import { Title, UiTable, UITab, ContentsDiv } from '@common/business_components'; +import { Title, UiTable, UITab } from '@common/business_components'; import { HeadMakeColSpan } from "@common/business_components/ui/table/HeadMakeColSpan"; import { Button } from "@common/components/ui"; import React, { useCallback, useState, useEffect } from 'react'; import NewWindowPortal from '@/app/map/newWindowPortal'; -import PbnstPrfmncSttnPopup from "@/app/map/pbnstPrfmncSttnPopup"; import { PbnstSttnModal } from "@/components/ui/modal/sttn/pbnstSttnModal/pbnstSttnModal"; import type { WaterReuseFacility } from "@/app/store/ruseFcltMng/fcltSttn/pbnstSttn/type"; import type { chgBuilding, customRainBdst } from "@/app/store/ruseFcltMng/fcltReg/rainReg"; @@ -16,6 +15,7 @@ import RswtrPrfmncsSttnPopup from "@/app/map/rswtrPrfmncSttnPopup"; import type { customRswtBdst } from "@/app/store/ruseFcltMng/fcltReg/rswtReg"; import HtdflPrfmncSttnPopup from "@/app/map/htdflPrfmncSttnPopup"; import type { customHtdflBdst } from "@/app/store/ruseFcltMng/fcltReg/htdflReg"; +import { getFetch } from "@/components/lib/api"; interface NewWindowPopupProps { featureData: any; @@ -24,13 +24,6 @@ interface NewWindowPopupProps { onClose: () => void; } -// 탭 -const TABS = [ - { val: "0", text: "재이용시설1" }, - { val: "1", text: "재이용시설2" }, - { val: "2", text: "재이용시설n" }, -]; - // 데이터의 속성(key)을 사용자에게 보여줄 한글 이름으로 변환하기 위한 맵 // 실제 데이터에 맞게 이 부분을 확장해야 합니다. const propertyNameMap: { [key: string]: string } = { @@ -47,7 +40,8 @@ const NewWindowPopup: React.FC = ({ checkedLayersData = [], onClose }) => { - const [activeTab, setActiveTab] = useState(TABS[0]?.val ?? "0"); + const [tabData, setTabData] = useState<{ id: string; name: string; data: any }[]>([]); + const [activeTab, setActiveTab] = useState('0'); // swcho : useState의 true, false 값 변경하여 목록, 상태 테스트 하였음 const [showDetailView, setShowDetailView] = useState(true); // 상세정보 화면 표시 상태 추가 @@ -108,9 +102,6 @@ const NewWindowPopup: React.FC = ({ return null; } - // 2. RuseMngTable에 필요한 tableData를 생성합니다. (단일 행 데이터) - // 선택된 피쳐 데이터 속성값 - // 물재이용시설 cellClick 이벤트 (상세정보) const pbnstCellClick = (index: number | undefined) => { if ( @@ -127,17 +118,34 @@ const NewWindowPopup: React.FC = ({ // setBdstTable(pbnstSttn.pbnstSttnRes); // }, [pbnstSttn.pbnstSttnRes]); + // 재이용시설 조회 + const fetchpbnstDetail = async (featureCd: string) => { + try { + const response = await getFetch("wr", `/reuseFc/pubFc/${featureCd}`); + // 중복 데이터 추가 방지 + setTabData((prev) => { + if (prev.some((tab) => tab.id === featureCd)) { + return prev; // 이미 존재하는 탭이면 추가하지 않음 + } + console.log('탭 idx',prev, prev.length) + return [...prev, { id: featureCd, name: `재이용시설 ${prev.length + 1}`, data: response }]; + }); + } catch (error) { + console.error("Error fetching data:", error); + } + }; useEffect(() => { if (featureData) { const selectedVal = featureData.getProperties(); - const featureCd = selectedVal?.swtrmt_fclt_cd + '1'; - + const featureCd = selectedVal?.swtrmt_fclt_cd; if (featureCd && featureCd !== pbnstRuseFcltPk) { setPbnstRuseFcltPk(featureCd); // 현재 호출된 featureCd 저장 - getPbnStnDetail(featureCd); + setActiveTab(featureCd) + fetchpbnstDetail(featureCd); // 상세정보 가져오기 } } }, [featureData]); + useEffect(() => { if (pbnstSttn.pbnstDetail) { setPbnDetaileData({ @@ -198,7 +206,8 @@ const NewWindowPopup: React.FC = ({ const hasWaterTreatmentLayer = checkedLayersData.some(layer => layer.data?.lyrNm?.includes('하수처리') || layer.data?.lyrNm?.includes('재이용시설') ); - + const actTabData = tabData.find((tab) => tab.id === activeTab); + // swcho : true, false 값 변경하여 시설 변경하여 테스트하였음 // 재이용시설 const isPbnst = true; @@ -223,17 +232,17 @@ const NewWindowPopup: React.FC = ({
({ - value: tab.val, - label: tab.text, - onClick: () => setActiveTab(tab.val), + defaultValue={activeTab} + tabList={tabData.map((tab) => ({ + value: tab.id, + label: tab.name, + onClick: () => setActiveTab(tab.id), }))} children={undefined} />
From f6f03a62cb233ac0000e76899675bfca7c2ae4de Mon Sep 17 00:00:00 2001 From: jyha00 Date: Wed, 2 Jul 2025 14:27:33 +0900 Subject: [PATCH 041/108] =?UTF-8?q?=EC=9A=B0=EB=A6=AC=EB=8F=99=EB=84=A4?= =?UTF-8?q?=EA=B0=80=EB=AD=84=20=EC=8B=9C=EB=8F=84=20=EC=8B=9C=EA=B5=B0?= =?UTF-8?q?=EA=B5=AC=20selectBox=20=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95?= =?UTF-8?q?,=20=EC=9E=AC=EC=9D=B4=EC=9A=A9=EC=8B=A4=EC=A0=81=ED=98=84?= =?UTF-8?q?=ED=99=A9=20=EC=B0=A8=ED=8A=B8=20=EC=83=89=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wr/app/matMng/coMng/drgtSttn/page.tsx | 41 ++++-- .../prfmncSttn/pbnstPrfmncSttn/page.tsx | 122 +++++++++++------- 2 files changed, 105 insertions(+), 58 deletions(-) diff --git a/apps/wr/app/matMng/coMng/drgtSttn/page.tsx b/apps/wr/app/matMng/coMng/drgtSttn/page.tsx index 317e05dc6..55dbeb454 100644 --- a/apps/wr/app/matMng/coMng/drgtSttn/page.tsx +++ b/apps/wr/app/matMng/coMng/drgtSttn/page.tsx @@ -53,8 +53,7 @@ export default function DrgtSttn() { useEffect(() => { let resData = Array.isArray(drgtRes) ? drgtRes : [drgtRes]; resData = resData.map((item) => { - console.log("item", item); - if(item.sggNm === '세종시'){ + if (item.sggNm === '세종시') { return { ...item, ctpvCd: '세종특별자치시', @@ -64,7 +63,7 @@ export default function DrgtSttn() { return { ...item, - ctpvCd: code.cityCd.find(ct => (ct.text == item.sggNm || (code.sggCd.find(sgg => sgg.text == item.sggNm)?.val.substring(0,2) == ct.val)))?.text, + ctpvCd: code.cityCd.find(ct => (ct.text == item.sggNm || (code.sggCd.find(sgg => sgg.text == item.sggNm)?.val.substring(0, 2) == ct.val)))?.text, sggNm: code.sggCd.find(sgg => sgg.text == item.sggNm)?.text } }) @@ -91,13 +90,24 @@ export default function DrgtSttn() { }, [drgtMoRes]); const getSelCity = (val: string | number) => { - const cityValue = String(val); // Ensure val is treated as a string - const data = getCity(cityValue, code.sggCd); - setSelectSgg(data); - setObjSearch({ city: cityValue, sgg: "" }); + const cityValue = String(val); + + if (cityValue === "0") { + setSelectSgg([]); + setSelectedSgg(''); + setObjSearch({ city: cityValue, sgg: '' }); + } else { + const data = getCity(cityValue, code.sggCd); + setSelectSgg(data); + + const firstSggVal = data.find(item => item.val === "0")?.val || (data[0]?.val ?? ""); + setSelectedSgg(firstSggVal); + setObjSearch({ city: cityValue, sgg: firstSggVal }); + } }; const getSgg = (val: string | number) => { + setSelectedSgg(String(val)); setObjSearch({ ...objSearch, sgg: String(val) }); }; @@ -191,13 +201,24 @@ export default function DrgtSttn() { sggText = code.sggCd.find((item) => item.val === objSearch?.sgg)?.text ?? ''; } else { sggText = code.sggCd.filter((item) => item.val.substring(0, 2) === objSearch?.city).map((item) => item.text); - + } + if (sidoText === '전체') { sidoText = '' } + if (sidoText === '세종특별자치시') { sidoText = '세종시'; sggText = sidoText; } - drgtSearch(fromDate ?? "", toDate ?? "", [sidoText, ...sggText]) + + const searchArr = []; + if (sidoText) searchArr.push(sidoText); + if (Array.isArray(sggText)) { + searchArr.push(...sggText); + } else if (sggText) { + searchArr.push(sggText); + } + + drgtSearch(fromDate ?? "", toDate ?? "", searchArr) }} > 조회 @@ -246,7 +267,7 @@ export default function DrgtSttn() { setDrgtModal({ ...drgtModal, isOpen: !drgtModal.isOpen }); }} bodyClass={"w-[50vw] max-w-none h-[80vh]"} - body={} + body={} /> diff --git a/apps/wr/app/ruseFcltMng/prfmncSttn/pbnstPrfmncSttn/page.tsx b/apps/wr/app/ruseFcltMng/prfmncSttn/pbnstPrfmncSttn/page.tsx index a50c08071..f689eb745 100644 --- a/apps/wr/app/ruseFcltMng/prfmncSttn/pbnstPrfmncSttn/page.tsx +++ b/apps/wr/app/ruseFcltMng/prfmncSttn/pbnstPrfmncSttn/page.tsx @@ -8,9 +8,11 @@ import { excelDownload, yearOptions } from "@/components/lib/func/commom"; import SearchInputText from "@/components/ui/searchInput/searchInput"; import { CHART_FORM, ContentsDiv, MainChart, ScrollArea, SearchBaseForm, SearchInput, SelectBox, Title, UiTable, type ChartConfig, type ReturnType } from "@common/business_components"; import ListDiv from "@common/business_components/ui/list_div"; -import { Button, Label } from "@common/components/ui"; +import { Button, Label, Table, TableBody, TableHeader } from "@common/components/ui"; import { useCallback, useEffect, useRef, useState } from "react"; import toast, { Toaster } from "react-hot-toast"; +import { headMakeprfmnc } from "../../prfmncReg/chkgdnReg/TableHead"; +import { InputTableBody } from "@/components/lib/func/TableData"; export default function PbnstPrfmncSttn() { const { @@ -34,9 +36,9 @@ export default function PbnstPrfmncSttn() { const [swtrmtFcltNm, setSwtrmtFcltNm] = useState(); const [pbnRuseNm, setPbnRuseNm] = useState(""); - const [searchYear, setSearchYear] = useState( - String(new Date().getFullYear()) - ); + const [searchYear, setSearchYear] = useState( + String(new Date().getFullYear()) + ); const [excelData, setExcelData] = useState([]); const excelClick = useCallback(() => { excelDownload( @@ -83,15 +85,20 @@ export default function PbnstPrfmncSttn() { type lineChartDataProps = { label: string, val: number, color: string }; const prfTableData = (prfList: PrfType[], year: string) => { - const inUsageData = structuredClone(initValInUsage); - const outUsageData = structuredClone(initValOutUsage); - - const inSubtotal = structuredClone(subTotalObj); - const outSubtotal = structuredClone(subTotalObj); + const inUsageData = structuredClone(initValInUsage).map(row => ({ + ...row, + 구분: "장내용수", + })); + const outUsageData = structuredClone(initValOutUsage).map(row => ({ + ...row, + 구분: "장외용수", + })); + const inSubtotal = { ...structuredClone(subTotalObj), 구분: "장내용수" }; + const outSubtotal = { ...structuredClone(subTotalObj), 구분: "장외용수" }; const prfFilter = prfList?.filter((e) => e.ym.slice(0, 4) === year) if (prfList && prfFilter.length > 0) { - + prfList?.forEach((prfItem) => { const month = Number(prfItem?.ym?.slice(4)); // "MM" const monthKey = `${month}월`; @@ -142,7 +149,7 @@ export default function PbnstPrfmncSttn() { setPrfData([]); } } - + const [chartId, setChartId] = useState(); useEffect(() => { // 누적 변수 초기화 @@ -198,8 +205,8 @@ export default function PbnstPrfmncSttn() { prfTableData([], year) rawData.forEach((item) => { // 실적 테이블 데이터 - if (chartId){ - if(item.prfList?.[0]?.ym.slice(0, 4) === year && item.prfList?.[0]?.pbnstRuseFcltFk === chartId){ + if (chartId) { + if (item.prfList?.[0]?.ym.slice(0, 4) === year && item.prfList?.[0]?.pbnstRuseFcltFk === chartId) { prfTableData(item.prfList, year); } item.prfList.filter((e) => e.ym.slice(0, 4) === year && e.pbnstRuseFcltFk === chartId).forEach((element) => { @@ -265,40 +272,40 @@ export default function PbnstPrfmncSttn() { }); // 도넛차트 데이터 - dnData.push({ label: "세척수", val: wshwtr, fill: "red" }); - dnData.push({ label: "냉각수", val: coolUswtr, fill: "orange" }); - dnData.push({ label: "청소수", val: cleanUswtr, fill: "yellow" }); - dnData.push({ label: "식수대", val: drwtUswtr, fill: "green" }); - dnData.push({ label: "희석용수", val: dltnUswtr, fill: "skyblue" }); - dnData.push({ label: "중수도", val: ithlRswtr, fill: "blue" }); - dnData.push({ label: "기타", val: ithlEtcUswtr, fill: "purple" }); - dnData.push({ label: "청소화장실용수", val: cleanToilUswtr, fill: "red" }); - dnData.push({ label: "세척살수용수", val: resn, fill: "orange" }); - dnData.push({ label: "조경용수", val: lndwtr, fill: "yellow" }); - dnData.push({ label: "친수용수", val: hydUswtr, fill: "green" }); - dnData.push({ label: "지하수충전", val: ugwtChr, fill: "skyblue" }); - dnData.push({ label: "농업용수", val: farmUswtr, fill: "blue" }); - dnData.push({ label: "하천등유지용수", val: rvrKeepUswtr, fill: "purple" }); - dnData.push({ label: "공업용수", val: indstUswtr, fill: "black" }); + dnData.push({ label: "세척수", val: wshwtr, fill: "#F7CFD8" }); + dnData.push({ label: "냉각수", val: coolUswtr, fill: "#F4F8D3" }); + dnData.push({ label: "청소수", val: cleanUswtr, fill: "#A6D6D6" }); + dnData.push({ label: "식수대", val: drwtUswtr, fill: "#8E7DBE" }); + dnData.push({ label: "희석용수", val: dltnUswtr, fill: "#FF8383" }); + dnData.push({ label: "중수도", val: ithlRswtr, fill: "#FFF574" }); + dnData.push({ label: "기타", val: ithlEtcUswtr, fill: "#A1D6CB" }); + dnData.push({ label: "청소화장실용수", val: cleanToilUswtr, fill: "#A19AD3" }); + dnData.push({ label: "세척살수용수", val: resn, fill: "#B1F0F7" }); + dnData.push({ label: "조경용수", val: lndwtr, fill: "#81BFDA" }); + dnData.push({ label: "친수용수", val: hydUswtr, fill: "#F5F0CD" }); + dnData.push({ label: "지하수충전", val: ugwtChr, fill: "#FADA7A" }); + dnData.push({ label: "농업용수", val: farmUswtr, fill: "#A6AEBF" }); + dnData.push({ label: "하천등유지용수", val: rvrKeepUswtr, fill: "#C5D3E8" }); + dnData.push({ label: "공업용수", val: indstUswtr, fill: "#D0E8C5" }); // 장내용수 도넛 - dnInData.push({ label: "세척수", val: wshwtr, fill: "yellow" }); - dnInData.push({ label: "냉각수", val: coolUswtr, fill: "green" }); - dnInData.push({ label: "청소수", val: cleanUswtr, fill: "yellow" }); - dnInData.push({ label: "식수대", val: drwtUswtr, fill: "green" }); - dnInData.push({ label: "희석용수", val: dltnUswtr, fill: "yellow" }); - dnInData.push({ label: "중수도", val: ithlRswtr, fill: "green" }); - dnInData.push({ label: "기타", val: ithlEtcUswtr, fill: "yellow" }); - dnInData.push({ label: "청소화장실용수", val: cleanToilUswtr, fill: "green" }); - dnInData.push({ label: "세척살수용수", val: resn, fill: "yellow" }); + dnInData.push({ label: "세척수", val: wshwtr, fill: "#F7CFD8" }); + dnInData.push({ label: "냉각수", val: coolUswtr, fill: "#F4F8D3" }); + dnInData.push({ label: "청소수", val: cleanUswtr, fill: "#A6D6D6" }); + dnInData.push({ label: "식수대", val: drwtUswtr, fill: "#8E7DBE" }); + dnInData.push({ label: "희석용수", val: dltnUswtr, fill: "#FF8383" }); + dnInData.push({ label: "중수도", val: ithlRswtr, fill: "#FFF574" }); + dnInData.push({ label: "기타", val: ithlEtcUswtr, fill: "#A1D6CB" }); + dnInData.push({ label: "청소화장실용수", val: cleanToilUswtr, fill: "#A19AD3" }); + dnInData.push({ label: "세척살수용수", val: resn, fill: "#B1F0F7" }); // 장외용수 도넛 - dnOutData.push({ label: "조경용수", val: lndwtr, fill: "yellow" }); - dnOutData.push({ label: "친수용수", val: hydUswtr, fill: "black" }); - dnOutData.push({ label: "지하수충전", val: ugwtChr, fill: "yellow" }); - dnOutData.push({ label: "농업용수", val: farmUswtr, fill: "black" }); - dnOutData.push({ label: "하천등유지용수", val: rvrKeepUswtr, fill: "yellow" }); - dnOutData.push({ label: "공업용수", val: indstUswtr, fill: "black" }); + dnOutData.push({ label: "조경용수", val: lndwtr, fill: "#81BFDA" }); + dnOutData.push({ label: "친수용수", val: hydUswtr, fill: "#F5F0CD" }); + dnOutData.push({ label: "지하수충전", val: ugwtChr, fill: "#FADA7A" }); + dnOutData.push({ label: "농업용수", val: farmUswtr, fill: "#A6AEBF" }); + dnOutData.push({ label: "하천등유지용수", val: rvrKeepUswtr, fill: "#C5D3E8" }); + dnOutData.push({ label: "공업용수", val: indstUswtr, fill: "#D0E8C5" }); // 라인차트 데이터 setInLineData({ @@ -466,7 +473,7 @@ export default function PbnstPrfmncSttn() { useEffect(() => { setbdstTable(prfmncRes); - if(prfmncRes?.[0]?.pbnstRuseFcltPk) setChartId(prfmncRes[0].pbnstRuseFcltPk) + if (prfmncRes?.[0]?.pbnstRuseFcltPk) setChartId(prfmncRes[0].pbnstRuseFcltPk) }, [prfmncRes]); @@ -619,12 +626,12 @@ export default function PbnstPrfmncSttn() {

하수재이용실적 현황

- -
+ {/* */} + {/*

{"하수처리수재이용 실적(m3) 등록결과"}

-
+
*/} -
+ + {/* + +
+

+ {"하수처리수재이용 실적(m3) 등록결과"} +

+
+ + + + {headMakeprfmnc(monthHeadList, [])} + + + {InputTableBody(prfData ?? [], monthHeadList, setPrfData)} + +
+
+
*/} + {/*
*/} From c50c2685c1df40e19b1b0f32534f72eb23fcfe71 Mon Sep 17 00:00:00 2001 From: jyha00 Date: Wed, 2 Jul 2025 14:30:18 +0900 Subject: [PATCH 042/108] =?UTF-8?q?=EC=9E=A0=EC=9E=AC=EC=88=98=EC=9A=94?= =?UTF-8?q?=EC=B2=98=20=EC=A1=B0=ED=9A=8C=20=EA=B8=B0=EB=8A=A5=20=EC=88=98?= =?UTF-8?q?=EC=A0=95,=20=EC=97=91=EC=85=80=20=EB=8B=A4=EC=9A=B4=EB=A1=9C?= =?UTF-8?q?=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wr/app/matMng/coMng/ldrmntCoSttn/page.tsx | 73 ++++++++++++------- .../app/store/matDmd/ldrmntCoSttn/excel.tsx | 26 ++----- .../app/store/matDmd/ldrmntCoSttn/index.tsx | 67 ++++++++++------- .../coReg/coRegExcel/coRegExcelModal.tsx | 24 +++--- 4 files changed, 104 insertions(+), 86 deletions(-) diff --git a/apps/wr/app/matMng/coMng/ldrmntCoSttn/page.tsx b/apps/wr/app/matMng/coMng/ldrmntCoSttn/page.tsx index c5daee29c..eb43b29bf 100644 --- a/apps/wr/app/matMng/coMng/ldrmntCoSttn/page.tsx +++ b/apps/wr/app/matMng/coMng/ldrmntCoSttn/page.tsx @@ -44,7 +44,7 @@ export default function LdrmntCoSttn() { const [searchTxt, setSearchTxt] = useState(); const ref = useRef({} as ReturnType); const [excelData, setExcelData] = useState(); - + const [isExcelDownload, setIsExcelDownload] = useState(false); const excelClick = useCallback(() => { excelDownload( @@ -57,14 +57,22 @@ export default function LdrmntCoSttn() { }, []); const excelDownloadClick = () => { - downloadExcel({ - data: excelData ?? [], - fileName: "잠재수요처 관리", - sheetNameArray: ["Sheet1"], - headerRange: "A1:F1" - }) + ldrmntSearch(1, "10000", "0", ""); + setIsExcelDownload(true); } + useEffect(() => { + if (isExcelDownload && excelData && excelData.length > 0) { + downloadExcel({ + data: excelData, + fileName: "잠재수요처 관리", + sheetNameArray: ["Sheet1"], + headerRange: "A1:F1" + }); + setIsExcelDownload(false); + } + }, [excelData, isExcelDownload]); + const [isExcelOpen, setIsExcelOpen] = useState<{ isModal: boolean; }>({ isModal: false }); @@ -73,14 +81,21 @@ export default function LdrmntCoSttn() { if (index !== undefined) { setDetailModal({ isOpen: true, data: ldrmntRes[index] }); } - } const getSelCity = (val: string | number) => { - const cityValue = String(val); // Ensure val is treated as a string - const data = getCity(cityValue, code.sggCd); - setSelectSgg(data); - setObjSearch({ city: cityValue, sgg: '' }); + const cityValue = String(val); + + if (cityValue === "0") { + setSelectSgg([]); + setObjSearch({ city: cityValue, sgg: "" }); + } else { + const data = getCity(cityValue, code.sggCd); + setSelectSgg(data); + + const firstSggVal = data.find(item => item.val === "0")?.val || (data[0]?.val ?? ""); + setObjSearch({ city: cityValue, sgg: firstSggVal }); + } }; const getSgg = (val: string | number) => { @@ -125,10 +140,9 @@ export default function LdrmntCoSttn() { setExcelData(data) }, [excelList]) - useEffect(() => { - let cityTxt = ''; - let sggTxt = ''; + let cityTxt = '' + let sggTxt = '' if (objSearch?.city.toString().length > 0) { cityTxt = code.cityCd.find((item) => item.val === objSearch?.city)?.text ?? ''; @@ -138,21 +152,23 @@ export default function LdrmntCoSttn() { sggTxt = code.sggCd.find((item) => item.val === objSearch?.sgg)?.text ?? ''; } - let cityNm = ""; + if (cityTxt === '전체' || cityTxt === '') { + cityTxt = '0' + } + + let cityNm = "" if (sggTxt !== "0") { - cityNm = cityTxt + " " + sggTxt; + cityNm = cityTxt + " " + sggTxt } else { - cityNm = cityTxt; + cityNm = cityTxt } - if (cityNm) { - if (cityTxt === '') cityNm = "0"; - ldrmntSearch(pageNation, cityNm, searchTxt || ""); + ldrmntSearch(pageNation, '20', cityNm, searchTxt || ""); } else { - ldrmntSearch(pageNation, "", searchTxt || ""); + ldrmntSearch(pageNation, '20', '0', searchTxt || ""); } - }, [pageNation, objSearch, code, searchTxt, ldrmntSearch]) + }, [pageNation]); return ( <> @@ -175,6 +191,7 @@ export default function LdrmntCoSttn() { label="시군구" selectClass={""} selectArray={selectSgg || []} + value={objSearch.sgg} // value props 추가 onSelectValue={getSgg} /> -
+
- + onClick={() => { + initHtdfl(searchTxt || ""); + }} + > + 조회 + +
@@ -126,41 +127,49 @@ export default function Home() { pageSize={100} total={10} headName={"건축물 목록"} - cellClick={(val, headId) => { cellClick(val || undefined, headId); } } + cellClick={(val, headId) => { + cellClick(val || undefined, headId); + }} onPageChange={setPageNation} - > - + >
- - - - + + + +
- } - bodyClass="w-[68vw] max-w-none h-[90vh]" - open={issNewReg} - onOpenChange={(open) => { - setIsNewReg(open); - }} - /> - - + } + bodyClass="w-[68vw] max-w-none h-[90vh]" + open={issNewReg} + onOpenChange={(open) => { + setIsNewReg(open); + }} + /> + + {/* } + body={ + + } onOpenChange={(open) => { setBdstChgProps({ ...bdstChgProps, isOpen: open }); - } - } - /> + }} + /> */} ); diff --git a/apps/wr/app/ruseFcltMng/fcltReg/rswtReg/page.tsx b/apps/wr/app/ruseFcltMng/fcltReg/rswtReg/page.tsx index d57c730ce..ea0dea649 100644 --- a/apps/wr/app/ruseFcltMng/fcltReg/rswtReg/page.tsx +++ b/apps/wr/app/ruseFcltMng/fcltReg/rswtReg/page.tsx @@ -151,7 +151,7 @@ export default function Home() {
} + body={} bodyClass="w-[68vw] max-w-none h-[90vh]" open={issNewReg} onOpenChange={(open) => { diff --git a/apps/wr/app/store/common/code/basicCode.tsx b/apps/wr/app/store/common/code/basicCode.tsx new file mode 100644 index 000000000..56c59b502 --- /dev/null +++ b/apps/wr/app/store/common/code/basicCode.tsx @@ -0,0 +1,204 @@ +import type { SelectDataType } from "@common/business_components"; + +export const yn: SelectDataType[] = [ + { val: "Y", text: "Y" }, + { val: "N", text: "N" }, +]; +export const bdstFilterYn: SelectDataType[] = [ + { val: "Y", text: "Y" }, + { val: "N", text: "N" }, + { val: "D", text: "확인불가" }, + { val: "F", text: "미가동" }, +]; +// 최우수(그린1)/우수(그린2), 우량(그린3), 일반(그린4), 확인불가 체크 +export const bdstGrntchClass: SelectDataType[] = [ + { val: "최우수(그린1)", text: "최우수(그린1)" }, + { val: "우수(그린2)", text: "우수(그린2)" }, + { val: "우량(그린3)", text: "우량(그린3)" }, + { val: "일반(그린4)", text: "일반(그린4)" }, + { val: "확인불가", text: "확인불가" }, +]; +export const bdstIncenYn: SelectDataType[] = [ + { val: "Y", text: "Y" }, + { val: "N", text: "N" }, + { val: "D", text: "확인불가" }, +]; +//처리장연계/자체+연계/자체처리 +export const erctUswtrPrcsMeth: SelectDataType[] = [ + { val: "처리장연계", text: "처리장연계" }, + { val: "자체+연계", text: "자체+연계" }, + { val: "자체처리", text: "자체처리" }, +]; + +export const rusePrcsMthdCd: SelectDataType[] = [ + { val: "물리학적처리", text: "물리학적처리" }, + { val: "생물학적처리", text: "생물학적처리" }, +]; + +export const ruseSeCd: SelectDataType[] = [ + { val: "신규", text: "신규" }, + { val: "증설", text: "증설" }, + { val: "관로", text: "관로" }, + { val: "감량", text: "감량" }, + { val: "개량", text: "개량" }, +]; + +export const ruseBizMthdCd: SelectDataType[] = [ + { val: "재정", text: "재정" }, + { val: "민간투자", text: "민간투자" }, + { val: "자체", text: "자체" }, +]; + +//공통 건축물 용도 +export const bdstUsgCd: SelectDataType[] = [ + { val: "-1", text: "확인불가" }, + { val: "1", text: "운동장" }, + { val: "2", text: "체육관" }, + { val: "3", text: "공공업무시설" }, + { val: "4", text: "공공기관의청사" }, + { val: "5", text: "아파트" }, + { val: "6", text: "연립주택" }, + { val: "7", text: "다세대주택" }, + { val: "8", text: "기숙사" }, + { val: "9", text: "초등학교" }, + { val: "10", text: "중학교" }, + { val: "11", text: "고등학교" }, + { val: "12", text: "전문대학" }, + { val: "13", text: "대학및대학교" }, + { val: "14", text: "골프장" }, + { val: "15", text: "대규모점포" }, + { val: "16", text: "기타" }, +]; + +export const rswtBdstType: SelectDataType[] = [ + { text: "숙박업", val: "17" }, + { text: "목욕장업", val: "18" }, + { text: "공장", val: "19" }, + { text: "발전시설", val: "20" }, + { text: "관광단지개발사업", val: "21" }, + { text: "도시개발사업", val: "22" }, + { text: "산업단지개발사업", val: "23" }, + { text: "택지개발사업", val: "24" }, + { text: "대규모점포(대형마트)", val: "25" }, + { text: "대규모점포(전문점)", val: "26" }, + { text: "대규모점포(백화점)", val: "27" }, + { text: "대규모점포(쇼핑센터)", val: "28" }, + { text: "대규모점포(복합쇼핑몰)", val: "29" }, + { text: "물류시설", val: "30" }, + { text: "운수시설(여객자동차터미널)", val: "31" }, + { text: "운수시설(철도시설)", val: "32" }, + { text: "운수시설(공항시설)", val: "33" }, + { text: "운수시설(항만시설)", val: "34" }, + { text: "업무시설(공공업무시설)", val: "35" }, + { text: "업무시설(금융업소)", val: "36" }, + { text: "업무시설(사무소)", val: "37" }, + { text: "업무시설(결혼상담소 등 소개업소)", val: "38" }, + { text: "업무시설(출판사)", val: "39" }, + { text: "업무시설(신문사)", val: "40" }, + { text: "업무시설(오피스텔)", val: "41" }, + { text: "교정시설", val: "42" }, + { text: "방송국", val: "43" }, + { text: "전신전화국", val: "44" }, + { text: "기타", val: "45" }, +]; + +export const orgwtKndCd = [ + { text: "오수(변기 오수 제외)", val: "1" }, + { text: "오수", val: "2" }, + { text: "폐수", val: "3" }, + { text: "오수 및 폐수", val: "4" }, + { text: "기타", val: "5" }, +]; + +export const chBuCd = [ + { val: "증축", text: "증축" }, + { val: "개축", text: "개축" }, + { val: "재축", text: "재축" }, +]; + +export const dmdUszCd = [ + { val: "조경용수", text: "조경용수" }, + { val: "청소화장실용수", text: "청소화장실용수" }, + { val: "농업용수", text: "농업용수" }, + { val: "공업용수", text: "공업용수" }, + { val: "친수용수", text: "친수용수" }, + { val: "기타", text: "기타" }, +]; + +export const ruseMngMtd = [ + { val: "자체운영", text: "자체운영" }, + { val: "공기업", text: "공기업" }, + { val: "민간(대행)", text: "민간(대행)" }, +]; + +export const pplQulity = [ + { val: "도복장강관", text: "도복장강관" }, + { val: "PE관", text: "PE관" }, + { val: "스테인레스관", text: "스테인레스관" }, + { val: "덕타일주철관", text: "덕타일주철관" }, + { val: "유리섬유복합관", text: "유리섬유복합관" }, + { val: "기타", text: "기타" }, +]; +export const pplLenght = [ + { val: "D100", text: "D100" }, + { val: "D200", text: "D200" }, + { val: "D250", text: "D250" }, + { val: "D300", text: "D300" }, + { val: "D350", text: "D350" }, + { val: "D400", text: "D400" }, + { val: "D450", text: "D450" }, + { val: "D500", text: "D500" }, + { val: "D700", text: "D700" }, + { val: "D900", text: "D900" }, + { val: "D1000​", text: "D1000​" }, +]; + +export const rainFcltCd = [ + { val: "지붕", text: "지붕" }, + { val: "건물옥상", text: "건물옥상" }, + { val: "골프장 연못", text: "골프장 연못" }, + { val: "운동장 바닥", text: "운동장 바닥" }, + { val: "운동장 지붕", text: "운동장 지붕" }, + { val: "주차장", text: "주차장" }, + { val: "태양광패널", text: "태양광패널" }, + { val: "연못", text: "연못" }, + { val: "기타", text: "기타" }, + { val: "확인불가", text: "확인불가" }, +]; + +//물리화학적처리(스크린, 응집, 침전)/생물학적처리(활성슬러지법 등)/소독(염소, UV 등)/기타 +export const rswtPrscCdMthdCd = [ + { + val: "물리화학적처리(스크린,응집,침전)", + text: "물리화학적처리(스크린,응집,침전)", + }, + { + val: "생물학적처리(활성슬러지법 등)", + text: "생물학적처리(활성슬러지법 등)", + }, + { val: "소독(염소, UV 등)", text: "소독(염소, UV 등)" }, + { val: "기타", text: "기타" }, +]; + +export const htdPowerMthdCd= [ + { + val: "석탄화력발전", + text: "석탄화력발전", + }, +]; + +//(공업용수/양식장/농업용수) +export const htdUtztnUsgCd= [ + { + val: "공업용수", + text: "공업용수", + }, + { + val: "양식장", + text: "양식장", + }, + { + val: "농업용수", + text: "농업용수", + }, +]; diff --git a/apps/wr/app/store/common/code/index.ts b/apps/wr/app/store/common/code/index.ts index 17ea1f2e7..b3cf48603 100644 --- a/apps/wr/app/store/common/code/index.ts +++ b/apps/wr/app/store/common/code/index.ts @@ -1,236 +1,13 @@ import { getFetch } from "@/components/lib/api"; import type { EnvPartSelectDataType, SelectDataType } from "@common/business_components/ui"; - - - +import { bdstFilterYn, bdstGrntchClass, bdstIncenYn, bdstUsgCd, chBuCd, dmdUszCd, erctUswtrPrcsMeth, htdPowerMthdCd, htdUtztnUsgCd, orgwtKndCd, pplLenght, pplQulity, rainFcltCd, rswtPrscCdMthdCd, ruseBizMthdCd, ruseMngMtd, rusePrcsMthdCd, ruseSeCd, yn } from "./basicCode"; export const btnClass = "p-4 m-2 text-white rounded-sm text-lg font-medium " -const yn: SelectDataType[] = [ - { val: 'Y', text: 'Y' }, - { val: 'N', text: 'N' }, -] -const bdstFilterYn: SelectDataType[] = [ - { val: 'Y', text: 'Y' }, - { val: 'N', text: 'N' }, - { val: 'D', text: '확인불가' }, - { val: 'F', text: '미가동' }, -] -// 최우수(그린1)/우수(그린2), 우량(그린3), 일반(그린4), 확인불가 체크 -const bdstGrntchClass: SelectDataType[] = [ - { val: '최우수(그린1)', text: '최우수(그린1)' }, - { val: '우수(그린2)', text: '우수(그린2)' }, - { val: '우량(그린3)', text: '우량(그린3)' }, - { val: '일반(그린4)', text: '일반(그린4)' }, - { val: '확인불가', text: '확인불가' }, - -] -const bdstIncenYn: SelectDataType[] = [ - { val: 'Y', text: 'Y' }, - { val: 'N', text: 'N' }, - { val: 'D', text: '확인불가' }, -] -//처리장연계/자체+연계/자체처리 -const erctUswtrPrcsMeth : SelectDataType[]= [ - { val: '처리장연계', text: '처리장연계' }, - { val: '자체+연계', text: '자체+연계' }, - { val: '자체처리', text: '자체처리' }, -] - -const rusePrcsMthdCd : SelectDataType[] = [ - { val: '물리학적처리', text: '물리학적처리' }, - { val: '생물학적처리', text: '생물학적처리' }, -] - -const ruseSeCd : SelectDataType[] = [ - { val: '신규', text: '신규' }, - { val: '증설', text: '증설' }, - { val: '관로', text: '관로' }, - { val: '감량', text: '감량' }, - { val: '개량', text: '개량' }, -] - -const ruseBizMthdCd: SelectDataType[] = [ - { val: '재정', text: '재정' }, - { val: '민간투자', text: '민간투자' }, - { val: '자체', text: '자체' }, -] - -//공통 건축물 용도 -const bdstUsgCd: SelectDataType[] = [ - { val: '-1', text: "확인불가" }, - { val: '1', text: "운동장" }, - { val: '2', text: "체육관" }, - { val: '3', text: "공공업무시설" }, - { val: '4', text: "공공기관의청사" }, - { val: '5', text: "아파트" }, - { val: '6', text: "연립주택" }, - { val: '7', text: "다세대주택" }, - { val: '8', text: "기숙사" }, - { val: '9', text: "초등학교" }, - { val: '10', text: "중학교" }, - { val: '11', text: "고등학교" }, - { val: '12', text: "전문대학" }, - { val: '13', text: "대학및대학교" }, - { val: '14', text: "골프장" }, - { val: '15', text: "대규모점포" }, - { val: '16', text: "기타" }, -]; -//빗물 집수면 종류 용도 -// const rainFcltCd = [ -// { val: "1", text: "지붕" }, -// { val: "2", text: "건물옥상" }, -// { val: "3", text: "골프장 연못" }, -// { val: "4", text: "운동장 바닥" }, -// { val: "5", text: "운동장 지붕" }, -// { val: "6", text: "주차장" }, -// { val: "7", text: "태양광패널" }, -// { val: "8", text: "연못" }, -// { val: "9", text: "기타" }, -// { val: "10", text: "확인불가" }, -// ]; -// 중수도 필수 원수종류 -const rswtBdstType: SelectDataType[] = [ - { text: "숙박업", val: "17" }, - { text: "목욕장업", val: "18" }, - { text: "공장", val: "19" }, - { text: "발전시설", val: "20" }, - { text: "관광단지개발사업", val: "21" }, - { text: "도시개발사업", val: "22" }, - { text: "산업단지개발사업", val: "23" }, - { text: "택지개발사업", val: "24" }, - { text: "대규모점포(대형마트)", val: "25" }, - { text: "대규모점포(전문점)", val: "26" }, - { text: "대규모점포(백화점)", val: "27" }, - { text: "대규모점포(쇼핑센터)", val: "28" }, - { text: "대규모점포(복합쇼핑몰)", val: "29" }, - { text: "물류시설", val: "30" }, - { text: "운수시설(여객자동차터미널)", val: "31" }, - { text: "운수시설(철도시설)", val: "32" }, - { text: "운수시설(공항시설)", val: "33" }, - { text: "운수시설(항만시설)", val: "34" }, - { text: "업무시설(공공업무시설)", val: "35" }, - { text: "업무시설(금융업소)", val: "36" }, - { text: "업무시설(사무소)", val: "37" }, - { text: "업무시설(결혼상담소 등 소개업소)", val: "38" }, - { text: "업무시설(출판사)", val: "39" }, - { text: "업무시설(신문사)", val: "40" }, - { text: "업무시설(오피스텔)", val: "41" }, - { text: "교정시설", val: "42" }, - { text: "방송국", val: "43" }, - { text: "전신전화국", val: "44" }, - { text: "기타", val: "45" } -]; - -const orgwtKndCd = [ - { text: "오수(변기 오수 제외)", val: "1" }, - { text: "오수", val: "2" }, - { text: "폐수", val: "3" }, - { text: "오수 및 폐수", val: "4" }, - { text: "기타", val: "5" }, -]; - -const chBuCd = [ - { val: "증축", text: "증축" }, - { val: "개축", text: "개축" }, - { val: "재축", text: "재축" }, -] - - - -const dmdUszCd = [ - { val: "조경용수", text: "조경용수" }, - { val: "청소화장실용수", text: "청소화장실용수" }, - { val: "농업용수", text: "농업용수" }, - { val: "공업용수", text: "공업용수" }, - { val: "친수용수", text: "친수용수" }, - { val: "기타", text: "기타" }, - -] - -const ruseMngMtd = [ - { val: "자체운영", text: "자체운영" }, - { val: "공기업", text: "공기업" }, - { val: "민간(대행)", text: "민간(대행)" }, -] - - -const pplQulity = [ - { val: "도복장강관", text: "도복장강관" }, - { val: "PE관", text: "PE관" }, - { val: "스테인레스관", text: "스테인레스관" }, - { val: "덕타일주철관", text: "덕타일주철관" }, - { val: "유리섬유복합관", text: "유리섬유복합관" }, - { val: "기타", text: "기타" }, - -] -const pplLenght = [ - { val: "D100", text: "D100" }, - { val: "D200", text: "D200" }, - { val: "D250", text: "D250" }, - { val: "D300", text: "D300" }, - { val: "D350", text: "D350" }, - { val: "D400", text: "D400" }, - { val: "D450", text: "D450" }, - { val: "D500", text: "D500" }, - { val: "D700", text: "D700" }, - { val: "D900", text: "D900" }, - { val: "D1000​", text: "D1000​" }, - -] export const buildingType = { RST:"RST", RNB:"RNB", HTB:"HTB" } - -const rainFcltCd = [ - { val: "지붕", text: "지붕" }, - { val: "건물옥상", text: "건물옥상" }, - { val: "골프장 연못", text: "골프장 연못" }, - { val: "운동장 바닥", text: "운동장 바닥" }, - { val: "운동장 지붕", text: "운동장 지붕" }, - { val: "주차장", text: "주차장" }, - { val: "태양광패널", text: "태양광패널" }, - { val: "연못", text: "연못" }, - { val: "기타", text: "기타" }, - { val: "확인불가", text: "확인불가" }, -]; - -export type comCodeState = { - code: { - isInit: boolean; - chBuCd: SelectDataType[] - rainFcltCd: SelectDataType[]; - bdstUsgCd: SelectDataType[]; - cityCd: SelectDataType[]; - sggCd: SelectDataType[]; - rvbsnCd: SelectDataType[]; - mdmbsnCd: SelectDataType[]; - lgbsnCd: SelectDataType[]; - lclgvrnmntCd: EnvPartSelectDataType[]; - orgwtKndCd: SelectDataType[]; - yn: SelectDataType[]; - pplQulity: SelectDataType[]; - pplLenght: SelectDataType[]; - dmdUszCd:SelectDataType[] - buildingType: { - RST:string, - RNB:string, - HTB:string - } - ruseMngMtd:SelectDataType[] - ruseSeCd: SelectDataType[]; - ruseBizMthdCd: SelectDataType[]; - rusePrcsMthdCd: SelectDataType[]; - erctUswtrPrcsMeth: SelectDataType[]; - bdstFilterYn: SelectDataType[]; - bdstIncenYn: SelectDataType[]; - bdstGrntchClass:SelectDataType[]; - }; -}; - -export type comCodeType = comCodeState & comCodeActions; - export const getSggCd = async (set: any) => { const sgg = await getFetch("common", "/addr/sgg"); /* const sggCd = sgg.map((item: any) => ({ @@ -356,6 +133,47 @@ export const getEnvPartCd = async (set: any) => { + +export type comCodeState = { + code: { + isInit: boolean; + chBuCd: SelectDataType[] + rainFcltCd: SelectDataType[]; + bdstUsgCd: SelectDataType[]; + cityCd: SelectDataType[]; + sggCd: SelectDataType[]; + rvbsnCd: SelectDataType[]; + mdmbsnCd: SelectDataType[]; + lgbsnCd: SelectDataType[]; + lclgvrnmntCd: EnvPartSelectDataType[]; + orgwtKndCd: SelectDataType[]; + yn: SelectDataType[]; + pplQulity: SelectDataType[]; + pplLenght: SelectDataType[]; + dmdUszCd:SelectDataType[] + buildingType: { + RST:string, + RNB:string, + HTB:string + } + ruseMngMtd:SelectDataType[] + ruseSeCd: SelectDataType[]; + ruseBizMthdCd: SelectDataType[]; + rusePrcsMthdCd: SelectDataType[]; + erctUswtrPrcsMeth: SelectDataType[]; + bdstFilterYn: SelectDataType[]; + bdstIncenYn: SelectDataType[]; + bdstGrntchClass:SelectDataType[]; + rswtPrscCdMthdCd:SelectDataType[]; + htdPowerMthdCd:SelectDataType[] + htdUtztnUsgCd:SelectDataType[] + }; +}; + +export type comCodeType = comCodeState & comCodeActions; + + + export const comCodeStateExport: comCodeState = { code: { isInit: false, @@ -381,7 +199,10 @@ export const comCodeStateExport: comCodeState = { erctUswtrPrcsMeth:erctUswtrPrcsMeth, bdstFilterYn:bdstFilterYn, bdstIncenYn:bdstIncenYn, - bdstGrntchClass:bdstGrntchClass + bdstGrntchClass:bdstGrntchClass, + rswtPrscCdMthdCd:rswtPrscCdMthdCd, + htdPowerMthdCd:htdPowerMthdCd, + htdUtztnUsgCd:htdUtztnUsgCd, }, }; @@ -389,9 +210,6 @@ export type comCodeActions = { codeInit: () => void; }; - - - export const codeActionExport: (set: any) => comCodeActions = (set: any) => { return { codeInit: async () => { diff --git a/apps/wr/app/store/ruseFcltMng/fcltReg/bdstReg/index.tsx b/apps/wr/app/store/ruseFcltMng/fcltReg/bdstReg/index.tsx index 931cd1305..418f4b45f 100644 --- a/apps/wr/app/store/ruseFcltMng/fcltReg/bdstReg/index.tsx +++ b/apps/wr/app/store/ruseFcltMng/fcltReg/bdstReg/index.tsx @@ -79,8 +79,9 @@ const isRswtBdst = (data: any): data is customRswtBdst =>{ data.reuseBuilding=chData; } - if(data?.chgBuilding.length>0){ - const chRgtr = data.chgBuilding.map((item) =>({...item, rgtr:TEST_UUID, mdfr: item.mdfr ?? null })) + + if('chgBuilding' in data && Array.isArray(data.chgBuilding) && data.chgBuilding.length > 0){ + const chRgtr = data.chgBuilding.map((item) =>({...item, rgtr:TEST_UUID, mdfr: item.mdfr ?? null })) data.chgBuilding =chRgtr; } return data; @@ -127,7 +128,7 @@ const isRswtBdst = (data: any): data is customRswtBdst =>{ data.reuseBuilding=chData; } - if(data?.chgBuilding.length>0){ + if('chgBuilding' in data && Array.isArray(data.chgBuilding) && data.chgBuilding.length > 0){ const chMdfr = data.chgBuilding.map((item) =>({...item, mdfr:TEST_UUID, archChgArea: item.archChgArea ?? '' })) data.chgBuilding =chMdfr; } diff --git a/apps/wr/app/store/ruseFcltMng/fcltReg/htdflReg/headList.tsx b/apps/wr/app/store/ruseFcltMng/fcltReg/htdflReg/headList.tsx new file mode 100644 index 000000000..da1fa6f09 --- /dev/null +++ b/apps/wr/app/store/ruseFcltMng/fcltReg/htdflReg/headList.tsx @@ -0,0 +1,10 @@ +import type { TableUpperProps } from "@/app/store/common/type/header"; + +export const htdTableHeadList: TableUpperProps[] = [ + { id: "cityText", title: "시도" }, + { id: "sggText", title: "시군구" }, + { id: "bdstNm", title: "발전소명" }, + { id: "daddr", title: "위치" }, + { id: "frstArchPrmsnDt", title: "준공년도" }, + { id: "archArea", title: "건축면적" }, +]; diff --git a/apps/wr/app/store/ruseFcltMng/fcltReg/htdflReg/index.tsx b/apps/wr/app/store/ruseFcltMng/fcltReg/htdflReg/index.tsx index 314d5762b..f2875e232 100644 --- a/apps/wr/app/store/ruseFcltMng/fcltReg/htdflReg/index.tsx +++ b/apps/wr/app/store/ruseFcltMng/fcltReg/htdflReg/index.tsx @@ -6,9 +6,12 @@ import { makeMdfr, makeRstr } from "../bdstReg"; import { baseReuseBuildingSchema, chgBuildingSchema, + htdflReuseBuildingSchema, validationMessages, } from "../rainReg/type"; // Import baseReuseBuildingSchema, chgBuildingSchema, validationMessages import type { TableUpperProps } from "@/app/store/common/type/header"; +import { htdTableHeadList } from "./headList"; +import toast from "react-hot-toast"; const hdtflObjSchema = z.object({ htdflFcltPk: z.number().nullish(), @@ -21,8 +24,8 @@ const hdtflObjSchema = z.object({ .positive({ message: validationMessages.required("시설 용량") }), plwtYn: z.enum(["Y", "N"]).default("Y"), orgwtWtritSpt: z - .number() - .positive({ message: validationMessages.required("원수 취수량") }), + .string() + .min(1, { message: validationMessages.required("원수 취수 지점") }), orgwtOcrnProcs: z .string() .min(1, { message: validationMessages.required("원수 발생 공정") }), @@ -53,7 +56,9 @@ const hdtflObjSchema = z.object({ ph: z.number().default(0), // Measurements, 0 is fine as default clIonQnt: z.number().default(0), // Measurements, 0 is fine as default cdtnv: z.number().default(0), // Measurements, 0 is fine as default - emsnExitTp: z.number().default(0), // Measurements, 0 is fine as default + emsnExitTp: z.number() + .max(100, { message: "온도는 100이상이 될 수 없습니다." }) + .default(0), // Changed max to 100 based on message, // Measurements, 0 is fine as default rgtr: z.string().nullish().default(null), mdfr: z.string().nullish().default(null), }); @@ -77,16 +82,15 @@ export const hdtflZod = z.object({ mdfr: z.string().nullish().default(null), reuseBuilding: z .array( - baseReuseBuildingSchema.extend({ + htdflReuseBuildingSchema.extend({ htdflObj: hdtflObjSchema, }) ) .default([]), - chgBuilding: z.array(chgBuildingSchema).default([]), // Use imported chgBuildingSchema + //온배수는 건축물은 등록 하는것이 다름 + // chgBuilding: z.array(chgBuildingSchema).nullish(), // Use imported chgBuildingSchema }); - export type customHtdflBdst = z.infer; - export type htdflFacState = { htdfl: { isInit: boolean; @@ -104,18 +108,16 @@ type htdflFacSActions = { const htdflRes: customHtdflBdst[] = []; export type htdflFacType = htdflFacState & htdflFacSActions; - export const htdflFacStateExport: htdflFacState = { htdfl: { isInit: false, - upHeadList: upHeadList, + upHeadList: htdTableHeadList, htdflRes: htdflRes, }, }; const initFetch = async (code: string | number) => { - const rswtRes = await getFetch("wr", `/facilityList/1/20/${code}`); - + const rswtRes = await getFetch("wr", `/drain/facilityList/1/20/${code}`); return rswtRes; }; export const htdflFacActionsExport: (set: any) => htdflFacSActions = ( @@ -128,8 +130,6 @@ export const htdflFacActionsExport: (set: any) => htdflFacSActions = ( "wr", `/drain/facilityList/${pageNation}/20/${code}` ); - console.log(pageNation); - // 여기서 상태 업데이트 return set((state: htdflFacState) => ({ ...state, htdfl: { @@ -142,7 +142,6 @@ export const htdflFacActionsExport: (set: any) => htdflFacSActions = ( console.log(e); } }, - initHtdfl: async (code: string) => { try { const htdflRes = await initFetch(code); @@ -162,10 +161,15 @@ export const htdflFacActionsExport: (set: any) => htdflFacSActions = ( }, htdflBdstUpdate: async (data: customHtdflBdst, code: string | number) => { try { + + console.log('data ', data) + const chData = await makeMdfr(data); - const res = await putFectch("/drain/facility", data); + console.log('chData ', chData) + + const res = await putFectch("/drain/facility", chData); if (res.ok) { - alert(" 수정이 완료되었습니다."); + toast.success("온배수 건축물 수정이 완료되었습니다."); } const htdflRes = await initFetch(code); @@ -178,40 +182,18 @@ export const htdflFacActionsExport: (set: any) => htdflFacSActions = ( }, })); } catch (errorUpdate) { - alert( - "업데이트 오류입니다. " + - (errorUpdate instanceof Error ? errorUpdate.message : "") - ); + toast.error("온배수 건축물 수정 오류입니다.."); } }, htdflBdstReg: async (data: customHtdflBdst, code: string | number) => { try { - const chData = await makeRstr(data); - const res = await postFectch("/drain/facility", data); - if (res.ok) { - if (data.chgBuilding.length > 0) { - await res - .json() - .then((resData: any) => { - const chPK = resData?.data?.bdstInfoPk; - - const makeCh = resData.data.chgBuilding.map( - (item: chgBuilding) => ({ - ...item, - bdstInfoFk: chPK, - }) - ); - const chch = postFectch("/reuseFc/chgBuilding", makeCh); - return chch; - }) - .then((chch: any) => { - if (chch.ok) { - alert("등록이 완료되었습니다."); - } - }); - } else { - alert("등록이 완료되었습니다."); - } + // data는 단일 객체이므로 .map을 사용할 수 없습니다. + // 등록 시에는 건축물 변경 이력이 없으므로 chgBuilding을 빈 배열로 설정하고, + // API가 배열을 기대하므로 단일 객체를 배열로 감싸줍니다. + const chData = makeRstr(data); + const res = await postFectch("/drain/facility", chData); + if (res.status ===200) { + toast.success("온배수 건축물 등록이 완료되었습니다."); } const htdflRes = await initFetch(code); set((state: htdflFacState) => ({ @@ -223,8 +205,9 @@ export const htdflFacActionsExport: (set: any) => htdflFacSActions = ( }, })); } catch (errorReg) { - alert("등록 오류입니다."); + toast.error("온배수 건축물 등록 오류입니다.."); + } - }, + } }; }; diff --git a/apps/wr/app/store/ruseFcltMng/fcltReg/rainReg/type.tsx b/apps/wr/app/store/ruseFcltMng/fcltReg/rainReg/type.tsx index 59d6633a2..2d17b4601 100644 --- a/apps/wr/app/store/ruseFcltMng/fcltReg/rainReg/type.tsx +++ b/apps/wr/app/store/ruseFcltMng/fcltReg/rainReg/type.tsx @@ -25,8 +25,7 @@ export const baseReuseBuildingSchema = z.object({ { message: validationMessages.yearFormat, } - ) - .nullish(), + ), grntchBdstCertNo: z.string().default(""), grntchBdstCertGrd: z.string().default(""), crgRdctYn: z.string().default("N").nullish(), /// 안쓰는 값 @@ -52,3 +51,40 @@ export const chgBuildingSchema = z.object({ rgtr:z.string().nullish(), mdfr:z.string().nullish(), }); + + + +export const htdflReuseBuildingSchema = z.object({ + bdstRuseFcltPk: z.number().nullish(), + bdstInfoFk: z.number().nullish(), + instlCmptnDt: z.coerce.date().default(() => new Date()), + instlCst: z.number().nullish(), + operMngCd: z.string().optional().nullish(), + anlOperCst: z.number().default(0), + instlCstSprtYn: z.string().default("Y"), + instlCstSprtAmt: z.number().nullish(), + grntchBdstCertYn: z.string().default("Y"), + grntchBdstCertYr: z + .string() + .max(4, { message: validationMessages.yearFormat }) + .refine( + (year) => year.length === 0 || /^\d{4}$/.test(year), + { + message: validationMessages.yearFormat, + } + ) + .nullish(), + grntchBdstCertNo: z.string().default(""), + grntchBdstCertGrd: z.string().default(""), + crgRdctYn: z.string().default("N").nullish(), /// 안쓰는 값 + crgRdctBss: z.string().default("").nullish(), /// 안쓰는 값 + crgRdctAmt: z.number().default(0), // 안쓰는 값 + ruseFcltFlg: z.string().default(""), + bdstUsgCd: z.number().default(0).nullish(), + lawTrgtYn: z.string().default("Y").nullish(), + rgtr: z.string().nullish(), + mdfr: z.string().nullish(), +}); + + + diff --git a/apps/wr/components/ui/form/fcltReg/bdstReg/bdstReg.tsx b/apps/wr/components/ui/form/fcltReg/bdstReg/bdstReg.tsx index c1bf7e2ca..8ae9ad4dd 100644 --- a/apps/wr/components/ui/form/fcltReg/bdstReg/bdstReg.tsx +++ b/apps/wr/components/ui/form/fcltReg/bdstReg/bdstReg.tsx @@ -16,10 +16,10 @@ import { type SelectDataType, } from "@common/business_components"; import { Button, Checkbox, Input } from "@common/components"; -import { useEffect, useState } from "react"; +import { useEffect, useState } from "react"; import { useFieldArray } from "react-hook-form"; -export default function BdstReg({ form }: { form: any }) { +export default function BdstReg({ form, type }: { form: any; type?: string }) { const { code } = useWrStore((state) => { return state; }); @@ -37,16 +37,15 @@ export default function BdstReg({ form }: { form: any }) { }); }; const removeSelected = () => { - const idxsToRemove = fields - .map((item, idx) => selectedIds.includes(item.id) ? idx : -1) - .filter(index => index > -1) - .sort((prevIndex, nextIndex) => nextIndex - prevIndex); + const idxsToRemove = fields + .map((item, idx) => (selectedIds.includes(item.id) ? idx : -1)) + .filter((index) => index > -1) + .sort((prevIndex, nextIndex) => nextIndex - prevIndex); remove(idxsToRemove); setSelectedIds([]); }; - const [selectSgg, setSelectSgg] = useState(); const [objSearch, setObjSearch] = useState({ sgg: "", @@ -83,18 +82,14 @@ export default function BdstReg({ form }: { form: any }) { const [selectedIds, setSelectedIds] = useState([]); const toggleSelect = (id: string) => { - setSelectedIds(prev => - prev.includes(id) - ? prev.filter(x => x !== id) - : [...prev, id] + setSelectedIds((prev) => + prev.includes(id) ? prev.filter((x) => x !== id) : [...prev, id] ); }; return ( <> -

- 건축물 기본 정보 -

+

건축물 기본 정보

-
- ( - - { - field.onChange(Number(val)); - }} - value={field.value??code.bdstUsgCd[0]?.val} - /> - - )} - /> -
+
field.onChange(date), }} - selectClass={'w-full'} + selectClass={"w-full"} > )} />
-
- ( - - { - handleCommaNumberChange(e, field.onChange); - }} - value={formatCommaNumber(field.value)} - /> - - )} - /> -
-
- ( - - { - field.onChange(val); - }} - value={field.value??code.yn[0]?.val } - /> - - )} - /> -
-

- 중축, 개축, 재축 -

-
- - -
- {fields.map((field, index) => ( -
-
- toggleSelect(field.id)} - /> -
-
- ( - - f.onChange(val)} - value={f.value ?? code.chBuCd[0]?.val} className={""} label={""} /> - - )} - /> -
-
- ( - - f.onChange(date), - }} - selectClass="w-full" + {type !== "HTB" ? ( + <> +
+
+ ( + + { + field.onChange(Number(val)); + }} + value={field.value ?? code.bdstUsgCd[0]?.val} + /> + + )} + /> +
+ +
+ ( + + { + handleCommaNumberChange(e, field.onChange); + }} + value={formatCommaNumber(field.value)} + /> + + )} + /> +
+
+ ( + + { + field.onChange(val); + }} + value={field.value ?? code.yn[0]?.val} + /> + + )} + /> +
+
+

중축, 개축, 재축

+
+ + +
+ {fields.map((field, index) => ( +
+
+ toggleSelect(field.id)} /> - - )} - /> -
-
- ( - - f.onChange} - value={f.value} - className={selectClass} +
+
+ ( + + f.onChange(val)} + value={f.value ?? code.chBuCd[0]?.val} + className={""} + label={""} + /> + + )} /> - - )} - /> -
-
- ))} -
+
+
+ ( + + f.onChange(date), + }} + selectClass="w-full" + /> + + )} + /> +
+
+ ( + + f.onChange} + value={f.value} + className={selectClass} + /> + + )} + /> +
+
+ ))} +
+ + ) : ( + <> + )} ); } diff --git a/apps/wr/components/ui/form/fcltReg/htdflReg/htdflRegForm.tsx b/apps/wr/components/ui/form/fcltReg/htdflReg/htdflRegForm.tsx index 70e62eb7f..5547f057b 100644 --- a/apps/wr/components/ui/form/fcltReg/htdflReg/htdflRegForm.tsx +++ b/apps/wr/components/ui/form/fcltReg/htdflReg/htdflRegForm.tsx @@ -23,15 +23,18 @@ import { handleCommaNumberChange, } from "@/components/lib/func/commom"; import { formH1Class } from "@/components/lib/class/comClass"; +import toast from "react-hot-toast"; export default function HtdflRegForm({ htdflForm, isModal, - readonly = false + readonly = false, + setIsModal, }: { htdflForm: customHtdflBdst; isModal: boolean; readonly?: boolean; + setIsModal?: (modal: boolean) => void; }) { const { code, htdflBdstUpdate, htdflBdstReg } = useWrStore((state) => state); @@ -41,16 +44,26 @@ export default function HtdflRegForm({ resolver: zodResolver(hdtflZod), defaultValues: defaultValues, }); + const [regCtpvCd, regSggcd] = form.watch(["ctpvCd", "sggCd"]); const onSubmit = (values: z.infer) => { if (isModal) { - htdflBdstReg(values, '41'); + const data = regCtpvCd ? regCtpvCd : regSggcd ? regSggcd : "noCd"; + + if (data === "noCd") { + toast.error("시도/시군구 코드를 선택해야합니다."); + return; + } + htdflBdstReg(values,data); + if (setIsModal) setIsModal(false); } else { - htdflBdstUpdate(values, '41'); + + console.log(' isModal ' ,isModal) + console.log(' values ' ,values) + htdflBdstUpdate(values, "41"); } }; - const cleanHtdflData = (raw: any): z.infer => { return { ...raw, @@ -80,9 +93,31 @@ export default function HtdflRegForm({ console.log("form errors:", form.formState.errors); }, [form.formState.errors]); + // form.reset은 비동기로 동작할 수 있어, useEffect 하나로 관리하여 의도치 않은 초기화를 방지합니다. useEffect(() => { - if (hdtflZod) form.reset(cleanHtdflData(htdflForm)); - }, [htdflForm]); + // Case 1: 수정 또는 '복사하여 등록' 등 데이터가 주입되는 경우 + if (htdflForm && Object.keys(htdflForm).length > 0) { + form.reset(cleanHtdflData(htdflForm)); + } + // Case 2: 데이터가 없는 순수 신규 등록 모드인 경우 + if(isModal) { + const firstYnValue = code.yn?.[0]?.val; + const firstHtdPowerMthdCd = code.htdPowerMthdCd?.[0]?.val; + const firstHtdUtztnUsgCd = code.htdUtztnUsgCd?.[0]?.val; + + if (firstHtdPowerMthdCd) { + form.setValue('reuseBuilding.0.htdflObj.etgtType', firstHtdPowerMthdCd, { shouldDirty: true }); + } + if (firstYnValue) { + form.setValue('reuseBuilding.0.htdflObj.plwtYn', firstYnValue as "Y" | "N", { shouldDirty: true }); + form.setValue('reuseBuilding.0.htdflObj.prcsProcsYn', firstYnValue as "Y" | "N", { shouldDirty: true }); + form.setValue('reuseBuilding.0.htdflObj.fmYn', firstYnValue as "Y" | "N", { shouldDirty: true }); + } + if (firstHtdUtztnUsgCd) { + form.setValue('reuseBuilding.0.htdflObj.utztnUsg', firstHtdUtztnUsgCd, { shouldDirty: true }); + } + } + }, [htdflForm, code, form]); return (
@@ -91,20 +126,30 @@ export default function HtdflRegForm({ {/*
*/}
-

온배수 시설 정보

+

+ 온배수 시설 등록 +

{!readonly && ( - )}
- {isModal && htdflForm != undefined ? : <>} + {isModal && htdflForm != undefined ? ( + + ) : ( + <> + )} {isModal ? ( -

- 온배수 시설 정보{" "} -

+
+

온배수 시설 정보

+
) : ( <> )} @@ -115,15 +160,14 @@ export default function HtdflRegForm({ control={form.control} name="reuseBuilding.0.htdflObj.etgtType" render={({ field }) => ( - - + {} : field.onChange} value={field.value ?? ""} - onChange={(e) => { - if (!readonly) field.onChange(e) - }} readOnly={readonly} /> @@ -136,13 +180,14 @@ export default function HtdflRegForm({ control={form.control} name="reuseBuilding.0.htdflObj.fclCpct" // 이름 이상함. render={({ field }) => ( - + { - if (!readonly) handleCommaNumberChange(e, field.onChange); + if (!readonly) + handleCommaNumberChange(e, field.onChange); }} value={formatCommaNumber(field.value)} readOnly={readonly} @@ -162,8 +207,8 @@ export default function HtdflRegForm({ label={""} selectClass={selectClass} selectArray={code.yn} - onSelectValue={readonly ? () => { } : field.onChange} - value={field.value ?? code.yn[0]?.val} + onSelectValue={readonly ? () => {} : field.onChange} + value={field.value ?? ""} readOnly={readonly} /> @@ -172,9 +217,7 @@ export default function HtdflRegForm({
-

- 온배수 시설 운영현황 -

+

온배수 시설 운영현황

@@ -183,14 +226,14 @@ export default function HtdflRegForm({ control={form.control} name="reuseBuilding.0.htdflObj.orgwtWtritSpt" render={({ field }) => ( - + { - if (!readonly) field.onChange(Number(e.target.value)); + if (!readonly) field.onChange((e.target.value)); }} readOnly={readonly} /> @@ -206,11 +249,11 @@ export default function HtdflRegForm({ { - if (!readonly) field.onChange + if (!readonly) field.onChange(e.target.value); }} readOnly={readonly} /> @@ -223,13 +266,35 @@ export default function HtdflRegForm({ control={form.control} name="reuseBuilding.0.htdflObj.wtritEntrcTp" render={({ field }) => ( - + { - if (!readonly) handleCommaNumberChange(e, field.onChange); + if (!readonly) + handleCommaNumberChange(e, field.onChange); + }} + value={formatCommaNumber(field.value)} + readOnly={readonly} + /> + + )} + /> +
+
+ ( + + { + if (!readonly) + handleCommaNumberChange(e, field.onChange); }} value={formatCommaNumber(field.value)} readOnly={readonly} @@ -249,7 +314,8 @@ export default function HtdflRegForm({ placeholder="1,000,000" type="text" onChange={(e) => { - if (!readonly) handleCommaNumberChange(e, field.onChange); + if (!readonly) + handleCommaNumberChange(e, field.onChange); }} value={formatCommaNumber(field.value)} readOnly={readonly} @@ -263,14 +329,14 @@ export default function HtdflRegForm({ control={form.control} name="reuseBuilding.0.htdflObj.prcsProcsYn" render={({ field }) => ( - + { } : field.onChange} - value={field.value ?? code.yn[0]?.val} + onSelectValue={readonly ? () => {} : field.onChange} + value={field.value ?? ""} readOnly={readonly} /> @@ -288,7 +354,8 @@ export default function HtdflRegForm({ placeholder="1,000,000" type="text" onChange={(e) => { - if (!readonly) handleCommaNumberChange(e, field.onChange); + if (!readonly) + handleCommaNumberChange(e, field.onChange); }} value={formatCommaNumber(field.value)} readOnly={readonly} @@ -308,8 +375,8 @@ export default function HtdflRegForm({ selectClass={selectClass} label={""} selectArray={code.yn} - onSelectValue={readonly ? () => { } : field.onChange} - value={field.value ?? code.yn[0]?.val} + onSelectValue={readonly ? () => {} : field.onChange} + value={field.value ?? ""} readOnly={readonly} /> @@ -327,7 +394,8 @@ export default function HtdflRegForm({ placeholder="1,000,000" type="text" onChange={(e) => { - if (!readonly) handleCommaNumberChange(e, field.onChange); + if (!readonly) + handleCommaNumberChange(e, field.onChange); }} value={formatCommaNumber(field.value)} readOnly={readonly} @@ -342,14 +410,13 @@ export default function HtdflRegForm({ name="reuseBuilding.0.htdflObj.utztnUsg" render={({ field }) => ( - {} : field.onChange} value={field.value ?? ""} - onChange={() => { - if (!readonly) field.onChange - }} readOnly={readonly} /> @@ -359,9 +426,7 @@ export default function HtdflRegForm({
-

- 요구수질 -

+

수질

@@ -376,7 +441,8 @@ export default function HtdflRegForm({ placeholder="10" type="text" onChange={(e) => { - if (!readonly) handleCommaNumberChange(e, field.onChange); + if (!readonly) + handleCommaNumberChange(e, field.onChange); }} value={formatCommaNumber(field.value)} readOnly={readonly} @@ -397,7 +463,8 @@ export default function HtdflRegForm({ placeholder="10" type="text" onChange={(e) => { - if (!readonly) handleCommaNumberChange(e, field.onChange); + if (!readonly) + handleCommaNumberChange(e, field.onChange); }} value={formatCommaNumber(field.value)} readOnly={readonly} @@ -420,7 +487,8 @@ export default function HtdflRegForm({ placeholder="10" type="text" onChange={(e) => { - if (!readonly) handleCommaNumberChange(e, field.onChange); + if (!readonly) + handleCommaNumberChange(e, field.onChange); }} value={formatCommaNumber(field.value)} readOnly={readonly} @@ -441,7 +509,8 @@ export default function HtdflRegForm({ placeholder="10" type="text" onChange={(e) => { - if (!readonly) handleCommaNumberChange(e, field.onChange); + if (!readonly) + handleCommaNumberChange(e, field.onChange); }} value={formatCommaNumber(field.value)} readOnly={readonly} @@ -464,7 +533,8 @@ export default function HtdflRegForm({ placeholder="10" type="text" onChange={(e) => { - if (!readonly) handleCommaNumberChange(e, field.onChange); + if (!readonly) + handleCommaNumberChange(e, field.onChange); }} value={formatCommaNumber(field.value)} readOnly={readonly} @@ -485,7 +555,8 @@ export default function HtdflRegForm({ placeholder="10" type="text" onChange={(e) => { - if (!readonly) handleCommaNumberChange(e, field.onChange); + if (!readonly) + handleCommaNumberChange(e, field.onChange); }} value={formatCommaNumber(field.value)} readOnly={readonly} @@ -508,7 +579,8 @@ export default function HtdflRegForm({ placeholder="10" type="text" onChange={(e) => { - if (!readonly) handleCommaNumberChange(e, field.onChange); + if (!readonly) + handleCommaNumberChange(e, field.onChange); }} value={formatCommaNumber(field.value)} readOnly={readonly} @@ -529,7 +601,8 @@ export default function HtdflRegForm({ placeholder="10" type="text" onChange={(e) => { - if (!readonly) handleCommaNumberChange(e, field.onChange); + if (!readonly) + handleCommaNumberChange(e, field.onChange); }} value={formatCommaNumber(field.value)} readOnly={readonly} @@ -552,7 +625,8 @@ export default function HtdflRegForm({ placeholder="10" type="text" onChange={(e) => { - if (!readonly) handleCommaNumberChange(e, field.onChange); + if (!readonly) + handleCommaNumberChange(e, field.onChange); }} value={formatCommaNumber(field.value)} readOnly={readonly} @@ -573,7 +647,8 @@ export default function HtdflRegForm({ placeholder="10" type="text" onChange={(e) => { - if (!readonly) handleCommaNumberChange(e, field.onChange); + if (!readonly) + handleCommaNumberChange(e, field.onChange); }} value={formatCommaNumber(field.value)} readOnly={readonly} @@ -596,7 +671,8 @@ export default function HtdflRegForm({ placeholder="10" type="text" onChange={(e) => { - if (!readonly) handleCommaNumberChange(e, field.onChange); + if (!readonly) + handleCommaNumberChange(e, field.onChange); }} value={formatCommaNumber(field.value)} readOnly={readonly} @@ -606,26 +682,6 @@ export default function HtdflRegForm({ />
-
- ( - - { - if (!readonly) handleCommaNumberChange(e, field.onChange); - }} - value={formatCommaNumber(field.value)} - readOnly={readonly} - /> - - )} - /> -
diff --git a/apps/wr/components/ui/form/fcltReg/pbnstReg/buseReg/bugRegForm.tsx b/apps/wr/components/ui/form/fcltReg/pbnstReg/buseReg/bugRegForm.tsx index d2f9b79e6..8ea07535d 100644 --- a/apps/wr/components/ui/form/fcltReg/pbnstReg/buseReg/bugRegForm.tsx +++ b/apps/wr/components/ui/form/fcltReg/pbnstReg/buseReg/bugRegForm.tsx @@ -194,6 +194,23 @@ export function PbnstBuseForm({ name: `items.${indexMng}.fcBizs.${indexBiz}.fcDemands`, }); + const demandsContainerRef = useRef(null); + const prevDemandsLengthRef = useRef(fcDemands.length); + + // 수요처 추가 시 새로 추가된 항목으로 스크롤하는 효과 + useEffect(() => { + // 수요처 항목이 추가되었는지 확인 (길이가 이전보다 길어졌는지) + if (fcDemands.length > prevDemandsLengthRef.current) { + // 컨테이너의 마지막 자식 요소(새로 추가된 수요처 폼)로 부드럽게 스크롤 + demandsContainerRef.current?.lastElementChild?.scrollIntoView({ + behavior: "smooth", + block: "end", + }); + } + // 다음 렌더링을 위해 현재 길이를 ref에 저장 + prevDemandsLengthRef.current = fcDemands.length; + }, [fcDemands.length]); + const { fields: fcOrgwtFiled } = useFieldArray({ control: form.control, name: `items.${indexMng}.fcBizs.${indexBiz}.fcOrgwts`, @@ -351,7 +368,7 @@ export function PbnstBuseForm({ 합계 @@ -488,7 +505,8 @@ export function PbnstBuseForm({
- {( - form.getValues( - `items.${indexMng}.fcBizs.${indexBiz}.fcDemands` - ) || [] - ).map((field, index) => ( +
+ {fcDemands.map((field, index) => (
( - +
- ))} + ))} +
diff --git a/apps/wr/components/ui/form/fcltReg/pbnstReg/mngReg/mngRegFormDetail/mngRegFormDetail.tsx b/apps/wr/components/ui/form/fcltReg/pbnstReg/mngReg/mngRegFormDetail/mngRegFormDetail.tsx index 30fab689e..fb50e1b89 100644 --- a/apps/wr/components/ui/form/fcltReg/pbnstReg/mngReg/mngRegFormDetail/mngRegFormDetail.tsx +++ b/apps/wr/components/ui/form/fcltReg/pbnstReg/mngReg/mngRegFormDetail/mngRegFormDetail.tsx @@ -14,6 +14,7 @@ import { Input } from "@common/components/ui"; export default function MngRegFormDetail({ formList, h1Title, + subTitle, form, moClass, sonClass, @@ -22,6 +23,7 @@ export default function MngRegFormDetail({ }: { formList: formDetailType[]; h1Title?: string; + subTitle?:string form: any; moClass?: string; indexBiz?: number; @@ -33,6 +35,8 @@ export default function MngRegFormDetail({ return ( <> {h1Title ?

{h1Title}

: <>} + {subTitle ?

{subTitle}

: <>} +
{formList?.map((formHead, index) => { const fieldId = diff --git a/apps/wr/components/ui/form/fcltReg/rainReg/rainRegForm.tsx b/apps/wr/components/ui/form/fcltReg/rainReg/rainRegForm.tsx index b893f9bf4..7333f6091 100644 --- a/apps/wr/components/ui/form/fcltReg/rainReg/rainRegForm.tsx +++ b/apps/wr/components/ui/form/fcltReg/rainReg/rainRegForm.tsx @@ -9,16 +9,10 @@ import { import { formH1Class, formH2Class } from "@/components/lib/class/comClass"; import { formatCommaNumber, - handleCommaNumberChange, handleMinusNumberChange, } from "@/components/lib/func/commom"; import { DatePicker, FormUi } from "@common/business_components"; -import { - Form, - FormField, - FormLabel, - SelectBox, -} from "@common/business_components/ui"; +import { Form, FormField, SelectBox } from "@common/business_components/ui"; import { Button, Checkbox, Input, Label } from "@common/components/ui"; import { zodResolver } from "@hookform/resolvers/zod"; import { useEffect, useState } from "react"; @@ -31,11 +25,11 @@ export default function RainRegForm({ rainForm, isModal, readonly = false, - setIsModal + setIsModal, }: { rainForm: customRainBdst; isModal: boolean; - setIsModal?:(modal:boolean) => void + setIsModal?: (modal: boolean) => void; readonly?: boolean; }) { const { code, rainBdstReg, rainBdstUpdate, rain } = useWrStore( @@ -100,20 +94,19 @@ export default function RainRegForm({ form.setValue("reuseBuilding.0.rainyObj.filtrTrtmfctPrs", ""); } - if(grntchBdstCertYn === "D") { + if (grntchBdstCertYn === "D") { form.setValue("reuseBuilding.0.grntchBdstCertYr", "0000"); form.setValue("reuseBuilding.0.grntchBdstCertNo", "확인불가"); form.setValue("reuseBuilding.0.grntchBdstCertGrd", "확인불가"); - }else if (instlCstSprtYn === "D") { + } else if (instlCstSprtYn === "D") { form.setValue("reuseBuilding.0.instlCstSprtAmt", -1); } else if (filtrTrtmfctYn === "D") { form.setValue("reuseBuilding.0.rainyObj.filtrTrtmfctCpct", -1); form.setValue("reuseBuilding.0.rainyObj.filtrTrtmfctPrs", "확인불가"); } - }, [grntchBdstCertYn, instlCstSprtYn, filtrTrtmfctYn]); -const [regCtpvCd , regSggcd] = form.watch(['ctpvCd','sggCd']) + const [regCtpvCd, regSggcd] = form.watch(["ctpvCd", "sggCd"]); // form 값이 변경될 때마다 로그 출력 useEffect(() => { @@ -127,15 +120,15 @@ const [regCtpvCd , regSggcd] = form.watch(['ctpvCd','sggCd']) const onSubmit = (values: z.infer) => { if (isModal) { - const data = regCtpvCd?regCtpvCd:regSggcd?regSggcd:'noCd' - - if(data ==='noCd'){ + const data = regCtpvCd ? regCtpvCd : regSggcd ? regSggcd : "noCd"; + + if (data === "noCd") { toast.error("시도/시군구 코드를 선택해야합니다."); - return + return; } rainBdstReg(values, data); - setIsModal(false) + if (setIsModal) setIsModal(false); } else { rainBdstUpdate(values, "41"); } @@ -207,29 +200,27 @@ const [regCtpvCd , regSggcd] = form.watch(['ctpvCd','sggCd']) const value = field.value; const isUnknown = value === null || value === -1; // 또는 Number(value) === -1 return ( -
- - + { + // 입력 시 자동으로 확인불가 해제 + if (isUnknown && !readonly) { + field.onChange(""); // 초기화 } - onFocus={() => { - // 입력 시 자동으로 확인불가 해제 - if (isUnknown && !readonly) { - field.onChange(""); // 초기화 - } - }} - onChange={(e) => { - if (readonly ) return; - handleMinusNumberChange(e, field.onChange); - }} - /> - -
+ }} + onChange={(e) => { + if (readonly) return; + handleMinusNumberChange(e, field.onChange); + }} + /> + ); }} /> @@ -246,34 +237,34 @@ const [regCtpvCd , regSggcd] = form.watch(['ctpvCd','sggCd']) - - { - const isUnknown = field.value === null || field.value === -1; // 또는 Number(value) === -1 + render={({ field }) => { + const isUnknown = field.value === null || field.value === -1; // 또는 Number(value) === -1 return ( - - { - // 입력 시 자동으로 확인불가 해제 - if (isUnknown && !readonly) { - field.onChange(""); // 초기화 + + { - if (readonly ) return; - handleMinusNumberChange(e, field.onChange); - }} - readOnly={readonly} - /> - - ) - }} + onFocus={() => { + // 입력 시 자동으로 확인불가 해제 + if (isUnknown && !readonly) { + field.onChange(""); // 초기화 + } + }} + onChange={(e) => { + if (readonly) return; + handleMinusNumberChange(e, field.onChange); + }} + readOnly={readonly} + /> + + ); + }} />
@@ -319,13 +310,12 @@ const [regCtpvCd , regSggcd] = form.watch(['ctpvCd','sggCd']) } }} onChange={(e) => { - if (readonly ) return; + if (readonly) return; handleMinusNumberChange(e, field.onChange); }} readOnly={readonly} /> -
); }} @@ -360,36 +350,35 @@ const [regCtpvCd , regSggcd] = form.watch(['ctpvCd','sggCd']) - - - { + render={({ field }) => { const isUnknown = field.value === null || field.value === -1; // 또는 Number(value) === -1 - return( - - { - // 입력 시 자동으로 확인불가 해제 - if (isUnknown && !readonly) { - field.onChange(""); // 초기화 + return ( + + { - if (readonly ) return; - handleMinusNumberChange(e, field.onChange); - }} - readOnly={readonly} - disabled={filtrTrtmfctYn !== "Y"} - /> - - ) - }} + onFocus={() => { + // 입력 시 자동으로 확인불가 해제 + if (isUnknown && !readonly) { + field.onChange(""); // 초기화 + } + }} + onChange={(e) => { + if (readonly) return; + handleMinusNumberChange(e, field.onChange); + }} + readOnly={readonly} + disabled={filtrTrtmfctYn !== "Y"} + /> + + ); + }} />
@@ -404,7 +393,7 @@ const [regCtpvCd , regSggcd] = form.watch(['ctpvCd','sggCd'])
-
); }} @@ -450,35 +438,34 @@ const [regCtpvCd , regSggcd] = form.watch(['ctpvCd','sggCd']) - { + render={({ field }) => { const value = field.value; const isUnknown = value === null || value === -1; // 또는 Number(value) === -1 - return ( - - { - // 입력 시 자동으로 확인불가 해제 - if (isUnknown && !readonly) { - field.onChange(""); // 초기화 + return ( + + { - if (readonly ) return; - handleMinusNumberChange(e, field.onChange); - }} - disabled={instlCstSprtYn !== "Y"} - /> - - )} - } + onFocus={() => { + // 입력 시 자동으로 확인불가 해제 + if (isUnknown && !readonly) { + field.onChange(""); // 초기화 + } + }} + onChange={(e) => { + if (readonly) return; + handleMinusNumberChange(e, field.onChange); + }} + disabled={instlCstSprtYn !== "Y"} + /> + + ); + }} />
@@ -508,10 +495,10 @@ const [regCtpvCd , regSggcd] = form.watch(['ctpvCd','sggCd']) - { - const isUnknown = field.value === null || field.value === "0000"; // 또는 Number(value) === -1 - return ( + render={({ field }) => { + const isUnknown = + field.value === null || field.value === "0000"; // 또는 Number(value) === -1 + return ( { - if (readonly) return - - if (e.target.value === '확인불가') { - field.onChange('0000'); - }else{ + if (readonly) return; + if (e.target.value === "확인불가") { + field.onChange("0000"); + } else { field.onChange(e.target.value); } }} disabled={grntchBdstCertYn !== "Y"} + max={4} /> - )} - } + ); + }} />
( - - { - if (!readonly) field.onChange(e); - }} - disabled={grntchBdstCertYn !== "Y"} - /> - - )} + render={({ field }) => { + const isUnknown = + field.value === null || field.value === "0000"; // 또는 Number(value) === -1 + return ( + + { + // 입력 시 자동으로 확인불가 해제 + if (isUnknown && !readonly) { + field.onChange(""); // 초기화 + } + }} + value={isUnknown ? "확인불가" : (field.value ?? "")} + onChange={(e) => { + if (readonly) return; + + if (e.target.value === "확인불가") { + field.onChange(""); + } else { + field.onChange(e.target.value); + } + }} + disabled={grntchBdstCertYn !== "Y"} + /> + + ); + }} />
diff --git a/apps/wr/components/ui/form/fcltReg/rswtrReg/rswtrRegForm.tsx b/apps/wr/components/ui/form/fcltReg/rswtrReg/rswtrRegForm.tsx index d820bddc1..ed0e51af6 100644 --- a/apps/wr/components/ui/form/fcltReg/rswtrReg/rswtrRegForm.tsx +++ b/apps/wr/components/ui/form/fcltReg/rswtrReg/rswtrRegForm.tsx @@ -10,6 +10,7 @@ import { formH1Class, formH2Class } from "@/components/lib/class/comClass"; import { formatCommaNumber, handleCommaNumberChange, + handleMinusNumberChange, } from "@/components/lib/func/commom"; import { DatePicker, @@ -21,7 +22,7 @@ import { import { Button, Input } from "@common/components/ui"; import { zodResolver } from "@hookform/resolvers/zod"; import { useEffect } from "react"; -import { useForm } from "react-hook-form"; +import { useForm, type UseFormReturn } from "react-hook-form"; import { z } from "zod"; import BdstReg from "../bdstReg/bdstReg"; @@ -29,7 +30,7 @@ export default function RswtrRegForm({ rswtForm, isModal, setIsModal, - readonly = false + readonly = false, }: { rswtForm: customRswtBdst; isModal: boolean; @@ -56,7 +57,7 @@ export default function RswtrRegForm({ const onSubmit = (values: z.infer) => { if (isModal) { rswtBdstReg(values, "41"); - if(setIsModal) setIsModal(false) + if (setIsModal) setIsModal(false); } else { rswtBdstUpdate(values, "41"); } @@ -73,21 +74,19 @@ export default function RswtrRegForm({ reuseBuilding: (raw?.reuseBuilding || []).map((item: any) => ({ ...item, instlCmptnDt: item?.instlCmptnDt - ? new Date(item.instlCmptnDt) - : new Date(), // item?.instlCmptnDt가 유효하지 않으면 현재 날짜로 초기화 - operMngCd: item?.operMngCd ?? "", // null → "" - instlCstSprtYn: item?.instlCstSprtYn ?? "Y", // null → "Y" - grntchBdstCertYn:item?.grntchBdstCertYn??"Y", - rswtrObj: { + ? new Date(item.instlCmptnDt) + : new Date(), // item?.instlCmptnDt가 유효하지 않으면 현재 날짜로 초기화 + operMngCd: item?.operMngCd ?? "", // null → "" + instlCstSprtYn: item?.instlCstSprtYn ?? "Y", // null → "Y" + grntchBdstCertYn: item?.grntchBdstCertYn ?? "Y", + rswtrObj: { ...item?.rswtrObj, filtrTrtmfctYn: item?.rainyObj?.filtrTrtmfctYn ?? "Y", - }, })), }; }; - useEffect(() => { console.log("form errors:", form.formState.errors); }, [form.formState.errors]); @@ -99,6 +98,16 @@ export default function RswtrRegForm({ } else if (instlCstSprtYn === "N") { form.setValue("reuseBuilding.0.instlCstSprtAmt", 0); } + + + if (grntchBdstCertYn === "D") { + form.setValue("reuseBuilding.0.grntchBdstCertYr", "0000"); + form.setValue("reuseBuilding.0.grntchBdstCertNo", "확인불가"); + form.setValue("reuseBuilding.0.grntchBdstCertGrd", "확인불가"); + } else if (instlCstSprtYn === "D") { + form.setValue("reuseBuilding.0.instlCstSprtAmt", -1); + } + }, [grntchBdstCertYn, instlCstSprtYn, filtrTrtmfctProcs, form]); useEffect(() => { @@ -116,7 +125,11 @@ export default function RswtrRegForm({ 건축물 운영정보 {!readonly && ( - )} @@ -155,68 +168,37 @@ export default function RswtrRegForm({ )} />
-
( - - { - if (!readonly) handleCommaNumberChange(e, field.onChange); - }} - value={formatCommaNumber(field.value)} - readOnly={readonly} - /> - - )} - /> -
-
-
-
- ( - - { - if (!readonly) handleCommaNumberChange(e, field.onChange); - }} - value={formatCommaNumber(field.value)} - readOnly={readonly} - /> - - )} - /> -
- -
- ( - - { - if (!readonly) handleCommaNumberChange(e, field.onChange); - }} - value={formatCommaNumber(field.value)} - readOnly={readonly} - /> - - )} + render={({ field }) => { + const value = field.value; + const isUnknown = value === null || value === -1; // 또는 Number(value) === -1 + return ( + + { + // 입력 시 자동으로 확인불가 해제 + if (isUnknown && !readonly) { + field.onChange(""); // 초기화 + } + }} + onChange={(e) => { + if (readonly) return; + handleMinusNumberChange(e, field.onChange); + }} + readOnly={readonly} + /> + + ); + }} />
@@ -229,20 +211,34 @@ export default function RswtrRegForm({ ( - - { - if (!readonly) field.onChange(Number(e.target.value)); - }} - readOnly={readonly} - /> - - )} + render={({ field }) => { + const value = field.value; + const isUnknown = value === null || value === -1; // 또는 Number(value) === -1 + + return ( + + { + // 입력 시 자동으로 확인불가 해제 + if (isUnknown && !readonly) { + field.onChange(""); // 초기화 + } + }} + onChange={(e) => { + if (readonly) return; + handleMinusNumberChange(e, field.onChange); + }} + readOnly={readonly} + /> + + ); + }} />
@@ -256,7 +252,7 @@ export default function RswtrRegForm({ label={""} selectClass={selectClass} selectArray={code.orgwtKndCd} - onSelectValue={readonly ? () => { } : field.onChange} + onSelectValue={readonly ? () => {} : field.onChange} value={field.value ?? code.orgwtKndCd[0]?.val} readOnly={readonly} /> @@ -270,14 +266,13 @@ export default function RswtrRegForm({ name="reuseBuilding.0.rswtrObj.filtrTrtmfctProcs" render={({ field }) => ( - { - if (!readonly) field.onChange - }} + {} : field.onChange} + value={field.value ?? code.rswtPrscCdMthdCd[0]?.val} readOnly={readonly} /> @@ -303,9 +298,9 @@ export default function RswtrRegForm({ className={""} selectClass={selectClass} label={""} - selectArray={code.yn} - onSelectValue={readonly ? () => { } : field.onChange} - value={field.value ?? code.yn[0]?.val} + selectArray={code.bdstIncenYn} + onSelectValue={readonly ? () => {} : field.onChange} + value={field.value ?? code.bdstIncenYn[0]?.val} readOnly={readonly} /> @@ -313,23 +308,37 @@ export default function RswtrRegForm({ />
- ( - - { - if (!readonly) handleCommaNumberChange(e, field.onChange); - }} - value={formatCommaNumber(field.value)} - readOnly={instlCstSprtYn !== "Y"} - /> - - )} + render={({ field }) => { + const value = field.value; + const isUnknown = value === null || value === -1; // 또는 Number(value) === -1 + return ( + + { + // 입력 시 자동으로 확인불가 해제 + if (isUnknown && !readonly) { + field.onChange(""); // 초기화 + } + }} + onChange={(e) => { + if (readonly) return; + handleMinusNumberChange(e, field.onChange); + }} + disabled={instlCstSprtYn !== "Y"} + /> + + ); + }} />
@@ -348,8 +357,8 @@ export default function RswtrRegForm({ className={""} selectClass={selectClass} label={""} - selectArray={code.yn} - onSelectValue={readonly ? () => { } : field.onChange} + selectArray={code.bdstIncenYn} + onSelectValue={readonly ? () => {} : field.onChange} value={field.value ?? code.yn[0]?.val} readOnly={readonly} /> @@ -361,41 +370,75 @@ export default function RswtrRegForm({ ( + render={({ field }) => + { + const isUnknown = field.value === null || field.value === "0000"; // 또는 Number(value) === -1 + return ( { - if (!readonly) field.onChange(e) + type="" + onFocus={() => { + // 입력 시 자동으로 확인불가 해제 + if (isUnknown && !readonly) { + field.onChange(""); // 초기화 + } }} - readOnly={grntchBdstCertYn !== "Y"} + value={ + isUnknown?'확인불가':field.value ?? ""} + onChange={(e) => { + if (readonly) return + if (e.target.value === '확인불가') { + field.onChange('0000'); + }else{ + field.onChange(e.target.value); + } + }} + disabled={grntchBdstCertYn !== "Y"} + max={4} /> - )} + )}} />
( + render={({ field }) => + { + const isUnknown = field.value === null || field.value === "0000"; // 또는 Number(value) === -1 + return ( { - if (!readonly) field.onChange(e) + onFocus={() => { + // 입력 시 자동으로 확인불가 해제 + if (isUnknown && !readonly) { + field.onChange(""); // 초기화 + } }} - readOnly={grntchBdstCertYn !== "Y"} + value={ + isUnknown?'확인불가':field.value ?? ""} + onChange={(e) => { + if (readonly) return + + if (e.target.value === '확인불가') { + field.onChange(''); + }else{ + field.onChange(e.target.value); + } + }} + disabled={grntchBdstCertYn !== "Y"} + + + /> - )} + )}} />
@@ -404,15 +447,14 @@ export default function RswtrRegForm({ name="reuseBuilding.0.grntchBdstCertGrd" render={({ field }) => ( - { - if (!readonly) field.onChange(e) - }} - readOnly={grntchBdstCertYn !== "Y"} + {} : field.onChange} + value={field.value ?? code.bdstGrntchClass[0]?.val} + readOnly={readonly} /> )} diff --git a/apps/wr/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal.tsx b/apps/wr/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal.tsx index 6ab1b25c4..bee445114 100644 --- a/apps/wr/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal.tsx +++ b/apps/wr/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal.tsx @@ -120,7 +120,12 @@ export function BdstReg({ moType,setIsModal,isModal }: BdstReg
-

건축물 목록 (유의 사항: 신규등록하려는 건축물이 목록에 없을 경우 하단 건축물 정보에 추가하여 주시기 바랍니다.)

+

+ 건축물 목록 + + (유의 사항: 신규등록하려는 건축물이 목록에 없을 경우 하단 건축물 정보에 추가하여 주시기 바랍니다.) + +

({ moType,setIsModal,isModal }: BdstReg :moType==='RST' ? {setIsModal}}> - : + :{setIsModal}}> }
diff --git a/apps/wr/components/ui/table/pbnstReg/ruseMngTableForm/ruseMngTableForm.tsx b/apps/wr/components/ui/table/pbnstReg/ruseMngTableForm/ruseMngTableForm.tsx index acb923687..6f4832c68 100644 --- a/apps/wr/components/ui/table/pbnstReg/ruseMngTableForm/ruseMngTableForm.tsx +++ b/apps/wr/components/ui/table/pbnstReg/ruseMngTableForm/ruseMngTableForm.tsx @@ -3,6 +3,10 @@ import type { TableUpperProps } from "@/app/store/common/type/header"; import type { chHeadType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList"; import type { customPbnst as OriginalCustomPbnst } from "@/app/store/ruseFcltMng/fcltReg/pbnstReg/type"; import { selectClass } from "@/app/store/ruseFcltMng/fcltReg/rainReg"; +import { + formatCommaNumber, + handleCommaNumberChange, +} from "@/components/lib/func/commom"; import { DatePicker, @@ -32,12 +36,24 @@ type RuseMngTableProps = { indexMng?: number; indexBiz?: number; form?: any; - formNm?:string + formNm?: string; }; const RuseMngTableForm = React.forwardRef( - ({ tableData, headList, upHeadList, code, onTableSet, indexMng, form,formNm,indexBiz }, ref) => { - + ( + { + tableData, + headList, + upHeadList, + code, + onTableSet, + indexMng, + form, + formNm, + indexBiz, + }, + ref + ) => { return ( <> @@ -73,26 +89,31 @@ const RuseMngTableForm = React.forwardRef( typeof indexMng === "number" && indexMng >= 0 && typeof indexBiz === "number" && - indexBiz >= 0? - `items.${indexMng}.fcBizs.${indexBiz}.${formNm}.${i}.${head.id}`: - typeof indexMng === "number" && indexMng >= 0? - `items.${indexMng}.${formNm}.${i}.${head.id}`: - `items.${indexMng}.${formNm}.${i}.${head.id}`} + indexBiz >= 0 + ? `items.${indexMng}.fcBizs.${indexBiz}.${formNm}.${i}.${head.id}` + : typeof indexMng === "number" && indexMng >= 0 + ? `items.${indexMng}.${formNm}.${i}.${head.id}` + : `items.${indexMng}.${formNm}.${i}.${head.id}` + } render={({ field }) => ( {head.edit === true ? ( { - field.onChange(txt.target.value); + if (head.type === "number") { + handleCommaNumberChange(txt, field.onChange); + } else { + field.onChange(txt.target.value); + } }} value={ head.type === "number" - ? (Number(field.value) ?? 0) + ? formatCommaNumber(field.value) : field.value || "" } - readOnly ={head.readonly ? true : false} + readOnly={head.readonly ? true : false} /> ) : head.type === "select" ? ( ( (item) => item.name === head.selectData )?.val ?? [] } - value={ - field.value || "" - } + value={field.value || ""} className={""} selectClass={selectClass} onSelectValue={(val) => { - - if(head.readonly === true){ - return - }else{ - + if (head.readonly === true) { + return; + } else { field.onChange(val); } }} - readOnly ={head.readonly ? true : false} + readOnly={head.readonly ? true : false} // Disabled prop removed as SelectBox does not support it > ) : head.type === "date" ? ( { - console.log("date", date); - field.onChange(date); // readonly 상태가 아닐 때만 값 변경 + field.onChange(date); // readonly 상태가 아닐 때만 값 변경 }, }} selectClass={"w-full"} - readOnly ={head.readonly ? true : false} + readOnly={head.readonly ? true : false} /> ) : head.type === "checkbox" ? ( { - field.onChange(checked); + field.onChange(checked); }} > ) : ( From 5d5b97d39f5eba6e453c3ba98f36cee9e6e14f2b Mon Sep 17 00:00:00 2001 From: YangJinSoo94 Date: Wed, 2 Jul 2025 17:26:10 +0900 Subject: [PATCH 045/108] =?UTF-8?q?=EB=AC=BC=EC=9E=AC=EC=9D=B4=EC=9A=A9?= =?UTF-8?q?=EC=82=AC=EB=A1=80=EA=B4=80=EB=A6=AC,=20=ED=86=B5=EA=B3=84=20?= =?UTF-8?q?=EA=B2=B0=ED=95=A8=EC=A1=B0=EC=B9=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wr/app/matMng/coMat/ruseCaseMng/page.tsx | 16 ++++++++++---- apps/wr/app/stats/rainStat/page.tsx | 3 ++- apps/wr/app/stats/rswtStat/page.tsx | 2 +- .../wr/app/store/matDmd/ruseCaseMng/index.tsx | 22 +++++++++++-------- 4 files changed, 28 insertions(+), 15 deletions(-) diff --git a/apps/wr/app/matMng/coMat/ruseCaseMng/page.tsx b/apps/wr/app/matMng/coMat/ruseCaseMng/page.tsx index 436b77736..517dab0c0 100644 --- a/apps/wr/app/matMng/coMat/ruseCaseMng/page.tsx +++ b/apps/wr/app/matMng/coMat/ruseCaseMng/page.tsx @@ -5,6 +5,7 @@ import { ruseUtzType } from "@/app/store/matDmd/ruseCaseMng/headList"; import { excelDownload, getCity } from "@/components/lib/func/commom"; import RuseCaseMngExcel from "@/components/ui/modal/coMat/ruseCaseMng/ruseCaseMngExcelModal"; import type { objSearchType } from "@/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal"; +import { ToastFormUi } from "@/components/ui/toast/toastFromUi"; import { ContentsDiv, filePickedBlobArray, @@ -20,11 +21,12 @@ import { } from "@common/business_components/ui"; import ListDiv from "@common/business_components/ui/list_div"; import { Button } from "@common/components/ui"; -import { useState } from "react"; +import { useEffect, useState } from "react"; +import { toast, Toaster } from "react-hot-toast"; export default function RuseCaseMng() { const { code, ruseCaseMng, selectRuseCaseMng } = useWrStore((state) => state); - const { ruseCaseMngList } = ruseCaseMng; + const { ruseCaseMngList, status } = ruseCaseMng; const [selectSgg, setSelectSgg] = useState([]); const [objSearch, setObjSearch] = useState({ sgg: "", city: "" }); @@ -142,7 +144,7 @@ export default function RuseCaseMng() { } ]; // 버퍼에 있는 엑셀데이터 전달 후 json으로 변환 - await filePickedBlobArray(blobRet, '시설정보및운영현황황', sheetParameter); + await filePickedBlobArray(blobRet, '물재이용사례현황', sheetParameter); }); } @@ -157,6 +159,12 @@ export default function RuseCaseMng() { ); } + useEffect(() => { + if(status === 200){ + setIsExcelOpen({ isModal: false }); + } + }, [status]); + return ( <> @@ -239,7 +247,7 @@ export default function RuseCaseMng() { - + {/* 엑셀 등록 모달 */} item.cityText !== '없음') : []; + setBdstTable(tbData); }, [code, rainStatList]); diff --git a/apps/wr/app/stats/rswtStat/page.tsx b/apps/wr/app/stats/rswtStat/page.tsx index c0741433c..997e95ca5 100644 --- a/apps/wr/app/stats/rswtStat/page.tsx +++ b/apps/wr/app/stats/rswtStat/page.tsx @@ -46,7 +46,7 @@ export default function RswtStat() { ...item, ctpvCd: item.cityText }; - }) + }).filter((item: RswtStatProps) => item.cityText !== '없음') : []; setRswtTable(tbData); }, [code, rswtStatList]) diff --git a/apps/wr/app/store/matDmd/ruseCaseMng/index.tsx b/apps/wr/app/store/matDmd/ruseCaseMng/index.tsx index 915740f79..76468d249 100644 --- a/apps/wr/app/store/matDmd/ruseCaseMng/index.tsx +++ b/apps/wr/app/store/matDmd/ruseCaseMng/index.tsx @@ -1,12 +1,12 @@ import { fetchType, getFetch, postFectch } from "@/components/lib/api"; import type { TableUpperProps } from "../../common/type/header"; import { ruseCaseHeadListExport } from "./headList"; -import { ToastFormUi } from "@/components/ui/toast/toastFromUi"; - +import { toast } from "react-hot-toast"; export type ruseCaseState = { ruseCaseMng: { ruseHeadList:TableUpperProps[]; ruseCaseMngList:ruseCaseMngExcelProps[]; + status: number; }; }; @@ -69,7 +69,6 @@ type ruseCaseActions = { }; export const fetchSearchRuseCaseMng = async (sidoCd: string, sigunguCd: string, usageCd : string, name: string) => { - const tailUrl = ((sigunguCd !== '전체' && sigunguCd !=='') ? `/${sigunguCd}` : (sidoCd != '전체' && sidoCd != '') ? `/${sidoCd}` : `/0`) + (usageCd? `/${usageCd}` : '/0') + (name? `/${name}` : ''); const resp = await getFetch(fetchType.WR,`/reuseFc/pubFcRuseCase${tailUrl}`); return resp; @@ -77,7 +76,7 @@ export const fetchSearchRuseCaseMng = async (sidoCd: string, sigunguCd: string, export const fetchInsertRuseCaseMng = async (data: any) => { const response = await postFectch('/reuseFc/pubFcRuseCase', data, fetchType.WR); - return response.data; + return {data:response.data, status:response.status}; }; export type ruseCaseType = ruseCaseState & ruseCaseActions; @@ -86,6 +85,7 @@ export const ruseCaseStateExport: ruseCaseState = { ruseCaseMng: { ruseHeadList:ruseCaseHeadListExport, ruseCaseMngList:[], + status: 0, }, }; @@ -95,7 +95,6 @@ export const ruseCaseActionsExport: (set: any) => ruseCaseActions = ( return { selectRuseCaseMng: async (sidoCd: string, sigunguCd: string, usageCd : string, name: string) => { const res = await fetchSearchRuseCaseMng(sidoCd, sigunguCd, usageCd, name); - console.log('res : ', res); set((state: ruseCaseState) => ({ ruseCaseMng: { ...state.ruseCaseMng, @@ -107,11 +106,16 @@ export const ruseCaseActionsExport: (set: any) => ruseCaseActions = ( insertRuseCaseMng: async (data: any) => { const response = await fetchInsertRuseCaseMng(data); if(response.status === 200){ - ToastFormUi({ - body: "잠재수요처 등록이 완료되었습니다. ", - title: "잠재수요처 등록", - }); + toast.success("물재이용사례 등록이 완료되었습니다. "); + }else{ + toast.error("물재이용사례 등록에 실패하였습니다. "); } + set((state: ruseCaseState) => ({ + ruseCaseMng: { + ...state.ruseCaseMng, + status: response.status, + }, + })); }, }; }; From 1fd522e49bfc3b7100ef827ffc8128d0c63b5fe0 Mon Sep 17 00:00:00 2001 From: JIeunhuh Date: Wed, 2 Jul 2025 17:38:44 +0900 Subject: [PATCH 046/108] =?UTF-8?q?=EC=8B=9C=EC=84=A4=ED=98=84=ED=99=A9=20?= =?UTF-8?q?=EB=AA=A9=EB=A1=9D=20=EB=9D=84=EC=9A=B0=EB=8A=94=20=EC=9E=91?= =?UTF-8?q?=EC=97=85=20=EC=A7=84=ED=96=89=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wr/app/map/newWindowPopup.tsx | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/apps/wr/app/map/newWindowPopup.tsx b/apps/wr/app/map/newWindowPopup.tsx index e6438656d..dd2334e29 100644 --- a/apps/wr/app/map/newWindowPopup.tsx +++ b/apps/wr/app/map/newWindowPopup.tsx @@ -43,7 +43,7 @@ const NewWindowPopup: React.FC = ({ const [tabData, setTabData] = useState<{ id: string; name: string; data: any }[]>([]); const [activeTab, setActiveTab] = useState('0'); // swcho : useState의 true, false 값 변경하여 목록, 상태 테스트 하였음 - const [showDetailView, setShowDetailView] = useState(true); // 상세정보 화면 표시 상태 추가 + const [showDetailView, setShowDetailView] = useState(false); // 상세정보 화면 표시 상태 추가 // onClose 함수를 useCallback으로 메모이제이션 const memoizedOnClose = useCallback(() => { @@ -109,14 +109,33 @@ const NewWindowPopup: React.FC = ({ pbnstSttn.pbnstSttnRes && pbnstSttn.pbnstSttnRes[index] ) { + console.log(pbnstSttn.pbnstSttnRes[index]) setPbnstRuseFcltPk(pbnstSttn.pbnstSttnRes[index].pbnstRuseFcltPk); getPbnStnDetail(pbnstSttn.pbnstSttnRes[index].pbnstRuseFcltPk); + setShowDetailView(true); // 상세정보 화면 표시 상태 변경 } }; - // useEffect(() => { - // setBdstTable(pbnstSttn.pbnstSttnRes); - // }, [pbnstSttn.pbnstSttnRes]); + useEffect(() => { + if (!pbnstSttn.isInit) { + pbnSttnInit("1", "13",{ + partValue: "A", + sidoValue: "0", + sigunValue: "", + regionValue: "", + unitWaterShedValue: "", + middleRegionValue: "", + envPartValue: "", + sidoText: "", + sigunText: "" + }, String(new Date().getFullYear())); + } + }, [pbnstSttn.isInit, pbnSttnInit]); + + useEffect(() => { + setBdstTable(pbnstSttn.pbnstSttnRes); + }, [pbnstSttn.pbnstSttnRes]); + // 재이용시설 조회 const fetchpbnstDetail = async (featureCd: string) => { @@ -127,7 +146,6 @@ const NewWindowPopup: React.FC = ({ if (prev.some((tab) => tab.id === featureCd)) { return prev; // 이미 존재하는 탭이면 추가하지 않음 } - console.log('탭 idx',prev, prev.length) return [...prev, { id: featureCd, name: `재이용시설 ${prev.length + 1}`, data: response }]; }); } catch (error) { @@ -207,7 +225,7 @@ const NewWindowPopup: React.FC = ({ layer.data?.lyrNm?.includes('하수처리') || layer.data?.lyrNm?.includes('재이용시설') ); const actTabData = tabData.find((tab) => tab.id === activeTab); - + // swcho : true, false 값 변경하여 시설 변경하여 테스트하였음 // 재이용시설 const isPbnst = true; From 34a70ca21ac4752175c5384a2e3fc9c1c958878f Mon Sep 17 00:00:00 2001 From: JIeunhuh Date: Wed, 2 Jul 2025 18:10:24 +0900 Subject: [PATCH 047/108] =?UTF-8?q?settimeout=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/common/business_components/src/ui/map/Gis.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/common/business_components/src/ui/map/Gis.js b/packages/common/business_components/src/ui/map/Gis.js index 5ac98f27f..d0dde20d8 100644 --- a/packages/common/business_components/src/ui/map/Gis.js +++ b/packages/common/business_components/src/ui/map/Gis.js @@ -342,9 +342,11 @@ function Gis({ // 체크된 레이어 정보를 처리하는 함수 const handleCheckedLayersChange = useCallback((checkedIds, checkedNodesData) => { - setCheckedLayers(checkedIds); - setCheckedLayersData(checkedNodesData); - onCheckedLayersChange?.(checkedIds, checkedNodesData); + setTimeout(() => { + setCheckedLayers(checkedIds); + setCheckedLayersData(checkedNodesData); + onCheckedLayersChange?.(checkedIds, checkedNodesData); + }, 0); }, [onCheckedLayersChange]); return ( From 7641cb332f3c20d64433e678b4285791512a1141 Mon Sep 17 00:00:00 2001 From: JIeunhuh Date: Wed, 2 Jul 2025 18:47:01 +0900 Subject: [PATCH 048/108] =?UTF-8?q?=EC=9E=AC=EC=9D=B4=EC=9A=A9=EC=8B=9C?= =?UTF-8?q?=EC=84=A4=20=ED=8C=9D=EC=97=85=20=ED=99=94=EB=A9=B4=20=EC=83=81?= =?UTF-8?q?=EC=84=B8=EA=B5=AC=ED=98=84=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wr/app/map/newWindowPopup.tsx | 49 +++++++++++++++--------------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/apps/wr/app/map/newWindowPopup.tsx b/apps/wr/app/map/newWindowPopup.tsx index dd2334e29..e14a4447d 100644 --- a/apps/wr/app/map/newWindowPopup.tsx +++ b/apps/wr/app/map/newWindowPopup.tsx @@ -109,9 +109,9 @@ const NewWindowPopup: React.FC = ({ pbnstSttn.pbnstSttnRes && pbnstSttn.pbnstSttnRes[index] ) { - console.log(pbnstSttn.pbnstSttnRes[index]) setPbnstRuseFcltPk(pbnstSttn.pbnstSttnRes[index].pbnstRuseFcltPk); - getPbnStnDetail(pbnstSttn.pbnstSttnRes[index].pbnstRuseFcltPk); + fetchpbnstDetail(pbnstSttn.pbnstSttnRes[index].pbnstRuseFcltPk) + setActiveTab(pbnstSttn.pbnstSttnRes[index].pbnstRuseFcltPk) setShowDetailView(true); // 상세정보 화면 표시 상태 변경 } }; @@ -136,33 +136,36 @@ const NewWindowPopup: React.FC = ({ setBdstTable(pbnstSttn.pbnstSttnRes); }, [pbnstSttn.pbnstSttnRes]); - +// 재이용시설 상세정보 + const setTabDataFunc = (res:any, pk:string) => { + setTabData((prev) => { + if (prev.some((tab) => tab.id === pk)) { + return prev; // 이미 존재하는 탭이면 추가하지 않음 + } + return [...prev, { id: pk, name: `재이용시설 ${prev.length + 1}`, data: res }]; + }); + } // 재이용시설 조회 const fetchpbnstDetail = async (featureCd: string) => { try { const response = await getFetch("wr", `/reuseFc/pubFc/${featureCd}`); // 중복 데이터 추가 방지 - setTabData((prev) => { - if (prev.some((tab) => tab.id === featureCd)) { - return prev; // 이미 존재하는 탭이면 추가하지 않음 - } - return [...prev, { id: featureCd, name: `재이용시설 ${prev.length + 1}`, data: response }]; - }); + setTabDataFunc(response, featureCd); } catch (error) { console.error("Error fetching data:", error); } }; - useEffect(() => { - if (featureData) { - const selectedVal = featureData.getProperties(); - const featureCd = selectedVal?.swtrmt_fclt_cd; - if (featureCd && featureCd !== pbnstRuseFcltPk) { - setPbnstRuseFcltPk(featureCd); // 현재 호출된 featureCd 저장 - setActiveTab(featureCd) - fetchpbnstDetail(featureCd); // 상세정보 가져오기 - } - } - }, [featureData]); + // useEffect(() => { + // if (featureData) { + // const selectedVal = featureData.getProperties(); + // const featureCd = selectedVal?.swtrmt_fclt_cd; + // if (featureCd && featureCd !== pbnstRuseFcltPk) { + // setPbnstRuseFcltPk(featureCd); // 현재 호출된 featureCd 저장 + // setActiveTab(featureCd) + // fetchpbnstDetail(featureCd); // 상세정보 가져오기 + // } + // } + // }, [featureData]); useEffect(() => { if (pbnstSttn.pbnstDetail) { @@ -225,7 +228,7 @@ const NewWindowPopup: React.FC = ({ layer.data?.lyrNm?.includes('하수처리') || layer.data?.lyrNm?.includes('재이용시설') ); const actTabData = tabData.find((tab) => tab.id === activeTab); - + console.log('tavbData',actTabData) // swcho : true, false 값 변경하여 시설 변경하여 테스트하였음 // 재이용시설 const isPbnst = true; @@ -235,8 +238,6 @@ const NewWindowPopup: React.FC = ({ const isRswt = false; // 온배수시설 const isHtdfl = false; - - return (
{/* 체크된 레이어가 없는 경우 (재이용시설 목록) */} @@ -260,7 +261,7 @@ const NewWindowPopup: React.FC = ({ />
From ed38451f84b9315f0c47486c68771d2f035dc554 Mon Sep 17 00:00:00 2001 From: leedj312 Date: Wed, 2 Jul 2025 20:19:02 +0900 Subject: [PATCH 049/108] =?UTF-8?q?=EA=B2=B0=ED=95=A8=EB=8C=80=EC=9E=A5=20?= =?UTF-8?q?=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 --- .../ruseFcltMng/prfmncReg/chkgdnReg/page.tsx | 6 +- .../prfmncReg/chkgdnReg/rainChkgdn/excel.tsx | 20 +-- .../prfmncReg/chkgdnReg/rainChkgdn/index.tsx | 168 ++++++++++-------- .../chkgdnSttn/rainChkgdnSttn/headList.tsx | 2 +- .../chkgdnSttn/rainChkgdnSttn/index.tsx | 3 +- apps/wr/components/lib/func/TableData.tsx | 3 +- .../ui/modal/chkgd/chkgdExcel/chkgdExcel.tsx | 71 ++++---- .../ui/modal/chkgd/chkgdModal/chkgdModal.tsx | 23 ++- .../chkgdTable/rainChkgd/rainChkgdTable.tsx | 7 +- apps/wr/public/sample_rainy.xlsx | Bin 11216 -> 11262 bytes 10 files changed, 166 insertions(+), 137 deletions(-) diff --git a/apps/wr/app/ruseFcltMng/prfmncReg/chkgdnReg/page.tsx b/apps/wr/app/ruseFcltMng/prfmncReg/chkgdnReg/page.tsx index 53a7a31f3..88a0de300 100644 --- a/apps/wr/app/ruseFcltMng/prfmncReg/chkgdnReg/page.tsx +++ b/apps/wr/app/ruseFcltMng/prfmncReg/chkgdnReg/page.tsx @@ -20,7 +20,6 @@ import { SearchDiv, SearchFormLeft, SearchFormRight, - SearchInput, SelectBox, Title, UiModal, @@ -29,7 +28,6 @@ import ListDiv from "@common/business_components/ui/list_div"; import { Button, Label } from "@common/components/ui"; import { useCallback, useEffect, useState } from "react"; import { Toaster } from "react-hot-toast"; -import { shallow } from "zustand/shallow"; export default function Page() { const { @@ -135,8 +133,8 @@ export default function Page() { ); useEffect(() => { - rainChkgdn.isSttn = false; - rswtChkgdn.isSttn = false; + //rainChkgdn.isSttn = false; + //rswtChkgdn.isSttn = false; if (!rainChkgdn.isSttn && !rswtChkgdn.isSttn) { rainChkHeadChange({ rainChkgdn }, true); rswtChkHeadChange({ rswtChkgdn }, true); diff --git a/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/excel.tsx b/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/excel.tsx index 5754c02fc..58e8d6bf0 100644 --- a/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/excel.tsx +++ b/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/excel.tsx @@ -94,15 +94,15 @@ export const excelToRainChkData = (excelData: any, year: string) => { } const makeGuidDetail = Array.from({ length: 12 }, (_, i) => { let month = `${i + 1}`; - const splyData = item[`보급_${month}월`]; + const splyData = item[`대체_${month}월`]; const ruseData = item[`빗물_${month}월`]; if (month.length < 2) month = "0" + month; return { utztnUsgCd: item["이용용도"], ym: year + month, chckYmd: checkYmd, - splyUswtrUtztnQnt: Number(splyData ?? 0), - ruseFcltUtztnQnt: Number(ruseData ?? 0), + splyUswtrUtztnQnt: Number(splyData) || 0, + ruseFcltUtztnQnt: Number(ruseData) || 0, flg: "RNB", }; }); @@ -120,13 +120,13 @@ export const excelToRainChkData = (excelData: any, year: string) => { rainyDetails: [ { utztnUsgCd: item["이용용도"] ?? "", - ph: Number(item["PH"] ?? 0), - trbt: Number(item["탁도"] ?? 0), - grssClfg: Number(item["총대장균군수"] ?? 0), - etc: item["기타"] ?? "", // 필드 누락 방지 - vltnDt: vltnDt ?? "", - vltnMttr: item["위반사항"] ?? "", - levyAmt: Number(item["과태료부과금액(천원)"] ?? 0), + ph: Number(item["PH"] || 0), + trbt: Number(item["탁도"] || 0), + grssClfg: Number(item["총대장균군수"] || 0), + etc: item["기타"] || "", // 필드 누락 방지 + vltnDt: vltnDt || "-", + vltnMttr: item["위반사항"] || "-", + levyAmt: Number(item["과태료부과금액(천원)"] || 0), rgtr: TEST_UUID, }, ], diff --git a/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/index.tsx b/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/index.tsx index 4af27a51c..4c7f2d514 100644 --- a/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/index.tsx +++ b/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/index.tsx @@ -1,7 +1,5 @@ import { buildingType } from "@/app/store/common/code"; -import { - TEST_UUID, -} from "@/app/store/ruseFcltMng/fcltReg/rainReg"; +import { TEST_UUID } from "@/app/store/ruseFcltMng/fcltReg/rainReg"; import { deleteFectch, fetchType, @@ -22,6 +20,7 @@ import { type chHeadType, } from "./headList"; import type { TableUpperProps } from "@/app/store/common/type/header"; +import toast from "react-hot-toast"; export type rainChkZodType = z.infer; const fcltDiv = [ @@ -52,7 +51,12 @@ export type rainChkgdnState = { }; type rainChkgdnActions = { - rainChkInit: (pageNo: string, cnt: string, sggCd: string, searchTxt: string) => void; + rainChkInit: ( + pageNo: string, + cnt: string, + sggCd: string, + searchTxt: string + ) => void; rainChkDetilUpdate(rainChk: rainChkZodType[]): Promise; rainChkGuidUpdate(rainChk: rainChkZodType): Promise; rainChkExcelDownload( @@ -98,11 +102,20 @@ const makeMdfrChk = (data: rainChkZodType[], type: string, toDo: string) => { return chData; }; -const initFetch = async (pageNo: string, cnt: string, txt: string | undefined, sgg: string | undefined) => { +const initFetch = async ( + pageNo: string, + cnt: string, + txt: string | undefined, + sgg: string | undefined +) => { const currentYear = new Date().getFullYear().toString(); const rainRes = await getFetch( "wr", - `/reuseChk/rainy` + (pageNo ? `/${pageNo}` : "/1") + (cnt ? `/${cnt}` : "/20") + (txt ? `/${txt}` : `/${currentYear}`) + (sgg ? `/${sgg}` : "/0") + `/reuseChk/rainy` + + (pageNo ? `/${pageNo}` : "/1") + + (cnt ? `/${cnt}` : "/20") + + (txt ? `/${txt}` : `/${currentYear}`) + + (sgg ? `/${sgg}` : "/0") ); return rainRes; }; @@ -131,32 +144,31 @@ export const rainChkgdnActionsExport: (set: any) => rainChkgdnActions = ( rainChkExcelDataUPload: async (excelData: rainChkZodType[]) => { try { const res = await postFectch("/reuseChk/rainy", excelData); - console.log("res", res); - console.log("res.ok", res.ok) - if (res.ok) { - ToastFormUi({ - body: "지도점검 등록이 완료되었습니다. ", - title: "지도점검 등록", - }); + if (res.status === 200) { + const currentYear = new Date().getFullYear().toString(); + const rainChkRes = await initFetch("1", "20", currentYear, "0"); + return set((state: rainChkgdnState) => ({ + ...state, + rainChkgdn: { + ...state.rainChkgdn, + rainChk: rainChkRes, + }, + })); + toast.success("지도점검 등록이 완료되었습니다."); + } else { + toast.error("지도점검 등록이 실패되었습니다."); } - const currentYear = new Date().getFullYear().toString(); - const rainChkRes = await initFetch("1", "20", currentYear, "0"); - return set((state: rainChkgdnState) => ({ - ...state, - rainChkgdn: { - ...state.rainChkgdn, - rainChk: rainChkRes, - }, - })); } catch (error) { console.log(error); - ToastFormUi({ - body: "지도점검 등록이 실패되었습니다.", - title: "지도점검 등록", - }); + toast.error("지도점검 등록이 실패되었습니다."); } }, - rainChkInit: async (pageNo: string, cnt: string, searchTxt: string, sggCd: string) => { + rainChkInit: async ( + pageNo: string, + cnt: string, + searchTxt: string, + sggCd: string + ) => { try { const rainChkRes = await initFetch(pageNo, cnt, searchTxt, sggCd); @@ -176,47 +188,49 @@ export const rainChkgdnActionsExport: (set: any) => rainChkgdnActions = ( const chData = await makeMdfrChk(rainChk, "rain", "rainDetil"); const res = await postFectch("/reuseChk/rainy", chData); - if (res.ok) { - ToastFormUi({ - body: "지도점검 보급수 및 밋물이용량 수정이 완료되었습니다. ", - title: "지도점검 수정", - }); + if (res.staus === 200) { + toast.success( + "지도점검 보급수 및 밋물이용량 수정이 완료되었습니다. " + ); + const currentYear = new Date().getFullYear().toString(); + const rainChkRes = await initFetch("1", "20", currentYear, "11"); + return set((state: rainChkgdnState) => ({ + ...state, + rainChkgdn: { + ...state.rainChkgdn, + rainChk: rainChkRes, + }, + })); + } else { + toast.error("지도점검 보급수 및 밋물이용량 수정 오류입니다."); } - const currentYear = new Date().getFullYear().toString(); - const rainChkRes = await initFetch(currentYear, "0"); - return set((state: rainChkgdnState) => ({ - ...state, - rainChkgdn: { - ...state.rainChkgdn, - rainChk: rainChkRes, - }, - })); } catch (error) { console.log(error); - alert("수정에 실패하였습니다."); + toast.error("지도점검 보급수 및 밋물이용량 수정 오류입니다."); } }, rainChkGuidUpdate: async (rainChk: rainChkZodType) => { try { //const chData = await makeMdfrChk(rainChk, "guideDetails", "rainDetil"); const res = await putFectch("/reuseChk/rainy", rainChk); - if (res.ok) { - ToastFormUi({ - body: "지도점검 수질 및 과태료 수정이 완료되었습니다. ", - title: "지도점검 수정", - }); + if (res.status === 200) { + toast.success("지도점검 수질 및 과태료 수정이 완료되었습니다. "); + + const currentYear = new Date().getFullYear().toString(); + const rainChkRes = await initFetch("1", "20", currentYear, "11"); + return set((state: rainChkgdnState) => ({ + ...state, + rainChkgdn: { + ...state.rainChkgdn, + rainChk: rainChkRes, + }, + })); + }else{ + toast.error("지도점검 수질 및 과태료 수정 오류입니다."); + } - const currentYear = new Date().getFullYear().toString(); - const rainChkRes = await initFetch(currentYear, "0"); - return set((state: rainChkgdnState) => ({ - ...state, - rainChkgdn: { - ...state.rainChkgdn, - rainChk: rainChkRes, - }, - })); } catch (error) { - console.log(error); + toast.error("지도점검 수질 및 과태료 수정 오류입니다."); } }, rainChkDelete: async (chkgdnPks: number[]) => { @@ -224,22 +238,23 @@ export const rainChkgdnActionsExport: (set: any) => rainChkgdnActions = ( const res = await deleteFectch(`/reuseChk/rainy`, chkgdnPks); if (res) { - ToastFormUi({ - body: "삭제가 완료되었습니다. ", - title: "지도점검 삭제", - }); + toast.success("삭제가 완료되었습니다. "); + + const currentYear = new Date().getFullYear().toString(); + const rainChkRes = await initFetch("1", "20", currentYear, "11"); + return set((state: rainChkgdnState) => ({ + ...state, + rainChkgdn: { + ...state.rainChkgdn, + rainChk: rainChkRes, + }, + })); + }else{ + toast.error("삭제시 오류가 발생하였습니다.. "); + } - const currentYear = new Date().getFullYear().toString(); - const rainChkRes = await initFetch(currentYear, "0"); - return set((state: rainChkgdnState) => ({ - ...state, - rainChkgdn: { - ...state.rainChkgdn, - rainChk: rainChkRes, - }, - })); } catch (error) { - ToastFormUi({ body: "삭제를 실패했습니다.. ", title: "지도점검 삭제" }); + toast.error("삭제시 오류가 발생하였습니다.. "); } }, rainChkHeadChange: async (rainChkgdn: rainChkgdnState, isEdit: boolean) => { @@ -249,16 +264,13 @@ export const rainChkgdnActionsExport: (set: any) => rainChkgdnActions = ( const ruseHeadList = rainChkgdn.rainChkgdn.ruseHeadList; rainChkgdnHeadList.forEach((head) => { - if (head.edit !== undefined) - head.edit = isEdit; + if (head.edit !== undefined) head.edit = isEdit; }); splHeadList.forEach((head) => { - if (head.edit !== undefined) - head.edit = isEdit; + if (head.edit !== undefined) head.edit = isEdit; }); ruseHeadList.forEach((head) => { - if (head.edit !== undefined) - head.edit = isEdit; + if (head.edit !== undefined) head.edit = isEdit; }); return set((state: rainChkgdnState) => ({ @@ -267,7 +279,7 @@ export const rainChkgdnActionsExport: (set: any) => rainChkgdnActions = ( ...state.rainChkgdn, rainChkgdnHeadList: rainChkgdnHeadList, splHeadList: splHeadList, - ruseHeadList: ruseHeadList + ruseHeadList: ruseHeadList, }, })); } catch (error) { diff --git a/apps/wr/app/store/prfmncSttn/chkgdnSttn/rainChkgdnSttn/headList.tsx b/apps/wr/app/store/prfmncSttn/chkgdnSttn/rainChkgdnSttn/headList.tsx index f112db0bd..8c90e0829 100644 --- a/apps/wr/app/store/prfmncSttn/chkgdnSttn/rainChkgdnSttn/headList.tsx +++ b/apps/wr/app/store/prfmncSttn/chkgdnSttn/rainChkgdnSttn/headList.tsx @@ -11,7 +11,7 @@ export const rainChkgdnHeadSttnExport: TableUpperProps[] = [ { id: "dtfcltCpct", title: "빗물저류조용량(m³)", mergeCell: true }, { id: "chckYmd", title: "점검일", mergeCell: true }, { id: "utztnUsgCd", title: "이용용도" }, - { id: "totalSplyUswtrUtztnQnt", title: "연간보급수이용량(m³)" }, + { id: "totalSplyUswtrUtztnQnt", title: "연간대체수이용량(m³)" }, { id: "totalRuseFcltUtztnQnt", title: "연간빗물이용량(m³)" }, { id: "ph", diff --git a/apps/wr/app/store/prfmncSttn/chkgdnSttn/rainChkgdnSttn/index.tsx b/apps/wr/app/store/prfmncSttn/chkgdnSttn/rainChkgdnSttn/index.tsx index ea3f58a40..2136dfebe 100644 --- a/apps/wr/app/store/prfmncSttn/chkgdnSttn/rainChkgdnSttn/index.tsx +++ b/apps/wr/app/store/prfmncSttn/chkgdnSttn/rainChkgdnSttn/index.tsx @@ -1,11 +1,10 @@ import type { rainChkZodType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn"; -import { type TableUpperProps } from "@/app/store/ruseFcltMng/fcltReg/rainReg"; import { getFetch } from "@/components/lib/api"; -import { ToastFormUi } from "@/components/ui/toast/toastFromUi"; import type { SelectDataType } from "@common/business_components/ui"; import { rainChkgdnHeadSttnExport, ruseSttnHeadListExport, splSttnHeadListExport } from "./headList"; import { rainHeadNmExport, type chHeadType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList"; import { buildingExcelRainSttn } from "./excel"; +import type { TableUpperProps } from "@/app/store/common/type/header"; const fcltDiv = [ { val: "RST", text: "중수도시설" }, diff --git a/apps/wr/components/lib/func/TableData.tsx b/apps/wr/components/lib/func/TableData.tsx index 9ac26827d..7a35e02c4 100644 --- a/apps/wr/components/lib/func/TableData.tsx +++ b/apps/wr/components/lib/func/TableData.tsx @@ -103,6 +103,7 @@ export const chRowspanTable = ( const key = head.id; const isMerged = head.mergeCell; const value = row[key]; + // 병합 대상이면 중복 렌더링 여부 판단 if (isMerged) { const renderedSet = renderedMap.get(key) ?? new Set(); @@ -152,7 +153,7 @@ export const chRowspanTable = ( onChange={(e) => { handleChange(rowIndex, head.id, e.target.value); }} - value={value ?? ""} + value={value || ""} disabled={!selectedIds.includes(row.chkgdnPk)} /> ) : ( diff --git a/apps/wr/components/ui/modal/chkgd/chkgdExcel/chkgdExcel.tsx b/apps/wr/components/ui/modal/chkgd/chkgdExcel/chkgdExcel.tsx index 15badc59d..8828420b8 100644 --- a/apps/wr/components/ui/modal/chkgd/chkgdExcel/chkgdExcel.tsx +++ b/apps/wr/components/ui/modal/chkgd/chkgdExcel/chkgdExcel.tsx @@ -1,7 +1,7 @@ import { useWrStore } from "@/app/store"; 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 { chkgdRegExcel } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList"; import type { rswtChkZodType } from "@/app/store/prfmncReg/chkgdnReg/rswrtChkgdn"; import { excelToRswtGuideData, @@ -18,24 +18,31 @@ import { SelectBox, Title, } from "@common/business_components/ui"; +import { Card } from "@common/business_components/ui/chart/lib/card"; import { Button, Input } from "@common/components/ui"; import { zodResolver } from "@hookform/resolvers/zod"; import { useEffect, useState } from "react"; import { useForm } from "react-hook-form"; +import toast from "react-hot-toast"; import type { z } from "zod"; export default function ChkgdExcel({ type }: { type: string }) { - const { - rswtChkgdn, - rainChkExcelDataUPload, - rswtChkExceDataUpload, - } = useWrStore((state) => { - return state; - }); - const [files, setFiles] = useState(null); + const { rswtChkgdn, rainChkExcelDataUPload, rswtChkExceDataUpload } = + useWrStore((state) => { + return state; + }); const [excelData, setExcelData] = useState< rswtChkZodType[] | rainChkZodType[] >(); + + const [file, setFile] = useState(null); + + const handleFileChange = (e: React.ChangeEvent) => { + const selectedFile = e.target.files?.[0]; + if (selectedFile) { + setFile(selectedFile); + } + }; const [excelRswtData, setExcelRswtData] = useState(); const currentYear = new Date().getFullYear(); @@ -49,7 +56,7 @@ export default function ChkgdExcel({ type }: { type: string }) { ); const defaultValues: z.infer = chkgdRegExcel.parse({ - chckYr: String(currentYear) + chckYr: String(currentYear), }); const form = useForm>({ @@ -58,14 +65,10 @@ export default function ChkgdExcel({ type }: { type: string }) { }); useEffect(() => { - if (files) { - const file = files[0]; + if (file) { if (form.getValues("chckYr").length < 1) { - ToastFormUi({ - body: "파일업로드 이전에 등록년도를 작성해주세요 ", - title: "지도점검 등록", - }); - setFiles(null); + toast.error("파일업로드 이전에 등록년도를 작성해주세요 "); + setFile(null); return; } if (file) @@ -87,7 +90,6 @@ export default function ChkgdExcel({ type }: { type: string }) { form.getValues("chckYr") ); - const keyGenerator = (item: rswtChkZodType) => `${item.bdstRuseFcltFk}|${item.chckYmd}|${item.utztnUsgCd}`; const totalData: rswtChkZodType[] = guideData.map((guide) => { @@ -98,12 +100,11 @@ export default function ChkgdExcel({ type }: { type: string }) { guide.utztnUsgCd === polution.utztnUsgCd ); - if (fusion) { return { ...guide, rswtrPollutions: fusion?.rswtrPollutions, - rswtrDetails:fusion?.rswtrDetails + rswtrDetails: fusion?.rswtrDetails, }; } else { return { @@ -118,19 +119,17 @@ export default function ChkgdExcel({ type }: { type: string }) { (item) => !guideKey.has(keyGenerator(item)) ); + const listTotalData: rswtChkZodType[] = [ + ...totalData, + ...onlypolution, + ]; - const listTotalData:rswtChkZodType[] =[ - ...totalData, - ...onlypolution - ] - - - setExcelRswtData(listTotalData); + setExcelRswtData(listTotalData); } }); } } - }, [files]); + }, [file]); function findMatchingItems( arrayA: T[], @@ -144,7 +143,6 @@ export default function ChkgdExcel({ type }: { type: string }) { (!condition || condition(item)) && bKeySet.has(keySelector(item)) ); } - const onSubmit = (values: z.infer) => { if (values.chckYr === "") { @@ -160,7 +158,6 @@ export default function ChkgdExcel({ type }: { type: string }) { }); return; } else { - rainChkExcelDataUPload(excelData as rainChkZodType[]); } } else if (type === "RST") { @@ -223,13 +220,13 @@ export default function ChkgdExcel({ type }: { type: string }) { name="file" render={({ field }) => ( - { - 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 4e3d61008..965bc7275 100644 --- a/apps/wr/components/ui/modal/chkgd/chkgdModal/chkgdModal.tsx +++ b/apps/wr/components/ui/modal/chkgd/chkgdModal/chkgdModal.tsx @@ -39,7 +39,6 @@ export default function ChkgdModal({ type, obj }: { type: string; obj: any }) { "4": [] as z.infer[], }); - const { rainChk, ruseHeadList, splHeadList } = rainChkgdn; const [updateData, setUpdateData] = useState(); const initStats = (): z.infer => ({ @@ -97,7 +96,6 @@ export default function ChkgdModal({ type, obj }: { type: string; obj: any }) { target = { utztnUsgCd: code, ruseTotal: 0, splTotal: 0 }; acc.push(target); } - // 월별 값 누적 target[ruseMonthKey] = (target[ruseMonthKey] ?? 0) + ruseValue; target[splMonthKey] = (target[splMonthKey] ?? 0) + splValue; @@ -106,7 +104,18 @@ export default function ChkgdModal({ type, obj }: { type: string; obj: any }) { target.splTotal += splValue; return acc; }, []); - setGuidData(result); + + if (result) { + // '기타' 항목을 배열의 맨 뒤로 정렬합니다. + result.sort((a: splRuseWaterType, b: splRuseWaterType) => { + if (a.utztnUsgCd === "기타") return 1; // a를 b 뒤로 보냅니다. + if (b.utztnUsgCd === "기타") return -1; // b를 a 뒤로 보냅니다. + return 0; // 순서를 유지합니다. + }); + } + + console.log("result ", result); + setGuidData(result as splRuseWaterType[]); if (obj.rswtrPollutions) { const resultPolution: Record = { @@ -159,6 +168,14 @@ export default function ChkgdModal({ type, obj }: { type: string; obj: any }) { if (!resultPolution[nodeKey]) { resultPolution[nodeKey] = []; } + + // '기타' 항목을 배열의 맨 뒤로 정렬합니다. + resultPolution[nodeKey].sort((a: any, b: any) => { + if (a.utztnUsgCd === "기타") return 1; // a를 b 뒤로 보냅니다. + if (b.utztnUsgCd === "기타") return -1; // b를 a 뒤로 보냅니다. + return 0; // 순서를 유지합니다. + }); + resultPolution[nodeKey].push(average); }); diff --git a/apps/wr/components/ui/table/chkgdTable/rainChkgd/rainChkgdTable.tsx b/apps/wr/components/ui/table/chkgdTable/rainChkgd/rainChkgdTable.tsx index 5166b461f..720a73ab4 100644 --- a/apps/wr/components/ui/table/chkgdTable/rainChkgd/rainChkgdTable.tsx +++ b/apps/wr/components/ui/table/chkgdTable/rainChkgd/rainChkgdTable.tsx @@ -70,10 +70,15 @@ export default function RainChkgdTable({ }, [makeTableData, handleClick]); + useEffect(() =>{ + + + },[rainChkgdn.rainChk]) useEffect(() => { setSelectedIds([])// 셀렉트 초기화를 위한 작업 if (rainChkData) { + const rainChg = rainChkData.map((item: any) => { const bud = tableData([item.building], { code }); return { @@ -82,6 +87,7 @@ export default function RainChkgdTable({ rowspan: item.rainyDetails?.length, }; }); + setMakeTableData(rainChkTableData(rainChg)); } }, [rainChkData, code]); @@ -106,7 +112,6 @@ export default function RainChkgdTable({ ); if (findRain != undefined) { - console.log(findRain) return { ...rd, ph: Number(findRain.ph), diff --git a/apps/wr/public/sample_rainy.xlsx b/apps/wr/public/sample_rainy.xlsx index 73a52d89c3472af48a29d34e8e4edd82fbfbeacc..ec7577c510bf5ffefdbf146c56ac3d6569ce5b0b 100644 GIT binary patch delta 5270 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 From 31140ea19046ab6e5d95cf223de149da7593fdfb Mon Sep 17 00:00:00 2001 From: jyha00 Date: Wed, 2 Jul 2025 20:19:48 +0900 Subject: [PATCH 050/108] =?UTF-8?q?=ED=95=98=EC=88=98=EC=9E=AC=EC=9D=B4?= =?UTF-8?q?=EC=9A=A9=EC=8B=9C=EC=84=A4=ED=98=84=ED=99=A9=20=EA=B2=B0?= =?UTF-8?q?=ED=95=A8=EB=8C=80=EC=9E=A5=20=EC=98=A4=EB=A5=98=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ruseFcltMng/fcltSttn/rainSttn/page.tsx | 149 ++++++++++++++++-- .../fcltSttn/rainSttn/headList.tsx | 8 +- .../ruseFcltMng/fcltSttn/rainSttn/index.tsx | 3 + .../sttn/pbnstSttnModal/pbnstSttnModal.tsx | 133 ++++++++-------- 4 files changed, 213 insertions(+), 80 deletions(-) diff --git a/apps/wr/app/ruseFcltMng/fcltSttn/rainSttn/page.tsx b/apps/wr/app/ruseFcltMng/fcltSttn/rainSttn/page.tsx index f101be91b..7e068bcd0 100644 --- a/apps/wr/app/ruseFcltMng/fcltSttn/rainSttn/page.tsx +++ b/apps/wr/app/ruseFcltMng/fcltSttn/rainSttn/page.tsx @@ -4,6 +4,7 @@ import { useWrStore } from "@/app/store"; import { btnClass } from "@/app/store/common/code"; import { excelDownload, + getCity, getCityOrSgg, tableData, } from "@/components/lib/func/commom"; @@ -11,10 +12,16 @@ import HeadChModal from "@/components/ui/modal/sttn/headChModal/headChModal"; import { ContentsDiv, SearchBaseForm, + SearchDiv, + SearchFormLeft, + SearchFormRight, + SearchInput, + SelectBox, Title, UiModal, UiTable, type ReturnType, + type SelectDataType, } from "@common/business_components/ui"; import ListDiv from "@common/business_components/ui/list_div"; import { Button } from "@common/components/ui"; @@ -26,6 +33,7 @@ import type { import { HeadMakeColSpan } from "@common/business_components/ui/table/HeadMakeColSpan"; import SttnDetailModal from "@/components/ui/modal/sttn/sttnDetailModal/sttnDetailModal"; import BdstChgModal from "@/components/ui/modal/fcltReg/bdstChgModal/bdstChgModal"; +import type { objSearchType } from "@/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal"; export default function RainSttn() { const { code, rainSttn, rainSttnSearch, rainSttnInit } = useWrStore( @@ -37,6 +45,8 @@ export default function RainSttn() { const [pageNation, setPageNation] = useState(1); const [isChHead, setIsChHead] = useState(false); const [bdstTable, setBdstTable] = useState(); + const [selectSgg, setSelectSgg] = useState() + const [objSearch, setObjSearch] = useState({ sgg: "", city: "" }); const [detailModal, setDetailModal] = useState<{ isOpen: boolean, data: customRainBdst | undefined }>({ data: undefined, isOpen: false }) const [bdstChgProps, setBdstChgProps] = useState<{ isOpen: boolean; @@ -56,6 +66,38 @@ export default function RainSttn() { } }; + const getSelCity = (val: string | number) => { + const cityValue = String(val); + + if (cityValue === "0") { + setSelectSgg([]); + setObjSearch({ city: cityValue, sgg: "" }); + } else { + const data = getCity(cityValue, code.sggCd); + setSelectSgg(data); + + const firstSggVal = data.find(item => item.val === "0")?.val || (data[0]?.val ?? ""); + setObjSearch({ city: cityValue, sgg: firstSggVal }); + } + }; + + const getSgg = (val: string | number) => { + console.log("val", val) + setObjSearch({ ...objSearch, sgg: String(val) }) + }; + + const getConValue = () => { + if (!objSearch.city || objSearch.city === "0") { + return "0"; + } + if (objSearch.sgg && objSearch.sgg !== "0") { + return objSearch.sgg; + } + return objSearch.city; + }; + + + useEffect(() => { if (!rainSttn.isInit) { rainSttnInit(codeData ?? "0"); @@ -63,19 +105,30 @@ export default function RainSttn() { }, [rainSttn.isInit, rainSttnInit]); useEffect(() => { - if (rainSttnRes.length > 0 && code.cityCd.length > 0) { - const data = tableData(rainSttnRes, { code }); - const chData = data - .map((item: any) => ({ + const data = tableData(rainSttnRes, { code }); + const chData = data + .map((item: any) => { + console.log("item", item); + console.log("왜안돼", item.reuseBuilding[0].rainyObj.dtfcltCpct) + 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 ? "Y" : "N", - })) - .filter((item: any) => item.rainyObj !== undefined); - - setBdstTable(chData); - } + }; + }) + .filter((item: any) => item.rainyObj !== undefined); + console.log("chData", chData) + setBdstTable(chData); }, [code, rainSttnRes, rainSttn.rainSttHeadList]); useEffect(() => { @@ -94,9 +147,14 @@ export default function RainSttn() { } }, [rainSttn.excelList]); + // const excelClick = useCallback(() => { + // if (codeData) rainSttnSearch(1, "10000", codeData, code.cityCd, code.sggCd); + // }, [rainSttnSearch, codeData, code.cityCd, code.sggCd]); const excelClick = useCallback(() => { - if (codeData) rainSttnSearch(1, "10000", codeData, code.cityCd, code.sggCd); - }, [rainSttnSearch, codeData, code.cityCd, code.sggCd]); + // 최신 검색 조건 반영 + const con = getConValue(); + rainSttnSearch(1, "10000", String(con), code.cityCd, code.sggCd); + }, [rainSttnSearch, objSearch, code.cityCd, code.sggCd]); return ( <> @@ -104,7 +162,70 @@ export default function RainSttn() {
- <SearchBaseForm + + + <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={code.cityCd} + onSelectValue={getSelCity} + /> + <SelectBox + className="col-span-2 w-1/6 h-full text-2xl ml-4" + label="시군구" + selectClass={""} + selectArray={selectSgg || []} + value={objSearch.sgg} // value props 추가 + onSelectValue={getSgg} + /> + </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 + size={"sm"} + onClick={() => { + const con = getConValue(); + rainSttnSearch(1, "20", String(con), code.cityCd, code.sggCd); + }} + > + 조회 + </Button> + <Button + size={"sm"} + className={`${btnClass} green`} + onClick={excelClick} + > + 다운로드 + </Button> + <Button + size={"sm"} + className={btnClass} + onClick={() => { + setIsChHead(true); + }} + > + 조회항목설정 + </Button> + </div> + </SearchFormRight> + </SearchDiv> + + + + + + + + + + + {/* <SearchBaseForm selectSidoData={code.cityCd} selectSigunData={code.sggCd} selectRegionData={code.rvbsnCd} @@ -137,14 +258,14 @@ export default function RainSttn() { btnClass={btnClass} ref={ref} isAdmin={false} - /> + /> */} </div> </div> <div className="grid grid-cols-6 gap-4 "> <div className="col-span-6"> <ListDiv> <UiTable - headName={[]} + headName={undefined} tableData={bdstTable || []} children={ <HeadMakeColSpan upHeadList={rainSttn.rainSttNodeList} /> diff --git a/apps/wr/app/store/ruseFcltMng/fcltSttn/rainSttn/headList.tsx b/apps/wr/app/store/ruseFcltMng/fcltSttn/rainSttn/headList.tsx index 7db00678d..8dd2a4d6f 100644 --- a/apps/wr/app/store/ruseFcltMng/fcltSttn/rainSttn/headList.tsx +++ b/apps/wr/app/store/ruseFcltMng/fcltSttn/rainSttn/headList.tsx @@ -31,8 +31,8 @@ export const rainSttHeadListExport: z.infer<typeof sttnHeadListType> = [ visible: true, }, { - id: "bdstUsgCd", - title: "건축물용도코드", + id: "bdsgUsgText", + title: "건축물용도", visible: true, }, { @@ -209,8 +209,8 @@ export const rainSttNodeListExport: CommonTableProps[] = [ colSpan: 1, }, { - id: "bdstUsgCd", - title: "건축물용도코드", + id: "bdsgUsgText", + title: "건축물용도", upName: "bdnstInfo", visible: true, row: 2, diff --git a/apps/wr/app/store/ruseFcltMng/fcltSttn/rainSttn/index.tsx b/apps/wr/app/store/ruseFcltMng/fcltSttn/rainSttn/index.tsx index 5aa54ee48..42d158d4e 100644 --- a/apps/wr/app/store/ruseFcltMng/fcltSttn/rainSttn/index.tsx +++ b/apps/wr/app/store/ruseFcltMng/fcltSttn/rainSttn/index.tsx @@ -153,10 +153,13 @@ export const rainSttnActionsExport: (set: any) => rainSttnActions = ( sidoCode: SelectDataType[], sggCode: SelectDataType[]) => { try { + let a = `/reuseFc/rainyInfoList/${page}/${total}/${con}` + console.log("주소", a) const rainSttnRes = await getFetch( fetchType.WR, `/reuseFc/rainyInfoList/${page}/${total}/${con}` ); + console.log("rainSttnRes", rainSttnRes) if (Number(total) > 9000) { const rainExcel = excelRainSttnParsing(rainSttnRes, sidoCode, sggCode); set((state: rainSttnState) => { diff --git a/apps/wr/components/ui/modal/sttn/pbnstSttnModal/pbnstSttnModal.tsx b/apps/wr/components/ui/modal/sttn/pbnstSttnModal/pbnstSttnModal.tsx index f4a3735a5..cec8babd0 100644 --- a/apps/wr/components/ui/modal/sttn/pbnstSttnModal/pbnstSttnModal.tsx +++ b/apps/wr/components/ui/modal/sttn/pbnstSttnModal/pbnstSttnModal.tsx @@ -13,6 +13,7 @@ import { type customPbnst, type fcBizType, } from "@/app/store/ruseFcltMng/fcltReg/pbnstReg/type"; +import { formH1Class } from "@/components/lib/class/comClass"; import { PbnstMngForm } from "@/components/ui/form/fcltReg/pbnstReg/mngReg/mngRegForm"; import { ScrollArea, Title, UiTable } from "@common/business_components/ui"; import { HeadMakeColSpan } from "@common/business_components/ui/table/HeadMakeColSpan"; @@ -31,7 +32,7 @@ import { z } from "zod"; export function PbnstSttnModal({ data, titleVisible }: { data: customPbnst; titleVisible?: boolean }) { const { code, pbnstSttn, prfmnc } = useWrStore((state) => state); const [fcltTab, setFcltTab] = useState<string>("fclt"); - const [ruseTab, setRuseTab] = useState<string>("inoutTotal"); + const [ruseTab, setRuseTab] = useState<string>("totalPrf"); const [ruseBizData, setRuseBizData] = useState<{ data: any[]; head: TableUpperProps[]; @@ -229,7 +230,8 @@ export function PbnstSttnModal({ data, titleVisible }: { data: customPbnst; titl (item) => item.pbnstRuseFcltPk === data.pbnstRuseFcltPk )?.prfList; - if (prfList) {console.log("prfList", prfList); + if (prfList) { + console.log("prfList", prfList); getTablePrfData(prfList); } @@ -243,12 +245,12 @@ export function PbnstSttnModal({ data, titleVisible }: { data: customPbnst; titl form.reset({ items: [data] }) } - },[data, form]) + }, [data, form]) - useEffect(()=>{ + useEffect(() => { console.log("prfData", prfData); - },[prfData]) + }, [prfData]) return ( <> <div> @@ -264,72 +266,79 @@ export function PbnstSttnModal({ data, titleVisible }: { data: customPbnst; titl indexMng={indexMng} setIndexMng={setIndexMng} /> - <Tabs defaultValue="fclt" onValueChange={setFcltTab}> - <TabsList className=""> - <TabsTrigger className="!text-xl" value="fclt"> - 하수처리수 재이용시설 현황 - </TabsTrigger> - <TabsTrigger className="!text-xl" value="sply"> - 재이용 원수 취수 현황 - </TabsTrigger> - <TabsTrigger className="!text-xl" value="ppi"> - 재이용 공급관로 현황 - </TabsTrigger> - <TabsTrigger className="!text-xl" value="guarantee"> - 재이용 보증수질 현황 - </TabsTrigger> - <TabsTrigger className="!text-xl" value="dmdCo"> - 재이용 수요처 현황 - </TabsTrigger> - </TabsList> - <TabsContent className="max-h-full" value={fcltTab}> - <UiTable - headName={undefined} - tableData={ruseTableData || []} - headlist={ruseHeadList || []} - pageSize={0} - total={0} - cellClick={undefined} - onPageChange={function (page: number): void { - throw new Error("Function not implemented."); - }} - children={ - fcltTab === "fclt" ? ( - <HeadMakeColSpan upHeadList={pbnstSttn.ruseFcltNodeList} /> - ) : undefined - } - ></UiTable> - </TabsContent> - </Tabs> - - <Tabs onValueChange={setRuseTab}> - <TabsList className=""> - <TabsTrigger className="!text-xl" value="totalPrf"> - 장내외합계 - </TabsTrigger> - <TabsTrigger className="!text-xl" value="inPrf"> - 장내용수 - </TabsTrigger> - <TabsTrigger className="!text-xl" value="outPrf"> - 장외용수 - </TabsTrigger> - </TabsList> - <TabsContent className="max-h-full" value={ruseTab}> - {prfData && ( + <div className="px-6"> + <div className=""> + <h1 className={formH1Class}>하수재이용시설 현황</h1> + </div> + <Tabs defaultValue="fclt" onValueChange={setFcltTab}> + <TabsList className="mt-4"> + <TabsTrigger className="!text-xl" value="fclt"> + 하수처리수 재이용시설 현황 + </TabsTrigger> + <TabsTrigger className="!text-xl" value="sply"> + 재이용 원수 취수 현황 + </TabsTrigger> + <TabsTrigger className="!text-xl" value="ppi"> + 재이용 공급관로 현황 + </TabsTrigger> + <TabsTrigger className="!text-xl" value="guarantee"> + 재이용 보증수질 현황 + </TabsTrigger> + <TabsTrigger className="!text-xl" value="dmdCo"> + 재이용 수요처 현황 + </TabsTrigger> + </TabsList> + <TabsContent className="max-h-full" value={fcltTab}> <UiTable headName={undefined} - tableData={prfData[ruseTab as keyof typeof prfData] || []} - headlist={monthHeadList || []} + tableData={ruseTableData || []} + headlist={ruseHeadList || []} pageSize={0} total={0} cellClick={undefined} onPageChange={function (page: number): void { throw new Error("Function not implemented."); }} + children={ + fcltTab === "fclt" ? ( + <HeadMakeColSpan upHeadList={pbnstSttn.ruseFcltNodeList} /> + ) : undefined + } ></UiTable> - )} - </TabsContent> - </Tabs> + </TabsContent> + </Tabs> + <div className=""> + <h1 className={formH1Class}>하수재이용실적</h1> + </div> + <Tabs defaultValue="totalPrf" onValueChange={setRuseTab}> + <TabsList className="mt-4"> + <TabsTrigger className="!text-xl" value="totalPrf"> + 장내외합계 + </TabsTrigger> + <TabsTrigger className="!text-xl" value="inPrf"> + 장내용수 + </TabsTrigger> + <TabsTrigger className="!text-xl" value="outPrf"> + 장외용수 + </TabsTrigger> + </TabsList> + <TabsContent className="max-h-full" value={ruseTab}> + {prfData && ( + <UiTable + headName={undefined} + tableData={prfData[ruseTab as keyof typeof prfData] || []} + headlist={monthHeadList || []} + pageSize={0} + total={0} + cellClick={undefined} + onPageChange={function (page: number): void { + throw new Error("Function not implemented."); + }} + ></UiTable> + )} + </TabsContent> + </Tabs> + </div> </ScrollArea> </div> </> From 915b6573579e3fcf3ad36859b1670cc5733cbd43 Mon Sep 17 00:00:00 2001 From: YangJinSoo94 <jschrome782@gmail.com> Date: Thu, 3 Jul 2025 09:41:38 +0900 Subject: [PATCH 051/108] =?UTF-8?q?=EB=AC=BC=EC=9E=AC=EC=9D=B4=EC=9A=A9?= =?UTF-8?q?=EC=82=AC=EB=A1=80=EA=B4=80=EB=A6=AC=20=EC=96=91=EC=8B=9D?= =?UTF-8?q?=EC=97=90=20=EC=9E=91=EC=84=B1=EB=B0=A9=EB=B2=95=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wr/public/sample_ruseCaseMng.xlsx | Bin 19429 -> 20324 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/apps/wr/public/sample_ruseCaseMng.xlsx b/apps/wr/public/sample_ruseCaseMng.xlsx index 3a88e0623e067d483927ce6dfdc452c2dbb2ee19..96027d557fe2cfc60d3de10c313c49d78f984591 100644 GIT binary patch delta 12615 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 LHo2<&Yxe&D6inE* delta 11787 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 b@&5KfxHLpqnF0q3gau-}y;%J6?&|*mOKXGP From 1f2316d8b4bc5ba23fbff224833aef0b19ed82c3 Mon Sep 17 00:00:00 2001 From: jyha00 <jyha@mind-one.co.kr> Date: Thu, 3 Jul 2025 10:50:02 +0900 Subject: [PATCH 052/108] =?UTF-8?q?=EC=A4=91=EC=88=98=EB=8F=84/=EC=98=A8?= =?UTF-8?q?=EB=B0=B0=EC=88=98=EC=8B=9C=EC=84=A4=ED=98=84=ED=99=A9=20?= =?UTF-8?q?=EA=B2=80=EC=83=89=EC=A1=B0=EA=B1=B4=20=EB=B3=80=EA=B2=BD,=20-1?= =?UTF-8?q?=EA=B0=92=20=ED=99=95=EC=9D=B8=EB=B6=88=EA=B0=80=EB=A1=9C=20?= =?UTF-8?q?=ED=91=9C=EC=B6=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ruseFcltMng/fcltSttn/htdflSttn/page.tsx | 176 ++++++++++++------ .../ruseFcltMng/fcltSttn/rainSttn/page.tsx | 62 +----- .../ruseFcltMng/fcltSttn/rswtSttn/page.tsx | 155 ++++++++++----- .../ruseFcltMng/fcltSttn/htdflSttn/index.tsx | 16 +- 4 files changed, 242 insertions(+), 167 deletions(-) diff --git a/apps/wr/app/ruseFcltMng/fcltSttn/htdflSttn/page.tsx b/apps/wr/app/ruseFcltMng/fcltSttn/htdflSttn/page.tsx index e43d5b04f..03b549270 100644 --- a/apps/wr/app/ruseFcltMng/fcltSttn/htdflSttn/page.tsx +++ b/apps/wr/app/ruseFcltMng/fcltSttn/htdflSttn/page.tsx @@ -8,6 +8,7 @@ import type { } from "@/app/store/ruseFcltMng/fcltReg/rainReg"; import { excelDownload, + getCity, getCityOrSgg, tableData, } from "@/components/lib/func/commom"; @@ -16,16 +17,22 @@ import SttnDetailModal from "@/components/ui/modal/sttn/sttnDetailModal/sttnDeta import { ContentsDiv, SearchBaseForm, + SearchDiv, + SearchFormLeft, + SearchFormRight, + SelectBox, Title, UiModal, UiTable, type ReturnType, + type SelectDataType, } from "@common/business_components/ui"; import ListDiv from "@common/business_components/ui/list_div"; import { Button } from "@common/components/ui"; import { useCallback, useEffect, useRef, useState } from "react"; import { excelHtdflSttnParsing } from "@/app/store/ruseFcltMng/fcltSttn/htdflSttn/excel"; import { HeadMakeColSpan } from "@common/business_components/ui/table/HeadMakeColSpan"; +import type { objSearchType } from "@/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal"; export default function HtdflSttn() { const { code, htdflSttn, htdflSttnSearch, initHtdfl } = useWrStore( @@ -38,6 +45,8 @@ export default function HtdflSttn() { const [pageNation, setPageNation] = useState(1); const [isChHead, setIsChHead] = useState(false); const [bdstTable, setBdstTable] = useState<customRainBdst[]>(); + const [selectSgg, setSelectSgg] = useState<SelectDataType[] | []>() + const [objSearch, setObjSearch] = useState<objSearchType>({ sgg: "", city: "" }); const [detailModal, setDetailModal] = useState<{ isOpen: boolean; data: customRainBdst | undefined; @@ -50,13 +59,44 @@ export default function HtdflSttn() { const codeData = getCityOrSgg(ref); const excelClick = useCallback(() => { - if (codeData) htdflSttnSearch(1, "10000", codeData); - }, [htdflSttnSearch, codeData]); + // 최신 검색 조건 반영 + const con = getConValue(); + htdflSttnSearch(1, "10000", String(con), code.cityCd, code.sggCd); + }, [htdflSttnSearch, objSearch, code.cityCd, code.sggCd]); useEffect(() => { - if (codeData != null) htdflSttnSearch(pageNation, "20", codeData); + if (codeData != null) htdflSttnSearch(pageNation, "20", codeData, code.cityCd, code.sggCd); }, [pageNation]); + const getSelCity = (val: string | number) => { + const cityValue = String(val); + + if (cityValue === "0") { + setSelectSgg([]); + setObjSearch({ city: cityValue, sgg: "" }); + } else { + const data = getCity(cityValue, code.sggCd); + setSelectSgg(data); + + 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 = () => { + if (!objSearch.city || objSearch.city === "0") { + return "0"; + } + if (objSearch.sgg && objSearch.sgg !== "0") { + return objSearch.sgg; + } + return objSearch.city; + }; + const cellClick = (index: number | undefined, headId: string) => { if ( headId !== "chBuildingYn" && @@ -67,20 +107,15 @@ export default function HtdflSttn() { } }; useEffect(() => { - const excelDown = async () => { - if (htdflSttn.excelList.length > 0) { - if (htdflSttn.excelList.length === 0) return; - const excelData = await tableData(htdflSttn.excelList, { code }); - const htdflExcel = excelHtdflSttnParsing(excelData, code.cityCd, code.sggCd); - excelDownload( - htdflExcel as [[]], - "온배수시설 현황", - "sample_htdflSttn.xlsx", - "A1:S3" - ); - } - }; - excelDown(); + if (htdflSttn.excelList.length > 0) { + if (htdflSttn.excelList.length === 0) return; + excelDownload( + htdflSttn.excelList, + "온배수시설 현황", + "sample_htdflSttn.xlsx", + "A1:S3" + ); + } }, [htdflSttn.excelList, code]); useEffect(() => { @@ -90,19 +125,16 @@ export default function HtdflSttn() { }, [htdflSttn.isInit, initHtdfl]); useEffect(() => { - if (htdflSttnRes.length > 0 && code.cityCd.length > 0) { - const data = tableData(htdflSttnRes, { code }); - const chData = data - .map((item: any) => ({ - ...item, - ...item.reuseBuilding[0], - ...item.reuseBuilding[0].htdflObj, - chBuildingYn: item.chgBuilding[0]?.bdstChgNm !== null ? "Y" : "N", - })) - .filter((item: any) => item.htdflObj !== undefined); - - setBdstTable(chData); - } + const data = tableData(htdflSttnRes, { code }); + const chData = data + .map((item: any) => ({ + ...item, + ...item.reuseBuilding[0], + ...item.reuseBuilding[0].htdflObj, + chBuildingYn: item.chgBuilding[0]?.bdstChgNm !== null ? "Y" : "N", + })) + .filter((item: any) => item.htdflObj !== undefined); + setBdstTable(chData); }, [code, htdflSttnRes, htdflSttn.htdflSttHeadList]); return ( @@ -111,43 +143,65 @@ export default function HtdflSttn() { <div className="grid grid-cols-4 gap-4 "> <div className="col-span-4"> <Title title={"온배수 시설 현황"} /> - <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; - } - htdflSttnSearch(pageNation, "20", con); - } } - excelClick={excelClick} - initClick={function (): void { } } - btnClass={btnClass} - ref={ref} isAdmin={false} /> + + <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={code.cityCd} + onSelectValue={getSelCity} + /> + <SelectBox + className="col-span-2 w-1/6 h-full text-2xl ml-4" + label="시군구" + selectClass={""} + selectArray={selectSgg || []} + value={objSearch.sgg} // value props 추가 + onSelectValue={getSgg} + /> + </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 + size={"sm"} + onClick={() => { + const con = getConValue(); + htdflSttnSearch(1, "20", String(con), code.cityCd, code.sggCd); + }} + > + 조회 + </Button> + <Button + size={"sm"} + className={`${btnClass} green`} + onClick={excelClick} + > + 다운로드 + </Button> + <Button + size={"sm"} + 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> <UiTable - headName={[]} + headName={undefined} tableData={bdstTable || []} children={ <HeadMakeColSpan upHeadList={htdflSttn.htdflSttNodeList} /> diff --git a/apps/wr/app/ruseFcltMng/fcltSttn/rainSttn/page.tsx b/apps/wr/app/ruseFcltMng/fcltSttn/rainSttn/page.tsx index 7e068bcd0..804e9d808 100644 --- a/apps/wr/app/ruseFcltMng/fcltSttn/rainSttn/page.tsx +++ b/apps/wr/app/ruseFcltMng/fcltSttn/rainSttn/page.tsx @@ -82,7 +82,6 @@ export default function RainSttn() { }; const getSgg = (val: string | number) => { - console.log("val", val) setObjSearch({ ...objSearch, sgg: String(val) }) }; @@ -96,8 +95,6 @@ export default function RainSttn() { return objSearch.city; }; - - useEffect(() => { if (!rainSttn.isInit) { rainSttnInit(codeData ?? "0"); @@ -108,8 +105,6 @@ export default function RainSttn() { const data = tableData(rainSttnRes, { code }); const chData = data .map((item: any) => { - console.log("item", item); - console.log("왜안돼", item.reuseBuilding[0].rainyObj.dtfcltCpct) return { ...item, ...item.reuseBuilding[0], @@ -124,10 +119,16 @@ export default function RainSttn() { ? "확인불가" : item.reuseBuilding[0].instlCstSprtAmt, chBuildingYn: item.chgBuilding[0]?.bdstChgNm !== null ? "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 ? "확인불가" : item.archArea, }; }) .filter((item: any) => item.rainyObj !== undefined); - console.log("chData", chData) setBdstTable(chData); }, [code, rainSttnRes, rainSttn.rainSttHeadList]); @@ -147,9 +148,6 @@ export default function RainSttn() { } }, [rainSttn.excelList]); - // const excelClick = useCallback(() => { - // if (codeData) rainSttnSearch(1, "10000", codeData, code.cityCd, code.sggCd); - // }, [rainSttnSearch, codeData, code.cityCd, code.sggCd]); const excelClick = useCallback(() => { // 최신 검색 조건 반영 const con = getConValue(); @@ -162,8 +160,6 @@ export default function RainSttn() { <div className="grid grid-cols-4 gap-4 "> <div className="col-span-4"> <Title title={"빗물재이용 시설 현황"} /> - - <SearchDiv> <SearchFormLeft> <div className="col-span-10 flex flex-row"> @@ -215,50 +211,6 @@ export default function RainSttn() { </div> </SearchFormRight> </SearchDiv> - - - - - - - - - - - {/* <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, code.cityCd, code.sggCd); - }} - excelClick={excelClick} - initClick={function (): void { }} - btnClass={btnClass} - ref={ref} - isAdmin={false} - /> */} </div> </div> <div className="grid grid-cols-6 gap-4 "> diff --git a/apps/wr/app/ruseFcltMng/fcltSttn/rswtSttn/page.tsx b/apps/wr/app/ruseFcltMng/fcltSttn/rswtSttn/page.tsx index f9b3c6187..05ee774f1 100644 --- a/apps/wr/app/ruseFcltMng/fcltSttn/rswtSttn/page.tsx +++ b/apps/wr/app/ruseFcltMng/fcltSttn/rswtSttn/page.tsx @@ -5,19 +5,26 @@ import type { chgBuilding } from "@/app/store/ruseFcltMng/fcltReg/rainReg"; import type { customRswtBdst } from "@/app/store/ruseFcltMng/fcltReg/rswtReg"; import { excelDownload, + getCity, getCityOrSgg, tableData, } from "@/components/lib/func/commom"; import BdstChgModal from "@/components/ui/modal/fcltReg/bdstChgModal/bdstChgModal"; +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, SearchBaseForm, + SearchDiv, + SearchFormLeft, + SearchFormRight, + SelectBox, Title, UiModal, UiTable, type ReturnType, + 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"; @@ -34,6 +41,8 @@ export default function RswtSttn() { const [isChHead, setIsChHead] = useState(false); const ref = useRef<ReturnType>({} as ReturnType); const [bdstTable, setBdstTable] = useState<customRswtBdst[]>(); + const [selectSgg, setSelectSgg] = useState<SelectDataType[] | []>() + const [objSearch, setObjSearch] = useState<objSearchType>({ sgg: "", city: "" }); const [detailModal, setDetailModal] = useState<{ isOpen: boolean, data: customRswtBdst | undefined }>({ data: undefined, isOpen: false }) const [bdstChgProps, setBdstChgProps] = useState<{ isOpen: boolean; @@ -58,6 +67,35 @@ export default function RswtSttn() { } } + const getSelCity = (val: string | number) => { + const cityValue = String(val); + + if (cityValue === "0") { + setSelectSgg([]); + setObjSearch({ city: cityValue, sgg: "" }); + } else { + const data = getCity(cityValue, code.sggCd); + setSelectSgg(data); + + 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 = () => { + if (!objSearch.city || objSearch.city === "0") { + return "0"; + } + if (objSearch.sgg && objSearch.sgg !== "0") { + return objSearch.sgg; + } + return objSearch.city; + }; + useEffect(() => { if (!rswtSttn.isInit) { rswtSttnInit("0"); @@ -65,22 +103,25 @@ export default function RswtSttn() { }, [rswtSttn.isInit, rswtSttnInit]); useEffect(() => { - if (rswtSttnRes?.length > 0 && code.cityCd.length > 0) { - const data = tableData(rswtSttnRes, { code }); - const chData = data - .map((item: customRswtBdst) => ({ + const data = tableData(rswtSttnRes, { code }); + const chData = data + .map((item: customRswtBdst) => { + console.log("item", item) + return { ...item, ...item.reuseBuilding[0], ...item.reuseBuilding[0]?.rswtrObj, - chBuildingYn: item?.chgBuilding[0]?.bdstChgNm !== null && item?.chgBuilding.length > 0 ? 'Y' : 'N' - })) - .filter( - (item: customRswtBdst) => - item.reuseBuilding[0]?.rswtrObj !== undefined - ); + chBuildingYn: item?.chgBuilding[0]?.bdstChgNm !== null && item?.chgBuilding.length > 0 ? 'Y' : 'N', + instlCst: item.reuseBuilding[0]?.instlCst === -1 ? "확인불가" : item.reuseBuilding[0]?.instlCst, + instlCstSprtAmt: item.reuseBuilding[0]?.instlCstSprtAmt === -1 ? "확인불가" : item.reuseBuilding[0]?.instlCstSprtAmt, + } + }) + .filter( + (item: customRswtBdst) => + item.reuseBuilding[0]?.rswtrObj !== undefined + ); - setBdstTable(chData); - } + setBdstTable(chData); }, [code, rswtSttnRes]); useEffect(() => { @@ -95,13 +136,11 @@ export default function RswtSttn() { } }, [rswtSttn.excelList]); - const excelClick = useCallback( - (returnType: ReturnType) => { - if (codeCityOrSgg) - rswtSttnSearch(1, "10000", codeCityOrSgg, code.cityCd, code.sggCd); - }, - [rswtSttnSearch, code.cityCd, code.sggCd, codeCityOrSgg] - ); + const excelClick = useCallback(() => { + // 최신 검색 조건 반영 + const con = getConValue(); + rswtSttnSearch(1, "10000", String(con), code.cityCd, code.sggCd); + }, [rswtSttnSearch, objSearch, code.cityCd, code.sggCd]); useEffect(() => { if (codeCityOrSgg != null) rswtSttnSearch(pageNation, "20", codeCityOrSgg, code.cityCd, code.sggCd); @@ -113,38 +152,64 @@ export default function RswtSttn() { <div className="grid grid-cols-4 gap-4 "> <div className="col-span-4"> <Title title={"중수도시설 현황"} /> - <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', …}' - if (codeCityOrSgg) - rswtSttnSearch(1, "20", codeCityOrSgg, code.cityCd, code.sggCd); - } } - excelClick={excelClick} - initClick={function (): void { } } - btnClass={btnClass} - ref={ref} isAdmin={false} /> + <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={code.cityCd} + onSelectValue={getSelCity} + /> + <SelectBox + className="col-span-2 w-1/6 h-full text-2xl ml-4" + label="시군구" + selectClass={""} + selectArray={selectSgg || []} + value={objSearch.sgg} // value props 추가 + onSelectValue={getSgg} + /> + </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 + size={"sm"} + onClick={() => { + const con = getConValue(); + rswtSttnSearch(1, "20", String(con), code.cityCd, code.sggCd); + }} + > + 조회 + </Button> + <Button + size={"sm"} + className={`${btnClass} green`} + onClick={excelClick} + > + 다운로드 + </Button> + <Button + size={"sm"} + 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> <UiTable - headName={[]} + headName={undefined} tableData={bdstTable || []} children={ <HeadMakeColSpan upHeadList={rswtSttn.rswtSttNodeList} /> diff --git a/apps/wr/app/store/ruseFcltMng/fcltSttn/htdflSttn/index.tsx b/apps/wr/app/store/ruseFcltMng/fcltSttn/htdflSttn/index.tsx index dccc8719c..ba81066be 100644 --- a/apps/wr/app/store/ruseFcltMng/fcltSttn/htdflSttn/index.tsx +++ b/apps/wr/app/store/ruseFcltMng/fcltSttn/htdflSttn/index.tsx @@ -5,6 +5,8 @@ import type { z } from "zod"; import { controlHeadView } from "../rainSttn"; import type { customHtdflBdst } from "../../fcltReg/htdflReg"; import { fetchType, getFetch } from "@/components/lib/api"; +import type { SelectDataType } from "@common/business_components"; +import { excelHtdflSttnParsing } from "./excel"; const htdflSttHeadList = htdflSttHeadListExport; const htdflSttNodeList = htdflSttNodeListExport; @@ -21,7 +23,8 @@ export type htdflSttnState = { }; type htdflSttnActions = { initHtdfl: (con: string) => void; - htdflSttnSearch: (page: number, total: string, con: string) => void; + htdflSttnSearch: (page: number, total: string, con: string, sidoCode: SelectDataType[], + sggCode: SelectDataType[]) => void; htdflChSttnHead: ( headList: z.infer<typeof sttnHeadListType>, ) => void; @@ -67,22 +70,23 @@ export const htdflSttnActionsExport: (set: any) => htdflSttnActions = ( console.log(error); } }, - htdflSttnSearch: async (page: number, total: string, con: string) => { + htdflSttnSearch: async (page: number, total: string, con: string, + sidoCode: SelectDataType[], + sggCode: SelectDataType[] + ) => { try { const htdflSttnRes = await getFetch( fetchType.WR, `/drain/facilityList/${page}/${total}/${con}` ); if (Number(total) > 9000) { - - + const htdflExcel = excelHtdflSttnParsing(htdflSttnRes, sidoCode, sggCode); set((state: htdflSttnState) => { - return { ...state, htdflSttn: { ...state.htdflSttn, - excelList: htdflSttnRes, + excelList: htdflExcel, }, }; }); From 819a6508bf2aae04da7368b9a7760ae3796b6616 Mon Sep 17 00:00:00 2001 From: JIeunhuh <entks301@naver.com> Date: Thu, 3 Jul 2025 15:16:53 +0900 Subject: [PATCH 053/108] =?UTF-8?q?=EC=9E=AC=EC=9D=B4=EC=9A=A9=EC=8B=9C?= =?UTF-8?q?=EC=84=A4=20=ED=98=84=ED=99=A9=20=ED=8E=98=EC=9D=B4=EC=A7=95=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wr/app/map/newWindowPopup.tsx | 354 +++++++++++++++-------------- 1 file changed, 188 insertions(+), 166 deletions(-) diff --git a/apps/wr/app/map/newWindowPopup.tsx b/apps/wr/app/map/newWindowPopup.tsx index e14a4447d..777c3dcf2 100644 --- a/apps/wr/app/map/newWindowPopup.tsx +++ b/apps/wr/app/map/newWindowPopup.tsx @@ -130,7 +130,7 @@ const NewWindowPopup: React.FC<NewWindowPopupProps> = ({ sigunText: "" }, String(new Date().getFullYear())); } - }, [pbnstSttn.isInit, pbnSttnInit]); + }, [pbnstSttn.isInit]); useEffect(() => { setBdstTable(pbnstSttn.pbnstSttnRes); @@ -155,6 +155,26 @@ const NewWindowPopup: React.FC<NewWindowPopupProps> = ({ console.error("Error fetching data:", error); } }; + // 재이용시설 페이징처리 관련 함수 + const onPageChangeHandler = useCallback((page: number) => { + pbnstSttnSearch( + String(page), + String(13), + { + partValue: "A", + sidoValue: "0", + sigunValue: "", + regionValue: "", + unitWaterShedValue: "", + middleRegionValue: "", + envPartValue: "", + sidoText: "", + sigunText: "", + }, + "", + String(new Date().getFullYear()) + ); + }, []); // useEffect(() => { // if (featureData) { // const selectedVal = featureData.getProperties(); @@ -238,179 +258,181 @@ const NewWindowPopup: React.FC<NewWindowPopupProps> = ({ const isRswt = false; // 온배수시설 const isHtdfl = false; - return ( - <div className="mt-4"> - {/* 체크된 레이어가 없는 경우 (재이용시설 목록) */} - {isPbnst && ( + if (isPbnst) { + if (showDetailView) { + // 재이용시설 상세 + return ( <> - {showDetailView ? ( - // 재이용시설 상세 - <> - <div className="flex justify-between items-center mb-4"> - <Title title='하수재이용시설 상세정보' /> - </div> - <div className="flex justify-between items-center mb-8"> - <UITab - defaultValue={activeTab} - tabList={tabData.map((tab) => ({ - value: tab.id, - label: tab.name, - onClick: () => setActiveTab(tab.id), - }))} - children={undefined} - /> - </div> - <PbnstSttnModal - data={actTabData?.data[0]|| ({} as customPbnst)} - titleVisible={false} - /> - </> - ) : ( - // 재이용시설 목록 - <> - <div className="flex justify-between items-center"> - <Title title='하수재이용시설 현황' /> - <div className="flex gap-2 mt-8"> - <Button className={btnClass} color="primary" onClick={excelClick}>엑셀다운로드</Button> - </div> - </div> - <UiTable - headName={[]} - tableData={bdstTable || []} - children={ - <HeadMakeColSpan upHeadList={pbnstSttn.pbnstSttNodeList} /> - } - headlist={pbnstSttn.pbnstSttHeadList} - pageSize={20} - total={100000} - cellClick={(index: number | undefined, headId: string) => { - pbnstCellClick(index); - }} - onPageChange={setPageNation} - /> - </> - )} + <div className="flex justify-between items-center mb-4"> + <Title title="하수재이용시설 상세정보" /> + </div> + <div className="flex justify-between items-center mb-8"> + <UITab + defaultValue={activeTab} + tabList={tabData.map((tab) => ({ + value: tab.id, + label: tab.name, + onClick: () => setActiveTab(tab.id), + }))} + children={undefined} + /> + </div> + <PbnstSttnModal + data={tabData.find((tab) => tab.id === activeTab)?.data[0] || ({} as customPbnst)} + titleVisible={false} + /> </> - )} - {/* 빗물이용시설 */} - {isRain && ( + ); + } else { + // 재이용시설 목록 + return ( <> - {showDetailView ? ( - // 빗물이용시설 상세 - <> - <div className="flex justify-between items-center mb-4"> - <Title title='빗물이용시설 상세정보' /> - </div> - <RainPrfmncSttnPopup rainForm={rainDetailModal.data as customRainBdst} isModal={false} /> - {/* TODO: 지도 점검 결과 현황 추가 할 것 */} - </> - ) : ( - // 빗물이용시설 목록 - <> - <div className="flex justify-between items-center"> - <Title title='빗물이용시설 현황' /> - <div className="flex gap-2 mt-8"> - <Button className={btnClass} color="primary" onClick={excelClick}>엑셀다운로드</Button> - </div> - </div> - <UiTable - headName={[]} - tableData={rainTable || []} - children={ - <HeadMakeColSpan upHeadList={rainSttn.rainSttNodeList} /> - } - headlist={rainSttn.rainSttHeadList} - pageSize={20} - total={100000} - cellClick={(index: number | undefined, headId: string) => { - rainCellClick(index ?? undefined, headId) - }} - onPageChange={setPageNation} - /> - </> - )} + <div className="flex justify-between items-center"> + <Title title="하수재이용시설 현황" /> + <div className="flex gap-2 mt-8"> + <Button className={btnClass} color="primary" onClick={excelClick}> + 엑셀다운로드 + </Button> + </div> + </div> + <UiTable + headName={[]} + tableData={bdstTable || []} + children={<HeadMakeColSpan upHeadList={pbnstSttn.pbnstSttNodeList} />} + headlist={pbnstSttn.pbnstSttHeadList} + pageSize={20} + total={100000} + cellClick={(index: number | undefined, headId: string) => { + pbnstCellClick(index); + }} + onPageChange={onPageChangeHandler} + /> </> - )} - {/* 중수도시설 */} - {isRswt && ( + ); + } + } + + if (isRain) { + if (showDetailView) { + // 빗물이용시설 상세 + return ( <> - {showDetailView ? ( - // 재이용시설 상세 - <> - <div className="flex justify-between items-center mb-4"> - <Title title='중수도시설 상세정보' /> - </div> - <RswtrPrfmncsSttnPopup rswtForm={rswtrDetailModal.data as customRswtBdst} isModal={false} /> - {/* TODO: 지도 점검 결과 현황 추가 할 것 */} - </> - ) : ( - // 재이용시설 목록 - <> - <div className="flex justify-between items-center"> - <Title title='중수도시설 현황' /> - <div className="flex gap-2 mt-8"> - <Button className={btnClass} color="primary" onClick={excelClick}>엑셀다운로드</Button> - </div> - </div> - <UiTable - headName={[]} - tableData={rswtrTable || []} - children={ - <HeadMakeColSpan upHeadList={rswtSttn.rswtSttNodeList} /> - } - headlist={rswtSttn.rswtSttHeadList} - pageSize={20} - total={100000} - cellClick={(index: number | undefined, headId: string) => { - rswtCellClick(index, headId); - }} - onPageChange={setPageNation} - /> - </> - )} + <div className="flex justify-between items-center mb-4"> + <Title title="빗물이용시설 상세정보" /> + </div> + <RainPrfmncSttnPopup rainForm={rainDetailModal.data as customRainBdst} isModal={false} /> </> - )} - {/* 온배수시설 */} - {isHtdfl && ( + ); + } else { + // 빗물이용시설 목록 + return ( <> - {showDetailView ? ( - // 재이용시설 상세 - <> - <div className="flex justify-between items-center mb-4"> - <Title title='온배수시설 상세정보' /> - </div> - {/* <HtdflPrfmncSttnPopup htdflForm={htdflDetailModal.data as customHtdflBdst} isModal={false} /> */} - <HtdflPrfmncSttnPopup htdflForm={htdflDetailModal.data as customHtdflBdst} isModal={false} /> - </> - ) : ( - // 재이용시설 목록 - <> - <div className="flex justify-between items-center"> - <Title title='온배수시설 현황' /> - <div className="flex gap-2 mt-8"> - <Button className={btnClass} color="primary" onClick={excelClick}>엑셀다운로드</Button> - </div> - </div> - <UiTable - headName={[]} - tableData={bdstTable || []} - children={ - <HeadMakeColSpan upHeadList={htdflSttn.htdflSttNodeList} /> - } - headlist={htdflSttn.htdflSttHeadList} - pageSize={20} - total={100000} - cellClick={(index: number | undefined, headId: string) => { - htdflCellClick(index ?? undefined, headId); - }} - onPageChange={setPageNation} - /> - </> - )} + <div className="flex justify-between items-center"> + <Title title="빗물이용시설 현황" /> + <div className="flex gap-2 mt-8"> + <Button className={btnClass} color="primary" onClick={excelClick}> + 엑셀다운로드 + </Button> + </div> + </div> + <UiTable + headName={[]} + tableData={rainTable || []} + children={<HeadMakeColSpan upHeadList={rainSttn.rainSttNodeList} />} + headlist={rainSttn.rainSttHeadList} + pageSize={20} + total={100000} + cellClick={(index: number | undefined, headId: string) => { + rainCellClick(index ?? undefined, headId); + }} + onPageChange={setPageNation} + /> </> - )} - </div> - ); + ); + } + } + + if (isRswt) { + if (showDetailView) { + // 중수도시설 상세 + return ( + <> + <div className="flex justify-between items-center mb-4"> + <Title title="중수도시설 상세정보" /> + </div> + <RswtrPrfmncsSttnPopup rswtForm={rswtrDetailModal.data as customRswtBdst} isModal={false} /> + </> + ); + } else { + // 중수도시설 목록 + return ( + <> + <div className="flex justify-between items-center"> + <Title title="중수도시설 현황" /> + <div className="flex gap-2 mt-8"> + <Button className={btnClass} color="primary" onClick={excelClick}> + 엑셀다운로드 + </Button> + </div> + </div> + <UiTable + headName={[]} + tableData={rswtrTable || []} + children={<HeadMakeColSpan upHeadList={rswtSttn.rswtSttNodeList} />} + headlist={rswtSttn.rswtSttHeadList} + pageSize={20} + total={100000} + cellClick={(index: number | undefined, headId: string) => { + rswtCellClick(index, headId); + }} + onPageChange={setPageNation} + /> + </> + ); + } + } + + if (isHtdfl) { + if (showDetailView) { + // 온배수시설 상세 + return ( + <> + <div className="flex justify-between items-center mb-4"> + <Title title="온배수시설 상세정보" /> + </div> + <HtdflPrfmncSttnPopup htdflForm={htdflDetailModal.data as customHtdflBdst} isModal={false} /> + </> + ); + } else { + // 온배수시설 목록 + return ( + <> + <div className="flex justify-between items-center"> + <Title title="온배수시설 현황" /> + <div className="flex gap-2 mt-8"> + <Button className={btnClass} color="primary" onClick={excelClick}> + 엑셀다운로드 + </Button> + </div> + </div> + <UiTable + headName={[]} + tableData={htdflTable || []} + children={<HeadMakeColSpan upHeadList={htdflSttn.htdflSttNodeList} />} + headlist={htdflSttn.htdflSttHeadList} + pageSize={20} + total={100000} + cellClick={(index: number | undefined, headId: string) => { + htdflCellClick(index ?? undefined, headId); + }} + onPageChange={setPageNation} + /> + </> + ); + } + } + + return null; // 조건에 맞는 콘텐츠가 없을 경우 }; return ( From 45a0ff043dc090b840edf734ca2f51b5a6e4d121 Mon Sep 17 00:00:00 2001 From: jyha00 <jyha@mind-one.co.kr> Date: Thu, 3 Jul 2025 16:32:23 +0900 Subject: [PATCH 054/108] =?UTF-8?q?=EC=9E=AC=EC=9D=B4=EC=9A=A9=EC=8B=A4?= =?UTF-8?q?=EC=A0=81=ED=98=84=ED=99=A9=20=EC=9E=A5=EB=82=B4=EC=99=B8?= =?UTF-8?q?=EC=9A=A9=EC=88=98=20=EC=B4=9D=EC=8B=A4=EC=A0=81=20=EC=B0=A8?= =?UTF-8?q?=ED=8A=B8=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../prfmncSttn/pbnstPrfmncSttn/page.tsx | 121 +++++++++--------- .../prfmncSttn/pbnstPrfmncSttn/index.tsx | 34 ++--- 2 files changed, 77 insertions(+), 78 deletions(-) diff --git a/apps/wr/app/ruseFcltMng/prfmncSttn/pbnstPrfmncSttn/page.tsx b/apps/wr/app/ruseFcltMng/prfmncSttn/pbnstPrfmncSttn/page.tsx index f689eb745..3e860d744 100644 --- a/apps/wr/app/ruseFcltMng/prfmncSttn/pbnstPrfmncSttn/page.tsx +++ b/apps/wr/app/ruseFcltMng/prfmncSttn/pbnstPrfmncSttn/page.tsx @@ -68,8 +68,6 @@ export default function PbnstPrfmncSttn() { chartData5: lineChartDataProps[]; chartData6: lineChartDataProps[]; chartData7: lineChartDataProps[]; - chartData8: lineChartDataProps[]; - chartData9: lineChartDataProps[]; }>(); const [outLineData, setOutLineData] = useState<{ chartData1: lineChartDataProps[]; @@ -78,6 +76,8 @@ export default function PbnstPrfmncSttn() { chartData4: lineChartDataProps[]; chartData5: lineChartDataProps[]; chartData6: lineChartDataProps[]; + chartData7: lineChartDataProps[]; + chartData8: lineChartDataProps[]; }>(); type dnChartForm = { label: string, val: number, fill?: string }; @@ -228,16 +228,18 @@ export default function PbnstPrfmncSttn() { indstUswtr += element.indstUswtr; // 월별 합계 - inUsageSum = element.wshwtr + inUsageSum = + element.wshwtr + element.coolUswtr + element.cleanUswtr + element.drwtUswtr + element.dltnUswtr + element.ithlRswtr + element.ithlEtcUswtr - + element.cleanToilUswtr - + element.resn; - outUsageSum = element.lndwtr + outUsageSum = + element.cleanToilUswtr + + element.resn + + element.lndwtr + element.hydUswtr + element.ugwtChr + element.farmUswtr @@ -258,9 +260,10 @@ export default function PbnstPrfmncSttn() { dltnUswtrData.push({ label: `${month}월`, val: element.dltnUswtr, color: "purple" }); ithlRswtrData.push({ label: `${month}월`, val: element.ithlRswtr, color: "green" }); ithlEtcUswtrData.push({ label: `${month}월`, val: element.ithlEtcUswtr, color: "blue" }); + + cleanToilUswtrData.push({ label: `${month}월`, val: element.cleanToilUswtr, color: "magenta" }); resnData.push({ label: `${month}월`, val: element.resn, color: "skyblue" }); - lndwtrData.push({ label: `${month}월`, val: element.lndwtr, color: "red" }); hydUswtrData.push({ label: `${month}월`, val: element.hydUswtr, color: "orange" }); ugwtChrData.push({ label: `${month}월`, val: element.ugwtChr, color: "yellow" }); @@ -271,22 +274,18 @@ export default function PbnstPrfmncSttn() { } }); + let dnTotalIn = 0; + let dnTotalOut = 0; + inUsageArr.forEach((item) => { + dnTotalIn += item + }) + outUsageArr.forEach((item) => { + dnTotalOut += item + }) + // 도넛차트 데이터 - dnData.push({ label: "세척수", val: wshwtr, fill: "#F7CFD8" }); - dnData.push({ label: "냉각수", val: coolUswtr, fill: "#F4F8D3" }); - dnData.push({ label: "청소수", val: cleanUswtr, fill: "#A6D6D6" }); - dnData.push({ label: "식수대", val: drwtUswtr, fill: "#8E7DBE" }); - dnData.push({ label: "희석용수", val: dltnUswtr, fill: "#FF8383" }); - dnData.push({ label: "중수도", val: ithlRswtr, fill: "#FFF574" }); - dnData.push({ label: "기타", val: ithlEtcUswtr, fill: "#A1D6CB" }); - dnData.push({ label: "청소화장실용수", val: cleanToilUswtr, fill: "#A19AD3" }); - dnData.push({ label: "세척살수용수", val: resn, fill: "#B1F0F7" }); - dnData.push({ label: "조경용수", val: lndwtr, fill: "#81BFDA" }); - dnData.push({ label: "친수용수", val: hydUswtr, fill: "#F5F0CD" }); - dnData.push({ label: "지하수충전", val: ugwtChr, fill: "#FADA7A" }); - dnData.push({ label: "농업용수", val: farmUswtr, fill: "#A6AEBF" }); - dnData.push({ label: "하천등유지용수", val: rvrKeepUswtr, fill: "#C5D3E8" }); - dnData.push({ label: "공업용수", val: indstUswtr, fill: "#D0E8C5" }); + dnData.push({ label: "장내용수", val: dnTotalIn, fill: "#8DD8FF" }); + dnData.push({ label: "장외용수", val: dnTotalOut, fill: "#FFF085" }); // 장내용수 도넛 dnInData.push({ label: "세척수", val: wshwtr, fill: "#F7CFD8" }); @@ -296,10 +295,10 @@ export default function PbnstPrfmncSttn() { dnInData.push({ label: "희석용수", val: dltnUswtr, fill: "#FF8383" }); dnInData.push({ label: "중수도", val: ithlRswtr, fill: "#FFF574" }); dnInData.push({ label: "기타", val: ithlEtcUswtr, fill: "#A1D6CB" }); - dnInData.push({ label: "청소화장실용수", val: cleanToilUswtr, fill: "#A19AD3" }); - dnInData.push({ label: "세척살수용수", val: resn, fill: "#B1F0F7" }); // 장외용수 도넛 + dnOutData.push({ label: "청소화장실용수", val: cleanToilUswtr, fill: "#A19AD3" }); + dnOutData.push({ label: "세척살수용수", val: resn, fill: "#B1F0F7" }); dnOutData.push({ label: "조경용수", val: lndwtr, fill: "#81BFDA" }); dnOutData.push({ label: "친수용수", val: hydUswtr, fill: "#F5F0CD" }); dnOutData.push({ label: "지하수충전", val: ugwtChr, fill: "#FADA7A" }); @@ -315,17 +314,17 @@ export default function PbnstPrfmncSttn() { chartData4: drwtUswtrData, chartData5: dltnUswtrData, chartData6: ithlRswtrData, - chartData7: ithlEtcUswtrData, - chartData8: cleanToilUswtrData, - chartData9: resnData + chartData7: ithlEtcUswtrData }); setOutLineData({ - chartData1: lndwtrData, - chartData2: hydUswtrData, - chartData3: ugwtChrData, - chartData4: farmUswtrData, - chartData5: rvrKeepUswtrData, - chartData6: indstUswtrData, + chartData1: cleanToilUswtrData, + chartData2: resnData, + chartData3: lndwtrData, + chartData4: hydUswtrData, + chartData5: ugwtChrData, + chartData6: farmUswtrData, + chartData7: rvrKeepUswtrData, + chartData8: indstUswtrData, }); setBarData(barData); setChartData(dnData); @@ -408,15 +407,7 @@ export default function PbnstPrfmncSttn() { chartData7: { label: '기타', color: "blue", - }, - chartData8: { - label: '청소화장실용수', - color: "magenta", - }, - chartData9: { - label: '세척살수용수', - color: "skyblue", - }, + } } satisfies ChartConfig; setLnInChartConfig(newChartConfig); } @@ -427,26 +418,34 @@ export default function PbnstPrfmncSttn() { if (outLineData?.chartData1) { const newChartConfig = { chartData1: { - label: '조경용수', + label: '청소화장실용수', color: "red", }, chartData2: { + label: '세척살수용수', + color: "pink", + }, + chartData3: { + label: '조경용수', + color: "gray", + }, + chartData4: { label: '친수용수', color: "orange", }, - chartData3: { + chartData5: { label: '지하수충전', color: "yellow", }, - chartData4: { + chartData6: { label: '농업용수', color: "green", }, - chartData5: { + chartData7: { label: '하천등유지용수', color: "blue", }, - chartData6: { + chartData8: { label: '공업용수', color: "purple", }, @@ -615,7 +614,7 @@ export default function PbnstPrfmncSttn() { <h1 className="">장내·외 월별 하수재이용실적 현황</h1> <div className="flex"> <div className="w-1/3"> - <MainChart chartConfig={barChartConfig} chartData={barData} chartTitle={"월별 장내·외 용수 총 실적"} chartForm={CHART_FORM.DOUBLEBAR} /> + <MainChart chartConfig={barChartConfig} chartData={barData} chartTitle={"월별 장내·외 용수 총 실적"} chartForm={CHART_FORM.DOUBLEBAR} showLegend={true} /> </div> <div className="w-1/3"> <MainChart chartConfig={lnInChartConfig} chartData={inLineData} chartTitle={"월별 장내 용수 실적"} chartForm={CHART_FORM.LINE} /> @@ -627,25 +626,25 @@ export default function PbnstPrfmncSttn() { <h1 className="">하수재이용실적 현황</h1> <ListDiv className="col-span-8 pr-6 "> {/* <ScrollArea className="h-[60vh]"> */} - {/* <div className="card-header"> + {/* <div className="card-header"> <h3 className="text-2xl font-medium text-label"> {"하수처리수재이용 실적(m3) 등록결과"} </h3> </div> */} - <UiTable - headName={undefined} - tableData={prfData || []} - headlist={monthHeadList} - pageSize={0} - total={0} - cellClick={undefined} - onPageChange={function (page: number): void { - throw new Error("Function not implemented."); - }}> - </UiTable> + <UiTable + headName={undefined} + tableData={prfData || []} + headlist={monthHeadList} + pageSize={0} + total={0} + cellClick={undefined} + onPageChange={function (page: number): void { + throw new Error("Function not implemented."); + }}> + </UiTable> - {/* <ListDiv className="col-span-8 pr-6 "> + {/* <ListDiv className="col-span-8 pr-6 "> <ScrollArea className="h-[60vh]"> <div className="card-header"> <h3 className="text-2xl font-medium text-label"> diff --git a/apps/wr/app/store/prfmncSttn/pbnstPrfmncSttn/index.tsx b/apps/wr/app/store/prfmncSttn/pbnstPrfmncSttn/index.tsx index b513085ae..db8b6a717 100644 --- a/apps/wr/app/store/prfmncSttn/pbnstPrfmncSttn/index.tsx +++ b/apps/wr/app/store/prfmncSttn/pbnstPrfmncSttn/index.tsx @@ -148,7 +148,7 @@ export const pbnstPrfmncSttnActionsExport: (set: any) => pbnstPrfmncSttnActions let usageSum = 0; let year = ""; const yearsData = [] - + res.data.forEach((item) => { if (item.prfList.length === 0) { const year = ""; // 연도 정보가 없으면 빈 문자열, 필요시 item에서 추출 @@ -161,26 +161,26 @@ export const pbnstPrfmncSttnActionsExport: (set: any) => pbnstPrfmncSttnActions item.prfList.forEach((acc) => { year = acc.ym?.substring(0, 4) ?? ""; inUsageSum = - (acc.wshwtr ?? 0) + - (acc.coolUswtr ?? 0) + - (acc.cleanUswtr ?? 0) + - (acc.drwtUswtr ?? 0) + - (acc.dltnUswtr ?? 0) + - (acc.ithlRswtr ?? 0) + - (acc.ithlEtcUswtr ?? 0) + - (acc.cleanToilUswtr ?? 0) + - (acc.resn ?? 0); + (acc.wshwtr ?? 0) + //조경용수 + (acc.coolUswtr ?? 0) + //냉각수 + (acc.cleanUswtr ?? 0) + //청소수 + (acc.drwtUswtr ?? 0) + //식수대 + (acc.dltnUswtr ?? 0) + //희석용수 + (acc.ithlRswtr ?? 0) + //중수도 + (acc.ithlEtcUswtr ?? 0) //기타 outUsageSum = - (acc.lndwtr ?? 0) + - (acc.hydUswtr ?? 0) + - (acc.ugwtChr ?? 0) + - (acc.farmUswtr ?? 0) + - (acc.rvrKeepUswtr ?? 0) + - (acc.indstUswtr ?? 0); + (acc.cleanToilUswtr ?? 0) + //청소화장실용수 + (acc.resn ?? 0)+ //세척살수용도 + (acc.lndwtr ?? 0) + //조경용수 + (acc.hydUswtr ?? 0) + //친수용수 + (acc.ugwtChr ?? 0) + //지하수충전 + (acc.farmUswtr ?? 0) + //농업용수 + (acc.rvrKeepUswtr ?? 0) + //하천등유지용수 + (acc.indstUswtr ?? 0); //공업용수 const yearData = yearsData.find( (yearItem) => yearItem.year === year && - yearItem.pbnstRuseFcltPk == acc.pbnstRuseFcltPk + yearItem.pbnstRuseFcltPk == acc.pbnstRuseFcltFk ); if (yearData) { From d936c71f7ee27e486f54d2518f93ba8cc496dc88 Mon Sep 17 00:00:00 2001 From: YangJinSoo94 <jschrome782@gmail.com> Date: Thu, 3 Jul 2025 17:04:19 +0900 Subject: [PATCH 055/108] =?UTF-8?q?y=EC=B6=95=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=ED=95=98=EB=8B=A8=EC=97=90=20=EC=9C=84=EC=B9=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/common/business_components/src/ui/chart/BarChart.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/common/business_components/src/ui/chart/BarChart.tsx b/packages/common/business_components/src/ui/chart/BarChart.tsx index 0802a72e4..62dd2333f 100644 --- a/packages/common/business_components/src/ui/chart/BarChart.tsx +++ b/packages/common/business_components/src/ui/chart/BarChart.tsx @@ -110,8 +110,7 @@ export default function Chart({chartConfig=undefined, chartData=[], chartTitle=' axisLine={true} fontSize={12} fontWeight={500} - domain={[0, Math.ceil(barMaxValue * 1)]} - label={yFieldText ? { value: yFieldText, className:"text-[13px] font-bold", position: 'insideLeft', angle: -90, offset: 20 } : undefined} + label={yFieldText ? { value: yFieldText, className:"text-[13px] font-bold", position: 'insideBottom', angle: 0, offset: -24 } : undefined} /> <ChartTooltip cursor={false} content={<CustomTooltip active={undefined} payload={undefined} label={undefined} />}/> {chartData && chartData.length > 0 && ( From ed96df4b22edbf501eded10894134275b9593f59 Mon Sep 17 00:00:00 2001 From: YangJinSoo94 <jschrome782@gmail.com> Date: Thu, 3 Jul 2025 17:24:15 +0900 Subject: [PATCH 056/108] =?UTF-8?q?barChart=20=EA=B8=80=EC=94=A8=20?= =?UTF-8?q?=EA=B0=80=EC=9A=B4=EB=8D=B0=20=EC=A0=95=EB=A0=AC,=20yFieldText?= =?UTF-8?q?=20=EC=A0=9C=EB=8C=80=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../business_components/src/ui/chart/BarChart.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/common/business_components/src/ui/chart/BarChart.tsx b/packages/common/business_components/src/ui/chart/BarChart.tsx index 62dd2333f..a7282830d 100644 --- a/packages/common/business_components/src/ui/chart/BarChart.tsx +++ b/packages/common/business_components/src/ui/chart/BarChart.tsx @@ -88,12 +88,12 @@ export default function Chart({chartConfig=undefined, chartData=[], chartTitle=' return ( <> {chartTitle && chartTitle !== "" && chartTitle !== "undefined" ? ( - <div className="!mt-3 text-[2.5rem] font-bold"> + <div className="!mt-3 text-[2.5rem] font-bold text-center"> {chartTitle} </div>): null} - <div className="flex justify-center"> - <ChartContainer config={chartConfig ?? {}} className={chartClassName + " !w-[90%]"}> - <BarChart accessibilityLayer data={chartData}> + <div className="flex justify-center h-full w-full"> + <ChartContainer config={chartConfig ?? {}} className={chartClassName + " !w-[88%]"}> + <BarChart accessibilityLayer data={chartData} margin={{ top: 5, right: 20, left: 30, bottom: 5 }}> <CartesianGrid vertical={false} /> <XAxis dataKey="label" @@ -110,7 +110,7 @@ export default function Chart({chartConfig=undefined, chartData=[], chartTitle=' axisLine={true} fontSize={12} fontWeight={500} - label={yFieldText ? { value: yFieldText, className:"text-[13px] font-bold", position: 'insideBottom', angle: 0, offset: -24 } : undefined} + label={yFieldText ? { value: yFieldText, className:"text-[13px] font-bold", position: 'insideLeft', angle: -90, offset: -24, orientation: "left" } : undefined} /> <ChartTooltip cursor={false} content={<CustomTooltip active={undefined} payload={undefined} label={undefined} />}/> {chartData && chartData.length > 0 && ( From 03179ede64699cd3ccdfe56efb44ea48bcfe6cbf Mon Sep 17 00:00:00 2001 From: JIeunhuh <entks301@naver.com> Date: Thu, 3 Jul 2025 18:02:11 +0900 Subject: [PATCH 057/108] =?UTF-8?q?gistree=20=EB=A0=88=EC=9D=B4=EC=96=B4?= =?UTF-8?q?=20=EC=98=86=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/common/business_components/src/ui/map/GisTree.js | 3 ++- .../business_components/src/ui/table/uiPageNations.tsx | 8 +++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/packages/common/business_components/src/ui/map/GisTree.js b/packages/common/business_components/src/ui/map/GisTree.js index 6a2e8face..9dbf2c42a 100644 --- a/packages/common/business_components/src/ui/map/GisTree.js +++ b/packages/common/business_components/src/ui/map/GisTree.js @@ -170,7 +170,7 @@ export default React.memo(function GisTree({ checkedNodes = [], setCheckedNodes image.src = canvas.toDataURL('image/png'); return image; }; - + if (!treeData || treeData.length === 0) return null; return ( @@ -203,6 +203,7 @@ export default React.memo(function GisTree({ checkedNodes = [], setCheckedNodes <input type="checkbox" checked={checked.includes(node.id)} onChange={() => handleCheck(node.id)} className="cursor-pointer" /> {node.icon && <span>{node.icon}</span>} <span className="text-[1.3rem] font-medium text-label">{node.text}</span> + {depth > 0 && <span><img className="cursor-pointer w-5 h-5" src='/images/map/layericon/Layer_Select_BTN.png' onClick={()=>{}} alt={node.text}></img></span>} </div> )} sort={(a, b) => { diff --git a/packages/common/business_components/src/ui/table/uiPageNations.tsx b/packages/common/business_components/src/ui/table/uiPageNations.tsx index 891fc4c81..a4ee3b4fc 100644 --- a/packages/common/business_components/src/ui/table/uiPageNations.tsx +++ b/packages/common/business_components/src/ui/table/uiPageNations.tsx @@ -40,7 +40,8 @@ export default function UiPageNations(commonPageNationsProps: CommonPageNationsP <PaginationItem> <PaginationPrevious href="#" - onClick={() => { + onClick={(e) => { + e.preventDefault(); if (startPage > 1) handlePageChange(startPage - 1); }} /> @@ -53,7 +54,7 @@ export default function UiPageNations(commonPageNationsProps: CommonPageNationsP <PaginationLink href="#" isActive={pageNum === pageProps.cur} - onClick={() => handlePageChange(pageNum)} + onClick={(e) => {e.preventDefault(); handlePageChange(pageNum)}} > {pageNum} </PaginationLink> @@ -65,7 +66,8 @@ export default function UiPageNations(commonPageNationsProps: CommonPageNationsP <PaginationItem> <PaginationNext href="#" - onClick={() => { + onClick={(e) => { + e.preventDefault(); if (endPage < totalPages) handlePageChange(endPage + 1); }} /> From 39977e449155c253e1715ac1db9f873ea89b311b Mon Sep 17 00:00:00 2001 From: JIeunhuh <entks301@naver.com> Date: Thu, 3 Jul 2025 18:03:24 +0900 Subject: [PATCH 058/108] =?UTF-8?q?=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../images/map/layericon/Layer_Select_BTN.png | Bin 0 -> 14434 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 apps/wr/public/images/map/layericon/Layer_Select_BTN.png diff --git a/apps/wr/public/images/map/layericon/Layer_Select_BTN.png b/apps/wr/public/images/map/layericon/Layer_Select_BTN.png new file mode 100644 index 0000000000000000000000000000000000000000..1e810feafce85d43db278f048d541272f6d82253 GIT binary patch literal 14434 zcmbumbyyVNA2xh;X;3f#5hPSP1q75_P*A$NrMtTpP*jv|kdg)|iKV+k5D=u5l$K_R z<sJO~p67c1fA+eVWoFKtIp@ro^SSTOeeZiEg%{TeDG31p*QH-RR{;PH{R;>9xX{;u z=h!Lqh3z6Ct%eVMeDO`+LHh)bFST6&Anv&OgT=8CQ$Yvsx=L!fsydjvdKf#K0S^xk zPAhvG7ZYPgGfoF*i<E6)N&x79^mB1F&re&kUJZJZNte4jE_G+-Bp<@_pVV+8>6o8O zBHkdGgF_$OipR%)^5iSF<W1#{q6a}=-&0UyKPSEMnK+2>7TwJorY^9Xu#4IZy6^|| zf%K^Qq5ZRm;?gzJ+l~5e>z_o^QKh`4UW18(id?AmwY+sTX{dewZy&X|9AdgR4i3ME z`g6i6^tc{bMa#x4qt%jf`+mHnrj;5}#RCVdveby{HFf4Xk-F6_&Kx{zPW=Z$3ER_) z3TLY8e?!NQ30iVe(6~vD^|eK3XAIUyx}q8L!io6Cj|1Eg3>=TwL=)zh;|Fb)laH$n zr?3iwLko85K8t%*qs^!0;&wPHs2S|EJa70Nm%#I>3az8{j{DCAy^aR!0}3|q%)Hhk zCDv0i*GD*fO;npAYPXoyBnuW{g-C^_SEsq%CD~%q80!8Cm-$`UdS16j_th{P2U~-_ zLJlWym%U?7e;vM-!ZYOd4GtV}S?i9b7F$%P=X7Il%(wM$S(Ej%;JwVx-cs%6VT|)z zg@Nd$5#e5ieCr2FwZ?y@Q_em(qmy_VS=`vcdO>AeRo~$szeOMC6h5fy`s)a(EFQyz z%cPZJw<*D~C8l@k=gbHzA+G|7fLeqdyZio-f_AK*`NaIsk+If~&l&2XWCe}~dG}@Y zyrutC%TD)dF2umVm!)mF>Ior#V~;#yQ+XM&fq}`kV~>3<>EuK~a`1<W!25Cp^NVn> zs$)P@cyaXzJ(l@J!Rj`WsQRz;BKsw6%%B`Kk&?9QU&Jf_)()woIn<1?Vs^OclN>p~ zIWse1W#<1TdrPHTg~0UyC~2VFsg82}cFLM_h)vCMWC7>gNP0?dX|&D$SUEu0#o4>& zSNiPbHaewG_f=In7Kko5$nVS7)f#e4H0Oom<cFyo!Ve|Yi+3)#H>Rk}XRMBMiI=2v z(4)-<W=;O~7?YT^biw#0)nvumxj(-Gy?NIZ<0svDx68zG!zEdJhySK(qIsw6n#FI@ z?6Xm(3L~eOJMmSXHurT++E~okMr0Gyqq{v?$wjR)eowsgc3am1UnKU@;1mXu0j{%; zCv8{96!9Lb&gn(tQlb;+P5vr|5z{cJe<!BVO0~PgBKBgWi6!{u)Ry7bxXXn8A%(kG zeZk>0e@x;|qLdxEmi&K-irJ+ZZpvRWOupC)vug5czaW>st<p(n)sr)CI)cT?5s8M^ z))7E6j7^prl;M_yn5r@qFYm1;Rc2-fzP>54tzCWCgvy!Ze<BV48+_ew#zghPg4O@~ zyh*g7Szk%N{DgpE_L;>1H?A+p&-4=V+|PV7r4;YLH4z>?$81#`)P7>-BF%5tHOsH| z*Kd>_+)><*kjC`R>D-ks=vwL9cS(@5^F3}sS7)Qn;NbLo&h*J^_xdvpRrj)`Qm)Oe z5|iV`EGHG?W8IR!*uUB|Pd|JWzRV$HxtR8dn<h9rAFz)I-~x!|n!jWi))-nQwC=0< z{v4*ULH<-U@7c3W9})H61TCGTdu)1`mBiymYXaNon`f9xH}!!t08Xdhj82Wte&y0R zV6myUUD=x4cG@W)q5jY?DOqZ{!)~V8-SAe{P;d47CRRPBE556N1Z2=O)dfe;dJ2ol zkNB9(F&+D!`lC=Q-3{HUWS@ZRifpyt$7+4=2Qjo|LtBFmotCsf$zk5p%z`T>mC5SW zC!wEJybk?@x;{!;aL4aOzEsO-`OIlIX`YLA5ubh`2NdmFds={G(ia{?eXBV4iB7wl zbs;zDel}y?+THhiNBxG!Wnv2HY4qC7p^B}$=P*#d<??oRVbJ!4fXFQqxs7C6-7y=M zY^d_b^BooEnm4zPUuz$o`Z-jkV4;XW3#;EnTvM^OsOIlHtQq!c&lL{$fM4$ExsilO zfxQdT`;(5xSQJpTlG8i<^}VwsABQWw(wccC>J@K`z#MC9Z44*AqEC%W%i}OZ-b_Ro z0N!tU=<cf|G-aEajIfp}M>r%M9h+V1s(rtW{7}8UNjFNQafTZfEqz?P|MI9c(g_Y8 z>7>4&ROXy*uA{BZS{@;4+GTBIG)im_HH7`OEgiO7iKqSMf&s`e;!h5;Gy4xEP&V;+ zv_hI19hAsaZ~NaYKj6Iew#~XaE<X0R0TLWFR~<e}(l5^~Kz`S&#i0<IptFWM?y_CX z&sG`_MM(W&(X?mk`gj*0zxNF)Xl?1BlntW!(Np|Q{sW0wj6i>5o1$7W<?P$^W_6hz z91N#m*;nf_)H7OUd3ksbA8Fk3AZ)RHTQ<178e&TVkV*r4`w|LRTIBB6^tE}-7i+CP zAO}VGzfmUOU7a@SDsY;-wHzU>!zTBk$t%%4<W4cX?EG$57@Xg0`D2QmQ_B$m_~u=M z)j!Q#+KaKu!c4lTIAAr7HpN_4&iO|Jd|Ya}06aIw9F$vsR0Z}r8%3oD7Ttt=bCk$0 z6;3_1kqYtpv>X~xoVR@Z72Rm>jtX*0mG_RY_WXVe89s>%qkXR>xaJk={|EvDjato_ zXL^)`d>HKs{&!?HmDnvaxwy950=z3)0!BU%JP`0}a-+X!=Dt|`osNzFG`@t+0B)3g z@2~PZyjPtB0HI_aaBtL8puv&=QP~Y#tk#orMWvixwzZ`N!9d27``uqyVADF{P{mlH zuwUCBB&u)#LT!b}m%SShw>JinftX~GQh(p*BI<kw06zESRO3)y&MbtDN`At{2SA2# zg%XLxfg*fi7U7~sLj{l+Czmk01wcnUiyrO`r*jhs-ykU;zyQby`K$nY?R_nX1ptJ4 zjoN#s4_DZS?>W>{#P!Y@g0fo-HvkCI8T=_Qm9(4KZrQ*GogY&pJAxVgt0hzumT^u3 z@{0&<-%z;`BJ%(YxW(YvvaP7WRj-OF%}652m;(X0p(gpQ?Y%dp5%b4O0A6>RAfdsE zEuzG649CI+bo5tSU`FP49YH2T2NlqG<HWX2iom(r!PX3P`T(^NSm(Pc!qhTt;Q;ZY zbC$3Me$_mQ!oHu~q|n*_wOcX;PT|hk{J`uc0NyvPDO*^*?mY??0U)RIUn#Y^Zt%AF z-d|ffkip98Os>E{gJsGO14?9GE!D3u7_)ypW1YV$tgaDN;kb~BOHo#>6sI2t07dbS zWkT*ekPn@_p;Ti1MgJi*kaU%VreOCLGIRjysnUzxalkxNGsq8(GiyOx&A;19vWc4s z*~68B?%)GNCM(ZT=Pbb^^F;uR1`?s+rpurc^H2Zs!13NA0LWAVxg#r}JLG3tl7W>= zXiU}L*f|#Nl7dNWsL>MR;}6Y&9CTx)XY^39F{QBz5HuL5X2b_giRf57VWRG&E9iPZ z3fd?=+f~5&@{#}>AS0dMq|}9@Z`>n;j$6OOzM2SyT~Y`D)FdUlnmlblhZGVZy$k=> zXub}o7K*6R`4yXb55sB4Y1Rb(_;asl`RGb_@%u~BG%?c>W*DE^?f$*<@!jzTw20=< z+c=#7JX8(5<vGR&)R_Z~ekcT_lx}%7p?WP?hfv(w4=-0Q1(ZW9Fhg7#X=dIM=;6B# zy`{Ys=MvW+V~+EmRzHz90yh+HE?PiSCqH{=dwqz;a)s7)h=hAIHafutcg9yUW~(ss zyfklTT*Tz%q;|KSJ&`INZbn++-lD)ZB_MLmyb%mV(1Y2!7gajYD}9R0W+^bC%(3Wo zT|h1$r#0zX<W{^PMUwKop}Kq!KEe)vO~1D9uzNkX<VWgGuBv|Wx=wtj#({Tuir?Z# z)TvqC*4l=YXFbT+`^*3`h1@z^*6p{}yy7qQzHVut4GH50$vtb)_SUN&pPK5Mv%69F zKq`mO#IS)7At^)W<}sx!RIeMazn1HE>PoqSvyHxP(eMMMK#h+~&hI(t9@a6*+nJkF zHoqfFnZOec&&5vrUUmL9Z+n}IZCf3Ns3nUAv9;JLnpa2qMb1iO7lTM@`0I)?SOTl$ z(8u3?4o-a7^@k&VJWqw=kGHDk;jg<m{FPSmFPDqSx>n7wJhESRz4>HBq%jJDhZk%U ze**A}*ZU?sQROC<#hJFwn5WWw`V_09mp{LeC{p8qY$>=BZ(hS-UhPICoVJ&UNB+iM z^jb__^KAO<i$PrEtq<`)EeBggC@DZAS4w<;d-)UNBl#^>F-|8^aH>Z07f+U^yg3`L z^f0es){q4qUa-jgJ~$r^@_BC|DV>Haa>9+(yz?J@q^qErSNC@eO~3(Wk({u6mEIZ> zUwg6iY&i$nr^lk}vL?+^ZSuTW&4Zv!4kuT3u^5K`v$g^^!vpi&<BoKUYPT{^%BQb+ z4#|S};DTu^Xe}`jo=*_76)70P>^)GjFPxr6XANGKBncw<csdn>*Xc&96`LX&QrUiL zjNDPhgJnd#KDVNFqJ-kyuYuWwp#CkFG>3aNlg;%THqqNCb_{!1eKMon{WT!H7|dr& zc+mt94E~mJ&2VH|>aM$p?npLLevYf(z+(Wl$pxFf7Zr2#m&-JMp4n>a=4I9orq5j4 z-1I<8oD4VMJs+rgG&VAIUtqU)>4m?g!ftRgz2C{SJ1R+(%#mzMCph@qJPhf`>U_`f zYVNEc*69)$@y?&@Nsiat*K^5Q)#jhyH>x16-o8j?w-V3Cg^lU+Spz6iSvQ<}Tdr8+ ze#+jt_N`p<cQTbo|6xSreCf9vs>Yy=@W+eJ^9$K-%{vzwl6QY>Xt$?7m}Es#$jt3@ zrNZ+i2-S$F52sW9e4GM(U*P)o9fXJ)&N}#xNkLVo^-v<`>?l>UoqIK^WX{%{s{VjI zX}3ypE%B_K^J09K%kXq!<-8R=E=_%V#F#LH^VIGMA==x!M@_6=b9L}iF*Sw*C2yu^ z)mNZobnq&`1~b_-d2?}mW5#2_Ak{?7S$bb6%0X8sOUI=uq=|jS<AFVV*dds>>{I_% zcB2sOkUlMK|DDu>j1ZX#+9@eUzcXGRk8cJZ){B3SKiO@K-8>8%@f&Z?f-Bv=c>If9 z`IKv0+Q&^HVLFPfp_B}rg{R@%^N^?xE1Fo28b6e5=P}}q(DnDDz-LUYMxLdQr-#8; zglX%?q^|jk_&q;Z*NT4nWc_~0nE1hm!jA#F;~{T0GuHH!Zw33DBITHJ!?VIA-12jp z*w@_ldhyV)MzmV9>PDNg`qDUE^h>c_met_i#O8KXy}{4X>(Z9W3PbCh+fC0#G*$6} zUeS9AevxB{tiwmk-ml%2(a?8Z<1qX7Cy-_<UJ#Hn>EO^MkZwx<@z-e$3EfZN4t-eg zovAph9Q_BgL7_qJ_%rn9>?0iTF{nRHz2`42Dh5wud9nZt;q($%e<sa2Lq<2!jNZOh z{YR5gBux;Plxbc2Y7qumuTEq1sIrN%w2HZpk~q&)e<Id<*__FV$$$~*%!Oc#U5X&F zG<_FLX;3?gt(o8L8Z$8gLjPdeD9SQ?MmT*@doe330x*lYTxzUSD1GvbfSD5>Cz8LI zJY-)oXE2LoU5PLd-d0Fh!M45ia&avp&;PjQ1FpUNT@}NEeJO<5U1sF)m+wzxX18q^ zM3~&zA4(L%ZH`b<MY~PVIL8pO!+xRMI4Wap7e86vYD!z=EE8MRZmO4yzzusi&L`E2 z>Re`B*U6tbesbh{)~LkhbQ|eU>3I6nQs^TO{v<5CbJvizhM}KQw|+B$;ZIAJ-k$(_ z%*H_7G>wPy#_3r3L0mNbuFWkzSjI!vx8a@vCMP{O?uN&Tlkq%_y5=7Fg$@?$ggBZ( z{r-%TnH%z!_PgIpBhGSe%xZNvC+hy*Cq>LYA7CUR+jEnnJ`<z<<UlRGDkJFO=nznH z-Ibqz^3*wnf>oFfJAv!lQb4u&K4!7Wnj||sFI|24JlT@~(R}UkMbOa0x>kp&;Ak2- zYCPmE{2!}QU)WS&`R}dAb2W?S4M+ef$GR0Vb9nNO?O~G5o{j{Hp<l2d3m|_l7e0qU z;A*`!0o%7(m+9W0OB#-`o0hIZRJe9!MNC1}co}o7UvJW!fiXlHiaf-2s2(kTf10Ye zWU~bsQD22Ogkq3v#sx1q)=94bR@JXlo1Ww0W!j4l%<Kz;uTN4$Ov2r;_#1VgP<NYL z;g7Gx$Q+FAS-|!2TR`pQ*dj96m@<PY_|8<2;=N(7m%pn^?4<oyL~hqJ`7a>1FIZO! z62>Lm1mbP=dZY&39D0rbB55hp-%aw~r6*yAsDHs<p9#fy-~JXma#jJ0+oYiCIb4bA z=wZT+-X;f8<8-2y{l02904&c%h<|bnbgFdD)q1RwH_ck2tMCNF&;`L@*ii3xSM$0k z?z<6+2BTjZy*skhFA8e>JvSwfBF2mXf{-4#y_%d*ef#LkO|{!WW}|Ex@lElYd{~HA zo%Ep5{qGxWZ7gkm-P||axT6NLdXnk*RwE9F=UrwulW~<i$dVMo;=z|kc%K)9JcVa} z)gHYYu*eI~B9&vhModXXi1qHln7J7@H;xjoVm{g`2J=ZUT$w0EdA{U2<sH&<LXe@> z7X#>71IGp}hAQ4zTWaim4R~!8bkpEj?eg1e#5(HXo487n3|yD8+_#ZPYT-6Y;XE{T zOcPWO{eb8}feW60yx=t`JZsj_a&QL}#OA?~654Z~&2j?S;SclqOh?R5yO@RszsXVD z2eG&W2>I)24zgeEO$3r=wNG%x%S(RL?`29Y()BX4!rpyYM5@1k8YxI6JWoSS+2iiG z)(u<z+3%@e?I>;%Q885Q-hv_ltQ<OUrMRb&bwX>hM-ft4ySUPKa>5zl0_fhb0EbPd z(|fv~1S!CwQrvDtJ2IoSVv9hUSIphfB26yur4E~$9PE5j?((p=w6X+RT#?DWr1m&I z9ju;X?a0DMctHW^vD!fw%G55#Xii5KF7Y*g_K8~w>2;4C8kS-Vt$0qbT3DYKfhEF( zn%6SfW#+-F(}wiaj++7KJYKg5?g@iv#++~)Yz!wF?KLiZ*io^SIo1kVlCx>16X7jo zqWfg^iX*MTZ%?CUGq<N_DnDdXL(7YkrQ)Hk^hE8(S#dX|Ch@oO%rO`Y`FP-$K^xB2 ze=yzX5Z7Y*f%LqVzt!L73+-9^RR$j2c{9}|o%%?GUafRQudnzvdZ)g4yphT8eU>jQ zRt=|%gB4K3wQ@}xGF<Sv-2=$E=kh>h$N_@bm1$i~a5g+AHt>JQPM0W68P*lUQU}bJ zSU|~OHy5y~z+r&yG09`xF5#w+{|L>07(Dch!ss8<c^!)fff4I_)X>8Ge-V194>^J) zP3JH|`|9%i&`)n}A3iYxV4YF3#z|=9){|VQZ2tX!JgSoLqporuC{(&CTFLrR_dk|! z5APa4yd=AOcEwwio;ji5;ge4QU{H%Ik61*)0DdvN=+_lx_}AdVt9E8w3!xG)As;Qe zVja(|e1GB3KUO{d!8y<D;^S|x^I{}hj{p9OpMILGx7^60v&ff$ZInIcgRjJXuBt+L zx5*q8kYXTuVynK(4i1P=StL8QY?BzCM)ma)CTYf)>E@;s1Jii(=$YlD-gx)v7qs7N zm2*E#X)o$pd@_z8hiH=89Rd)-_vFvy=NEN5N@=tk=3KN#&1&Iq(m13?8RQ*SW=F$C zKl{1Ye-}Rd$h(<276?Nmb?Ra#atL(^bU;hUE9#4;%wlrUkw)zihQ9M<9;=^4zPUA% zc*$PJb6FN{VxB$s=VYjFAWwU-!UMcIb|W&$6n`HDYR^6t8S;NMQl#=?G?<0+a%OB7 zJ@|t5H<tqSH4uA~0MJXnrCE;=ds1|U6_cK6W_DPXrt%a&?rtf1&&q4OaWP+eD>bN@ z2ISX3G+$kYE^_YD-_S#%!lyXJ{XIX$hM{KbI8Vh6t(&RE(1OGDjA{)&H^C{k2O;Ch zRKguhI&+PB8f(#)$sc*w+Oo{u+~uKH3zJ~eNh{K*=hg8<8dwk7{XW?ike}joqi_AX ztZvBB@4BY^;(FtknmagPS=|?|^!3)u)W*x6^Wd~Kj?Tf}2-{G)1-@RFl>JN+bZpz4 zDLA}+iYG^Z#Z~oQ&{ja~V#g=Fxnn@_CH0H%a!0hO?#CTtPT-G2rhiRG2Ps0{Z0_Q7 zcHo%PN0}Pi)5aa2`Qg7QR4{~mtP2yO52M_8d9fv6Il1lSXA+y_+|Di}K9Z7!jRi8h z%4KNq^xDwG!oA@fmfsr7Y_7SRiPIqkKM3L|y@8gVB)<O3XCp@ii|?#W-{;nvU8b^2 z0ly+_F-NJ_e1I>8PYAEa6i3)SVVja=;$y3L={SV-mlIg@0?3_W!|QP~;4XBBF^#pu zSw8)Cvp7V8)6&Taf;RxOPUXyJR)$Eyr<;!gLY7jl*7A6Z39s?h(fc4ezwM{yw>)s8 zv!&zP<DoFC^gPGO@d6VtYyq*t#)q6ABND*td*+@zbofIRVGoyL6Y4kllcqAM43fMs z_j-@wD-CM+tCaJrL555|7<<1=Rr9t#SI>JQDUZLI{7dW2lNP-5*WrQ!GSyBQi~^A} z^d`9kh~mE#MqxF!GA%!jP^2O!vLgj~Q}c4}dMy**g-w-ihx${4(}{PASe1N++^Dt) zAsnSZXbtDKde!oJef4)TC3QcQ*rAkwmXGQktFPHiL45dti~<Ewo|_~GaR+9XCd|)u zO`=Hh7k_sJR86a&sNx{YAkKQfpIi>>^-af9F+X>zJxmO|8VB50-(vwR7j!VvrAQOs zVyC%TZTpnubM0uI0uu?z=V_~cDvWM3TgD0V)3yTso?Um3EMchPoSDLM5*k&2APE=l z5>T4Ec)dEC6s^;aspZ@u$Tu}s4?QVfiZ<np8KeGFZ9zSo6yvj4Q=w2kPv+<odn2W7 zY~ZCvYPhAD^@4*!{>1nG%AT9uu5+<PKAQI42`M^74R0yVl?6EjwrsSI(;kLzZ>9@U zWZx{N8p3iPeyU2qoCF<fkS$?bN=_CeA4Al18)!Clv5cbM2ITu#Msej4t6U=P60m~& zPYO(1>(LLzo>W%J8ESm&vug-%`l{Jut!Tz2*(kS?F1DF?v>|oEA(>7G%a4hCp~e{R zOqh{kmRE13ha+?<W5E2Jw_=kBVq}R*58sI5JX#Sm_YR~B)v}t|AwE;`u}G?xrpEjG zScGwfbADmxL%l<^t7N3-`j-b5zoEE5f%@*iQ!yJ8P%5hwD2@(W_E{g<egONUy(jwW zwdb`m1qtGNEO?o_(eQ2TCzSR_>tD~k5*`vBknL;c5yLuvKlw%3`$NpZtgv-xc}U*X zCd0#K6MWq9z$bf3#H0gnDZPDAVC`FV#xVNqd*SXy2_QMp(CH8}&jstbB)B2=n&U4I z1qWzi&nMq+ha)81zFl-><EeHd7oZH&sm@9GoP#@BBajM5rqxU!FFeyh&1mK`T#2n# zco@ZFu5CRS-*el~&E>d%z*Q?c<*h~_F<~dqgjs<J!s)a9>90UcFhar}JJ$Q}%LBnC zSCtLQBzu`gA%kTxvDokw|6PYgN-=015$hqdfA}Tw3<cfvkxp&1V$v*~mEOA2E4t;9 zoNDQ}B*c_wZ{8?Hl}Fgo+7u3Guf2RdV|}!%Jm+&HjfJY6_R-rLn_iNd_ZD!A3o3fo zZzgJUl&32|Sak~`aq3yvDoo-+=-0{e+QY<-*P0HJRwmdhJbEhgv8g>9h%UAXJf@Fj z(`ine*Ab`M+TG2bWI*uqp7sw)@UeIEcJe$op8(&aS9FL<cW|YOroEKc_2rfMp0SyO zekT6ryK5W;#d<osE#=YSe!@85!00G%3nq<6dvHI%&?ck4V!0`)e>|ojPLSUSC%3wn zmLFy4Arw|LBsxhWOMPqP0W3c%tr~8yMyu1-*Lq8OF!DHH>Qm33)MIU3<~5;yF2LLf zMTZi{;MaJ6GRp5U1b}yJM0@qGlHU38E5eNA_Fm7Y%8?(;4F);}6hYZZIoQ}c7TtTS zTzu#*aZ1Wyxxs{*H&hi=Bez>Av5=wfj@dYMoybhN?zn|Mn(;NxAL@`FB|%%B@kus^ zJR{zX=Mm#Kia~F-Dmy^g;UaS9i68G4Y002$%tSEk&JG{@=#QPJO@1NE?y8zi^jiHz zb;c`Pt~(7A`ntHbN8&6-Php5H^_*w-p?=A^JN&93W9)JJk`lowO>J^(;p;D=R|AI| zWgKt>pW{8pFxTX;bPF#gH;SBvT*K^e7=pI2rH0JT{r);;i6X`YcS|w)g3;}s*{dY| z1VLPIkw<7k+rMGmmBXgm{pdj^Dw+o4{W-Ji@J%BlV2-LJKoA^Blu+%Tw*}!&q#ZYh zH__IiFTc@LN;WoTfwE})3*&1LCA`_h+;`de^h^~CRkK!d>z-8fxy!NH+Y^A`kdy~~ zBi~IV)21y{x^E2ptXS9mcGs5sl7y~C@3%j``u>w&N{P7m+vN%ToN;R<OJ<nYIotw% zwwsrfU_4`KIe^6+t`vC3P}9X+Ya;uZe*ZV^!ujQdwEf-j89E0>pr%%dodE@#IzgW@ zw=7dA!S%2%k13ACV@#08P6?c9t9Lc;DZ=QI2{!n{<JZEM%+42b@#|Qu=js5sGkWC( zSn*y(2OlM*%{>8HsiB{6B9B-=E-d=Aja@g{$3>vmKS415!P4GwfLWQF{Q46Jp+RwH z#w+;9h-stY`!Y#(dP;;xuj{54E$xX!jR!F(4C4dCXj-a(fH_M$rqR;qgw1wC>I7Qn zZ@a}YZA~_y%=+?P`xjJEvML};!fNF)6!}K;*J)5w)?ayD3w|0SEtGb=8(5l6jEx8r zb~|U|4$(Lt_3HlF<`O}hm{h;DG4iaS|42ly)^xQpWiTP##djlwVuuW<>9e@Q^%l=L z2oHZm2yhuB3kXE5-R2Qd3F5)L<Vra3>-SA@iEzM7;Lw&gzwrJ5GMLytx)i10zN2`? ztJ9y<CTXz8BO_MlI86SMQuvRln7ZurS&yxl^mM_e^sZ-&eia*N%^@-W8U2D_n36;P z9hwA#25jK>yTQaZGrNKz)$iAPjDz;vZ0js4zOp>~j;yHm*It}Aqx_Q+31ak<jk)KX z{l0TgY46s^nv;Hjav(3kqgRF(?=I4_pZ1sTomj3zP}my^oa!w9w2PM7v&430#s6Q% z9a2Kb{+-jy4`f@|Gbe|^Awi&LYr{lzuH&E1K#GDU%q>Wg5r1=)U?dj4Vp>=G_m>O1 zb`1Z4_RJa!*y}bEC=Xwl-=qB`$CuD(`8e!~8MKAg;Ua0b0aCEAjYRYRVtaNtBmjhb zE`jAs{7dK;@*Js~Jfwrvp3e_VX#YtrHwd81`AF>uTiE{bjY=hx3`spD1oYPbAK7O? z6j$rNriJ~^J1h*TO&ndz5hl87MxQF)BMEvyQRSzwYr+4eLV5dWVPOA#t?kx>?UW=g zr-|FSo2@fK$08>kHS>eCl?peHagqM*PAytl)VPhcnAmO3ZSSJ&HI>!QTG?ne)Pm?I zJ;tOZ=Uz(h?WJ#@lhSSG|Ey?QgpCU{@A~`Xml**g^(zr(Y!o~*CO}Z0eOefLyfYB( zM(i}<U9fn!%T!?N!6c^_YQHuqud>v6t>TVfnVa8w3mDVv8Fe%X50HcXvcl}k(V`UU z_wReHQY0~wIgid=>QHhvN{^%*i4^;#vZcH2Dlcwti#!5<6gx$5Ek*Q%zi1^U<y~uZ zPIX^ZkWP=aQc+BwX62;aR%z+4$`U@C6wRv?D__l-dv|OPYhiu8&igQwzj8&phPa89 zjkU;rNA)dn|De6ARaOSpo{Xlr4iELbZ-&cOn#E}|nTIaO$Mx5J=8W=Fv?EF0WS7P& zXxGjuHyfT75SmSI*so-uVtjuW!J+;ePqp?I5}V=1%d@m`^p_M-nyeYx14`Zi<&w>* zB#YC1Zz~)imHpUne!Wv3$m~5Rw?6wcjzQR;8Vy7T+%3_o7DtWNS`Wqe7CY|H05#1q z><mH(@}@XRow;$Q#tMdKXr!Ckk5f!MWioaJ))}lDxqW;%$hHCD(LD1KA~S17uB|(| ztUldyc_h@23k8UPRMG!dyC?{oue%DRM~(@&EM)cPZ@FAO963@}5G%pJ3LrpPHIXE1 zz`pi(Y=4hH)^x)?B{kwBCLM;jd<V$`#+a<?2K4VHwu^{8o;8t85<nLkcig)6I~2{^ zsHX^1!bI55Pd0_m97A~=0=a>@)MqMqFi@cOiV*y+OsGC~hoC~V-(Ak6xyg+)0-z3Q zhI}STjUsO7M)rgh{>hDfS;MLQ6hfdFWka?&Ns$ZoWE5bggcI730J0!9bl?<S4OLzH z^I+^I_`_R7c_|Z;StZ5jA*3sv94Q#i<Fv*H0e&QD@>K@4VJ(GSSA%j4F6|+$bFc1q zUd0_5%tDKu{jF*d@JCtvM-UXP(^!6D`=4+ZrOA=I{(X#oGiP~m{HfhmxyN18qH9&C zz7EY0&4_2s<0t{k*mjmCx10&BB@SnSf-I`4y~@1VFn_#Nk{A>1x)wD^o49csmh!1` zH6!5|-xR!ijh&&uo_^2ruwiCzqx6%R?1zAxD!OR(O#Vs77{Q^jSkh7~pb{#8lVKf; zu+F`?-8eu)G(N`6$5+<!gJ68_gxJOyV3Qhcvb~zgTEqf$Wvu6p3=p<O67j~cEw(9n zOt0>Hj}NyDdnbK~6TJ7Uz}ZV^Kd|}RQ#F$QFNWUm14yb?>LRXNk-&83VLYET#drIh zAN!>Jm}5+6)->GQ6dW7ljkV|tevbQb_#K8$zE*I`q#E<v@ZM?Q%s0%p-^CjyZ#8r? z1$*BW=!_8p7RJ-brV78*9IsWj+R7LAS(Mkm5M8{Yjv0)#S`qqYZ8&GmN78g6a)cTa zKa45-2%#(`fy`<P1*?X(#&GVYc$A;jE>XPqDihC^@=r7lU)<?%zJHVRsJ*PvtnTs2 zUi2kJ=ZhfGR;QNy?8ww#mRo4a^iRu!J9U-H>%<Mjci@!O5r=oY=T7EQ%+Ca+O*WVy z@SImBi9Elbmr!(TRK;e@u3v>QM1LoZd6azZf!6g~<{t;87K?qpW<zr^XyJK3N6&&R zv*}a8!zJaO2_CB@l;4>)#@7s!*4Bqn>whGbl}79+=6GBYZyU=vg;9M&s0HaZJ+Pqg z>kPlOEgWKG%H1Bl=Y!i6PX5##VN%6Y@w!i28~y}SPJ$SGeX5X4<9loF@_K=x&(O>K za@G?4tE_08=e0--Yh<Lh)w=Sd?+I<DT&_n-;+Njy>)=$p#}D!9h~Fs>^gv8?#<zHk zJ3}Dbp@{5n$8d5{%azv5%H+V#t=q0Cpuw~=aw)ANpt8TL5=eY4bF#hQ;XkFo^ryJf zmsH<{)>c({m}%XFK2=_=(2bP&bU`>N(^R`&E$L>1`;j`yyY4RUg}t4;aM-)<(wEfY zVm(-4e)j!Sp>GJmBTB4d*{VvH=<KE$KD#khCi<ug)8B}q!@)7}j3)&s7jbyj+{afn z1CMtFnAF@wrV3v76IZ@4J9X_jOIUlgn5iS=^-cUJpdc9Gv>*>t%Z9+bqCcbSUZWTK z+2AOHewrXL9Vzdi-)81YZ^7apHUj^QkK_=vP(UT|bKJdG>6<b7iRJwvc*q@}UGYkD zy-z-3La!>BcZEcUY36M>tFXNc9BEBB85q$Dy`kUZN%%ba<!RkVO&I=k^!pTCmWJvu zubB|h*lv4I1|C_i8!0u^d;r})YenVXT(<Qws!6kqy%5qcJCT7>x14KBvqO6>f>>LA zgSMKr2CP;h(#0LWVQe<C-dZCf(E-ih?ex!^cdD9mi3_>Zm)Jka*y3D^oxG&64@fw8 zvwR1cTm-okOpy7xV+{Bp|4BGjNsOq@?oFQdl?|N#a(W!Fd>Mjl1wCz{8K%V{g0bO0 z%Y%o{7<si-W!tN8!H?k=%>?D<O^>u>M-=M)Iwy#J%;i6}okuFdJZ++b8o2=7@*d*y zeeGTUWIm9^VWpIHz5U=ASzNq0hi=tv;&J`iCWf--TDZF~+qAGzD_B8)xq}=O6|@L( z!>YyuaTKV95||N9hMM!FANk%hZaap_!I<<3?Y$M3sSoXZ!ZXFO%j;kGsskFQCVY2( zPGKXhi#kk@WG}N>Td8L2i>a1p?=_!?u2{|IC~>tP6d^cZ$m}x7v&81Chb6F{AER?R zN3kEU0)st&(Bf{#uQ?Q;<mIb;=F9ZN2!B$7SX8sFG$i>m__n%L+pY?xqzi^eF<TO8 zYF4<|8FLW=8Tv_8khPYNzMxeoIZr7#mgll{IOJnUO0qwhgWbNox9$#<7`~5#N0tdO zVxoUVQf4qI*%rCs47GZ9vWcOn)XSiqAyfTA5Bo+bT<I#h)jG#>xK&iBbVsFKnk~eR zOJ0dBYCrMwLu@m=a%CfOIro_w2NaTd+dqre;R7>r4%naFyI>qVKe$LbA~6%>M9W}Y zCrW8(tOtpA4d5+~Gi`d@0l5jsETHdFSw8va>DPLD1?m^@Sz*-i%r1>h9t5S+3t!qb zJ}`bz`IPs|w7LbC=I~aa>_++zdQ@u>vHfhKAPsmKhMmz>B5JvS#W>w9|17gl-K;TQ zJ!W&d6l1dAF}5QF&PS73f5m-*_Y`vDsd$JzDdU@%C5arH$6H7&84X$)FiW`)LkNo| z?<(3fy)w3ZQzGa%BcP9-W;k3c^sM*Ydp(|V6=PIiz=4@K97vC-?@$KR>IwB7bdN2c zA~m;@0?hY1R;Im{NPq?@2aH0Uz*PQ@mCXGTVaH!gUiDQu*&kH9Z?)_UZi$sn1<bry z87RY~%>gCR_JP$1D?;H1AsSzPz1^vPQ6gw$WXL<+n=Pc$&5KJUhbb739yix)DsppM zHv(g-Q7&i`vMqL?HXV0{Dv3D=74UCtlwCm^bNXauYpmC3EDA6RiP=212nfi%c<A;B zD7iyauV<O4<GB4oM^n0P{ra7g3Z5ztH>(%Yy0YF;BaD%kOcSZm7d?ZeP2QJ*y0^OE z;8G>3oXZ-Irf^9)>iBD^eq-~6`Utw8Oa6@3;bOWa`mk|QYoeZiQVy%!q%1@Xiao|Q zM!$^xm$8_OB8MouKM6vPCzG$fY_s!R&5%XsHW%!dg%HRnkq8UCDSM^B7seu>H2lLW zT#wJJ$v_Cmya<ty?+WtkY?s}35=;$4UO9U5Z2&V=WIg}NoyCF-y}AmYRh5Ci&G<j# z(GOKfvP}YDm;`cXdHyF0EU7Sbar~F&hiU}Y%u}RSd1c5NWftd&kAyA+ZvVNqoURN+ znd(MtGx=u_1|z<5{p=y_I%jC#UeOBccmLA;1y@Nbk?W60y8-}0OlS0j+~u6@hc;9| zP5;W&w0Na2NGx1!l{AzmAV=#B$k(J!ff^}+QCNkvKm;d4hV_wsr#_a_8&do$8&QV> z60$L|(_mo(I=WH>+OV+i-x21kBmZQD#7jIdrk-yFik6&OVj))(^M75+R1w4ZYq}i# z3Jx;9df)9R^t@L_`d1mB9J&kZ0}?IWh^Ob=5PLyfkMq77A{{OD&W2D`ATCSC4oMbj z&(q%c_(ojy?gl9VwB=I^TSQ)o8ETLsUksU=NVgC7N$H>#7e2E8ucCjWbYI<c^F#e@ zqk%3M1P>*{Q*5!Z0Ro|p-tBj4xw4=Is{j2T-%<zvG!aNqu?<!YgMu$3<PE#|wVH+- z>wiW#3@PFr?amh_sP|v;EnwJ1GmuUDs`;Me;k;1efy4u7KO+P(lW9f$#{#3-hil08 z?>}LM&W<THU_&VGrv{srE@agsvmNM@2+v`A&yQoY@<Dib&o4%vcmy)LeV<g*&RA$f z2R*tjq7QM<KJwSejgI|!*jCU(=!p;0HueXr@f_Dih97;Fhb5kUHVUh?yYYnsfCoI2 z`|VZarSKaL*AdT%OjR;$+NoE(4)lqj-ggr3H?WL;kO&J%QftI+aXAaNnWRS`ZH#^o z4~LY$!U1%zJ3qYTcP=2aJ@4NlDSrhirH|{je4bTX)Y{R0h0Jvl{e$lKlLq|5=Wabm z8kRyD?E=kSZNWSo*VJ*jN5lWsfjwmF-LkgRZSU16a*u5sdNKogBNs5;8M7%eQSN{C z$qL(#djhz&JeCZ8X38G$fgB;M60Ah)!OYoLU6AEAu_|P4WhB;UF>T{p;%_N=AeW5V zyUaxO&r$Ol0MNx=5sVdoxxBKU6+%_vRPn8W%s^@Ax^<qW{^T4ZLKb>oc(>j@)0H)z zD0EAiMc<#+)gHtCRV|?4+mdsJrGUje<Z3&R2m=&+`-@m8&mk`N<dfAFyAnE#6*AZ6 z1$y34ReFg-chK&wo+%7!PUFJ*w-Wp6qUo=T8_)E3^luAo{~<so>GKqNMMPDu`HvIt z`}%(Lgeoksv)?1AUA73ZpP1n!m|f}n%mh%)P3VWx%|tVsAw+Md@^G#kxXRk6o@dvj zxrnFo*N#y;GYu@3DecTgge^0(T!huczj|*;=N{X;4URN^?j`{(kE$Eo*F^0F=}p(> z<)hCg;&*axtDj>dRL1v8`<KLN!aP|~`vy*KKJ^a_umDg@=0{yn)}~TrT4|0p-^`rN zW1)xD_;v3d%V2`><rO!R4Mpv?M+P5OUI#6sw$N=jocH?~<niZfrpd%oMKJ=c)6`P; z30ipEgGjfXT9~+`H&)EWu4+wgGk_%U_*{K~%Bud7xXN#%m~>-)Ajoq1L93(;`&XV2 z$@%kKufVyCxICQk-ob#HA2T_;q23~p-*9qoD(B2qe}Cp+z<71%$3EY&MXAHA-q;39 z8Jy0`K0W;B`E!AK(|((`=KJsH0A1|WU~)B^{|V4@k9rk*kx;Q5xA*z=&kPT>>QB>{ zk^vLi^?hA!_l_ZVYlFLJQGgV!UUu6`bv~|;o1%XiTtwlff3#(|`P2JFq|X7lj4)R^ z>IA2c%0o8Pzp}Kqq-UAw{8e6{uE{(R7#6Ee6gaT14Hz)3CwHuuoRzW|HMAGo4TNtW zD!yR?(Fc1rQ?yZD18YdKq!RAq{Vqj{n#~nEJ165~9H7=!$>#gYV`O+*&%F#zyICQ~ zD73OFR4uAIc7bu5$L6me!_yhEyi^-8$C>vIG!=V{7OuUtEbeOY+t1U@`$YkXv}NtW z4x;1KQ$4)X;`XDn=V8&gq<4d*J@|904V=2!zUVLcCCm$jCFrAEd1Gb^Y4YISGpq$1 zYbYKOy2&hOL+1Cd0fL)QKjgZ!e64(xubnj2OYf*`jW$nmKAwOdiYQtS#n2QcSwO#L zHgTPro~27c2H{12!G_iLzDdAtwuZRIr8|&FWmeQ{qO#NN*=mX_V*QDC{CO&NTV@vC zt4p$zS`P)<%k3Tk?PBx36j_yx-SNy+maWT3o9VIgvFoYAN>R;lf1`uD&Bh}wH4jx@ zD2JB*sH|<$$eYAO$#Th36P+sK2%=Ta8Q|}Xb7=n9^Whr!OxSGET75<*jJOKv8eE*X zKw7+fbedQ-`JW|NwrU^^{@O)valPI5p|et%f{<*E65X%mJoP&L8W!-JXG{?)tm=y4 zzZ2I;lFq(~&HJBU6qtCatTpkEmIRzXO>OWpTVDf$-mwa67#-Nh;ZnU>{l2~aOX-|V z75?VdiQVmyetC`NhyJqY_W*H^@1*=NM#1OTV6%VYg@_v+8{3v6@0NS|lY^6C+r##U zL5RU088M|HBS$BGeQtBH<1N3<W`mjz4BvA2Kdx-<w~$Sa6(s*clj<uSiz%i2<MTnE ze`9;%`llGd$R7pIHOnUtWd-;q7@`f51>;BZJDBi5l}nym)r9_N_l2MvosCM8vFX&- zoX+XV$TKtdplpZ|!H|j3YGbUt4`N4dGy2*0wgnEIn``PO3noV8&vajWO-a*gp0@Ip z8u2eMfq6?ZksQ3)S=RPH5t)`hd3N<Xq_6s|vJP4e!9I&mVs>H$<D)XN@R0r{F8ebI zH5O;%hXoJse&{2)nS!yj>}TIM(f57}{V)nLKgQOe!Y+gROPtnBjvLH>F@1L+Q+_Yj z_I+}*tCOmy!f(kNB}bJTj`xR%Vf()4IyR0^i~OB}#nn00&$H*=$_rvU+|DTZfEll_ z+MOJ;QLL0~;(`O2bB>G{yvUCmG)ej<wffG-HYozVUrOt;Z+To8+xjjp4%unByl|Os zHPcP3jFPp<2`2)lI-OE?VrX2}3}WWj?Ds`>^gM<(7)4?QOA=X+ZoVm>Y>*tPHaEGA z>e|oo4K<B%8J$WxQxo7*>Ba;2Lpdd;L^B8Z&W7}6M9Z^ckGtFg{EmwlFB#l?p@97V d_A$!Fh@EA1-a2yU5WT8NT2kS8iG)$m{|5uK)Q|uG literal 0 HcmV?d00001 From c9138b72cacb72e09db39d32f072f6a7e5adfe3b Mon Sep 17 00:00:00 2001 From: YangJinSoo94 <jschrome782@gmail.com> Date: Thu, 3 Jul 2025 18:56:25 +0900 Subject: [PATCH 059/108] =?UTF-8?q?=EC=B0=A8=ED=8A=B8=20=EC=A0=9C=EB=AA=A9?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/business_components/src/ui/chart/BarChart.tsx | 6 ++++++ .../business_components/src/ui/chart/LineBarChart.tsx | 4 ++-- .../common/business_components/src/ui/chart/LineChart.tsx | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/common/business_components/src/ui/chart/BarChart.tsx b/packages/common/business_components/src/ui/chart/BarChart.tsx index a7282830d..994feb3b3 100644 --- a/packages/common/business_components/src/ui/chart/BarChart.tsx +++ b/packages/common/business_components/src/ui/chart/BarChart.tsx @@ -110,6 +110,12 @@ export default function Chart({chartConfig=undefined, chartData=[], chartTitle=' axisLine={true} fontSize={12} fontWeight={500} + tickFormatter={(value) => { + // 소수점이 있는 경우 소수점을 유지하고 천단위 콤마 추가 + return value.toString().includes('.') + ? value.toLocaleString('en-US', { minimumFractionDigits: 0, maximumFractionDigits: 2 }) + : value.toLocaleString(); + }} label={yFieldText ? { value: yFieldText, className:"text-[13px] font-bold", position: 'insideLeft', angle: -90, offset: -24, orientation: "left" } : undefined} /> <ChartTooltip cursor={false} content={<CustomTooltip active={undefined} payload={undefined} label={undefined} />}/> diff --git a/packages/common/business_components/src/ui/chart/LineBarChart.tsx b/packages/common/business_components/src/ui/chart/LineBarChart.tsx index 8c7202104..11984cc14 100644 --- a/packages/common/business_components/src/ui/chart/LineBarChart.tsx +++ b/packages/common/business_components/src/ui/chart/LineBarChart.tsx @@ -127,7 +127,7 @@ export default function LineBarchart({ className="text-[12px] font-bold" tickCount={6} orientation="left" - domain={[0, Math.ceil(lineMaxValue * 1)]} + tickFormatter={(value) => value.toLocaleString()} label={{ value: yFieldText, angle: -90, position: 'insideLeft' }} />} {/* 오른쪽 Y축: barVal */} @@ -136,7 +136,7 @@ export default function LineBarchart({ className="text-[12px] font-bold" tickCount={6} orientation="right" - domain={[0, Math.ceil(barMaxValue * 1)]} + tickFormatter={(value) => value.toLocaleString()} label={{ value: yFieldText2, angle: -90, position: 'insideRight' }} reversed={true} />} diff --git a/packages/common/business_components/src/ui/chart/LineChart.tsx b/packages/common/business_components/src/ui/chart/LineChart.tsx index 28b4ceabf..6c1b6e7bd 100644 --- a/packages/common/business_components/src/ui/chart/LineChart.tsx +++ b/packages/common/business_components/src/ui/chart/LineChart.tsx @@ -193,7 +193,7 @@ export default function Linechart({ > <CartesianGrid vertical={false} /> <XAxis dataKey="label" fontSize="8px" fontWeight="bold" /> - <YAxis yAxisId="left" className="text-[12px] font-bold" domain={[0, Math.ceil(lineMaxValue * 1.1)]} tickCount={6} orientation="left" label={{ value: yFieldText, className:"text-[12px] font-bold", angle: -90, position: 'insideLeft' }} /> + <YAxis yAxisId="left" className="text-[12px] font-bold" tickFormatter={(value) => value.toLocaleString()} tickCount={6} orientation="left" label={{ value: yFieldText, className:"text-[12px] font-bold", angle: -90, position: 'insideLeft' }} /> <Tooltip content={<CustomTooltip />} /> {showLegend && <Legend content={CustomLegend as ContentType}/>} {Object.keys(chartData).map((key) => { From b20948fcfe67039f4e02fa04e3945ad08dd93431 Mon Sep 17 00:00:00 2001 From: jyha00 <jyha@mind-one.co.kr> Date: Thu, 3 Jul 2025 19:46:11 +0900 Subject: [PATCH 060/108] =?UTF-8?q?=EC=9E=A0=EC=9E=AC=EC=88=98=EC=9A=94?= =?UTF-8?q?=EC=B2=98=20=EC=97=91=EC=85=80=20=EB=93=B1=EB=A1=9D=20=EC=8B=9C?= =?UTF-8?q?=20=EB=A1=9C=EB=94=A9=EB=B0=94=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wr/app/matMng/coMng/ldrmntCoSttn/page.tsx | 7 +- .../app/store/matDmd/ldrmntCoSttn/index.tsx | 64 +++++++++++++------ .../coReg/coRegExcel/coRegExcelModal.tsx | 39 +++++------ 3 files changed, 69 insertions(+), 41 deletions(-) diff --git a/apps/wr/app/matMng/coMng/ldrmntCoSttn/page.tsx b/apps/wr/app/matMng/coMng/ldrmntCoSttn/page.tsx index eb43b29bf..e33bcc265 100644 --- a/apps/wr/app/matMng/coMng/ldrmntCoSttn/page.tsx +++ b/apps/wr/app/matMng/coMng/ldrmntCoSttn/page.tsx @@ -28,8 +28,10 @@ import { type SelectDataType } from "@common/business_components/ui"; import ListDiv from "@common/business_components/ui/list_div"; +import LoadingOverlay from "@common/business_components/ui/loadingBar"; import { Button } from "@common/components/ui"; import { useCallback, useEffect, useRef, useState } from "react"; +import { Toaster } from "react-hot-toast"; export default function LdrmntCoSttn() { const { code, ldrmnt, ldrmntInit, ldrmntSearch, } = useWrStore( @@ -273,7 +275,7 @@ export default function LdrmntCoSttn() { <div className="overflow-x-auto"> <ListDiv className="min-w-max"> <UiTable - headName={[]} + headName={undefined} tableData={bdstTable || []} headlist={ldrmntHeadListExport} pageSize={20} @@ -299,8 +301,9 @@ export default function LdrmntCoSttn() { setIsExcelOpen({ isModal: !isExcelOpen.isModal }) }} bodyClass={"w-[40vw] max-w-none"} - body={<CoRegExcel />} + body={<CoRegExcel isModal={(isOpen) => setIsExcelOpen({ isModal: isOpen })} />} /> + <Toaster position="top-center"></Toaster> </div> diff --git a/apps/wr/app/store/matDmd/ldrmntCoSttn/index.tsx b/apps/wr/app/store/matDmd/ldrmntCoSttn/index.tsx index 6a5f18247..289ced6e1 100644 --- a/apps/wr/app/store/matDmd/ldrmntCoSttn/index.tsx +++ b/apps/wr/app/store/matDmd/ldrmntCoSttn/index.tsx @@ -20,6 +20,7 @@ export type ldrmntState = { excelList: ExcelType[]; isInit: boolean; ldrmntRes: customLdrmnt[]; + isLoading: boolean; }; }; @@ -35,6 +36,7 @@ type ldrmntActions = { ldrmntSearch: (page: number, sggCd: string, total: string, coNm: string) => void; ldrmntUpdate: (ldrmntRes: customLdrmnt) => void; ldrmntDelete: () => void; + loadingOpen: (isOpen: boolean) => void }; export type ldrmntType = ldrmntState & ldrmntActions; @@ -46,6 +48,7 @@ export const ldrmntStateExport: ldrmntState = { excelList: [], isInit: false, ldrmntRes: ldrmntRes, + isLoading: false, }, }; @@ -75,31 +78,30 @@ export const ldrmntActionsExport: (set: any) => ldrmntActions = ( console.log(e) } }, + loadingOpen: (isOpen) => { + return set((state: ldrmntState) => { + return { + ...state, + ldrmnt: { + ...state.ldrmnt, + isLoading: isOpen, + }, + }; + }); + }, ldrmntRegData: async (data) => { - try { - const response = await postFectch( - "/reuseFc/request/temp", - data, - fetchType.WR - ); + await deleteFectch( + `/reuseFc/request/temp`, + ); - console.log('response ', response) + const response = await postFectch( + "/reuseFc/request/temp", + data, + fetchType.WR + ); - if (response.status === 200) { - ToastFormUi({ - body: "잠재수요처 등록이 완료되었습니다. ", - title: "잠재수요처 등록", - }); + console.log('response ', response) - } - } catch (e) { - console.log(e) - ToastFormUi({ - body: "잠재수요처 등록이 실패하였습니다. ", - title: "잠재수요처 등록", - }); - - } }, ldrmntSearch: async (page: number, total: string, sggCd: string, coNm: string) => { let searchUrl = ""; @@ -111,6 +113,7 @@ export const ldrmntActionsExport: (set: any) => ldrmntActions = ( searchUrl = `/reuseFc/request/temp/${page}/${total}/${sggCd}`; } } + try { const ldrmntRes = await getFetch( fetchType.WR, @@ -178,11 +181,30 @@ export const ldrmntActionsExport: (set: any) => ldrmntActions = ( }, ldrmntDelete: async () => { try { + set((state: ldrmntState) => { + return { + ...state, + ldrmnt: { + ...state.ldrmnt, + isLoading: true, + }, + }; + }); const response = await deleteFectch( `/reuseFc/request/temp`, ); } catch (error) { + console.log("error", error) + set((state: ldrmntState) => { + return { + ...state, + ldrmnt: { + ...state.ldrmnt, + isLoading: false, + }, + }; + }); console.error("Error ldrmntDelete:", error); } }, diff --git a/apps/wr/components/ui/modal/coReg/coRegExcel/coRegExcelModal.tsx b/apps/wr/components/ui/modal/coReg/coRegExcel/coRegExcelModal.tsx index 89f15a3cc..6f6afe186 100644 --- a/apps/wr/components/ui/modal/coReg/coRegExcel/coRegExcelModal.tsx +++ b/apps/wr/components/ui/modal/coReg/coRegExcel/coRegExcelModal.tsx @@ -4,32 +4,36 @@ import type { customLdrmnt } from "@/app/store/matDmd/ldrmntCoSttn"; import { names } from "@/app/store/matDmd/ldrmntCoSttn/excel"; import { filePicked, FileUpload, FormUi, Title } from "@common/business_components/ui"; import { Form, FormField } from "@common/business_components/ui/form"; +import LoadingOverlay from "@common/business_components/ui/loadingBar"; import { Button, Progress } from "@common/components/ui"; import { zodResolver } from "@hookform/resolvers/zod"; import { useEffect, useState } from "react"; import { useForm } from "react-hook-form"; +import toast from "react-hot-toast"; import type { z } from "zod"; -export default function CoRegExcel() { - - +export default function CoRegExcel({ isModal }: { isModal: (open: boolean) => void }) { + const { ldrmnt,loadingOpen } = useWrStore((state) => state); + const { isLoading } = ldrmnt; const form = useForm<z.infer<typeof coRegExcel>>({ resolver: zodResolver(coRegExcel), }); const { ldrmntRegData, ldrmntDelete } = useWrStore((state) => state); const [excelData, setExcelData] = useState<customLdrmnt[]>(); - const [file, setFiles] = useState<File | undefined>(); + useEffect(() => { + console.log("ldrmnt", ldrmnt) + }, [ldrmnt]) + const onSubmit = (values: z.infer<typeof coRegExcel>) => { - ldrmntDelete(); + loadingOpen(true); if (file) { - filePicked(file, 0).then((res) => { - //TODO:loading + filePicked(file, 0).then(async (res) => { + try { let array: { [key: string]: string | number }[] = [] - let isError = false; if (Array.isArray(res)) { res.some((item, index) => { let object: { [key: string]: string | number } = {} @@ -43,26 +47,24 @@ export default function CoRegExcel() { } return false; }) - if (isError === true) - return true; + array.push(object) - if (index != 0 && index % 5000 == 0) { - ldrmntRegData(array); - array = [] - array.length = 0; - } return false; }) } - if (isError === false) - ldrmntRegData(array); + await ldrmntRegData(array); + isModal(false); + loadingOpen(false); + toast.success(`잠재수요처 등록이 완료되었습니다.`); } catch (e) { console.log(e) + loadingOpen(false); alert(e) return true; } - + }).catch((e)=>{ + console.log("file", e); }); }; } @@ -70,6 +72,7 @@ export default function CoRegExcel() { return ( <> <div> + <LoadingOverlay isLoading={isLoading}></LoadingOverlay> <Form {...form}> <form onSubmit={form.handleSubmit(onSubmit)}> <div> From 01ebab4a94be128d2919b44ef5d2bfe5b721759e Mon Sep 17 00:00:00 2001 From: jyha00 <jyha@mind-one.co.kr> Date: Thu, 3 Jul 2025 20:21:01 +0900 Subject: [PATCH 061/108] =?UTF-8?q?=EB=A1=9C=EB=94=A9=EB=B0=94=20import=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/common/business_components/src/ui/index.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/common/business_components/src/ui/index.ts b/packages/common/business_components/src/ui/index.ts index fd1e6fe9e..06c641259 100644 --- a/packages/common/business_components/src/ui/index.ts +++ b/packages/common/business_components/src/ui/index.ts @@ -21,4 +21,5 @@ export * from "./table_div"; export * from "./title"; export * from "./sdmInsertForm"; export * from "./popover_ui"; -export * from "./date_month_picker"; \ No newline at end of file +export * from "./date_month_picker"; +export * from "./loadingBar"; \ No newline at end of file From 2cc8d5209ceda52f21c5037cef86a593ccdb9c66 Mon Sep 17 00:00:00 2001 From: leedj312 <dlehd467@gmail.com> Date: Thu, 3 Jul 2025 20:22:19 +0900 Subject: [PATCH 062/108] =?UTF-8?q?=EA=B2=B0=ED=95=A8=EB=8C=80=EC=9E=A5=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wr/app/matMng/coMat/coMatRvw/page.tsx | 18 +- apps/wr/app/matMng/coMng/coSttn/page.tsx | 9 +- .../ruseFcltMng/prfmncReg/chkgdnReg/page.tsx | 190 +++++++++++++----- .../wr/app/store/matDmd/coMatRvw/headList.tsx | 4 +- apps/wr/app/store/matDmd/coMatRvw/index.tsx | 6 + apps/wr/app/store/matDmd/coMatRvw/type.tsx | 2 +- apps/wr/app/store/matDmd/coSttn/headList.tsx | 5 + apps/wr/app/store/matDmd/coSttn/index.tsx | 2 +- .../prfmncReg/chkgdnReg/rainChkgdn/excel.tsx | 6 +- .../chkgdnReg/rainChkgdn/headList.tsx | 16 +- .../prfmncReg/chkgdnReg/rainChkgdn/index.tsx | 164 +++++++++++---- .../chkgdnReg/rswrtChkgdn/headList.tsx | 20 +- .../prfmncReg/chkgdnReg/rswrtChkgdn/index.tsx | 182 ++++++++++++----- .../chkgdnSttn/rainChkgdnSttn/index.tsx | 4 +- .../chkgdnSttn/rswtChkgdnSttn/headList.tsx | 2 +- .../fcltSttn/rainSttn/headList.tsx | 4 +- apps/wr/components/lib/api/index.ts | 23 +++ apps/wr/components/lib/func/TableData.tsx | 43 ++-- apps/wr/components/lib/func/commom.tsx | 42 ++-- .../ui/form/coMat/conCostForm/conCostForm.tsx | 6 +- .../ui/modal/chkgd/chkgdExcel/chkgdExcel.tsx | 21 +- .../ui/modal/chkgd/chkgdModal/chkgdModal.tsx | 18 +- .../guideDetailTable/guideDetailTable.tsx | 5 +- .../chkgdTable/rainChkgd/rainChkgdTable.tsx | 25 ++- .../chkgdTable/rswtChkgd/rswtChkgdTable.tsx | 14 +- 25 files changed, 606 insertions(+), 225 deletions(-) diff --git a/apps/wr/app/matMng/coMat/coMatRvw/page.tsx b/apps/wr/app/matMng/coMat/coMatRvw/page.tsx index c1e601320..5d4592ad5 100644 --- a/apps/wr/app/matMng/coMat/coMatRvw/page.tsx +++ b/apps/wr/app/matMng/coMat/coMatRvw/page.tsx @@ -1,5 +1,4 @@ "use client"; - import { useWrStore } from "@/app/store"; import { DmdCoReqSchema, @@ -9,6 +8,7 @@ import { } from "@/app/store/matDmd/coMatRvw/type"; import { formH1Class, formTableName } from "@/components/lib/class/comClass"; import { + formatPhoneNumber, getCity, getdmdMatFlag, tableData, @@ -112,18 +112,20 @@ export default function CoMatRvw() { }, [form.watch("useUsgCd")]); useEffect(() => { - if (coMatRvwRes.length > 0) { + if (coMatRvwRes&&coMatRvwRes.length > 0) { const data = coMatRvwRes?.map((item, index) => { let flag = ""; - flag = getdmdMatFlag(item?.demandMatching?.splyFcltFlg ??""); + + flag = getdmdMatFlag(item?.demandMatching?.splyFcltFlg||undefined); + return { ...item, ctpvCd: item.demandCo?.ctpvCd, sggCd: item.demandCo?.sggCd, dmdCoNm: item.demandCo?.dmdCoNm, daddr: item.demandCo?.daddr, - telno: item.demandCo?.telno, - regDt: moment(item?.demandCo?.regDt).format("YYYY-MM-DD"), + telno: formatPhoneNumber(item.demandCo?.telno||'') , + splyFcltFlg: flag, }; }); @@ -181,7 +183,7 @@ export default function CoMatRvw() { <div className="col-span-7 w-full"> <div className="flex flex-row items-center"> <Label className="text-xl text-gray-600 mx-6"> - 하수 재이용년도 + 수요신청년도 </Label> <SelectBox className="h-full w-32 text-2xl" @@ -269,7 +271,7 @@ export default function CoMatRvw() { ></UiTable> </ListDiv> <hr /> - <ScrollArea className="h-[49vh]"> + {/* <ScrollArea className="h-[49vh]"> */} <ListDiv> <h3 className="mt-4 ml-2 text-2xl font-medium text-label"> 수요신청 상세현황 @@ -662,7 +664,7 @@ export default function CoMatRvw() { </Form> <ConCostForm form={form} type={"mat"} /> </ListDiv> - </ScrollArea> + {/* </ScrollArea> */} </div> </div> </ContentsDiv> diff --git a/apps/wr/app/matMng/coMng/coSttn/page.tsx b/apps/wr/app/matMng/coMng/coSttn/page.tsx index 181a5d82f..9b682a2aa 100644 --- a/apps/wr/app/matMng/coMng/coSttn/page.tsx +++ b/apps/wr/app/matMng/coMng/coSttn/page.tsx @@ -26,6 +26,7 @@ import ListDiv from "@common/business_components/ui/list_div"; import SdmInsertForm from "@common/business_components/ui/sdmInsertForm"; import type { DemandCompType } from "@common/business_components/ui/sdmInsertForm/form"; import { Button } from "@common/components/ui"; +import moment from "moment"; import { useCallback, useEffect, useState } from "react"; import toast, { Toaster } from "react-hot-toast"; @@ -83,6 +84,10 @@ export default function CoSttn() { const data = tableData(coSttnRes, { code }); const chData = data.map((item: any) => ({ ...item, + regDt: moment(item.regDt).format("YYYY-MM-DD") + + + })); // .filter((item: any) => item !== undefined); setBdstTable(chData); @@ -182,7 +187,7 @@ export default function CoSttn() { onClick={() => { /* 저장 로직 */ if (!upadateData) { - <ToastFormUi body={"수요처 변경을 원하는 행을 선택해주세요"} title={"수요처관리"}></ToastFormUi>; + toast.error("수요처 변경을 원하는 행을 선택해주세요") }else{ setIsDmdModal({ ...isDmdModal, @@ -190,7 +195,7 @@ export default function CoSttn() { } }} > - 저장 + 수정 </Button> <Button size={"sm"} diff --git a/apps/wr/app/ruseFcltMng/prfmncReg/chkgdnReg/page.tsx b/apps/wr/app/ruseFcltMng/prfmncReg/chkgdnReg/page.tsx index 88a0de300..033d3a23d 100644 --- a/apps/wr/app/ruseFcltMng/prfmncReg/chkgdnReg/page.tsx +++ b/apps/wr/app/ruseFcltMng/prfmncReg/chkgdnReg/page.tsx @@ -1,10 +1,14 @@ "use client"; import { useWrStore } from "@/app/store"; -import { type rainChkZodType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn"; -import type { - rswtChkZodType, - rswtrChkDetailsZodType, +import { + commonCd, + rainPageSize, + type rainChkZodType, +} from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn"; +import { + rswtPageSize, + type rswtrChkDetailsZodType, } from "@/app/store/prfmncReg/chkgdnReg/rswrtChkgdn"; import { excelArrayDownload, @@ -15,6 +19,7 @@ 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"; import RswtChkgdTable from "@/components/ui/table/chkgdTable/rswtChkgd/rswtChkgdTable"; +import { SearchInput } from "@common/business_components"; import { ContentsDiv, SearchDiv, @@ -25,9 +30,16 @@ import { UiModal, } from "@common/business_components/ui"; import ListDiv from "@common/business_components/ui/list_div"; +import LoadingOverlay from "@common/business_components/ui/loadingBar"; import { Button, Label } from "@common/components/ui"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@common/components/ui/tooltip"; import { useCallback, useEffect, useState } from "react"; -import { Toaster } from "react-hot-toast"; +import toast, { Toaster } from "react-hot-toast"; export default function Page() { const { @@ -39,8 +51,10 @@ export default function Page() { rainChkDelete, rainChkExcelDownload, rainChkHeadChange, + rainChkNmSearch, isRainSttn, rswtChkgdnInit, + rswtChkNmSearch, rswtChkPolInit, rswtChkDetilsUpdate, rswtChkDelete, @@ -57,39 +71,84 @@ export default function Page() { rswtrChkDetailsZodType[] >([]); const [deleteIdx, setDeleteIdx] = useState<number[]>([]); - const [pageNation, setPageNation] = useState(1); + const [rainPageNation, setRainPageNation] = useState(1); + const [rswtPageNation, setRswtPageNation] = useState(1); + const [fcltNm, setFcltNm] = useState<string>(""); const [selFcltDiv, setSelFcltDiv] = useState("RNB"); const [isModal, setIsModal] = useState({ open: false, data: {}, }); - const cellClick = () => { - if (searchYear) - if (selFcltDiv === "RNB") { - rainChkInit("1", "20", searchYear, "0"); - } else { - rswtChkgdnInit("1", "20", searchYear, "0"); - } - }; - const [searchYear, setSearchYear] = useState<string>( String(new Date().getFullYear()) ); + const cellClick = () => { + if (searchYear) + if (selFcltDiv === "RNB") { + if (fcltNm) { + rainChkNmSearch( + rainPageNation, + rainPageSize, + searchYear, + commonCd, + fcltNm + ); + } else { + rainChkInit(rainPageNation, rainPageSize, searchYear, commonCd); + } + } else { + if (fcltNm) { + rswtChkNmSearch( + rainPageNation, + rainPageSize, + searchYear, + commonCd, + fcltNm + ); + } else { + rswtChkgdnInit(rswtPageNation, rswtPageSize, searchYear, commonCd); + } + } + }; + const rainPageChange = (pageNo: number) => { + if (pageNo > 0) { + setRainPageNation(pageNo); + rainChkInit(pageNo, rainPageSize, searchYear, commonCd); + } + }; + const rswtPageChange = (pageNo: number) => { + if (pageNo > 0) { + setRswtPageNation(pageNo); + rswtChkgdnInit(pageNo, rswtPageSize, searchYear, commonCd); + } + }; useEffect(() => { if (!rainChkgdn.isInit) { - rainChkInit("1", "20", new Date().getFullYear().toString(), "0"); + rainChkInit( + 1, + rainPageSize, + new Date().getFullYear().toString(), + commonCd + ); isRainSttn(false); setIsModal({ data: {}, open: false }); setIsRegExcel(false); } + }, [rainChkgdn.isInit, rainChkInit, rainPageSize]); + useEffect(() => { if (!rswtChkgdn.isInit) { - rswtChkgdnInit("1", "20", new Date().getFullYear().toString(), "0"); + rswtChkgdnInit( + 1, + rswtPageSize, + new Date().getFullYear().toString(), + commonCd + ); setIsModal({ data: {}, open: false }); setIsRegExcel(false); } - }, [rainChkgdn.isInit, rainChkInit]); + }, [rswtChkgdn.isInit, rswtChkgdnInit, rswtPageSize]); useEffect(() => { if (rswtChkgdn.excelList[0] && rswtChkgdn.excelList[0]?.data.length > 1) { @@ -133,8 +192,8 @@ export default function Page() { ); useEffect(() => { - //rainChkgdn.isSttn = false; - //rswtChkgdn.isSttn = false; + rainChkgdn.isSttn = false; + rswtChkgdn.isSttn = false; if (!rainChkgdn.isSttn && !rswtChkgdn.isSttn) { rainChkHeadChange({ rainChkgdn }, true); rswtChkHeadChange({ rswtChkgdn }, true); @@ -152,9 +211,17 @@ export default function Page() { }; const chkUpdateTable = (selFcltDiv: string) => { if (selFcltDiv === "RNB") { - rainChkDetilUpdate(updateRainDetail); + if (updateRainDetail && updateRainDetail.length > 0) { + rainChkDetilUpdate(updateRainDetail); + } else { + toast.error("수정을 원하는 행을 선택하고 저장버튼을 눌러주세요 "); + } } else { - rswtChkDetilsUpdate(updateRswtDetail); + if (updateRswtDetail && updateRswtDetail.length > 0) { + rswtChkDetilsUpdate(updateRswtDetail); + } else { + toast.error("수정을 원하는 행을 선택하고 저장버튼을 눌러주세요 "); + } } }; @@ -176,10 +243,8 @@ export default function Page() { selectArray={yearOptions} value={searchYear} onSelectValue={(val: string | number) => { - setSearchYear(String(val)) - } - - } + setSearchYear(String(val)); + }} /> </div> <div className="col-span-2 flex flex-row space-x-2"> @@ -193,12 +258,22 @@ export default function Page() { selectArray={rainChkgdn.fcltDiv} onSelectValue={(val: string | number) => { if (val === "RST") rswtChkPolInit(); - setSelFcltDiv(String(val)) - } - } + setSelFcltDiv(String(val)); + }} value={selFcltDiv ?? rainChkgdn.fcltDiv[0]?.val} /> </div> + <div className="col-span-2 flex flex-row space-x-2"> + <Label className="w-28 mt-2 text-xl text-gray-600"> + {"시설명"} + </Label> + <SearchInput + className={"w-full h-full text-2xl"} + label={""} + textValue={fcltNm || ""} + setText={setFcltNm} + /> + </div> </SearchFormLeft> <SearchFormRight className="w-2/12 mx-5 flex flex-col items-center justify-center space-x-2 "> <div className="space-y"> @@ -219,14 +294,28 @@ export default function Page() { > 삭제 </Button> - <Button - size={"sm"} - onClick={() => { - chkUpdateTable(selFcltDiv); - }} - > - 저장 - </Button> + + <TooltipProvider> + <Tooltip> + <TooltipTrigger asChild> + <Button + size={"sm"} + onClick={() => { + chkUpdateTable(selFcltDiv); + }} + > + 저장 + </Button> + </TooltipTrigger> + <TooltipContent side="bottom" sideOffset={5}> + <h2 className="text-xl"> + 아래 테이블에 선택을 체크하면 입력셀이 활성화 됩니다. + </h2> + </TooltipContent> + </Tooltip> + </TooltipProvider> + + <Button size={"sm"} onClick={() => { @@ -253,8 +342,8 @@ export default function Page() { detailUp={setUpdateRainDetail} handleClick={handleClick} rainChkData={rainChkgdn.rainChk} - pageNation={pageNation} - onPageChange={setPageNation} + pageNation={rainPageNation} + onPageChange={rainPageChange} /> ) : ( <RswtChkgdTable @@ -262,8 +351,8 @@ export default function Page() { deleteRswtData={setDeleteIdx} handleRswtClick={handleClick} rswtChkData={rswtChkgdn.rswtChkRes} - pageNation={pageNation} - onPageChange={setPageNation} + pageNation={rswtPageNation} + onPageChange={rswtPageChange} /> )} </ListDiv> @@ -271,17 +360,28 @@ export default function Page() { </div> <UiModal open={isModal.open} - onOpenChange={() => { - setIsModal({ open: !open, data: {} }); + onOpenChange={(open: boolean) => { + setIsModal((prev) => ({ ...prev, open })); }} bodyClass={"w-[80vw] max-w-none"} - body={<ChkgdModal type={selFcltDiv} obj={isModal.data} />} + body={ + <ChkgdModal + type={selFcltDiv} + obj={isModal.data} + setIsModal={(open) => setIsModal((prev) => ({ ...prev, open }))} + /> + } ></UiModal> <UiModal open={isRegExcel} onOpenChange={setIsRegExcel} bodyClass={"w-[30vw] max-w-none"} - body={<ChkgdExcel type={selFcltDiv}></ChkgdExcel>} + body={ + <ChkgdExcel + type={selFcltDiv} + setIsRegExcel={setIsRegExcel} + ></ChkgdExcel> + } /> <Toaster position="top-center" /> </ContentsDiv> diff --git a/apps/wr/app/store/matDmd/coMatRvw/headList.tsx b/apps/wr/app/store/matDmd/coMatRvw/headList.tsx index 7b43e4b00..6b783ea68 100644 --- a/apps/wr/app/store/matDmd/coMatRvw/headList.tsx +++ b/apps/wr/app/store/matDmd/coMatRvw/headList.tsx @@ -29,12 +29,12 @@ export const coRvwHeadListExport: CommonTableProps[]= [ visible: true, }, { - id: "regDt", //바꾸기 + id: "dmndTmng", //바꾸기 title: "신청일자", visible: true, }, { - id: "state", //바꾸기 + id: "splyFcltFlg", //바꾸기 title: "진행상태", visible: true, }, diff --git a/apps/wr/app/store/matDmd/coMatRvw/index.tsx b/apps/wr/app/store/matDmd/coMatRvw/index.tsx index 89836957b..21d4e0252 100644 --- a/apps/wr/app/store/matDmd/coMatRvw/index.tsx +++ b/apps/wr/app/store/matDmd/coMatRvw/index.tsx @@ -103,12 +103,18 @@ export const coRvwActionsExport: (set: any) => coRvwActions = (set: any) => { data ); + if(isUpdate){ + toast.error("수요 매칭 검토 검토로 저장되었습니다."); + } + const coMatRvwRes = coMatInit( 1, 10, new Date().getFullYear().toString(), new Date().getFullYear().toString(), ); + + return set((state: coRvwState) => ({ ...state, coRvw: { diff --git a/apps/wr/app/store/matDmd/coMatRvw/type.tsx b/apps/wr/app/store/matDmd/coMatRvw/type.tsx index 9f146c0fb..fb80ec415 100644 --- a/apps/wr/app/store/matDmd/coMatRvw/type.tsx +++ b/apps/wr/app/store/matDmd/coMatRvw/type.tsx @@ -53,7 +53,7 @@ import { z } from "zod"; rgtr: z.string().nullish(), mdfcnDt: z.coerce.date().nullish(), mdfr: z.string().nullish(), - dmdAplyPswd: z.string(), + dmdAplyPswd: z.string().nullish(), demandInfo: z.array(DemandInfoSchema.nullish()).nullish(), // 안쓰는 거임 demandMatchings: z.array(DemandMatchingSchema.nullish()).nullish()// 안쓰는 거임 }); diff --git a/apps/wr/app/store/matDmd/coSttn/headList.tsx b/apps/wr/app/store/matDmd/coSttn/headList.tsx index 80fed0d76..62c3002ac 100644 --- a/apps/wr/app/store/matDmd/coSttn/headList.tsx +++ b/apps/wr/app/store/matDmd/coSttn/headList.tsx @@ -44,6 +44,11 @@ export const coSttHeadListExport: z.infer<typeof sttnHeadListType> = [ title: "전화번호", visible: true, }, + { + id: "regDt", + title: "등록일자", + visible: true, + }, ]; export const coSttNodeListExport: CommonTableProps[] = [ diff --git a/apps/wr/app/store/matDmd/coSttn/index.tsx b/apps/wr/app/store/matDmd/coSttn/index.tsx index 0de37bbfc..5e8baf859 100644 --- a/apps/wr/app/store/matDmd/coSttn/index.tsx +++ b/apps/wr/app/store/matDmd/coSttn/index.tsx @@ -2,7 +2,7 @@ import type { CommonTableProps } from "@/app/store/common/type/header"; import { fetchType, getFetch, getFetchWithCount } from "@/components/lib/api"; import { coSttHeadListExport, coSttNodeListExport } from "./headList"; import { excelCoSttnParsing } from "./excel"; -import type { DemandCompType } from "@common/business_components/ui"; +import type { DemandCompType } from "@common/business_components/ui/sdmInsertForm/form"; // 테이블 헤드 리스트 const coSttNodeList = coSttNodeListExport; diff --git a/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/excel.tsx b/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/excel.tsx index 58e8d6bf0..77045f165 100644 --- a/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/excel.tsx +++ b/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/excel.tsx @@ -124,9 +124,9 @@ export const excelToRainChkData = (excelData: any, year: string) => { trbt: Number(item["탁도"] || 0), grssClfg: Number(item["총대장균군수"] || 0), etc: item["기타"] || "", // 필드 누락 방지 - vltnDt: vltnDt || "-", - vltnMttr: item["위반사항"] || "-", - levyAmt: Number(item["과태료부과금액(천원)"] || 0), + vltnDt: vltnDt || null, + vltnMttr: item["위반사항"] || "", + levyAmt: Number(item["과태료부과금액(천원)"] || null), 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 index d7335e129..3cf1b5b11 100644 --- a/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList.tsx +++ b/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList.tsx @@ -114,10 +114,22 @@ export type splRuseWaterType = { ruse12: number; }; -export const rainHeadNmExport: chHeadType[] = [ +export const rainMainHeadNmExport: chHeadType[] = [ + { headNm: "useQuilty", text: "이용수질(필요시)", length: 3 }, + { headNm: "feeHist", text: "과태료 부과내역", length: 3 }, +]; + +export const rainRuseHeadNmExport: chHeadType[] = [ + { headNm: "useQuilty", text: "이용수질(필요시)", length: 3 }, + { headNm: "feeHist", text: "과태료 부과내역", length: 3 }, + { headNm: "ruseWater", text: "연간대체수 이용량(m³)", length: 13 }, + { headNm: "detailQulity", text: "분기", length: 11 }, +]; + + +export const rainSplHeadNmExport: chHeadType[] = [ { headNm: "useQuilty", text: "이용수질(필요시)", length: 3 }, { headNm: "feeHist", text: "과태료 부과내역", length: 3 }, - { headNm: "ruseWater", text: "연간보급수 이용량(m³)", length: 13 }, { headNm: "detailQulity", text: "분기", length: 11 }, { headNm: "splWater", text: "연간빗물 이용량(m³)", length: 13 }, ]; diff --git a/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/index.tsx b/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/index.tsx index 4c7f2d514..63d2d1d03 100644 --- a/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/index.tsx +++ b/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/index.tsx @@ -3,18 +3,20 @@ import { TEST_UUID } from "@/app/store/ruseFcltMng/fcltReg/rainReg"; import { deleteFectch, fetchType, + getCountFetch, getFetch, postFectch, putFectch, } from "@/components/lib/api"; -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, + rainMainHeadNmExport, + rainRuseHeadNmExport, + rainSplHeadNmExport, ruseHeadListExport, splHeadListExport, type chHeadType, @@ -22,38 +24,47 @@ import { import type { TableUpperProps } from "@/app/store/common/type/header"; import toast from "react-hot-toast"; +export const rainPageSize = 10; +export const commonCd = "41"; + export type rainChkZodType = z.infer<typeof rainChkZod>; const fcltDiv = [ { val: "RST", text: "중수도시설" }, { val: "RNB", text: "빗물이용시설" }, ]; -const rainHeadNm = rainHeadNmExport; +const rainRuseHeadNm = rainRuseHeadNmExport; +const rainSplHeadNm = rainSplHeadNmExport; const rainChkgdnHeadList = rainChkgdnHeadListExport; const ruseHeadList = ruseHeadListExport; const splHeadList = splHeadListExport; +const rainMainHeadNm = rainMainHeadNmExport const rainChk: rainChkZodType[] = []; export type rainChkgdnState = { rainChkgdn: { rainChkgdnHeadList: TableUpperProps[]; + rainSplHeadNm:chHeadType[] + rainRuseHeadNm:chHeadType[] rainChk: rainChkZodType[]; - rainHeadNm: chHeadType[]; ruseHeadList: TableUpperProps[]; splHeadList: TableUpperProps[]; isInit: boolean; isModalClose: boolean; fcltDiv: SelectDataType[]; + rainMainHeadNm:chHeadType[] excelList: []; isSttn: boolean; + rainChkCount: number; + isLoading: boolean; }; }; type rainChkgdnActions = { rainChkInit: ( - pageNo: string, - cnt: string, + pageNo: number, + cnt: number, sggCd: string, searchTxt: string ) => void; @@ -66,6 +77,13 @@ type rainChkgdnActions = { rainChkExcelDataUPload(excelData: rainChkZodType[]): Promise<void>; rainChkDelete(chkgdnPks: number[]): Promise<void>; rainChkHeadChange: (rainChkgdn: rainChkgdnState, isEdit: boolean) => void; + rainChkNmSearch: ( + pageNo: number, + cnt: number, + year: string, + sggCd: string, + txt: string + ) => void; }; export type rainChkgdnType = rainChkgdnState & rainChkgdnActions; @@ -73,7 +91,9 @@ export type rainChkgdnType = rainChkgdnState & rainChkgdnActions; export const rainChkgdnStateExport: rainChkgdnState = { rainChkgdn: { rainChkgdnHeadList: rainChkgdnHeadList, - rainHeadNm: rainHeadNm, + rainRuseHeadNm: rainRuseHeadNm, + rainSplHeadNm:rainSplHeadNm, + rainMainHeadNm:rainMainHeadNm, rainChk: rainChk, splHeadList: splHeadList, ruseHeadList: ruseHeadList, @@ -82,6 +102,8 @@ export const rainChkgdnStateExport: rainChkgdnState = { excelList: [], fcltDiv: fcltDiv, isSttn: false, + rainChkCount: 1, + isLoading: false, }, }; @@ -103,20 +125,24 @@ const makeMdfrChk = (data: rainChkZodType[], type: string, toDo: string) => { }; const initFetch = async ( - pageNo: string, - cnt: string, - txt: string | undefined, + pageNo: number, + cnt: number, + year: string | undefined, sgg: string | undefined ) => { const currentYear = new Date().getFullYear().toString(); - const rainRes = await getFetch( + + console.log("cnt ", cnt); + const rainRes = await getCountFetch( "wr", `/reuseChk/rainy` + (pageNo ? `/${pageNo}` : "/1") + - (cnt ? `/${cnt}` : "/20") + - (txt ? `/${txt}` : `/${currentYear}`) + + (cnt ? `/${cnt}` : "/10") + + (year ? `/${year}` : `/${currentYear}`) + (sgg ? `/${sgg}` : "/0") ); + + console.log(" rainRes ", rainRes); return rainRes; }; @@ -142,16 +168,24 @@ export const rainChkgdnActionsExport: (set: any) => rainChkgdnActions = ( })); }, rainChkExcelDataUPload: async (excelData: rainChkZodType[]) => { + set((state: rainChkgdnType) => ( + { rainChkgdn: { ...state.rainChkgdn, isLoading: true } })); try { const res = await postFectch("/reuseChk/rainy", excelData); if (res.status === 200) { const currentYear = new Date().getFullYear().toString(); - const rainChkRes = await initFetch("1", "20", currentYear, "0"); - return set((state: rainChkgdnState) => ({ + const rainChkRes = await initFetch( + 1, + rainPageSize, + currentYear, + commonCd + ); + set((state: rainChkgdnState) => ({ ...state, rainChkgdn: { ...state.rainChkgdn, - rainChk: rainChkRes, + rainChk: rainChkRes.data, + rainChkCount: rainChkRes.count, }, })); toast.success("지도점검 등록이 완료되었습니다."); @@ -161,22 +195,62 @@ export const rainChkgdnActionsExport: (set: any) => rainChkgdnActions = ( } catch (error) { console.log(error); toast.error("지도점검 등록이 실패되었습니다."); + } finally { + set((state: rainChkgdnType) => ({ rainChkgdn: { ...state.rainChkgdn, isLoading: false } })); } }, rainChkInit: async ( - pageNo: string, - cnt: string, - searchTxt: string, + pageNo: number, + cnt: number, + year: string, sggCd: string ) => { + set((state: rainChkgdnType) => ({ rainChkgdn: { ...state.rainChkgdn, isLoading: true } })); try { - const rainChkRes = await initFetch(pageNo, cnt, searchTxt, sggCd); - + const rainChkRes = await initFetch(pageNo, cnt, year, sggCd); + set((state: rainChkgdnState) => ({ + ...state, + rainChkgdn: { + ...state.rainChkgdn, + rainChk: rainChkRes.data, + rainChkCount: rainChkRes.count, + isInit: true, // 무한 루프 방지를 위해 isInit을 true로 설정 + }, + })); + } catch (error) { + console.log(error); + // 에러가 발생하더라도 무한 루프에 빠지지 않도록 isInit을 true로 설정합니다. + set((state: rainChkgdnType) => ({ + rainChkgdn: { ...state.rainChkgdn, isInit: true }, + })); + } finally { + set((state: rainChkgdnType) => ({ rainChkgdn: { ...state.rainChkgdn, isLoading: false } })); + } + }, + rainChkNmSearch: async ( + pageNo: number, + cnt: number, + year: string, + sggCd: string, + txt: string + ) => { + try { + const currentYear = new Date().getFullYear(); + const rainChkRes = await getCountFetch( + "wr", + `/reuseChk/rainy` + + (pageNo ? `/${pageNo}` : "/1") + + (cnt ? `/${cnt}` : "/10") + + (year ? `/${year}` : `/${currentYear}`) + + (sggCd ? `/${sggCd}` : "/0") + + (txt ? `/${txt}` : "") + ); return set((state: rainChkgdnState) => ({ ...state, rainChkgdn: { ...state.rainChkgdn, - rainChk: rainChkRes, + rainChk: rainChkRes.data, + rainChkCount: rainChkRes.count, }, })); } catch (error) { @@ -187,26 +261,32 @@ export const rainChkgdnActionsExport: (set: any) => rainChkgdnActions = ( try { const chData = await makeMdfrChk(rainChk, "rain", "rainDetil"); - const res = await postFectch("/reuseChk/rainy", chData); - if (res.staus === 200) { + const res = await putFectch("/reuseChk/rainy/list", chData); + if (res) { toast.success( - "지도점검 보급수 및 밋물이용량 수정이 완료되었습니다. " + "지도점검 대체수 및 밋물이용량 수정이 완료되었습니다. " ); const currentYear = new Date().getFullYear().toString(); - const rainChkRes = await initFetch("1", "20", currentYear, "11"); + const rainChkRes = await initFetch( + 1, + rainPageSize, + currentYear, + "41" + ); return set((state: rainChkgdnState) => ({ ...state, rainChkgdn: { ...state.rainChkgdn, - rainChk: rainChkRes, + rainChk: rainChkRes.data, + rainChkCount: rainChkRes.count, }, })); } else { - toast.error("지도점검 보급수 및 밋물이용량 수정 오류입니다."); + toast.error("지도점검 대체수 및 밋물이용량 수정 오류입니다."); } } catch (error) { console.log(error); - toast.error("지도점검 보급수 및 밋물이용량 수정 오류입니다."); + toast.error("지도점검 대체수 및 밋물이용량 수정 오류입니다."); } }, rainChkGuidUpdate: async (rainChk: rainChkZodType) => { @@ -217,17 +297,22 @@ export const rainChkgdnActionsExport: (set: any) => rainChkgdnActions = ( toast.success("지도점검 수질 및 과태료 수정이 완료되었습니다. "); const currentYear = new Date().getFullYear().toString(); - const rainChkRes = await initFetch("1", "20", currentYear, "11"); + const rainChkRes = await initFetch( + 1, + rainPageSize, + currentYear, + "41" + ); return set((state: rainChkgdnState) => ({ ...state, rainChkgdn: { ...state.rainChkgdn, - rainChk: rainChkRes, + rainChk: rainChkRes.data, + rainChkCount: rainChkRes.count, }, })); - }else{ + } else { toast.error("지도점검 수질 및 과태료 수정 오류입니다."); - } } catch (error) { toast.error("지도점검 수질 및 과태료 수정 오류입니다."); @@ -241,17 +326,22 @@ export const rainChkgdnActionsExport: (set: any) => rainChkgdnActions = ( toast.success("삭제가 완료되었습니다. "); const currentYear = new Date().getFullYear().toString(); - const rainChkRes = await initFetch("1", "20", currentYear, "11"); + const rainChkRes = await initFetch( + 1, + rainPageSize, + currentYear, + "41" + ); return set((state: rainChkgdnState) => ({ ...state, rainChkgdn: { ...state.rainChkgdn, - rainChk: rainChkRes, + rainChk: rainChkRes.data, + rainChkCount: rainChkRes.count, }, })); - }else{ + } else { toast.error("삭제시 오류가 발생하였습니다.. "); - } } catch (error) { toast.error("삭제시 오류가 발생하였습니다.. "); diff --git a/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/headList.tsx b/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/headList.tsx index 0e690dbb9..a7fea419d 100644 --- a/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/headList.tsx +++ b/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/headList.tsx @@ -109,6 +109,24 @@ export const rswtChkgdZod = z.object({ rswtrPollutions: z.array(rswtrPolutionsZode), }); + + +export const rswtRuseHeadNmExport: chHeadType[] = [ + { headNm: "useQuilty", text: "이용수질(필요시)", length: 3 }, + { headNm: "feeHist", text: "과태료 부과내역", length: 3 }, + { headNm: "ruseWater", text: "연간중수 이용량(m³)", length: 13 }, + { headNm: "detailQulity", text: "분기", length: 11 }, +]; + +export const rswtSplHeadNmExport: chHeadType[] = [ + { headNm: "useQuilty", text: "이용수질(필요시)", length: 3 }, + { headNm: "feeHist", text: "과태료 부과내역", length: 3 }, + { headNm: "detailQulity", text: "분기", length: 11 }, + { headNm: "splWater", text: "연간대체수 이용량(m³)", length: 13 }, +]; + + + export const rswtHeadNmExport: chHeadType[] = [ { headNm: "useQuilty", text: "연간 평균 이용수질", length: 10 }, { headNm: "feeHist", text: "과태료 부과내역", length: 3 }, @@ -126,7 +144,7 @@ export const rswtChkHeadListExport: TableUpperProps[] = [ { id: "spwtPositn", title: "채수지점" ,mergeCell: true}, { id: "chckYmd", title: "점검일" ,mergeCell: true}, { id: "utztnUsgCd", title: "이용용도" }, - { id: "splyUswtrUtztnQnt", title: "연간보급수이용량(m³)" ,type: "number"}, + { id: "splyUswtrUtztnQnt", title: "연간대체수이용량(m³)" ,type: "number"}, { id: "ruseFcltUtztnQnt", title: "연간중수도이용량(m³)",type: "number"}, { diff --git a/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/index.tsx b/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/index.tsx index acc6ccaba..d40c1190c 100644 --- a/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/index.tsx +++ b/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/index.tsx @@ -1,6 +1,4 @@ -import { - TEST_UUID, -} from "@/app/store/ruseFcltMng/fcltReg/rainReg"; +import { TEST_UUID } from "@/app/store/ruseFcltMng/fcltReg/rainReg"; import type { SelectDataType, SheetParameter, @@ -8,6 +6,7 @@ import type { import { deleteFectch, fetchType, + getCountFetch, getFetch, postFectch, putFectch, @@ -20,15 +19,24 @@ import { rswtDetailHeadListExport, rswtHeadNmExport, rswtrDetailsZod, + rswtRuseHeadNmExport, + rswtSplHeadNmExport, } from "./headList"; import { z } from "zod"; import { ToastFormUi } from "@/components/ui/toast/toastFromUi"; import { type chHeadType } from "../rainChkgdn/headList"; import type { TableUpperProps } from "@/app/store/common/type/header"; +import toast from "react-hot-toast"; + +export const rswtPageSize = 10; const rswtChkHeadList = rswtChkHeadListExport; const rswtDetailHeadList = rswtDetailHeadListExport; const rswtHeadNm = rswtHeadNmExport; +const rswtRuseHeadNm =rswtRuseHeadNmExport +const rswtSplHeadNm =rswtSplHeadNmExport + + export type rswtChkZodType = z.infer<typeof rswtChkgdZod>; export type rswtrChkDetailsZodType = z.infer<typeof rswtrDetailsZod>; export type rswtChkgdnState = { @@ -40,13 +48,29 @@ export type rswtChkgdnState = { excelList: SheetParameter[]; rswtChkRes: rswtChkZodType[]; rswtChkPolGuide: rswtChkZodType[]; + rswtSplHeadNm:chHeadType[] + rswtRuseHeadNm:chHeadType[] isInit: boolean; - isSttn: boolean + isSttn: boolean; + isLoading:boolean; + rswtChkCount: number; }; }; type rswtChkgdnActions = { - rswtChkgdnInit: (pageNo: string, cnt: string, year: string, sggCd: string) => void; + rswtChkgdnInit: ( + pageNo: number, + cnt: number, + year: string, + sggCd: string + ) => void; + rswtChkNmSearch: ( + pageNo: number, + cnt: number, + year: string, + sggCd: string, + txt: string + ) => void; rswtrChkExcelDownload( sidoCode: SelectDataType[], sggCode: SelectDataType[] @@ -67,27 +91,41 @@ export const rswtChkgdnStateExport: rswtChkgdnState = { rswtChkHeadList: rswtChkHeadList, rswtDetailHeadList: rswtDetailHeadList, rswtHeadNm: rswtHeadNm, - excelList: [{ - data: [[]], headerNum: 0, headerRange: "A1:V3", - sheetName: "", - startCol: '1' - }], + rswtRuseHeadNm:rswtRuseHeadNm, + rswtSplHeadNm:rswtSplHeadNm, + + excelList: [ + { + data: [[]], + headerNum: 0, + headerRange: "A1:V3", + sheetName: "", + startCol: "1", + }, + ], rswtChkRes: [], rswtChkPolGuide: [], isInit: false, isSttn: false, + isLoading:false, + rswtChkCount: 1, }, }; -const initFetch = async (pageNo: string, cnt: string, year: string | undefined, sgg: string | undefined) => { +const initFetch = async ( + pageNo: number, + cnt: number, + year: string | undefined, + sgg: string | undefined +) => { const currentYear = new Date().getFullYear().toString(); - const rswtChkRes = await getFetch( + const rswtChkRes = await getCountFetch( "wr", `/reuseChk/reuseWater` + - (pageNo ? `/${pageNo}` : "/1") + - (cnt ? `/${cnt}` : "/20") + - (year ? `/${year}` : `/${currentYear}`) + - (sgg ? `/${sgg}` : "/0") + (pageNo ? `/${pageNo}` : "/1") + + (cnt ? `/${cnt}` : "/20") + + (year ? `/${year}` : `/${currentYear}`) + + (sgg ? `/${sgg}` : "/0") ); return rswtChkRes; }; @@ -139,15 +177,59 @@ export const rswtChkgdnActionsExport: (set: any) => rswtChkgdnActions = ( }, })); }, - rswtChkgdnInit: async (pageNo: string, cnt: string, year: string, sggCd: string) => { + rswtChkgdnInit: async ( + pageNo: number, + cnt: number, + year: string, + sggCd: string + ) => { + set((state: rswtChkgdnType) => ({ rswtChkgdn: { ...state.rswtChkgdn, isLoading: true } })); try { const rswtChkRes = await initFetch(pageNo, cnt, year, sggCd); + set((state: rswtChkgdnState) => ({ + ...state, + rswtChkgdn: { + ...state.rswtChkgdn, + rswtChkRes: rswtChkRes.data, + rswtChkCount: rswtChkRes.count, + isInit: true, // 무한 루프를 방지하기 위해 isInit을 true로 설정 + }, + })); + } catch (error) { + console.log(error); + set((state: rswtChkgdnType) => ({ + rswtChkgdn: { ...state.rswtChkgdn, isInit: true }, + })); + } finally { + set((state: rswtChkgdnType) => ({ rswtChkgdn: { ...state.rswtChkgdn, isLoading: false } })); + } + }, + rswtChkNmSearch: async ( + pageNo: number, + cnt: number, + year: string, + sggCd: string, + txt: string + ) => { + try { + const currentYear = new Date().getFullYear().toString(); + const rswtChkRes = await getCountFetch( + "wr", + `/reuseChk/reuseWater` + + (pageNo ? `/${pageNo}` : "/1") + + (cnt ? `/${cnt}` : "/20") + + (year ? `/${year}` : `/${currentYear}`) + + (sggCd ? `/${sggCd}` : "/0") + + (txt ? `/${txt}` : "") + ); + return set((state: rswtChkgdnState) => ({ ...state, rswtChkgdn: { ...state.rswtChkgdn, - rswtChkRes: rswtChkRes, + rswtChkRes: rswtChkRes.data, + rswtChkCount: rswtChkRes.count, isInit: false, }, })); @@ -157,6 +239,9 @@ export const rswtChkgdnActionsExport: (set: any) => rswtChkgdnActions = ( }, rswtChkExceDataUpload: async (excelData: rswtChkZodType[]) => { try { + set((state: rswtChkgdnType) => ( + { rswtChkgdn: { ...state.rswtChkgdn, isLoading: true } })); + console.log('excelData ', excelData) const res = await postFectch("/reuseChk/reuseWater", excelData); if (res.ok) { ToastFormUi({ @@ -165,65 +250,66 @@ export const rswtChkgdnActionsExport: (set: any) => rswtChkgdnActions = ( }); } const currentYear = new Date().getFullYear().toString(); - const rswtChkRes = await initFetch("1", "20", currentYear, "0"); + const rswtChkRes = await initFetch(1, rswtPageSize, currentYear, "0"); return set((state: rswtChkgdnState) => ({ ...state, rswtChkgdn: { ...state.rswtChkgdn, - rswtChkRes: rswtChkRes + rswtChkRes: rswtChkRes.data, + rswtChkCount: rswtChkRes.count, }, })); } catch (error) { console.log(error); + }finally{ + set((state: rswtChkgdnType) => ( + { rswtChkgdn: { ...state.rswtChkgdn, isLoading: false } })); } + }, rswtChkDetilsUpdate: async (rswtChk: rswtrChkDetailsZodType[]) => { try { const chData = await makeMdfrChk(rswtChk); - const res = await putFectch("/reuseChk/reuseWater", chData); if (res.ok) { - ToastFormUi({ - body: "지도점검 과태료 수정 수정이 완료되었습니다. ", - title: "지도점검 수정", - }); + toast.success("지도점검 과태료 수정이 완료되었습니다. ",) } const currentYear = new Date().getFullYear().toString(); - const rswtChkRes = await initFetch("1", "20", currentYear, "0"); + const rswtChkRes = await initFetch(1, rswtPageSize, currentYear, "0"); return set((state: rswtChkgdnState) => ({ ...state, rswtChkgdn: { ...state.rswtChkgdn, - rswtChkRes: rswtChkRes, + rswtChkRes: rswtChkRes.data, + rswtChkCount: rswtChkRes.count, }, })); } catch (error) { console.log(error); - alert("수정에 실패하였습니다."); + toast.error("지도점검 과태료 수정 오류입니다. ",) + } }, rswtChkDelete: async (chkgdnPks: number[]) => { try { const res = await deleteFectch(`/reuseChk/reuseWater`, chkgdnPks); - if (res.ok) { - ToastFormUi({ - body: "삭제가 완료되었습니다. ", - title: "지도점검 삭제", - }); + if (res) { + toast.success("삭제가 완료되었습니다.") } const currentYear = new Date().getFullYear().toString(); - const rswtChkRes = await initFetch("1", "20", currentYear, "0"); + const rswtChkRes = await initFetch(1, rswtPageSize, currentYear, "0"); return set((state: rswtChkgdnState) => ({ ...state, rswtChkgdn: { ...state.rswtChkgdn, - rswtChkRes: rswtChkRes, + rswtChkRes: rswtChkRes.data, + rswtChkCount: rswtChkRes.count, }, })); } catch (error) { - ToastFormUi({ body: "삭제를 실패했습니다.. ", title: "지도점검 삭제" }); + toast.error("삭제 오류입니다.") } }, rswtChkPolAndGuide: async (chkGdnPk: number) => { @@ -240,7 +326,11 @@ export const rswtChkgdnActionsExport: (set: any) => rswtChkgdnActions = ( rswtChkPolGuide: rswtChkPolGuide, }, })); - } catch (error) { } + } catch (error) { + + + + } }, rswtChkPolInit: () => { try { @@ -251,7 +341,7 @@ export const rswtChkgdnActionsExport: (set: any) => rswtChkgdnActions = ( rswtChkPolGuide: undefined, }, })); - } catch (error) { } + } catch (error) {} }, rwstPolAndGuideUpdate: async (rswtChk: rswtChkZodType[]) => { try { @@ -261,13 +351,10 @@ export const rswtChkgdnActionsExport: (set: any) => rswtChkgdnActions = ( chData[0] ); if (res.ok) { - ToastFormUi({ - body: "지도점검 연간 수질 및 보급수/중수도 수정이 완료되었습니다. ", - title: "지도점검 수정", - }); + toast.success("지도점검 연간 수질 및 대체수/중수도 수정이 완료되었습니다.") } const currentYear = new Date().getFullYear().toString(); - const rswtChkRes = await initFetch("1", "20", currentYear, "0"); + const rswtChkRes = await initFetch(1, rswtPageSize, currentYear, "0"); return set((state: rswtChkgdnState) => ({ ...state, rswtChkgdn: { @@ -277,10 +364,9 @@ export const rswtChkgdnActionsExport: (set: any) => rswtChkgdnActions = ( })); } catch (error) { console.log(error); - ToastFormUi({ - body: "지도점검 연간 수질 및 보급수/중수도 수정이 실패하였습니다.. ", - title: "지도점검 수정", - }); + + toast.error("지도점검 연간 수질 및 대체수/중수도 수정 오류입니다.") + } }, rswtChkHeadChange: async (rswtChkgdn: rswtChkgdnState, isEdit: boolean) => { @@ -301,7 +387,7 @@ export const rswtChkgdnActionsExport: (set: any) => rswtChkgdnActions = ( rswtDetailHeadList: rswtDetailHeadList, }, })); - } catch (e) { } + } catch (e) {} }, }; }; diff --git a/apps/wr/app/store/prfmncSttn/chkgdnSttn/rainChkgdnSttn/index.tsx b/apps/wr/app/store/prfmncSttn/chkgdnSttn/rainChkgdnSttn/index.tsx index 2136dfebe..bbbb3a61e 100644 --- a/apps/wr/app/store/prfmncSttn/chkgdnSttn/rainChkgdnSttn/index.tsx +++ b/apps/wr/app/store/prfmncSttn/chkgdnSttn/rainChkgdnSttn/index.tsx @@ -2,7 +2,7 @@ import type { rainChkZodType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn" import { getFetch } from "@/components/lib/api"; import type { SelectDataType } from "@common/business_components/ui"; import { rainChkgdnHeadSttnExport, ruseSttnHeadListExport, splSttnHeadListExport } from "./headList"; -import { rainHeadNmExport, type chHeadType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList"; +import { rainMainHeadNmExport, type chHeadType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList"; import { buildingExcelRainSttn } from "./excel"; import type { TableUpperProps } from "@/app/store/common/type/header"; @@ -15,7 +15,7 @@ const fcltDiv = [ const rainChkgdnHeadList = rainChkgdnHeadSttnExport; const ruseHeadList = ruseSttnHeadListExport; const splHeadList = splSttnHeadListExport; -const rainHeadNm = rainHeadNmExport +const rainHeadNm = rainMainHeadNmExport const rainChk: rainChkZodType[] = []; diff --git a/apps/wr/app/store/prfmncSttn/chkgdnSttn/rswtChkgdnSttn/headList.tsx b/apps/wr/app/store/prfmncSttn/chkgdnSttn/rswtChkgdnSttn/headList.tsx index 91f6bf718..a505f1b0c 100644 --- a/apps/wr/app/store/prfmncSttn/chkgdnSttn/rswtChkgdnSttn/headList.tsx +++ b/apps/wr/app/store/prfmncSttn/chkgdnSttn/rswtChkgdnSttn/headList.tsx @@ -11,7 +11,7 @@ export const rswtChkSttnHeadListExport: TableUpperProps[] = [ { id: "spwtPositn", title: "채수지점", mergeCell: true }, { id: "chckYmd", title: "점검일", mergeCell: true }, { id: "utztnUsgCd", title: "이용용도" }, - { id: "splyUswtrUtztnQnt", title: "연간보급수이용량(m³)", type: "number" }, + { id: "splyUswtrUtztnQnt", title: "연간대체수이용량(m³)", type: "number" }, { id: "ruseFcltUtztnQnt", title: "연간중수도이용량(m³)", type: "number" }, { diff --git a/apps/wr/app/store/ruseFcltMng/fcltSttn/rainSttn/headList.tsx b/apps/wr/app/store/ruseFcltMng/fcltSttn/rainSttn/headList.tsx index 8dd2a4d6f..ef662cc8c 100644 --- a/apps/wr/app/store/ruseFcltMng/fcltSttn/rainSttn/headList.tsx +++ b/apps/wr/app/store/ruseFcltMng/fcltSttn/rainSttn/headList.tsx @@ -426,7 +426,7 @@ export const rainSttNodeListExport: CommonTableProps[] = [ export const rainSttnDetailNodeListExport:CommonTableProps[] = [ { id: "totalSplyUswtrUtztnQnt", - title: "연간보급수이용량(m³)", + title: "연간대체수이용량(m³)", visible: true, row: 1, rowSpan: 2, @@ -511,7 +511,7 @@ export const rainSttnDetailNodeListExport:CommonTableProps[] = [ export const rainSttnDetailHeadListExport:CommonTableProps[] = [ { id: "totalSplyUswtrUtztnQnt", - title: "연간보급수이용량(m³)", + title: "연간대체수이용량(m³)", visible: true, row: 1, rowSpan: 1, diff --git a/apps/wr/components/lib/api/index.ts b/apps/wr/components/lib/api/index.ts index 452788eb0..69bc1bb68 100644 --- a/apps/wr/components/lib/api/index.ts +++ b/apps/wr/components/lib/api/index.ts @@ -46,6 +46,29 @@ export const getFetch = async (type: string, url: string) => { } }; + +export const getCountFetch = async (type: string, url: string) => { + const div = + type === fetchType.COMMON + ? DEV_URL_FO + : type === fetchType.LOCAL + ? LOCAL_URL + : type === fetchType.CS + ? '' + : DEV_URL_WR; + + const response = await fetch(div + url, { + method: "GET", + headers: { + accept: "application/json", + }, + }); + // JSON 응답 처리 + const res = await response.json(); + return res; + +}; + export const getFetchWithCount = async (type: string, url: string) => { const div = type === fetchType.COMMON diff --git a/apps/wr/components/lib/func/TableData.tsx b/apps/wr/components/lib/func/TableData.tsx index 7a35e02c4..5be5d9fb3 100644 --- a/apps/wr/components/lib/func/TableData.tsx +++ b/apps/wr/components/lib/func/TableData.tsx @@ -54,27 +54,30 @@ export const rainChkTableData = (chkData: rainChkZodType[]) => { export const rswtChkTableData = (chkData: rswtChkZodType[]) => { // 1. chkgdnFk + utztnUsgCd 기준 누계 정보 만들기 const makeRswtData: any[] = []; - chkData?.forEach((root) => { - root.rswtrDetails.forEach((detail) => { - let flatRswtChk = {}; - const findGuide = root.guideDetails.find( - (guide) => guide.utztnUsgCd === detail.utztnUsgCd - ); - const findPoluttion = root.rswtrPollutions.find( - (polution) => polution.utztnUsgCd === detail.utztnUsgCd - ); - const operMngCd = root?.reuseBuilding.operMngCd; - flatRswtChk = { - ...root, - ...detail, - ...findGuide, - ...findPoluttion, - ...flatRswtChk, - operMngCd: operMngCd, - }; - makeRswtData.push(flatRswtChk); + if(chkData&& chkData.length>0){ + + chkData?.forEach((root) => { + root.rswtrDetails.forEach((detail) => { + let flatRswtChk = {}; + const findGuide = root.guideDetails.find( + (guide) => guide.utztnUsgCd === detail.utztnUsgCd + ); + const findPoluttion = root.rswtrPollutions.find( + (polution) => polution.utztnUsgCd === detail.utztnUsgCd + ); + const operMngCd = root?.reuseBuilding.operMngCd; + flatRswtChk = { + ...root, + ...detail, + ...findGuide, + ...findPoluttion, + ...flatRswtChk, + operMngCd: operMngCd, + }; + makeRswtData.push(flatRswtChk); + }); }); - }); + } return makeRswtData; }; diff --git a/apps/wr/components/lib/func/commom.tsx b/apps/wr/components/lib/func/commom.tsx index 8569d4eb3..50e425722 100644 --- a/apps/wr/components/lib/func/commom.tsx +++ b/apps/wr/components/lib/func/commom.tsx @@ -165,18 +165,34 @@ export const yearOptions: { val: string; text: string }[] = Array.from( }) ); -export const getdmdMatFlag = (flag: string|undefined) => { - let flagMat = ""; - - if(flag === undefined || flag === null) { - flagMat = "신청"; - }else if(flag.trim() === "Sub") { - flagMat = "검토"; - - }else if(flag.trim() === "matched") { - flagMat = "매칭완료"; - }else { - flagMat = "신청"; +export const getdmdMatFlag = (flag: string | undefined | null): string => { + // 1. undefined나 null인 경우, '신청'을 반환합니다. + // (이 검사를 먼저 함으로써 아래에서 .trim()을 안전하게 사용할 수 있습니다.) + if (flag == null) { + return "신청"; } - return flagMat; + + // 2. 이제 flag가 문자열임이 보장되므로 .trim()을 호출해 앞뒤 공백을 제거합니다. + const trimmedFlag = flag.trim(); + + if (trimmedFlag === "Sub") { + return "검토"; + } else if (trimmedFlag === "matched") { + return "매칭완료"; + } + // 3. 그 외 모든 경우(빈 문자열 포함)는 '신청'으로 처리합니다. + return "신청"; }; + + + +export const formatPhoneNumber=(tel:string) => { + const onlyNums = tel.replace(/[^0-9]/g, ''); + if (onlyNums.length === 10) { + return onlyNums.replace(/(\d{2})(\d{4})(\d{4})/, '$1-$2-$3'); + } else if (onlyNums.length === 11) { + return onlyNums.replace(/(\d{3})(\d{4})(\d{4})/, '$1-$2-$3'); + } else { + return tel; // 유효하지 않으면 빈 문자열 + } +} \ No newline at end of file diff --git a/apps/wr/components/ui/form/coMat/conCostForm/conCostForm.tsx b/apps/wr/components/ui/form/coMat/conCostForm/conCostForm.tsx index b22f9474e..3d59d0a61 100644 --- a/apps/wr/components/ui/form/coMat/conCostForm/conCostForm.tsx +++ b/apps/wr/components/ui/form/coMat/conCostForm/conCostForm.tsx @@ -368,7 +368,7 @@ export default function ConCostForm({ childrenTriger={ <> <Label className="underline"> - 총사업비 (백만원) <i className="diveicon di-search" /> + 처리시설 총사업비(백만원) <i className="diveicon di-search" /> </Label> </> } @@ -405,7 +405,7 @@ export default function ConCostForm({ )} /> </div> - <div className="col-span-3 mt-4"> + {/* <div className="col-span-3 mt-4"> <div className="flex items-center justify-between"> <Label>공급처 선정</Label> <Button size={"sm"} onClick={(e) => {}} className="bg-none"> @@ -428,7 +428,7 @@ export default function ConCostForm({ </FormUi> )} /> - </div> + </div> */} <div className="col-span-4 mt-4"> <FormField control={form.control} diff --git a/apps/wr/components/ui/modal/chkgd/chkgdExcel/chkgdExcel.tsx b/apps/wr/components/ui/modal/chkgd/chkgdExcel/chkgdExcel.tsx index 8828420b8..5aa67fd3a 100644 --- a/apps/wr/components/ui/modal/chkgd/chkgdExcel/chkgdExcel.tsx +++ b/apps/wr/components/ui/modal/chkgd/chkgdExcel/chkgdExcel.tsx @@ -7,11 +7,9 @@ import { excelToRswtGuideData, excelToRswtPolution, } from "@/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/excel"; -import { ToastFormUi } from "@/components/ui/toast/toastFromUi"; import { filePicked, filePickedArray, - FileUpload, Form, FormField, FormUi, @@ -26,7 +24,7 @@ import { useForm } from "react-hook-form"; import toast from "react-hot-toast"; import type { z } from "zod"; -export default function ChkgdExcel({ type }: { type: string }) { +export default function ChkgdExcel({ type,setIsRegExcel }: { type: string, setIsRegExcel:(open:boolean) =>void }) { const { rswtChkgdn, rainChkExcelDataUPload, rswtChkExceDataUpload } = useWrStore((state) => { return state; @@ -146,29 +144,28 @@ export default function ChkgdExcel({ type }: { type: string }) { const onSubmit = (values: z.infer<typeof chkgdRegExcel>) => { if (values.chckYr === "") { - ToastFormUi({ body: "등록년도를 작성해주세요", title: "지도점검 등록" }); + toast.error("등록년도를 작성해주세요"); return; } if (type === "RNB") { if (excelData === undefined || excelData?.length === 0) { - ToastFormUi({ - body: "빈 파일 및 파일 오류 입니다.", - title: "지도점검 등록", - }); + toast.error("빈 파일 및 파일 오류 입니다."); + return; } else { rainChkExcelDataUPload(excelData as rainChkZodType[]); + setIsRegExcel(false) } } else if (type === "RST") { if (excelRswtData === undefined || excelRswtData?.length === 0) { - ToastFormUi({ - body: "빈 파일 및 파일 오류 입니다.", - title: "중수지도점검 등록", - }); + toast.error("빈 파일 및 파일 오류 입니다."); + return; } else { rswtChkExceDataUpload(excelRswtData as rswtChkZodType[]); + setIsRegExcel(false) + } } }; diff --git a/apps/wr/components/ui/modal/chkgd/chkgdModal/chkgdModal.tsx b/apps/wr/components/ui/modal/chkgd/chkgdModal/chkgdModal.tsx index 965bc7275..d62406e9f 100644 --- a/apps/wr/components/ui/modal/chkgd/chkgdModal/chkgdModal.tsx +++ b/apps/wr/components/ui/modal/chkgd/chkgdModal/chkgdModal.tsx @@ -10,7 +10,7 @@ import type { z } from "zod"; import React from "react"; import type { TableUpperProps } from "@/app/store/common/type/header"; -export default function ChkgdModal({ type, obj }: { type: string; obj: any }) { +export default function ChkgdModal({ type, obj ,setIsModal }: { type: string; obj: any , setIsModal:(open:boolean)=>void}) { const { rainChkgdn, rswtChkgdn, @@ -79,6 +79,8 @@ export default function ChkgdModal({ type, obj }: { type: string; obj: any }) { ]); useEffect(() => { + console.log('obj ', obj) + const result = obj.guideDetails?.reduce((acc: any, cur: any) => { const code = cur.utztnUsgCd; const month = cur.ym?.slice(4); // "MM" @@ -114,9 +116,10 @@ export default function ChkgdModal({ type, obj }: { type: string; obj: any }) { }); } - console.log("result ", result); setGuidData(result as splRuseWaterType[]); + + console.log('obj ', obj) if (obj.rswtrPollutions) { const resultPolution: Record<string, any[]> = { "1": [], @@ -217,12 +220,14 @@ export default function ChkgdModal({ type, obj }: { type: string; obj: any }) { obj.guideDetails = realGuideDetail; if (type === "RNB") { rainChkGuidUpdate(obj); + setIsModal(false) } else { const polution = Object.values(upPolutionData) .flat() .filter((item) => item.utztnUsgCd !== "평균"); obj.rswtrPollutions = polution; rwstPolAndGuideUpdate([obj]); + setIsModal(false) } }; @@ -262,12 +267,13 @@ export default function ChkgdModal({ type, obj }: { type: string; obj: any }) { <ScrollArea className="h-[60vh]"> <div className="card-header"> <h3 className="text-2xl font-medium text-label"> - {`연간보급수이용량`} + {`연간대체수이용량`} </h3> </div> <GuideDetailsTable chgRes={guidData || []} - headList={headList.ruseHeadList} + headList={headList.splHeadList} + upheadList={type === "RNB"? rainChkgdn.rainSplHeadNm :rswtChkgdn.rswtSplHeadNm} type={""} setChGuidData={(data) => { setChGuidData(data); @@ -280,7 +286,8 @@ export default function ChkgdModal({ type, obj }: { type: string; obj: any }) { </div> <GuideDetailsTable chgRes={guidData || []} - headList={headList.splHeadList} + headList={headList.ruseHeadList} + upheadList={type === "RNB"? rainChkgdn.rainRuseHeadNm :rswtChkgdn.rswtRuseHeadNm} type={""} setChGuidData={(data) => { setChGuidData(data); @@ -302,6 +309,7 @@ export default function ChkgdModal({ type, obj }: { type: string; obj: any }) { <GuideDetailsTable chgRes={value} headList={headList?.rswtDetailHeadList} + upheadList={rswtChkgdn.rswtSplHeadNm} type={""} setChGuidData={(data) => { getPolutionData(data); diff --git a/apps/wr/components/ui/table/chkgdTable/guideDetailTable/guideDetailTable.tsx b/apps/wr/components/ui/table/chkgdTable/guideDetailTable/guideDetailTable.tsx index 94be4ebb6..2bf33e7c2 100644 --- a/apps/wr/components/ui/table/chkgdTable/guideDetailTable/guideDetailTable.tsx +++ b/apps/wr/components/ui/table/chkgdTable/guideDetailTable/guideDetailTable.tsx @@ -4,6 +4,7 @@ import { useWrStore } from "@/app/store"; import type { TableUpperProps } from "@/app/store/common/type/header"; import type { } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn"; +import type { chHeadType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList"; import { InputTableBody } from "@/components/lib/func/TableData"; import { Input, @@ -18,11 +19,13 @@ import { useEffect, useState, type Dispatch, type SetStateAction } from "react" export function GuideDetailsTable({ chgRes, headList, + upheadList, type, setChGuidData, }: { chgRes: any[]; headList: TableUpperProps[]; + upheadList:chHeadType[] type: string; setChGuidData: (chgRes: any[]) => void; }) { @@ -44,7 +47,7 @@ export function GuideDetailsTable({ <> <div className={`card !m-[1.2rem] !p-0r `}> <Table variant="secondary"> - <TableHeader className="font-bold">{headMakeprfmnc(headList, rainChkgdn?.rainHeadNm)}</TableHeader> + <TableHeader className="font-bold">{headMakeprfmnc(headList, upheadList)}</TableHeader> <TableBody > {InputTableBody(inputData ?? [],headList, setInputData) } </TableBody> diff --git a/apps/wr/components/ui/table/chkgdTable/rainChkgd/rainChkgdTable.tsx b/apps/wr/components/ui/table/chkgdTable/rainChkgd/rainChkgdTable.tsx index 720a73ab4..c6daac3b6 100644 --- a/apps/wr/components/ui/table/chkgdTable/rainChkgd/rainChkgdTable.tsx +++ b/apps/wr/components/ui/table/chkgdTable/rainChkgd/rainChkgdTable.tsx @@ -1,7 +1,7 @@ "use client"; import { headMakeprfmnc } from "@/app/ruseFcltMng/prfmncReg/chkgdnReg/TableHead"; import { useWrStore } from "@/app/store"; -import type { rainChkZodType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn"; +import { rainPageSize, type rainChkZodType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn"; import type { chHeadType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList"; import type { TableUpperProps } from "@/app/store/common/type/header"; import { tableData } from "@/components/lib/func/commom"; @@ -17,6 +17,7 @@ import { } from "@common/components/ui"; import { useCallback, useEffect, useState } from "react"; import UiPageNations from "@common/business_components/ui/table/uiPageNations"; +import LoadingOverlay from "@common/business_components/ui/loadingBar"; export default function RainChkgdTable({ detailUp, handleClick = () => { }, @@ -70,16 +71,12 @@ export default function RainChkgdTable({ }, [makeTableData, handleClick]); - useEffect(() =>{ - - - },[rainChkgdn.rainChk]) useEffect(() => { setSelectedIds([])// 셀렉트 초기화를 위한 작업 - if (rainChkData) { + if (rainChkData&&rainChkData.length>0) { - const rainChg = rainChkData.map((item: any) => { + const rainChg = rainChkData?.map((item: any) => { const bud = tableData([item.building], { code }); return { ...item, @@ -89,6 +86,8 @@ export default function RainChkgdTable({ }); setMakeTableData(rainChkTableData(rainChg)); + }else{ + setMakeTableData([]); } }, [rainChkData, code]); @@ -167,7 +166,7 @@ export default function RainChkgdTable({ } else { setHeadList({ headList: rainChkgdn.rainChkgdnHeadList, - upHeadList: rainChkgdn.rainHeadNm + upHeadList: rainChkgdn.rainMainHeadNm }) } @@ -175,9 +174,15 @@ export default function RainChkgdTable({ }, [rainChkSttn.isSttn, rainChkgdn.rainChkgdnHeadList, rainChkSttn.rainChkgdnHeadList]); + useEffect(() =>{ + console.log('rainChkgdn.count ', rainChkgdn.rainChkCount) + },[rainChkgdn.rainChkCount]) + return ( <> + <LoadingOverlay isLoading={rainChkgdn.isLoading}></LoadingOverlay> <Table variant="secondary"> + <TableHeader className="font-bold"> {headMakeprfmnc( headList?.headList, @@ -197,9 +202,9 @@ export default function RainChkgdTable({ </TableBody> </Table> <UiPageNations - total={rainChkData?.length ?? 0} + total={rainChkgdn.rainChkCount||20} cur={pageNation} - pageSize={20} + pageSize={rainPageSize} onPageChange={onPageChange} /> </> diff --git a/apps/wr/components/ui/table/chkgdTable/rswtChkgd/rswtChkgdTable.tsx b/apps/wr/components/ui/table/chkgdTable/rswtChkgd/rswtChkgdTable.tsx index 31891a595..5264dcdee 100644 --- a/apps/wr/components/ui/table/chkgdTable/rswtChkgd/rswtChkgdTable.tsx +++ b/apps/wr/components/ui/table/chkgdTable/rswtChkgd/rswtChkgdTable.tsx @@ -2,9 +2,10 @@ import { headMakeprfmnc } from "@/app/ruseFcltMng/prfmncReg/chkgdnReg/TableHead"; import { useWrStore } from "@/app/store"; import type { chHeadType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList"; -import type { - rswtChkZodType, - rswtrChkDetailsZodType, +import { + rswtPageSize, + type rswtChkZodType, + type rswtrChkDetailsZodType, } from "@/app/store/prfmncReg/chkgdnReg/rswrtChkgdn"; import type { TableUpperProps } from "@/app/store/common/type/header"; import { tableData } from "@/components/lib/func/commom"; @@ -15,6 +16,7 @@ import { import { Table, TableBody, TableHeader } from "@common/components/ui"; import { useCallback, useEffect, useState } from "react"; import UiPageNations from "@common/business_components/ui/table/uiPageNations"; +import LoadingOverlay from "@common/business_components/ui/loadingBar"; export default function RswtChkgdTable({ detaiRswtlUpdate, @@ -72,7 +74,6 @@ export default function RswtChkgdTable({ }, [rswtChkgdn.rswtChkPolGuide]); useEffect(() => { - console.log("makeTableData ", makeTableData) getRswtSttnExcel(makeTableData) const selectedData = makeTableData .map((item) => { @@ -133,6 +134,7 @@ export default function RswtChkgdTable({ return ( <> + <LoadingOverlay isLoading={rswtChkgdn.isLoading}></LoadingOverlay> <Table variant="secondary"> <TableHeader className="font-bold"> {headMakeprfmnc(headList?.headList, headList?.upHeadList)} @@ -150,9 +152,9 @@ export default function RswtChkgdTable({ </TableBody> </Table> <UiPageNations - total={rswtChkData?.length ?? 0} + total={rswtChkgdn.rswtChkCount} cur={pageNation} - pageSize={20} + pageSize={rswtPageSize} onPageChange={onPageChange} /> </> From be4bbdb3f102ab86868d42830f15f7e7803cb4f9 Mon Sep 17 00:00:00 2001 From: JIeunhuh <entks301@naver.com> Date: Fri, 4 Jul 2025 11:04:56 +0900 Subject: [PATCH 063/108] =?UTF-8?q?gistree=20=EC=9D=B4=EB=AF=B8=EC=A7=80?= =?UTF-8?q?=20=ED=81=B4=EB=A6=AD=EC=9D=B4=EB=B2=A4=ED=8A=B8=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../business_components/src/ui/map/Gis.js | 207 ++++++++++++++++-- .../business_components/src/ui/map/GisTree.js | 43 +++- 2 files changed, 219 insertions(+), 31 deletions(-) diff --git a/packages/common/business_components/src/ui/map/Gis.js b/packages/common/business_components/src/ui/map/Gis.js index fa1cf7ab4..dd8135d1a 100644 --- a/packages/common/business_components/src/ui/map/Gis.js +++ b/packages/common/business_components/src/ui/map/Gis.js @@ -1,5 +1,6 @@ import { Search } from 'lucide-react'; import OlMap from 'ol/Map'; +import Overlay from 'ol/Overlay'; import View from 'ol/View'; import { defaults as defaultControls, OverviewMap } from 'ol/control'; import { defaults } from 'ol/interaction'; @@ -14,27 +15,53 @@ import GisToolBar from './GisToolBar'; import GisTree from './GisTree'; import './lib/css/gis.common.css'; import GisApp from './lib/gis.core'; +// import FacilityInfoPopup from './FacilityInfoPopup'; export const GisContext = createContext({ gisCore: null, gisMap: null, treeData: null, checkedLayerIds: null }); export const useGisContext = () => useContext(GisContext); -function Gis({ isOpen , gisMaps, isSearchModalOpen, treeData = [], isSearch = false, isMiniMap = false, setIsOpen, setFeatureData, featureData, searchView : SearchView, detailView: DetailView, onFeatureSelect, setIsSearchModalOpen }) { +function Gis({ + isOpen, + gisMaps, + isSearchModalOpen, + treeData = [], + isSearch = false, + isMiniMap = false, + setIsOpen, + setFeatureData, + featureData, + searchView: SearchView, + detailView: DetailView, + onFeatureSelect, + setIsSearchModalOpen, + onCheckedLayersChange +}) { const mapRef = useRef(); const [mapObject, setMapObject] = useState({}); const [tabVisible, setTabVisible] = useState(true); + const [layerNm, setLayerNm] = useState('') // const [isOpen, setIsOpen] = useState(false); // 팝업 열기/닫기 상태 // const [featureData, setFeatureData] = useState(null) // 선택된 피쳐 데이터 // const [isSearchModalOpen, setIsSearchModalOpen] = useState(false); // SearchModal 열림 상태 const gisCore = useMemo(() => new GisApp.core({ targetId: mapRef.current }), []); const mapInstanceRef = useRef(null); const checkedLayerIds = useRef([]); + const [viewMode, setViewMode] = useState("detail"); + + // 팝업 데이터를 저장할 상태와 오버레이 참조를 저장할 ref 추가 + // const [facilityInfoPopupData, setFacilityInfoPopupData] = useState([]); + // const facilityOverlaysRef = useRef([]); + // 체크된 레이어 상태 관리 + const [checkedLayers, setCheckedLayers] = useState([]); + const [checkedLayersData, setCheckedLayersData] = useState([]); + let gisMap = null; const closePopup = () => { setIsOpen(false); // 팝업 닫기 setFeatureData(null) }; - // SearchModal, GisContent 등에서 호출 + // Feature Click Event const handleFeatureSelect = useCallback((data) => { gisMap = mapInstanceRef.current; @@ -89,8 +116,10 @@ function Gis({ isOpen , gisMaps, isSearchModalOpen, treeData = [], isSearch = fa setFeatureData(data); setIsOpen(true); setIsSearchModalOpen(false); + onFeatureSelect?.(data); // 선택된 피쳐 데이터 콜백 호출 }, [mapInstanceRef]); + // 지도 생성 및 초기화 useEffect(() => { if (!mapRef.current) return; if (mapInstanceRef.current) return; // 이미 생성된 맵이 있다면 중단 @@ -104,7 +133,7 @@ function Gis({ isOpen , gisMaps, isSearchModalOpen, treeData = [], isSearch = fa ]); register(proj4); - //맵 생성 + // OpenLayers 지도 객체 생성 const map = new OlMap({ layers: [gisCore.baseLayers['base'], gisCore.baseLayers['satellite']], renderer: 'webgl', @@ -132,8 +161,8 @@ function Gis({ isOpen , gisMaps, isSearchModalOpen, treeData = [], isSearch = fa gisMaps.current = map - //미니맵 - if(isMiniMap){ + // 미니맵 추가 + if (isMiniMap) { const overviewMap = new OverviewMap({ layers: [GisApp.Module.core.baseMiniMapLayers['base']], view: new View({ @@ -147,6 +176,7 @@ function Gis({ isOpen , gisMaps, isSearchModalOpen, treeData = [], isSearch = fa map.addControl(overviewMap); } + // 스케일 컨트롤 및 베이스 레이어 설정 gisCore.__proto__.map = map; gisCore.scaleControl(); gisCore.changeBaseLayer('base'); @@ -154,16 +184,20 @@ function Gis({ isOpen , gisMaps, isSearchModalOpen, treeData = [], isSearch = fa mapInstanceRef.current = map; // 생성 완료 플래그 설정 }, []); + // treeData 변경 시 레이어 코드 생성 useEffect(() => { if (!treeData || treeData.length === 0) return; + // 기존 오버레이 및 레이어 모두 삭제 gisCore.deleteAllOverlay(); gisCore.deleteAllLayer(); + // 트리 데이터를 레이어 코드 객체로 변환 const layerCode = gisCore.convertLayerInfosToLayerCode(treeData, 'lyrSn'); - GisApp.layerCode = layerCode; + GisApp.layerCode = layerCode; // 전역 레이어 코드 설정 //setTreeData(data); + // 편집 가능한 레이어 목록 생성 const layerItems = getLayerItems(layerCode); //setEditLayerItems(prev => [...layerItems]) }, [treeData]); @@ -193,31 +227,164 @@ function Gis({ isOpen , gisMaps, isSearchModalOpen, treeData = [], isSearch = fa } } + // // 데이터 로드 및 팝업 생성 로직 + // useEffect(() => { + // if (!mapInstanceRef.current) return; // 맵이 생성되지 않았으면 실행 안 함 + + // // 1. 팝업 데이터 가져오기 (feature.js의 getData와 유사) + // const fetchPopupData = async () => { + // // 실제 API 호출 로직으로 대체해야 합니다. + // // 예시: const response = await yourApiService.getFcltyWtCnt(); + // // setFacilityInfoPopupData(response.data); + + // // 임시 목업 데이터 + // const mockData = [ + // { ftrIdn: 1, ftrCde: 'SA206', bplcNm: '처리장A', fcltyUseYn: 'Y', x: 127.005, y: 37.282, aCnt: 10, cCnt: 5, eCnt: 3, mCnt: 8, iCnt: 2 }, + // { ftrIdn: 2, ftrCde: 'SA112', bplcNm: '처리장B', fcltyUseYn: 'N', x: 127.010, y: 37.285, aCnt: 8, cCnt: 4, eCnt: 2, mCnt: 6, iCnt: 1 } + // ]; + // setFacilityInfoPopupData(mockData); + // }; + + // fetchPopupData(); + + // }, [mapInstanceRef.current]); // 맵 인스턴스가 생성된 후 한 번만 실행 + + // // 데이터가 변경되면 오버레이를 생성/업데이트하는 로직 + // useEffect(() => { + // const map = mapInstanceRef.current; + // if (!map || facilityInfoPopupData.length === 0) return; + + // // 기존 오버레이 정리 + // facilityOverlaysRef.current.forEach(overlay => map.removeOverlay(overlay)); + // facilityOverlaysRef.current = []; + + // facilityInfoPopupData.forEach(data => { + // const popupId = `${data.ftrIdn}_${data.ftrCde}`; + + // // 팝업을 담을 div 엘리먼트 생성 + // const popupElement = document.createElement('div'); + // popupElement.id = popupId; + + // // OpenLayers Overlay 생성 + // const position = olProj.transform([Number(data.x), Number(data.y)], GisApp.FROM_PROJ, GisApp.TO_PROJ); + // const overlay = new Overlay({ + // element: popupElement, + // position: position, + // offset: [-75, -210], // 팝업 너비의 절반, 높이만큼 오프셋 조정 + // positioning: 'bottom-center', + // className: 'facility-popup-overlay' // 필요시 공통 클래스 추가 + // }); + + // map.addOverlay(overlay); + // facilityOverlaysRef.current.push(overlay); + + // // React 컴포넌트를 popupElement에 렌더링 + // const root = createRoot(popupElement); + // root.render(<FacilityInfoPopup data={data} />); + // }); + + // // 초기 가시성 업데이트 + // updatePopupVisibility(); + + // }, [facilityInfoPopupData]); // 팝업 데이터가 변경될 때마다 실행 + + // // 레이어 가시성에 따라 팝업 가시성을 제어하는 함수 + // const updatePopupVisibility = useCallback(() => { + // const map = mapInstanceRef.current; + // if (!map) return; + + // const visibleLayers = new Set(); + // map.getLayers().forEach(layer => { + // if (layer.getVisible()) { + // // feature.js의 로직처럼 typeName이나 ftrCde를 사용해 필터링 + // const typeName = layer.get('typeName'); + // if (typeName === 'VW_WTL_FCLTY_AS') { // 예시 레이어 이름 + // visibleLayers.add(layer.get('ftrCde')); + // } + // } + // }); + + // facilityOverlaysRef.current.forEach(overlay => { + // const element = overlay.getElement(); + // const ftrCde = element.querySelector('table')?.classList[0]; // FacilityPopup의 클래스에서 ftrCde 가져오기 + + // if (visibleLayers.has(ftrCde)) { + // element.style.display = ''; + // } else { + // element.style.display = 'none'; + // } + // }); + // }, []); + + // GisTree에서 체크 상태가 변경될 때 이 함수를 호출하도록 연결해야 합니다. + // 예시: GisTree 컴포넌트에 onCheck 콜백을 추가하고, 그 콜백에서 이 함수를 호출 + // useEffect(() => { + // // 레이어 가시성이 변경될 때마다 팝업 가시성 업데이트 + // const map = mapInstanceRef.current; + // if (!map) return; + + // const handleLayerVisibilityChange = () => { + // updatePopupVisibility(); + // }; + + // map.getLayers().on('change:length', handleLayerVisibilityChange); + // map.getLayers().forEach(layer => { + // layer.on('change:visible', handleLayerVisibilityChange); + // }); + + // return () => { + // // 클린업 + // map.getLayers().un('change:length', handleLayerVisibilityChange); + // map.getLayers().forEach(layer => { + // layer.un('change:visible', handleLayerVisibilityChange); + // }); + // }; + // }, [updatePopupVisibility]); + + // 체크된 레이어 정보를 처리하는 함수 + const handleCheckedLayersChange = useCallback((checkedIds, checkedNodesData) => { + setTimeout(() => { + setCheckedLayers(checkedIds); + setCheckedLayersData(checkedNodesData); + onCheckedLayersChange?.(checkedIds, checkedNodesData); + }, 0); + }, [onCheckedLayersChange]); + return ( <> <GisContext.Provider value={{ gisCore, gisMap: mapInstanceRef.current, treeData, checkedLayerIds }}> <div className="h-[calc(100vh-10rem)] flex relative"> <div ref={mapRef} className="gis-map flex-1 relative" style={{ height: '100%', padding: 0 }} /> - <GisTree visible={tabVisible} setVisible={setTabVisible} /> - + <GisTree + visible={tabVisible} + setVisible={setTabVisible} + onCheckedChange={handleCheckedLayersChange} + onLayerIconClick={(nodeNm)=>{setLayerNm(nodeNm); setIsOpen(true); setViewMode("detail")}} + /> + {/* <GisContent onFeatureSelect={handleFeatureSelect} isSearch={isSearch} isSearchModalOpen={isSearchModalOpen} setIsSearchModalOpen={setIsSearchModalOpen} /> */} - - {/* isSearchModalOpen이 true면 visible=false로 팝업 숨김 */} - {isOpen && featureData && !isSearchModalOpen && ( + + {/* DetailView가 존재할 때만 렌더링 isSearchModalOpen이 true면 visible=false로 팝업 숨김 */} + {isOpen && !isSearchModalOpen && ( <DetailView featureData={featureData} + seletectedLayerNm={layerNm} + viewMode={viewMode} onClose={closePopup} // 닫기 콜백 전달 /> )} - <SearchView - open={isSearchModalOpen} - onOpenChange={setIsSearchModalOpen} - onFeatureSelect={onFeatureSelect} - /> - <GisLonLat/> - <GisToolBar/> - <GisMapToggle/> - <GisSelect onFeatureSelect={onFeatureSelect} /> + {/* SearchView가 존재할 때만 렌더링 */} + {SearchView && ( + <SearchView + open={isSearchModalOpen} + onOpenChange={setIsSearchModalOpen} + onFeatureSelect={handleFeatureSelect} + /> + )} + <GisLonLat /> + <GisToolBar /> + <GisMapToggle /> + <GisSelect onFeatureSelect={handleFeatureSelect} /> </div> {isSearch && ( diff --git a/packages/common/business_components/src/ui/map/GisTree.js b/packages/common/business_components/src/ui/map/GisTree.js index 9dbf2c42a..5b30dc7f4 100644 --- a/packages/common/business_components/src/ui/map/GisTree.js +++ b/packages/common/business_components/src/ui/map/GisTree.js @@ -1,15 +1,16 @@ -import GisApp from './lib/gis.core'; -import React, { useEffect, useState } from 'react'; -import { useGisContext } from './Gis'; -import { DndProvider } from "react-dnd"; import { - Tree, MultiBackend, + Tree, getBackendOptions } from "@minoru/react-dnd-treeview"; import { ChevronLeft, ChevronRight } from 'lucide-react'; +import React, { useEffect, useState } from 'react'; +import { DndProvider } from "react-dnd"; -export default React.memo(function GisTree({ checkedNodes = [], setCheckedNodes = () => { }, visible, setVisible }) { +import { useGisContext } from './Gis'; +import GisApp from './lib/gis.core'; + +export default React.memo(function GisTree({ checkedNodes = [], setCheckedNodes = () => { }, visible, setVisible, onCheckedChange, onLayerIconClick }) { const { gisCore, treeData } = useGisContext(); const [checked, setChecked] = useState([]); const [expanded, setExpanded] = useState([]); @@ -24,19 +25,31 @@ export default React.memo(function GisTree({ checkedNodes = [], setCheckedNodes } }, [treeData]); + // 트리 노드 체크 시 레이어 표시/숨김 useEffect(() => { if (nodes.length > 0) { + // 기본으로 체크된 노드들 찾기 const defaultChecked = getDefaultCheckedNodes(nodes); setChecked(defaultChecked); + console.log('defaultChecked : ', defaultChecked); + + // 기본으로 확장된 노드들 설정 const defaultExpanded = Array.from(new Set([ ...defaultChecked, ...defaultChecked.flatMap((id) => getAncestors(id, nodes)) ])); setExpanded(defaultExpanded); - gisCore.setLayerOnMap(defaultChecked, true); - } - }, [nodes]); + // 기본 체크된 레이어들을 지도에 표시 + gisCore.setLayerOnMap(defaultChecked, true); + + // 초기 체크된 레이어 정보를 상위로 전달 + const defaultCheckedNodesData = nodes.filter(node => defaultChecked.includes(node.id)); + onCheckedChange?.(defaultChecked, defaultCheckedNodesData); + } + }, [nodes]); // onCheckedChange를 의존성에 추가 + + // 초기 표시 여부가 'Y'인 레이어들 찾기 const getDefaultCheckedNodes = (nodes, values = []) => { for (let i = 0; i < nodes.length; i++) { const node = nodes[i]; @@ -94,6 +107,11 @@ export default React.memo(function GisTree({ checkedNodes = [], setCheckedNodes const unchecked = allIds.filter((nid) => !newChecked.includes(nid)); gisCore.setLayerOnMap(newChecked, true); gisCore.setLayerOnMap(unchecked, false); + + // 체크된 노드 정보와 함께 노드 데이터를 상위로 전달 + const checkedNodesData = nodes.filter(node => newChecked.includes(node.id)); + onCheckedChange?.(newChecked, checkedNodesData); + return newChecked; }); }; @@ -170,7 +188,10 @@ export default React.memo(function GisTree({ checkedNodes = [], setCheckedNodes image.src = canvas.toDataURL('image/png'); return image; }; - + const handleLayerImgClick = (nodeNm) => { + console.log('click!', nodeNm) + onLayerIconClick(nodeNm) + } if (!treeData || treeData.length === 0) return null; return ( @@ -203,7 +224,7 @@ export default React.memo(function GisTree({ checkedNodes = [], setCheckedNodes <input type="checkbox" checked={checked.includes(node.id)} onChange={() => handleCheck(node.id)} className="cursor-pointer" /> {node.icon && <span>{node.icon}</span>} <span className="text-[1.3rem] font-medium text-label">{node.text}</span> - {depth > 0 && <span><img className="cursor-pointer w-5 h-5" src='/images/map/layericon/Layer_Select_BTN.png' onClick={()=>{}} alt={node.text}></img></span>} + {depth > 0 && <span><img className="cursor-pointer w-5 h-5" src='/images/map/layericon/Layer_Select_BTN.png' onClick={()=>{handleLayerImgClick(node.text)}} alt={node.text}></img></span>} </div> )} sort={(a, b) => { From f592ab31d86a86ce305ce8b4a65170340d03186a Mon Sep 17 00:00:00 2001 From: jyha00 <jyha@mind-one.co.kr> Date: Fri, 4 Jul 2025 13:10:23 +0900 Subject: [PATCH 064/108] =?UTF-8?q?=EC=A7=80=EB=8F=84=EC=A0=90=EA=B2=80?= =?UTF-8?q?=EB=93=B1=EB=A1=9D=20=EB=B0=8F=20=ED=98=84=ED=99=A9=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=95=20=EC=88=98=EC=A0=95,?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ruseFcltMng/prfmncReg/chkgdnReg/page.tsx | 5 +- .../prfmncSttn/chkgdnSttn/page.tsx | 40 ++++++++++--- .../prfmncReg/chkgdnReg/rainChkgdn/index.tsx | 5 +- .../prfmncReg/chkgdnReg/rswrtChkgdn/index.tsx | 4 +- .../chkgdnSttn/rainChkgdnSttn/index.tsx | 58 +++++++++++-------- .../chkgdnSttn/rswtChkgdnSttn/index.tsx | 32 +++++----- .../ui/modal/chkgd/chkgdModal/chkgdModal.tsx | 21 ++++--- .../chkgdTable/rainChkgd/rainChkgdTable.tsx | 14 ++--- .../chkgdTable/rswtChkgd/rswtChkgdTable.tsx | 4 +- 9 files changed, 107 insertions(+), 76 deletions(-) diff --git a/apps/wr/app/ruseFcltMng/prfmncReg/chkgdnReg/page.tsx b/apps/wr/app/ruseFcltMng/prfmncReg/chkgdnReg/page.tsx index 033d3a23d..328bb8b88 100644 --- a/apps/wr/app/ruseFcltMng/prfmncReg/chkgdnReg/page.tsx +++ b/apps/wr/app/ruseFcltMng/prfmncReg/chkgdnReg/page.tsx @@ -315,7 +315,7 @@ export default function Page() { </Tooltip> </TooltipProvider> - + <Button size={"sm"} onClick={() => { @@ -344,7 +344,7 @@ export default function Page() { rainChkData={rainChkgdn.rainChk} pageNation={rainPageNation} onPageChange={rainPageChange} - /> + pageCount={rainChkgdn.rainChkCount} /> ) : ( <RswtChkgdTable detaiRswtlUpdate={setUpdateRSwtDetail} @@ -353,6 +353,7 @@ export default function Page() { rswtChkData={rswtChkgdn.rswtChkRes} pageNation={rswtPageNation} onPageChange={rswtPageChange} + pageCount={rswtChkgdn.rswtChkCount} /> )} </ListDiv> diff --git a/apps/wr/app/ruseFcltMng/prfmncSttn/chkgdnSttn/page.tsx b/apps/wr/app/ruseFcltMng/prfmncSttn/chkgdnSttn/page.tsx index 8960f468d..60040833b 100644 --- a/apps/wr/app/ruseFcltMng/prfmncSttn/chkgdnSttn/page.tsx +++ b/apps/wr/app/ruseFcltMng/prfmncSttn/chkgdnSttn/page.tsx @@ -1,6 +1,8 @@ "use client"; import { useWrStore } from "@/app/store"; +import { commonCd, rainPageSize } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn"; +import { rswtPageSize } from "@/app/store/prfmncReg/chkgdnReg/rswrtChkgdn"; import { excelDownload, yearOptions @@ -39,6 +41,8 @@ export default function Page() { }); const [selFcltDiv, setSelFcltDiv] = useState("RNB"); + const [rainPageNation, setRainPageNation] = useState(1); + const [rswtPageNation, setRswtPageNation] = useState(1); const [isModal, setIsModal] = useState({ open: false, data: {}, @@ -47,29 +51,43 @@ export default function Page() { const cellClick = () => { if (searchYear) { if (selFcltDiv === "RNB") { - rainChkSttnSearch(searchYear, "0"); + rainChkSttnSearch(rainPageNation, rainPageSize, searchYear, commonCd); } else { - rswtChkSttnSearch(searchYear, "0"); + rswtChkSttnSearch(rswtPageNation, rswtPageSize, searchYear, commonCd); } } }; + const rainPageChange = (pageNo: number) => { + if (pageNo > 0) { + setRainPageNation(pageNo); + rainChkSttnInit(pageNo, rainPageSize, searchYear, commonCd); + } + }; + + const rswtPageChange = (pageNo: number) => { + if (pageNo > 0) { + setRswtPageNation(pageNo); + rswtChkSttnInit(pageNo, rswtPageSize, searchYear, commonCd); + } + }; + useEffect(() => { if (!rainChkSttn.isInit) { isRainSttn(true) - rainChkSttnInit(new Date().getFullYear().toString(), "0"); + rainChkSttnInit(1, rainPageSize, new Date().getFullYear().toString(), commonCd); setIsModal({ data: {}, open: false }); } - }, [rainChkSttn.isInit, rainChkSttnInit]); + }, [rainChkSttn.isInit, rainChkSttnInit, rainPageSize]); useEffect(() => { - if (!rswtChkSttn.isInit) { setIsModal({ data: {}, open: false }); - rswtChkSttnInit(new Date().getFullYear().toString(), "0"); + rswtChkSttnInit(1, rswtPageSize, new Date().getFullYear().toString(), commonCd); isRswtSttn(true) } - }, [rswtChkSttn.isInit, rswtChkSttnInit]) + }, [rswtChkSttn.isInit, rswtChkSttnInit, rswtPageSize]) + const handleClick = useCallback( @@ -163,6 +181,9 @@ export default function Page() { detailUp={() => { }} handleClick={handleClick} rainChkData={rainChkSttn.rainChkRes} + pageNation={rainPageNation} + onPageChange={rainPageChange} + pageCount={rainChkSttn.rainChkCount} /> ) : ( <RswtChkgdTable @@ -170,6 +191,9 @@ export default function Page() { deleteRswtData={() => { }} handleRswtClick={handleClick} rswtChkData={rswtChkSttn.rswtChkRes} + pageNation={rswtPageNation} + onPageChange={rswtPageChange} + pageCount={rswtChkSttn.rswtChkCount} /> )} </ListDiv> @@ -181,7 +205,7 @@ export default function Page() { setIsModal({ open: !open, data: {} }); }} bodyClass={"w-[80vw] max-w-none"} - body={<ChkgdModal type={selFcltDiv} obj={isModal.data} />} + body={<ChkgdModal type={selFcltDiv} obj={isModal.data} setIsModal={() => { }} />} ></UiModal> <Toaster position="top-center" /> </ContentsDiv> diff --git a/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/index.tsx b/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/index.tsx index 63d2d1d03..36dd0a815 100644 --- a/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/index.tsx +++ b/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/index.tsx @@ -24,7 +24,7 @@ import { import type { TableUpperProps } from "@/app/store/common/type/header"; import toast from "react-hot-toast"; -export const rainPageSize = 10; +export const rainPageSize = 5; export const commonCd = "41"; export type rainChkZodType = z.infer<typeof rainChkZod>; @@ -132,7 +132,6 @@ const initFetch = async ( ) => { const currentYear = new Date().getFullYear().toString(); - console.log("cnt ", cnt); const rainRes = await getCountFetch( "wr", `/reuseChk/rainy` + @@ -142,7 +141,6 @@ const initFetch = async ( (sgg ? `/${sgg}` : "/0") ); - console.log(" rainRes ", rainRes); return rainRes; }; @@ -193,7 +191,6 @@ export const rainChkgdnActionsExport: (set: any) => rainChkgdnActions = ( toast.error("지도점검 등록이 실패되었습니다."); } } catch (error) { - console.log(error); toast.error("지도점검 등록이 실패되었습니다."); } finally { set((state: rainChkgdnType) => ({ rainChkgdn: { ...state.rainChkgdn, isLoading: false } })); diff --git a/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/index.tsx b/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/index.tsx index d40c1190c..6e0a805b6 100644 --- a/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/index.tsx +++ b/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/index.tsx @@ -28,7 +28,7 @@ import { type chHeadType } from "../rainChkgdn/headList"; import type { TableUpperProps } from "@/app/store/common/type/header"; import toast from "react-hot-toast"; -export const rswtPageSize = 10; +export const rswtPageSize = 5; const rswtChkHeadList = rswtChkHeadListExport; const rswtDetailHeadList = rswtDetailHeadListExport; @@ -43,7 +43,6 @@ export type rswtChkgdnState = { rswtChkgdn: { rswtChkHeadList: TableUpperProps[]; rswtDetailHeadList: TableUpperProps[]; - rswtHeadNm: chHeadType[]; excelList: SheetParameter[]; rswtChkRes: rswtChkZodType[]; @@ -241,7 +240,6 @@ export const rswtChkgdnActionsExport: (set: any) => rswtChkgdnActions = ( try { set((state: rswtChkgdnType) => ( { rswtChkgdn: { ...state.rswtChkgdn, isLoading: true } })); - console.log('excelData ', excelData) const res = await postFectch("/reuseChk/reuseWater", excelData); if (res.ok) { ToastFormUi({ diff --git a/apps/wr/app/store/prfmncSttn/chkgdnSttn/rainChkgdnSttn/index.tsx b/apps/wr/app/store/prfmncSttn/chkgdnSttn/rainChkgdnSttn/index.tsx index bbbb3a61e..895f096ef 100644 --- a/apps/wr/app/store/prfmncSttn/chkgdnSttn/rainChkgdnSttn/index.tsx +++ b/apps/wr/app/store/prfmncSttn/chkgdnSttn/rainChkgdnSttn/index.tsx @@ -1,8 +1,8 @@ import type { rainChkZodType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn"; -import { getFetch } from "@/components/lib/api"; +import { getCountFetch, getFetch } from "@/components/lib/api"; import type { SelectDataType } from "@common/business_components/ui"; import { rainChkgdnHeadSttnExport, ruseSttnHeadListExport, splSttnHeadListExport } from "./headList"; -import { rainMainHeadNmExport, type chHeadType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList"; +import { rainMainHeadNmExport, type chHeadType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList"; import { buildingExcelRainSttn } from "./excel"; import type { TableUpperProps } from "@/app/store/common/type/header"; @@ -24,20 +24,21 @@ export type rainChkSttnState = { rainChkRes: rainChkZodType[]; fcltDiv: SelectDataType[]; rainChkgdnHeadList: TableUpperProps[]; - rainHeadNm:chHeadType[]; + rainHeadNm: chHeadType[]; ruseHeadList: TableUpperProps[]; splHeadList: TableUpperProps[]; - sttnExcelList:[] + sttnExcelList: [] isInit: boolean; - isSttn:boolean; + isSttn: boolean; + rainChkCount: number; }; }; type rainChkSttnActions = { - rainChkSttnInit: (searchTxt: string, sggCd: string) => void; - rainChkSttnSearch: (searchTxt: string, sggCd: string) => void; - isRainSttn:(isSttn:boolean) => void; - getRainSttnExcel:(excelList:rainChkZodType[]) => void; + rainChkSttnInit: (pageNo: number, cnt: number, searchTxt: string, sggCd: string) => void; + rainChkSttnSearch: (pageNo: number, cnt: number, searchTxt: string, sggCd: string) => void; + isRainSttn: (isSttn: boolean) => void; + getRainSttnExcel: (excelList: rainChkZodType[]) => void; }; export type rainChkSttnType = rainChkSttnState & rainChkSttnActions; @@ -49,18 +50,23 @@ export const rainChkSttnStateExport: rainChkSttnState = { rainChkgdnHeadList: rainChkgdnHeadList, ruseHeadList: ruseHeadList, splHeadList: splHeadList, - rainHeadNm:rainHeadNm, + rainHeadNm: rainHeadNm, isInit: false, - sttnExcelList:[], - isSttn:false, + sttnExcelList: [], + isSttn: false, + rainChkCount: 1, }, }; -const initFetch = async (txt: string | undefined, sgg: string | undefined) => { +const initFetch = async (pageNo: number, cnt: number, txt: string | undefined, sgg: string | undefined) => { const currentYear = new Date().getFullYear().toString(); - const rainRes = await getFetch( + const rainRes = await getCountFetch( "wr", - `/reuseChk/rainy/` + (txt ? `${txt}` : currentYear) + (sgg ? `/${sgg}` : "/0") + `/reuseChk/rainy` + + (pageNo ? `/${pageNo}` : "/1") + + (cnt ? `/${cnt}` : "/5") + + (txt ? `/${txt}` : `/${currentYear}`) + + (sgg ? `/${sgg}` : "/0") ); return rainRes; }; @@ -68,48 +74,50 @@ export const rainChkSttnActionsExport: (set: any) => rainChkSttnActions = ( set: any ) => { return { - rainChkSttnInit: async (searchTxt: string, sggCd: string) => { + rainChkSttnInit: async (pageNo: number, cnt: number, searchTxt: string, sggCd: string) => { try { - const rainChkRes = await initFetch(searchTxt, sggCd); + const rainChkRes = await initFetch(pageNo, cnt, searchTxt, sggCd); return set((state: rainChkSttnState) => ({ ...state, rainChkSttn: { ...state.rainChkSttn, - rainChkRes:rainChkRes + rainChkRes: rainChkRes.data, + rainChkCount: rainChkRes.count, + isSttn: true }, })); } catch (error) { console.log(error); } }, - rainChkSttnSearch: async (searchTxt: string, sggCd: string) => { + rainChkSttnSearch: async (pageNo: number, cnt: number, searchTxt: string, sggCd: string) => { try { - const rainChkRes = await initFetch(searchTxt, sggCd); + const rainChkRes = await initFetch(pageNo, cnt, searchTxt, sggCd); return set((state: rainChkSttnState) => ({ ...state, rainChkSttn: { ...state.rainChkSttn, - rainChkRes:rainChkRes + rainChkRes: rainChkRes }, })); } catch (error) { console.log(error); } - },isRainSttn :(isSttn:boolean) =>{ + }, isRainSttn: (isSttn: boolean) => { try { return set((state: rainChkSttnState) => ({ ...state, rainChkSttn: { ...state.rainChkSttn, - isSttn:isSttn + isSttn: isSttn }, })); } catch (error) { console.log(error); } - },getRainSttnExcel: async (excelList:rainChkZodType[]) => { + }, getRainSttnExcel: async (excelList: rainChkZodType[]) => { try { const excelData = buildingExcelRainSttn(excelList) @@ -118,7 +126,7 @@ export const rainChkSttnActionsExport: (set: any) => rainChkSttnActions = ( ...state, rainChkSttn: { ...state.rainChkSttn, - sttnExcelList:excelData + sttnExcelList: excelData }, })); } catch (error) { diff --git a/apps/wr/app/store/prfmncSttn/chkgdnSttn/rswtChkgdnSttn/index.tsx b/apps/wr/app/store/prfmncSttn/chkgdnSttn/rswtChkgdnSttn/index.tsx index cd95ef1fc..519cd39b2 100644 --- a/apps/wr/app/store/prfmncSttn/chkgdnSttn/rswtChkgdnSttn/index.tsx +++ b/apps/wr/app/store/prfmncSttn/chkgdnSttn/rswtChkgdnSttn/index.tsx @@ -3,7 +3,7 @@ import { rswtChkSttnHeadListExport, rswtDetailSttnHeadListExport } from "./headL import type { TableUpperProps } from "@/app/store/common/type/header"; import type { chHeadType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList"; import type { rswtChkZodType } from "@/app/store/prfmncReg/chkgdnReg/rswrtChkgdn"; -import { getFetch } from "@/components/lib/api"; +import { getCountFetch, getFetch } from "@/components/lib/api"; import { buildingExcelRswtSttn } from "./excel"; const rswtChkHeadList = rswtChkSttnHeadListExport; @@ -20,14 +20,15 @@ export type rswtChkSttnState = { rswtChkRes: rswtChkZodType[]; isInit: boolean; isSttn: boolean; + rswtChkCount: number; }; }; type rswtChkSttnActions = { - rswtChkSttnInit: (searchTxt: string, sggCd: string) => void; + rswtChkSttnInit: (pageNo: number, cnt: number, searchTxt: string, sggCd: string) => void; isRswtSttn: (isSttn: boolean) => void; getRswtSttnExcel: (excelList: rswtChkZodType[]) => void; - rswtChkSttnSearch: (searchTxt: string, sggCd: string) => void; + rswtChkSttnSearch: (pageNo: number, cnt: number, searchTxt: string, sggCd: string) => void; }; export type rswtChkSttnType = rswtChkSttnState & rswtChkSttnActions; @@ -41,15 +42,18 @@ export const rswtChkStateExport: rswtChkSttnState = { rswtChkRes: [], isInit: false, isSttn: false, + rswtChkCount: 1, }, }; -const initFetch = async (year: string | undefined, sgg: string | undefined) => { +const initFetch = async (pageNo: number, cnt: number, year: string | undefined, sgg: string | undefined) => { const currentYear = new Date().getFullYear().toString(); - const rswtChkRes = await getFetch( + const rswtChkRes = await getCountFetch( "wr", - `/reuseChk/reuseWater/` + - (year ? `${year}` : currentYear) + + `/reuseChk/reuseWater` + + (pageNo ? `/${pageNo}` : "/1") + + (cnt ? `/${cnt}` : "/20") + + (year ? `/${year}` : `/${currentYear}`) + (sgg ? `/${sgg}` : "/0") ); return rswtChkRes; @@ -60,14 +64,15 @@ export const rswtChkSttnActionsExport: (set: any) => rswtChkSttnActions = ( set: any ) => { return { - rswtChkSttnInit: async (searchTxt: string, sggCd: string) => { + rswtChkSttnInit: async (pageNo: number, cnt: number, searchTxt: string, sggCd: string) => { try { - const rswtChkRes = await initFetch(searchTxt, sggCd); + const rswtChkRes = await initFetch(pageNo, cnt, searchTxt, sggCd); return set((state: rswtChkSttnState) => ({ ...state, rswtChkSttn: { ...state.rswtChkSttn, - rswtChkRes: rswtChkRes, + rswtChkRes: rswtChkRes.data, + rswtChkCount: rswtChkRes.count, isInit: false, }, })); @@ -104,15 +109,16 @@ export const rswtChkSttnActionsExport: (set: any) => rswtChkSttnActions = ( console.log(error); } }, - rswtChkSttnSearch: async (searchTxt: string, sggCd: string) => { + rswtChkSttnSearch: async (pageNo: number, cnt: number, searchTxt: string, sggCd: string) => { try { - const rswtChkRes = await initFetch(searchTxt, sggCd); + const rswtChkRes = await initFetch(pageNo, cnt, searchTxt, sggCd); return set((state: rswtChkSttnState) => ({ ...state, rswtChkSttn: { ...state.rswtChkSttn, - rswtChkRes: rswtChkRes + rswtChkRes: rswtChkRes.data, + rswtChkCount: rswtChkRes.count, }, })); } catch (error) { diff --git a/apps/wr/components/ui/modal/chkgd/chkgdModal/chkgdModal.tsx b/apps/wr/components/ui/modal/chkgd/chkgdModal/chkgdModal.tsx index d62406e9f..515c74490 100644 --- a/apps/wr/components/ui/modal/chkgd/chkgdModal/chkgdModal.tsx +++ b/apps/wr/components/ui/modal/chkgd/chkgdModal/chkgdModal.tsx @@ -1,16 +1,14 @@ import { useWrStore } from "@/app/store"; -import { ScrollArea, Title } from "@common/business_components/ui"; -import { Button } from "@common/components/ui"; -import { useEffect, useState } from "react"; -import { headMakeprfmnc } from "@/app/ruseFcltMng/prfmncReg/chkgdnReg/TableHead"; -import { GuideDetailsTable } from "@/components/ui/table/chkgdTable/guideDetailTable/guideDetailTable"; +import type { TableUpperProps } from "@/app/store/common/type/header"; import type { splRuseWaterType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList"; import type { rswtrPolutionsZode } from "@/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/headList"; +import { GuideDetailsTable } from "@/components/ui/table/chkgdTable/guideDetailTable/guideDetailTable"; +import { ScrollArea, Title } from "@common/business_components/ui"; +import { Button } from "@common/components/ui"; +import React, { useEffect, useState } from "react"; import type { z } from "zod"; -import React from "react"; -import type { TableUpperProps } from "@/app/store/common/type/header"; -export default function ChkgdModal({ type, obj ,setIsModal }: { type: string; obj: any , setIsModal:(open:boolean)=>void}) { +export default function ChkgdModal({ type, obj, setIsModal }: { type: string; obj: any, setIsModal: (open: boolean) => void }) { const { rainChkgdn, rswtChkgdn, @@ -253,7 +251,8 @@ export default function ChkgdModal({ type, obj ,setIsModal }: { type: string; ob </div> <div className="flex justify-between"> <h3 className="text-2xl font-bold text-gray-900 mt-2 ml-6"> - {`(${obj.bdstNm})`} + {type === "RNB" + ? `(${obj.bdstNm})` : `(${obj.building.bdstNm})`} </h3> {!rainChkSttn.isSttn ? ( <Button className="" onClick={allDataUpdate}> @@ -273,7 +272,7 @@ export default function ChkgdModal({ type, obj ,setIsModal }: { type: string; ob <GuideDetailsTable chgRes={guidData || []} headList={headList.splHeadList} - upheadList={type === "RNB"? rainChkgdn.rainSplHeadNm :rswtChkgdn.rswtSplHeadNm} + upheadList={type === "RNB" ? rainChkgdn.rainSplHeadNm : rswtChkgdn.rswtSplHeadNm} type={""} setChGuidData={(data) => { setChGuidData(data); @@ -287,7 +286,7 @@ export default function ChkgdModal({ type, obj ,setIsModal }: { type: string; ob <GuideDetailsTable chgRes={guidData || []} headList={headList.ruseHeadList} - upheadList={type === "RNB"? rainChkgdn.rainRuseHeadNm :rswtChkgdn.rswtRuseHeadNm} + upheadList={type === "RNB" ? rainChkgdn.rainRuseHeadNm : rswtChkgdn.rswtRuseHeadNm} type={""} setChGuidData={(data) => { setChGuidData(data); diff --git a/apps/wr/components/ui/table/chkgdTable/rainChkgd/rainChkgdTable.tsx b/apps/wr/components/ui/table/chkgdTable/rainChkgd/rainChkgdTable.tsx index c6daac3b6..687b27611 100644 --- a/apps/wr/components/ui/table/chkgdTable/rainChkgd/rainChkgdTable.tsx +++ b/apps/wr/components/ui/table/chkgdTable/rainChkgd/rainChkgdTable.tsx @@ -25,6 +25,7 @@ export default function RainChkgdTable({ rainChkData, pageNation, onPageChange, + pageCount, }: { detailUp: (data: rainChkZodType[]) => void; handleClick?: (data: any) => void; @@ -32,6 +33,7 @@ export default function RainChkgdTable({ rainChkData?: rainChkZodType[]; pageNation: number; onPageChange: (page: number) => void; + pageCount: number; }) { const { code, rainChkgdn, rainChkSttn, getRainSttnExcel } = useWrStore((state) => state); const [makeTableData, setMakeTableData] = useState<any[]>([]); @@ -74,7 +76,7 @@ export default function RainChkgdTable({ useEffect(() => { setSelectedIds([])// 셀렉트 초기화를 위한 작업 - if (rainChkData&&rainChkData.length>0) { + if (rainChkData && rainChkData.length > 0) { const rainChg = rainChkData?.map((item: any) => { const bud = tableData([item.building], { code }); @@ -86,7 +88,7 @@ export default function RainChkgdTable({ }); setMakeTableData(rainChkTableData(rainChg)); - }else{ + } else { setMakeTableData([]); } }, [rainChkData, code]); @@ -170,14 +172,8 @@ export default function RainChkgdTable({ }) } - }, [rainChkSttn.isSttn, rainChkgdn.rainChkgdnHeadList, rainChkSttn.rainChkgdnHeadList]); - - useEffect(() =>{ - console.log('rainChkgdn.count ', rainChkgdn.rainChkCount) - },[rainChkgdn.rainChkCount]) - return ( <> <LoadingOverlay isLoading={rainChkgdn.isLoading}></LoadingOverlay> @@ -202,7 +198,7 @@ export default function RainChkgdTable({ </TableBody> </Table> <UiPageNations - total={rainChkgdn.rainChkCount||20} + total={pageCount} cur={pageNation} pageSize={rainPageSize} onPageChange={onPageChange} diff --git a/apps/wr/components/ui/table/chkgdTable/rswtChkgd/rswtChkgdTable.tsx b/apps/wr/components/ui/table/chkgdTable/rswtChkgd/rswtChkgdTable.tsx index 5264dcdee..e71238706 100644 --- a/apps/wr/components/ui/table/chkgdTable/rswtChkgd/rswtChkgdTable.tsx +++ b/apps/wr/components/ui/table/chkgdTable/rswtChkgd/rswtChkgdTable.tsx @@ -25,6 +25,7 @@ export default function RswtChkgdTable({ rswtChkData, pageNation, onPageChange, + pageCount, }: { detaiRswtlUpdate: (data: rswtrChkDetailsZodType[]) => void; handleRswtClick?: (data: any) => void; @@ -32,6 +33,7 @@ export default function RswtChkgdTable({ rswtChkData: rswtChkZodType[]; pageNation: number; onPageChange: (page: number) => void; + pageCount: number; }) { const { code, rswtChkgdn, rswtChkSttn, rswtChkgdnInit, rswtChkPolAndGuide, getRswtSttnExcel } = useWrStore( (state) => state @@ -152,7 +154,7 @@ export default function RswtChkgdTable({ </TableBody> </Table> <UiPageNations - total={rswtChkgdn.rswtChkCount} + total={pageCount} cur={pageNation} pageSize={rswtPageSize} onPageChange={onPageChange} From f379425fa70a01650bc49f661afd7a9ecfbdab7d Mon Sep 17 00:00:00 2001 From: leedj312 <dlehd467@gmail.com> Date: Fri, 4 Jul 2025 13:31:24 +0900 Subject: [PATCH 065/108] =?UTF-8?q?=EA=B2=B0=ED=95=A8=EB=8C=80=EC=9E=A5=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/ruseFcltMng/fcltReg/htdflReg/page.tsx | 25 +++++++++++++--- .../app/ruseFcltMng/fcltReg/rainReg/page.tsx | 4 +++ .../app/ruseFcltMng/fcltReg/rswtReg/page.tsx | 8 ++++- .../prfmncReg/chkgdnReg/rainChkgdn/index.tsx | 12 ++++---- .../prfmncReg/chkgdnReg/rswrtChkgdn/index.tsx | 6 ---- .../ruseFcltMng/fcltReg/htdflReg/index.tsx | 30 ++++++++++++++----- .../ruseFcltMng/fcltReg/rainReg/index.tsx | 2 +- .../ruseFcltMng/fcltReg/rainReg/type.tsx | 6 ++-- .../ruseFcltMng/fcltReg/rswtReg/index.tsx | 24 +++++++-------- .../ui/form/fcltReg/bdstReg/bdstReg.tsx | 10 +++---- .../ui/form/fcltReg/rainReg/rainRegForm.tsx | 1 + .../ui/form/fcltReg/rswtrReg/rswtrRegForm.tsx | 21 +++++++++---- 12 files changed, 97 insertions(+), 52 deletions(-) diff --git a/apps/wr/app/ruseFcltMng/fcltReg/htdflReg/page.tsx b/apps/wr/app/ruseFcltMng/fcltReg/htdflReg/page.tsx index 33cfa3bba..3016314a2 100644 --- a/apps/wr/app/ruseFcltMng/fcltReg/htdflReg/page.tsx +++ b/apps/wr/app/ruseFcltMng/fcltReg/htdflReg/page.tsx @@ -23,7 +23,7 @@ import type { customHtdflBdst } from "@/app/store/ruseFcltMng/fcltReg/htdflReg"; import { BdstReg } from "@/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal"; export default function Home() { - const { code, htdfl, initHtdfl, getHtdflBdstData } = useWrStore( + const { code, htdfl, initHtdflReg, getHtdflBdstData ,htdflNmSearch} = useWrStore( (state) => state ); const [searchTxt, setSearchTxt] = useState(""); @@ -42,18 +42,27 @@ export default function Home() { useEffect(() => { if (!htdfl.isInit) { + initHtdflReg('0') } - }, [htdfl.isInit, initHtdfl]); + }, [htdfl.isInit, initHtdflReg]); useEffect(() => { - if (code.sggCd.length > 0) { + if (htdflRes&&htdflRes.length>0) { + + console.log('htdflRes ', htdflRes) const htdflData = tableData(htdflRes, { code }).map( (item: customHtdflBdst) => { + let archArea = item?.archArea.split('.')[0]!=='-1'?item?.archArea.split('.')[0]:'확인불가' + return { ...item, + archArea:archArea + }; } ); setbdstTable(htdflData); + }else{ + setbdstTable([]); } }, [code, htdflRes]); @@ -104,7 +113,15 @@ export default function Home() { <Button size={"sm"} onClick={() => { - initHtdfl(searchTxt || ""); + console.log(' 조회가 없어11') + if(searchTxt&&searchTxt.length>0){ + htdflNmSearch(String(0),searchTxt) + }else{ + + console.log(' 조회가 없어') + initHtdflReg(String(0) ); + + } }} > 조회 diff --git a/apps/wr/app/ruseFcltMng/fcltReg/rainReg/page.tsx b/apps/wr/app/ruseFcltMng/fcltReg/rainReg/page.tsx index b468fad03..f954ce7f7 100644 --- a/apps/wr/app/ruseFcltMng/fcltReg/rainReg/page.tsx +++ b/apps/wr/app/ruseFcltMng/fcltReg/rainReg/page.tsx @@ -51,8 +51,12 @@ export default function Home() { if (code.sggCd.length > 0) { const rainData = tableData(rainRes, { code }).map( (item: customRainBdst) => { + + let archArea = item?.archArea.split('.')[0]!=='-1'?item?.archArea.split('.')[0]:'확인불가' + return { ...item, + archArea:archArea, bdstChgYn: item.chgBuilding.length > 0 ? "Y" : "N", }; } diff --git a/apps/wr/app/ruseFcltMng/fcltReg/rswtReg/page.tsx b/apps/wr/app/ruseFcltMng/fcltReg/rswtReg/page.tsx index ea0dea649..cd91a7fbc 100644 --- a/apps/wr/app/ruseFcltMng/fcltReg/rswtReg/page.tsx +++ b/apps/wr/app/ruseFcltMng/fcltReg/rswtReg/page.tsx @@ -20,6 +20,7 @@ import { import ListDiv from "@common/business_components/ui/list_div"; import { Button } from "@common/components"; import { useEffect, useState } from "react"; +import { Toaster } from "react-hot-toast"; export default function Home() { const { code, rswt, initRswt, rswtBdstSearch, getRswtBdstData } = useWrStore( @@ -45,8 +46,12 @@ export default function Home() { if (code.sggCd.length > 0 && rswtRes?.length > 0) { const rswtData = tableData(rswtRes, { code })?.map( (item: customRswtBdst) => { + + let archArea = item?.archArea.split('.')[0]!=='-1'?item?.archArea.split('.')[0]:'확인불가' + return { ...item, + archArea:archArea, bdstChgYn: item.chgBuilding[0]?.bdstChgNm != null ? "Y" : "N", bdstUsgCd: item.reuseBuilding[0]?.bdstUsgCd, }; @@ -168,7 +173,8 @@ export default function Home() { setBdstChgProps({ ...bdstChgProps, isOpen: open }); }} /> - </ContentsDiv> + <Toaster position="top-center" /> + </ContentsDiv> </> ); } diff --git a/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/index.tsx b/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/index.tsx index 63d2d1d03..c6683accb 100644 --- a/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/index.tsx +++ b/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/index.tsx @@ -205,7 +205,7 @@ export const rainChkgdnActionsExport: (set: any) => rainChkgdnActions = ( year: string, sggCd: string ) => { - set((state: rainChkgdnType) => ({ rainChkgdn: { ...state.rainChkgdn, isLoading: true } })); + // set((state: rainChkgdnType) => ({ rainChkgdn: { ...state.rainChkgdn, isLoading: true } })); try { const rainChkRes = await initFetch(pageNo, cnt, year, sggCd); set((state: rainChkgdnState) => ({ @@ -214,17 +214,17 @@ export const rainChkgdnActionsExport: (set: any) => rainChkgdnActions = ( ...state.rainChkgdn, rainChk: rainChkRes.data, rainChkCount: rainChkRes.count, - isInit: true, // 무한 루프 방지를 위해 isInit을 true로 설정 + isInit: false, // 무한 루프 방지를 위해 isInit을 true로 설정 }, })); } catch (error) { console.log(error); // 에러가 발생하더라도 무한 루프에 빠지지 않도록 isInit을 true로 설정합니다. - set((state: rainChkgdnType) => ({ - rainChkgdn: { ...state.rainChkgdn, isInit: true }, - })); + // set((state: rainChkgdnType) => ({ + // rainChkgdn: { ...state.rainChkgdn, isInit: true }, + // })); } finally { - set((state: rainChkgdnType) => ({ rainChkgdn: { ...state.rainChkgdn, isLoading: false } })); + // set((state: rainChkgdnType) => ({ rainChkgdn: { ...state.rainChkgdn, isLoading: false } })); } }, rainChkNmSearch: async ( diff --git a/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/index.tsx b/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/index.tsx index d40c1190c..6edee720b 100644 --- a/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/index.tsx +++ b/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/index.tsx @@ -183,7 +183,6 @@ export const rswtChkgdnActionsExport: (set: any) => rswtChkgdnActions = ( year: string, sggCd: string ) => { - set((state: rswtChkgdnType) => ({ rswtChkgdn: { ...state.rswtChkgdn, isLoading: true } })); try { const rswtChkRes = await initFetch(pageNo, cnt, year, sggCd); @@ -198,11 +197,7 @@ export const rswtChkgdnActionsExport: (set: any) => rswtChkgdnActions = ( })); } catch (error) { console.log(error); - set((state: rswtChkgdnType) => ({ - rswtChkgdn: { ...state.rswtChkgdn, isInit: true }, - })); } finally { - set((state: rswtChkgdnType) => ({ rswtChkgdn: { ...state.rswtChkgdn, isLoading: false } })); } }, rswtChkNmSearch: async ( @@ -230,7 +225,6 @@ export const rswtChkgdnActionsExport: (set: any) => rswtChkgdnActions = ( ...state.rswtChkgdn, rswtChkRes: rswtChkRes.data, rswtChkCount: rswtChkRes.count, - isInit: false, }, })); } catch (error) { diff --git a/apps/wr/app/store/ruseFcltMng/fcltReg/htdflReg/index.tsx b/apps/wr/app/store/ruseFcltMng/fcltReg/htdflReg/index.tsx index f2875e232..4e764d7ae 100644 --- a/apps/wr/app/store/ruseFcltMng/fcltReg/htdflReg/index.tsx +++ b/apps/wr/app/store/ruseFcltMng/fcltReg/htdflReg/index.tsx @@ -76,8 +76,7 @@ export const hdtflZod = z.object({ .date() .default(() => new Date()) .nullish(), - archArea: z.number().default(0), - archChgArea: z.number().nullish().default(0), + archArea: z.string().default(''), rgtr: z.string().nullish().default(null), mdfr: z.string().nullish().default(null), reuseBuilding: z @@ -99,7 +98,8 @@ export type htdflFacState = { }; }; type htdflFacSActions = { - initHtdfl: (code: string) => void; + initHtdflReg: (code: string) => void; + htdflNmSearch: (code: string, fcltNm:string) => void; htdflBdstUpdate: (data: customHtdflBdst, code: string | number) => void; htdflBdstReg: (data: customHtdflBdst, code: string | number) => void; getHtdflBdstData: (pageNation: number, code: string | number) => void; @@ -142,9 +142,11 @@ export const htdflFacActionsExport: (set: any) => htdflFacSActions = ( console.log(e); } }, - initHtdfl: async (code: string) => { + initHtdflReg: async (code: string) => { try { const htdflRes = await initFetch(code); + + console.log('htdflRes ', htdflRes) set((state: htdflFacState) => { return { ...state, @@ -162,10 +164,8 @@ export const htdflFacActionsExport: (set: any) => htdflFacSActions = ( htdflBdstUpdate: async (data: customHtdflBdst, code: string | number) => { try { - console.log('data ', data) const chData = await makeMdfr(data); - console.log('chData ', chData) const res = await putFectch("/drain/facility", chData); if (res.ok) { @@ -208,6 +208,22 @@ export const htdflFacActionsExport: (set: any) => htdflFacSActions = ( toast.error("온배수 건축물 등록 오류입니다.."); } - } + },htdflNmSearch :async(code:string | number, fcltNm:string) =>{ + try { + const htdflRes = await getFetch("wr", `/drain/facilityList/1/20/${code}/${fcltNm}`); + set((state: htdflFacState) => { + return { + ...state, + htdfl: { + ...state.htdfl, + htdflRes: htdflRes, + isInit: false, + }, + }; + }); + } catch (error) { + console.log(error); + } + }, }; }; diff --git a/apps/wr/app/store/ruseFcltMng/fcltReg/rainReg/index.tsx b/apps/wr/app/store/ruseFcltMng/fcltReg/rainReg/index.tsx index d4cfebfca..6ceb75531 100644 --- a/apps/wr/app/store/ruseFcltMng/fcltReg/rainReg/index.tsx +++ b/apps/wr/app/store/ruseFcltMng/fcltReg/rainReg/index.tsx @@ -34,7 +34,7 @@ export const rainZod = z.object({ bdstNm: z.string().default(""), frstArchPrmsnDt: z.date().default(() => new Date()), archChgPrmsnDt: z.coerce.date().nullish().default(null), - archArea: z.number().default(0), + archArea: z.string().default(''), archChgArea: z.number().default(0), rgtr:z.string().nullish(), mdfr:z.string().nullish(), diff --git a/apps/wr/app/store/ruseFcltMng/fcltReg/rainReg/type.tsx b/apps/wr/app/store/ruseFcltMng/fcltReg/rainReg/type.tsx index 2d17b4601..32fea3ebb 100644 --- a/apps/wr/app/store/ruseFcltMng/fcltReg/rainReg/type.tsx +++ b/apps/wr/app/store/ruseFcltMng/fcltReg/rainReg/type.tsx @@ -25,9 +25,9 @@ export const baseReuseBuildingSchema = z.object({ { message: validationMessages.yearFormat, } - ), - grntchBdstCertNo: z.string().default(""), - grntchBdstCertGrd: z.string().default(""), + ).nullish(), + grntchBdstCertNo: z.string().default("").nullish(), + grntchBdstCertGrd: z.string().default("").nullish(), crgRdctYn: z.string().default("N").nullish(), /// 안쓰는 값 crgRdctBss: z.string().default("").nullish(), /// 안쓰는 값 crgRdctAmt: z.number().default(0), // 안쓰는 값 diff --git a/apps/wr/app/store/ruseFcltMng/fcltReg/rswtReg/index.tsx b/apps/wr/app/store/ruseFcltMng/fcltReg/rswtReg/index.tsx index 178badd3b..165a65021 100644 --- a/apps/wr/app/store/ruseFcltMng/fcltReg/rswtReg/index.tsx +++ b/apps/wr/app/store/ruseFcltMng/fcltReg/rswtReg/index.tsx @@ -5,11 +5,12 @@ import type { TableUpperProps } from "@/app/store/common/type/header"; import { z } from "zod"; import { baseReuseBuildingSchema, chgBuildingSchema, validationMessages } from "../rainReg/type"; import { makeMdfr, makeRstr } from "../bdstReg"; +import toast from "react-hot-toast"; const rswtrObjSchema = z.object({ bdstRuseFcltFk: z.number().nullish(), - wtrUsqty: z.number({ required_error: validationMessages.required("물사용량(m³)") }).positive({ message: validationMessages.required("물사용량(m³)") }), - oblgTrgtWtrUsqty: z.number().nullish().default(0), + // wtrUsqty: z.number({ required_error: validationMessages.required("물사용량(m³)") }).positive({ message: validationMessages.required("물사용량(m³)") }), + //oblgTrgtWtrUsqty: z.number().nullish().default(0), orgwtKndCd: z.string({ required_error: validationMessages.requiredSelect("원수 종류") }).min(1, { message: validationMessages.requiredSelect("원수 종류") }), filtrTrtmfctCpct: z.number({ required_error: validationMessages.required("처리시설 용량") }).positive({ message: validationMessages.required("처리시설 용량") }), filtrTrtmfctProcs: z.string({ required_error: validationMessages.required("주요처리공정") }).min(1, { message: validationMessages.required("주요처리공정") }), @@ -36,8 +37,7 @@ export const rswtZod = z.object({ frstArchPrmsnDt: z.date().default(() => new Date()), archChgPrmsnDt: z.coerce.date().nullish().default(null), chBuildingYn: z.string().nullish(), - archArea: z.number().default(0), - archChgArea: z.number().default(0), + archArea: z.string().default(''), rgtr: z.string().nullish().default(null), mdfr: z.string().nullish().default(null), bdstUsgText: z.string().default(""), @@ -152,8 +152,8 @@ export const rswtFacActionsExport: (set: any) => rswtFacSActions = ( try { const chData = await makeMdfr(data); const res = await putFectch("/reuseFc/waterreuseFc", chData); - if (res.ok) { - alert("등록이 완료되었습니다."); + if (res.status === 200) { + toast.success("중수도 시설 수정이 완료되었습니다."); const rswtRes = await initFetch(code); set((state: rswtFacState) => ({ ...state, @@ -165,10 +165,8 @@ export const rswtFacActionsExport: (set: any) => rswtFacSActions = ( })); } } catch (errorUpdate) { - alert( - "업데이트 오류입니다. " + - (errorUpdate instanceof Error ? errorUpdate.message : "") - ); + toast.error("중수도 시설 수정 오류입니다."); + } finally { } }, @@ -193,11 +191,11 @@ export const rswtFacActionsExport: (set: any) => rswtFacSActions = ( }) .then((chch: any) => { if (chch.ok) { - alert("등록이 완료되었습니다."); + toast.success("중수도 시설이 등록이 되었습니다."); } }); } else { - alert("등록이 완료되었습니다."); + toast.success("중수도 시설이 등록이 되었습니다."); } } const rainRes = await initFetch(code); @@ -211,7 +209,7 @@ export const rswtFacActionsExport: (set: any) => rswtFacSActions = ( })); } catch (e) { console.log("e", e); - alert("등록 오류입니다."); + toast.success("중수도 시설이 등록 오류입니다."); } finally { } }, diff --git a/apps/wr/components/ui/form/fcltReg/bdstReg/bdstReg.tsx b/apps/wr/components/ui/form/fcltReg/bdstReg/bdstReg.tsx index 8ae9ad4dd..04b36cbc5 100644 --- a/apps/wr/components/ui/form/fcltReg/bdstReg/bdstReg.tsx +++ b/apps/wr/components/ui/form/fcltReg/bdstReg/bdstReg.tsx @@ -89,7 +89,7 @@ export default function BdstReg({ form, type }: { form: any; type?: string }) { return ( <> - <h1 className={formH1Class}>건축물 기본 정보</h1> + <h1 className={formH1Class}> { type !== "HTB"?"건축물 기본 정보": '발전소 기본 정보'}</h1> <div className="grid grid-cols-6 gap-2 mt-4"> <div className="col-span-3"> <FormField @@ -140,10 +140,10 @@ export default function BdstReg({ form, type }: { form: any; type?: string }) { control={form.control} name="bdstNm" render={({ field }) => ( - <FormUi label="건축물명"> + <FormUi label={ type !== "HTB"?"건축물명": '발전소명'}> <Input className={selectClass} - placeholder="건축물 명 입력하세요" + placeholder= {type !== "HTB"?"건축물 명 입력하세요":'발전소명 입력하세요' } type="text" {...field} /> @@ -156,7 +156,7 @@ export default function BdstReg({ form, type }: { form: any; type?: string }) { control={form.control} name="daddr" render={({ field }) => ( - <FormUi label="건축물 주소"> + <FormUi label= { type !== "HTB"?"건축물 주소": '발전소 주소'} > <Input className={selectClass} placeholder="위치를 입력해주세요" @@ -173,7 +173,7 @@ export default function BdstReg({ form, type }: { form: any; type?: string }) { control={form.control} name="frstArchPrmsnDt" render={({ field }) => ( - <FormUi label="최초건축허가일"> + <FormUi label= { type !== "HTB"?"최초건축허가일": '준공년도'} > <DatePicker mode={"single"} field={{ diff --git a/apps/wr/components/ui/form/fcltReg/rainReg/rainRegForm.tsx b/apps/wr/components/ui/form/fcltReg/rainReg/rainRegForm.tsx index 7333f6091..0bf7cf786 100644 --- a/apps/wr/components/ui/form/fcltReg/rainReg/rainRegForm.tsx +++ b/apps/wr/components/ui/form/fcltReg/rainReg/rainRegForm.tsx @@ -54,6 +54,7 @@ export default function RainRegForm({ raw?.reuseBuilding && raw.reuseBuilding.length > 0 ? raw.reuseBuilding : [{}]; + return { ...raw, frstArchPrmsnDt: raw?.frstArchPrmsnDt diff --git a/apps/wr/components/ui/form/fcltReg/rswtrReg/rswtrRegForm.tsx b/apps/wr/components/ui/form/fcltReg/rswtrReg/rswtrRegForm.tsx index ed0e51af6..587cde3fb 100644 --- a/apps/wr/components/ui/form/fcltReg/rswtrReg/rswtrRegForm.tsx +++ b/apps/wr/components/ui/form/fcltReg/rswtrReg/rswtrRegForm.tsx @@ -59,10 +59,16 @@ export default function RswtrRegForm({ rswtBdstReg(values, "41"); if (setIsModal) setIsModal(false); } else { + + rswtBdstUpdate(values, "41"); } }; - const cleanRswtData = (raw: any): z.infer<typeof rswtZod> => { + + const filtrTrtmfctCpct = form.watch('reuseBuilding.0.filtrTrtmfctCpct') + const cleanRswtData = (raw: any): z.infer<typeof rswtZod> => { + + console.log('raw ', raw) return { ...raw, frstArchPrmsnDt: raw?.frstArchPrmsnDt @@ -82,11 +88,17 @@ export default function RswtrRegForm({ rswtrObj: { ...item?.rswtrObj, filtrTrtmfctYn: item?.rainyObj?.filtrTrtmfctYn ?? "Y", + filtrTrtmfctCpct:item.rswtrObj.filtrTrtmfctCpct || 0, + filtrTrtmfctProcs:item.rswtrObj.filtrTrtmfctProcs||code.rswtPrscCdMthdCd[0]?.val, + orgwtKndCd:item?.rswtrObj.orgwtKndCd || code.orgwtKndCd[0]?.val , }, })), }; }; + useEffect(() =>{ + console.log(' formform ', form.watch()) + },[form.formState]) useEffect(() => { console.log("form errors:", form.formState.errors); }, [form.formState.errors]); @@ -253,7 +265,7 @@ export default function RswtrRegForm({ selectClass={selectClass} selectArray={code.orgwtKndCd} onSelectValue={readonly ? () => {} : field.onChange} - value={field.value ?? code.orgwtKndCd[0]?.val} + value={field.value || code.orgwtKndCd[0]?.val} readOnly={readonly} /> </FormUi> @@ -272,7 +284,7 @@ export default function RswtrRegForm({ label={""} selectArray={code.rswtPrscCdMthdCd} onSelectValue={readonly ? () => {} : field.onChange} - value={field.value ?? code.rswtPrscCdMthdCd[0]?.val} + value={field.value || code.rswtPrscCdMthdCd[0]?.val} readOnly={readonly} /> </FormUi> @@ -433,9 +445,6 @@ export default function RswtrRegForm({ } }} disabled={grntchBdstCertYn !== "Y"} - - - /> </FormUi> )}} From e579684b28a93d5451ccc3282e0ed0de91686ba4 Mon Sep 17 00:00:00 2001 From: yschoi <yschoi> Date: Fri, 4 Jul 2025 14:00:20 +0900 Subject: [PATCH 066/108] =?UTF-8?q?=EC=9E=AC=EC=9D=B4=EC=9A=A9=EC=8B=9C?= =?UTF-8?q?=EC=84=A4=20=EC=A7=80=EB=8F=84=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 +- apps/wr/.env.development | 12 ++++++++++++ apps/wr/.env.local | 14 ++++++++++++++ apps/wr/.gitignore | 2 +- apps/wr/app/map/service/map.ts | 4 ++-- packages/common/business_components/package.json | 1 + .../common/business_components/src/ui/map/Gis.js | 2 ++ 7 files changed, 33 insertions(+), 4 deletions(-) create mode 100644 apps/wr/.env.development create mode 100644 apps/wr/.env.local diff --git a/.gitignore b/.gitignore index d0bb49da9..6b685ea40 100644 --- a/.gitignore +++ b/.gitignore @@ -18,5 +18,5 @@ apps/wr/.next/**/* apps/fo/.next/**/* # env -.env* +#.env* .qodo diff --git a/apps/wr/.env.development b/apps/wr/.env.development new file mode 100644 index 000000000..dccfb1453 --- /dev/null +++ b/apps/wr/.env.development @@ -0,0 +1,12 @@ +NEXT_PUBLIC_SITE_ID = WR +NEXT_PUBLIC_PORT = 11301 +NEXT_PUBLIC_PROXY_HOST = http://con.mind-one.co.kr:11301 +NEXT_PUBLIC_SERVER_API_URL = http://dev.onasys.co.kr:8000 +NEXT_PUBLIC_GEOSERVER_URL = http://rsone.iptime.org:10010/geoserver +NEXT_PUBLIC_TEMP_GEOSERVER_URL = http://con.mind-one.co.kr:11306/geoserver #TODO 운영에서 바꿔야 함 +NEXT_PUBLIC_CLAIM_NAME = Authorization +NEXT_PUBLIC_AUTH_USER_ID = token-id +NEXT_PUBLIC_ACCESS_TOKEN = access-token +NEXT_PUBLIC_REFRESH_TOKEN = refresh-token + +NEXT_PUBLIC_SEWERAGE_API_URL=http://con.mind-one.co.kr:11083/sewerage-service/api # 개발 서버 API diff --git a/apps/wr/.env.local b/apps/wr/.env.local new file mode 100644 index 000000000..858181f32 --- /dev/null +++ b/apps/wr/.env.local @@ -0,0 +1,14 @@ +NEXT_PUBLIC_SITE_ID = WR +NEXT_PUBLIC_PORT = 3001 +NEXT_PUBLIC_PROXY_HOST = http://localhost:3001 +NEXT_PUBLIC_SERVER_API_URL = http://dev.onasys.co.kr:8000 +NEXT_PUBLIC_GEOSERVER_URL = http://rsone.iptime.org:10010/geoserver +NEXT_PUBLIC_TEMP_GEOSERVER_URL = http://con.mind-one.co.kr:11306/geoserver #TODO 운영에서 바꿔야 함 +NEXT_PUBLIC_CLAIM_NAME = Authorization +NEXT_PUBLIC_AUTH_USER_ID = token-id +NEXT_PUBLIC_ACCESS_TOKEN = access-token +NEXT_PUBLIC_REFRESH_TOKEN = refresh-token + +NEXT_PUBLIC_SEWERAGE_API_URL=http://con.mind-one.co.kr:11083/sewerage-service/api # 개발 서버 API +#NEXT_PUBLIC_SEWERAGE_API_URL=http://192.168.1.128:8883/sewerage-service/api # 최용성 +#NEXT_PUBLIC_SEWERAGE_API_URL=http://192.168.1.66:8883/sewerage-service/api # 박이정 diff --git a/apps/wr/.gitignore b/apps/wr/.gitignore index 5ef6a5207..8c5b2ee3a 100644 --- a/apps/wr/.gitignore +++ b/apps/wr/.gitignore @@ -31,7 +31,7 @@ yarn-error.log* .pnpm-debug.log* # env files (can opt-in for committing if needed) -.env* +#.env* # vercel .vercel diff --git a/apps/wr/app/map/service/map.ts b/apps/wr/app/map/service/map.ts index bf0c8a694..5002afef7 100644 --- a/apps/wr/app/map/service/map.ts +++ b/apps/wr/app/map/service/map.ts @@ -2,8 +2,8 @@ /** * 관망도 API */ -// const API_URL = `${process.env.NEXT_PUBLIC_SEWERAGE_API_URL}/v1/map` -const API_URL = "http://con.mind-one.co.kr:11083/sewerage-service/api/v1/map"; + const API_URL = `${process.env.NEXT_PUBLIC_SEWERAGE_API_URL}/v1/map` +//const API_URL = "http://con.mind-one.co.kr:11083/sewerage-service/api/v1/map"; export type LyrStngProps = { lyr_sn: string diff --git a/packages/common/business_components/package.json b/packages/common/business_components/package.json index da6f8c344..2c0b6ea47 100644 --- a/packages/common/business_components/package.json +++ b/packages/common/business_components/package.json @@ -44,6 +44,7 @@ "recharts": "^2.15.1", "sonner": "^2.0.1", "tailwind-merge": "^3.0.1", + "tailwindcss-animate": "^1.0.7", "xlsx": "^0.18.5", "xlsx-populate": "^1.21.0", "zod": "^3.24.2" diff --git a/packages/common/business_components/src/ui/map/Gis.js b/packages/common/business_components/src/ui/map/Gis.js index 6d46e77aa..6e13d70c5 100644 --- a/packages/common/business_components/src/ui/map/Gis.js +++ b/packages/common/business_components/src/ui/map/Gis.js @@ -209,11 +209,13 @@ function Gis({ isOpen, gisMaps, isSearchModalOpen, treeData = [], isSearch = fal onClose={closePopup} // 닫기 콜백 전달 /> )} + {SearchView &&( <SearchView open={isSearchModalOpen} onOpenChange={setIsSearchModalOpen} onFeatureSelect={onFeatureSelect} /> + )} <GisLonLat /> <GisToolBar /> <GisMapToggle /> From ea0c85b0c3d7fcf99ddca71da3fdb60910eaefe5 Mon Sep 17 00:00:00 2001 From: jyha00 <jyha@mind-one.co.kr> Date: Fri, 4 Jul 2025 14:30:02 +0900 Subject: [PATCH 067/108] =?UTF-8?q?=EC=A7=80=EB=8F=84=EC=A0=90=EA=B2=80?= =?UTF-8?q?=EA=B2=B0=EA=B3=BC=20=ED=98=84=ED=99=A9=20=EA=B2=80=EC=83=89?= =?UTF-8?q?=EC=A1=B0=EA=B1=B4=20=EC=8B=9C=EC=84=A4=EB=AA=85=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ruseFcltMng/prfmncReg/chkgdnReg/page.tsx | 7 ++-- .../prfmncSttn/chkgdnSttn/page.tsx | 41 +++++++++++++++++-- .../prfmncReg/chkgdnReg/rswrtChkgdn/index.tsx | 3 +- .../chkgdnSttn/rainChkgdnSttn/index.tsx | 28 ++++++++++--- .../chkgdnSttn/rswtChkgdnSttn/index.tsx | 22 ++++++++-- 5 files changed, 83 insertions(+), 18 deletions(-) diff --git a/apps/wr/app/ruseFcltMng/prfmncReg/chkgdnReg/page.tsx b/apps/wr/app/ruseFcltMng/prfmncReg/chkgdnReg/page.tsx index 328bb8b88..c5f7b198a 100644 --- a/apps/wr/app/ruseFcltMng/prfmncReg/chkgdnReg/page.tsx +++ b/apps/wr/app/ruseFcltMng/prfmncReg/chkgdnReg/page.tsx @@ -83,7 +83,7 @@ export default function Page() { String(new Date().getFullYear()) ); const cellClick = () => { - if (searchYear) + if (searchYear) { if (selFcltDiv === "RNB") { if (fcltNm) { rainChkNmSearch( @@ -99,8 +99,8 @@ export default function Page() { } else { if (fcltNm) { rswtChkNmSearch( - rainPageNation, - rainPageSize, + rswtPageNation, + rswtPageSize, searchYear, commonCd, fcltNm @@ -109,6 +109,7 @@ export default function Page() { rswtChkgdnInit(rswtPageNation, rswtPageSize, searchYear, commonCd); } } + } }; const rainPageChange = (pageNo: number) => { if (pageNo > 0) { diff --git a/apps/wr/app/ruseFcltMng/prfmncSttn/chkgdnSttn/page.tsx b/apps/wr/app/ruseFcltMng/prfmncSttn/chkgdnSttn/page.tsx index 60040833b..592a6826e 100644 --- a/apps/wr/app/ruseFcltMng/prfmncSttn/chkgdnSttn/page.tsx +++ b/apps/wr/app/ruseFcltMng/prfmncSttn/chkgdnSttn/page.tsx @@ -15,6 +15,7 @@ import { SearchDiv, SearchFormLeft, SearchFormRight, + SearchInput, SelectBox, Title, UiModal @@ -30,9 +31,9 @@ export default function Page() { rainChkSttn, rswtChkSttn, rainChkSttnInit, - rainChkSttnSearch, - rswtChkSttnSearch, + rainChkSttnNmSearch, rswtChkSttnInit, + rswtChkNmSttnSearch, isRainSttn, isRswtSttn, rswtChkPolInit, @@ -43,6 +44,7 @@ export default function Page() { const [selFcltDiv, setSelFcltDiv] = useState("RNB"); const [rainPageNation, setRainPageNation] = useState(1); const [rswtPageNation, setRswtPageNation] = useState(1); + const [fcltNm, setFcltNm] = useState<string>(""); const [isModal, setIsModal] = useState({ open: false, data: {}, @@ -51,9 +53,29 @@ export default function Page() { const cellClick = () => { if (searchYear) { if (selFcltDiv === "RNB") { - rainChkSttnSearch(rainPageNation, rainPageSize, searchYear, commonCd); + if (fcltNm) { + rainChkSttnNmSearch( + rainPageNation, + rainPageSize, + searchYear, + commonCd, + fcltNm + ); + } else { + rainChkSttnInit(rainPageNation, rainPageSize, searchYear, commonCd); + } } else { - rswtChkSttnSearch(rswtPageNation, rswtPageSize, searchYear, commonCd); + if (fcltNm) { + rswtChkNmSttnSearch( + rswtPageNation, + rswtPageSize, + searchYear, + commonCd, + fcltNm + ); + } else { + rswtChkSttnInit(rswtPageNation, rswtPageSize, searchYear, commonCd); + } } } }; @@ -151,6 +173,17 @@ export default function Page() { value={selFcltDiv ?? rainChkSttn.fcltDiv[0]?.val} /> </div> + <div className="col-span-2 flex flex-row space-x-2"> + <Label className="w-28 mt-2 text-xl text-gray-600"> + {"시설명"} + </Label> + <SearchInput + className={"w-full text-2xl"} + label={""} + textValue={fcltNm || ""} + setText={setFcltNm} + /> + </div> </SearchFormLeft> <SearchFormRight> <div className="space-y"> diff --git a/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/index.tsx b/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/index.tsx index 6e0a805b6..235b4f3e2 100644 --- a/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/index.tsx +++ b/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/index.tsx @@ -217,12 +217,11 @@ export const rswtChkgdnActionsExport: (set: any) => rswtChkgdnActions = ( "wr", `/reuseChk/reuseWater` + (pageNo ? `/${pageNo}` : "/1") + - (cnt ? `/${cnt}` : "/20") + + (cnt ? `/${cnt}` : "/5") + (year ? `/${year}` : `/${currentYear}`) + (sggCd ? `/${sggCd}` : "/0") + (txt ? `/${txt}` : "") ); - return set((state: rswtChkgdnState) => ({ ...state, rswtChkgdn: { diff --git a/apps/wr/app/store/prfmncSttn/chkgdnSttn/rainChkgdnSttn/index.tsx b/apps/wr/app/store/prfmncSttn/chkgdnSttn/rainChkgdnSttn/index.tsx index 895f096ef..43c3969e2 100644 --- a/apps/wr/app/store/prfmncSttn/chkgdnSttn/rainChkgdnSttn/index.tsx +++ b/apps/wr/app/store/prfmncSttn/chkgdnSttn/rainChkgdnSttn/index.tsx @@ -36,9 +36,9 @@ export type rainChkSttnState = { type rainChkSttnActions = { rainChkSttnInit: (pageNo: number, cnt: number, searchTxt: string, sggCd: string) => void; - rainChkSttnSearch: (pageNo: number, cnt: number, searchTxt: string, sggCd: string) => void; isRainSttn: (isSttn: boolean) => void; getRainSttnExcel: (excelList: rainChkZodType[]) => void; + rainChkSttnNmSearch: (pageNo: number, cnt: number, year: string, sggCd: string, txt: string) => void }; export type rainChkSttnType = rainChkSttnState & rainChkSttnActions; @@ -91,21 +91,37 @@ export const rainChkSttnActionsExport: (set: any) => rainChkSttnActions = ( console.log(error); } }, - rainChkSttnSearch: async (pageNo: number, cnt: number, searchTxt: string, sggCd: string) => { + rainChkSttnNmSearch: async ( + pageNo: number, + cnt: number, + year: string, + sggCd: string, + txt: string + ) => { try { - const rainChkRes = await initFetch(pageNo, cnt, searchTxt, sggCd); - + const currentYear = new Date().getFullYear(); + const rainChkRes = await getCountFetch( + "wr", + `/reuseChk/rainy` + + (pageNo ? `/${pageNo}` : "/1") + + (cnt ? `/${cnt}` : "/5") + + (year ? `/${year}` : `/${currentYear}`) + + (sggCd ? `/${sggCd}` : "/0") + + (txt ? `/${txt}` : "") + ); return set((state: rainChkSttnState) => ({ ...state, rainChkSttn: { ...state.rainChkSttn, - rainChkRes: rainChkRes + rainChkRes: rainChkRes.data, + rainChkCount: rainChkRes.count, }, })); } catch (error) { console.log(error); } - }, isRainSttn: (isSttn: boolean) => { + }, + isRainSttn: (isSttn: boolean) => { try { return set((state: rainChkSttnState) => ({ ...state, diff --git a/apps/wr/app/store/prfmncSttn/chkgdnSttn/rswtChkgdnSttn/index.tsx b/apps/wr/app/store/prfmncSttn/chkgdnSttn/rswtChkgdnSttn/index.tsx index 519cd39b2..f1d7a7d19 100644 --- a/apps/wr/app/store/prfmncSttn/chkgdnSttn/rswtChkgdnSttn/index.tsx +++ b/apps/wr/app/store/prfmncSttn/chkgdnSttn/rswtChkgdnSttn/index.tsx @@ -28,7 +28,7 @@ type rswtChkSttnActions = { rswtChkSttnInit: (pageNo: number, cnt: number, searchTxt: string, sggCd: string) => void; isRswtSttn: (isSttn: boolean) => void; getRswtSttnExcel: (excelList: rswtChkZodType[]) => void; - rswtChkSttnSearch: (pageNo: number, cnt: number, searchTxt: string, sggCd: string) => void; + rswtChkNmSttnSearch: (pageNo: number, cnt: number, year: string, sggCd: string, txt: string) => void; }; export type rswtChkSttnType = rswtChkSttnState & rswtChkSttnActions; @@ -109,9 +109,24 @@ export const rswtChkSttnActionsExport: (set: any) => rswtChkSttnActions = ( console.log(error); } }, - rswtChkSttnSearch: async (pageNo: number, cnt: number, searchTxt: string, sggCd: string) => { + rswtChkNmSttnSearch: async ( + pageNo: number, + cnt: number, + year: string, + sggCd: string, + txt: string + ) => { try { - const rswtChkRes = await initFetch(pageNo, cnt, searchTxt, sggCd); + const currentYear = new Date().getFullYear().toString(); + const rswtChkRes = await getCountFetch( + "wr", + `/reuseChk/reuseWater` + + (pageNo ? `/${pageNo}` : "/1") + + (cnt ? `/${cnt}` : "/20") + + (year ? `/${year}` : `/${currentYear}`) + + (sggCd ? `/${sggCd}` : "/0") + + (txt ? `/${txt}` : "") + ); return set((state: rswtChkSttnState) => ({ ...state, @@ -119,6 +134,7 @@ export const rswtChkSttnActionsExport: (set: any) => rswtChkSttnActions = ( ...state.rswtChkSttn, rswtChkRes: rswtChkRes.data, rswtChkCount: rswtChkRes.count, + isInit: false, }, })); } catch (error) { From 7bbab96c4be6404ef868c85c60b7b94ccd5cb506 Mon Sep 17 00:00:00 2001 From: YangJinSoo94 <jschrome782@gmail.com> Date: Fri, 4 Jul 2025 15:12:51 +0900 Subject: [PATCH 068/108] =?UTF-8?q?=EB=AC=BC=EC=9E=AC=EC=9D=B4=EC=9A=A9?= =?UTF-8?q?=EC=82=AC=EB=A1=80=EA=B4=80=EB=A6=AC=20=EC=A1=B0=ED=9A=8C?= =?UTF-8?q?=EA=B8=B0=EB=8A=A5(=ED=8E=98=EC=9D=B4=EC=A7=80=EB=84=A4?= =?UTF-8?q?=EC=9D=B4=EC=85=98=20=EC=B6=94=EA=B0=80)=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wr/app/matMng/coMat/ruseCaseMng/page.tsx | 16 ++++++++----- .../wr/app/store/matDmd/ruseCaseMng/index.tsx | 23 +++++++++++-------- 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/apps/wr/app/matMng/coMat/ruseCaseMng/page.tsx b/apps/wr/app/matMng/coMat/ruseCaseMng/page.tsx index 517dab0c0..a81a85e19 100644 --- a/apps/wr/app/matMng/coMat/ruseCaseMng/page.tsx +++ b/apps/wr/app/matMng/coMat/ruseCaseMng/page.tsx @@ -26,7 +26,7 @@ import { toast, Toaster } from "react-hot-toast"; export default function RuseCaseMng() { const { code, ruseCaseMng, selectRuseCaseMng } = useWrStore((state) => state); - const { ruseCaseMngList, status } = ruseCaseMng; + const { ruseCaseMngList, status, count } = ruseCaseMng; const [selectSgg, setSelectSgg] = useState<SelectDataType[] | []>([]); const [objSearch, setObjSearch] = useState<objSearchType>({ sgg: "", city: "" }); @@ -36,8 +36,10 @@ export default function RuseCaseMng() { const [name, setName] = useState<string>(""); // 물재이용사례 조회 - const searchRuseCaseMng = async () => { + const searchRuseCaseMng = async (pageNo: number, pageSize: number) => { const res = await selectRuseCaseMng( + pageNo, + pageSize, sidoCd ?? "", sigunguCd ?? "", selectUtz[0] ?? "", @@ -70,7 +72,7 @@ export default function RuseCaseMng() { // 다운로드 const download = () => { - searchRuseCaseMng(); + searchRuseCaseMng(1, 10000);; fetch(`/sample_ruseCaseMng.xlsx`, {}).then(async (response) => { if (!response.ok) { @@ -210,7 +212,7 @@ export default function RuseCaseMng() { <SearchFormRight className="w-2/12 mx-5 flex flex-col items-center justify-center space-x-2"> <div className="space-y"> <div className="flex flex-row space-x-2"> - <Button size={"sm"} onClick={() => { searchRuseCaseMng() }}> + <Button size={"sm"} onClick={() => { searchRuseCaseMng(1, 20) }}> 조회 </Button> <Button size={"sm"} onClick={() => { setIsExcelOpen({ isModal: true }); }}> @@ -238,9 +240,11 @@ export default function RuseCaseMng() { children={undefined} headlist={ruseCaseMng.ruseHeadList} pageSize={20} - total={100000} + total={count} cellClick={(index: number | undefined, headId: string) => {}} - onPageChange={() => {}} + onPageChange={(pageNo: number) => { + searchRuseCaseMng(pageNo, 10); + }} /> </div> </ListDiv> diff --git a/apps/wr/app/store/matDmd/ruseCaseMng/index.tsx b/apps/wr/app/store/matDmd/ruseCaseMng/index.tsx index 76468d249..5a2ad235e 100644 --- a/apps/wr/app/store/matDmd/ruseCaseMng/index.tsx +++ b/apps/wr/app/store/matDmd/ruseCaseMng/index.tsx @@ -1,4 +1,4 @@ -import { fetchType, getFetch, postFectch } from "@/components/lib/api"; +import { fetchType, getFetch, getFetchWithCount, postFectch } from "@/components/lib/api"; import type { TableUpperProps } from "../../common/type/header"; import { ruseCaseHeadListExport } from "./headList"; import { toast } from "react-hot-toast"; @@ -7,6 +7,7 @@ export type ruseCaseState = { ruseHeadList:TableUpperProps[]; ruseCaseMngList:ruseCaseMngExcelProps[]; status: number; + count: number; }; }; @@ -64,14 +65,14 @@ export interface ruseCaseMngExcelProps { } type ruseCaseActions = { - selectRuseCaseMng: (sidoCd: string, sigunguCd: string, usageCd : string, name: string) => Promise<void>; + selectRuseCaseMng: (pageNo: number, pageSize: number, sidoCd: string, sigunguCd: string, usageCd : string, name: string) => Promise<void>; insertRuseCaseMng: (data: any) => Promise<void>; }; -export const fetchSearchRuseCaseMng = async (sidoCd: string, sigunguCd: string, usageCd : string, name: string) => { - const tailUrl = ((sigunguCd !== '전체' && sigunguCd !=='') ? `/${sigunguCd}` : (sidoCd != '전체' && sidoCd != '') ? `/${sidoCd}` : `/0`) + (usageCd? `/${usageCd}` : '/0') + (name? `/${name}` : ''); - const resp = await getFetch(fetchType.WR,`/reuseFc/pubFcRuseCase${tailUrl}`); - return resp; +export const fetchSearchRuseCaseMng = async (pageNo: number, pageSize: number, sidoCd: string, sigunguCd: string, usageCd : string, name: string) => { + const tailUrl = '/'+pageNo+'/'+pageSize+ ((sigunguCd !== '전체' && sigunguCd !=='') ? `/${sigunguCd}` : (sidoCd != '전체' && sidoCd != '') ? `/${sidoCd}` : `/0`) + (usageCd? `/${usageCd}` : '/0') + (name? `/${name}` : ''); + const resp = await getFetchWithCount(fetchType.WR,`/reuseFc/pubFcRuseCase${tailUrl}`); + return {data:resp.data, count:resp.count}; }; export const fetchInsertRuseCaseMng = async (data: any) => { @@ -86,6 +87,7 @@ export const ruseCaseStateExport: ruseCaseState = { ruseHeadList:ruseCaseHeadListExport, ruseCaseMngList:[], status: 0, + count: 0, }, }; @@ -93,12 +95,13 @@ export const ruseCaseActionsExport: (set: any) => ruseCaseActions = ( set: any ) => { return { - selectRuseCaseMng: async (sidoCd: string, sigunguCd: string, usageCd : string, name: string) => { - const res = await fetchSearchRuseCaseMng(sidoCd, sigunguCd, usageCd, name); + selectRuseCaseMng: async (pageNo: number, pageSize: number, sidoCd: string, sigunguCd: string, usageCd : string, name: string) => { + const res = await fetchSearchRuseCaseMng(pageNo, pageSize, sidoCd, sigunguCd, usageCd, name); set((state: ruseCaseState) => ({ ruseCaseMng: { ...state.ruseCaseMng, - ruseCaseMngList: res, + ruseCaseMngList: res.data, + count: res.count, }, })); }, @@ -118,4 +121,4 @@ export const ruseCaseActionsExport: (set: any) => ruseCaseActions = ( })); }, }; -}; +}; \ No newline at end of file From cec35e67241df033c783397ac1c7321bb255e22a Mon Sep 17 00:00:00 2001 From: truthwing <truthplumage@gmail.com> Date: Fri, 4 Jul 2025 15:40:13 +0900 Subject: [PATCH 069/108] jyha product --- .../ruseFcltMng/prfmncReg/chkgdnReg/page.tsx | 159 +++++++++++++---- .../prfmncSttn/chkgdnSttn/page.tsx | 27 ++- .../prfmncReg/chkgdnReg/rainChkgdn/index.tsx | 163 ++++++++++++++---- .../prfmncReg/chkgdnReg/rswrtChkgdn/index.tsx | 151 ++++++++++------ .../chkgdnSttn/rainChkgdnSttn/index.tsx | 40 +++-- .../ui/modal/chkgd/chkgdModal/chkgdModal.tsx | 27 +-- .../chkgdTable/rainChkgd/rainChkgdTable.tsx | 29 ++-- .../chkgdTable/rswtChkgd/rswtChkgdTable.tsx | 21 ++- 8 files changed, 439 insertions(+), 178 deletions(-) diff --git a/apps/wr/app/ruseFcltMng/prfmncReg/chkgdnReg/page.tsx b/apps/wr/app/ruseFcltMng/prfmncReg/chkgdnReg/page.tsx index d03ae70fe..0ac9145a5 100644 --- a/apps/wr/app/ruseFcltMng/prfmncReg/chkgdnReg/page.tsx +++ b/apps/wr/app/ruseFcltMng/prfmncReg/chkgdnReg/page.tsx @@ -1,10 +1,14 @@ "use client"; import { useWrStore } from "@/app/store"; -import { type rainChkZodType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn"; -import type { - rswtChkZodType, - rswtrChkDetailsZodType, +import { + commonCd, + rainPageSize, + type rainChkZodType, +} from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn"; +import { + rswtPageSize, + type rswtrChkDetailsZodType, } from "@/app/store/prfmncReg/chkgdnReg/rswrtChkgdn"; import { excelArrayDownload, @@ -15,6 +19,7 @@ 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"; import RswtChkgdTable from "@/components/ui/table/chkgdTable/rswtChkgd/rswtChkgdTable"; +import { SearchInput } from "@common/business_components"; import { ContentsDiv, SearchDiv, @@ -26,8 +31,14 @@ import { } from "@common/business_components/ui"; import ListDiv from "@common/business_components/ui/list_div"; import { Button, Label } from "@common/components/ui"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@common/components/ui/tooltip"; import { useCallback, useEffect, useState } from "react"; -import { Toaster } from "react-hot-toast"; +import toast, { Toaster } from "react-hot-toast"; export default function Page() { const { @@ -39,8 +50,10 @@ export default function Page() { rainChkDelete, rainChkExcelDownload, rainChkHeadChange, + rainChkNmSearch, isRainSttn, rswtChkgdnInit, + rswtChkNmSearch, rswtChkPolInit, rswtChkDetilsUpdate, rswtChkDelete, @@ -57,21 +70,36 @@ export default function Page() { rswtrChkDetailsZodType[] >([]); const [deleteIdx, setDeleteIdx] = useState<number[]>([]); - const [pageNation, setPageNation] = useState(1); + const [rainPageNation, setRainPageNation] = useState(1); + const [rswtPageNation, setRswtPageNation] = useState(1); + const [fcltNm, setFcltNm] = useState<string>(""); const [selFcltDiv, setSelFcltDiv] = useState("RNB"); const [isModal, setIsModal] = useState({ open: false, data: {}, }); + const [searchYear, setSearchYear] = useState<string>( + String(new Date().getFullYear()) + ); const cellClick = () => { - if (searchYear) { + if (searchYear) if (selFcltDiv === "RNB") { - rainChkInit("1", "20", searchYear, "0"); + if (fcltNm) { + rainChkNmSearch( + rainPageNation, + rainPageSize, + searchYear, + commonCd, + fcltNm + ); + } else { + rainChkInit(rainPageNation, rainPageSize, searchYear, commonCd); + } } else { if (fcltNm) { rswtChkNmSearch( - rswtPageNation, - rswtPageSize, + rainPageNation, + rainPageSize, searchYear, commonCd, fcltNm @@ -80,27 +108,46 @@ export default function Page() { rswtChkgdnInit(rswtPageNation, rswtPageSize, searchYear, commonCd); } } + }; + const rainPageChange = (pageNo: number) => { + if (pageNo > 0) { + setRainPageNation(pageNo); + rainChkInit(pageNo, rainPageSize, searchYear, commonCd); + } + }; + const rswtPageChange = (pageNo: number) => { + if (pageNo > 0) { + setRswtPageNation(pageNo); + rswtChkgdnInit(pageNo, rswtPageSize, searchYear, commonCd); } }; - const [searchYear, setSearchYear] = useState<string>( - String(new Date().getFullYear()) - ); - useEffect(() => { if (!rainChkgdn.isInit) { - rainChkInit("1", "20", new Date().getFullYear().toString(), "0"); + rainChkInit( + 1, + rainPageSize, + new Date().getFullYear().toString(), + commonCd + ); isRainSttn(false); setIsModal({ data: {}, open: false }); setIsRegExcel(false); } + }, [rainChkgdn.isInit, rainChkInit, rainPageSize]); + useEffect(() => { if (!rswtChkgdn.isInit) { - rswtChkgdnInit("1", "20", new Date().getFullYear().toString(), "0"); + rswtChkgdnInit( + 1, + rswtPageSize, + new Date().getFullYear().toString(), + commonCd + ); setIsModal({ data: {}, open: false }); setIsRegExcel(false); } - }, [rainChkgdn.isInit, rainChkInit]); + }, [rswtChkgdn.isInit, rswtChkgdnInit, rswtPageSize]); useEffect(() => { if (rswtChkgdn.excelList[0] && rswtChkgdn.excelList[0]?.data.length > 1) { @@ -144,8 +191,8 @@ export default function Page() { ); useEffect(() => { - //rainChkgdn.isSttn = false; - //rswtChkgdn.isSttn = false; + rainChkgdn.isSttn = false; + rswtChkgdn.isSttn = false; if (!rainChkgdn.isSttn && !rswtChkgdn.isSttn) { rainChkHeadChange({ rainChkgdn }, true); rswtChkHeadChange({ rswtChkgdn }, true); @@ -163,9 +210,17 @@ export default function Page() { }; const chkUpdateTable = (selFcltDiv: string) => { if (selFcltDiv === "RNB") { + if (updateRainDetail && updateRainDetail.length > 0) { rainChkDetilUpdate(updateRainDetail); } else { + toast.error("수정을 원하는 행을 선택하고 저장버튼을 눌러주세요 "); + } + } else { + if (updateRswtDetail && updateRswtDetail.length > 0) { rswtChkDetilsUpdate(updateRswtDetail); + } else { + toast.error("수정을 원하는 행을 선택하고 저장버튼을 눌러주세요 "); + } } }; @@ -187,10 +242,8 @@ export default function Page() { selectArray={yearOptions} value={searchYear} onSelectValue={(val: string | number) => { - setSearchYear(String(val)) - } - - } + setSearchYear(String(val)); + }} /> </div> <div className="col-span-2 flex flex-row space-x-2"> @@ -204,12 +257,22 @@ export default function Page() { selectArray={rainChkgdn.fcltDiv} onSelectValue={(val: string | number) => { if (val === "RST") rswtChkPolInit(); - setSelFcltDiv(String(val)) - } - } + setSelFcltDiv(String(val)); + }} value={selFcltDiv ?? rainChkgdn.fcltDiv[0]?.val} /> </div> + <div className="col-span-2 flex flex-row space-x-2"> + <Label className="w-28 mt-2 text-xl text-gray-600"> + {"시설명"} + </Label> + <SearchInput + className={"w-full h-full text-2xl"} + label={""} + textValue={fcltNm || ""} + setText={setFcltNm} + /> + </div> </SearchFormLeft> <SearchFormRight className="w-2/12 mx-5 flex flex-col items-center justify-center space-x-2 "> <div className="space-y"> @@ -230,6 +293,10 @@ export default function Page() { > 삭제 </Button> + + <TooltipProvider> + <Tooltip> + <TooltipTrigger asChild> <Button size={"sm"} onClick={() => { @@ -238,6 +305,16 @@ export default function Page() { > 저장 </Button> + </TooltipTrigger> + <TooltipContent side="bottom" sideOffset={5}> + <h2 className="text-xl"> + 아래 테이블에 선택을 체크하면 입력셀이 활성화 됩니다. + </h2> + </TooltipContent> + </Tooltip> + </TooltipProvider> + + <Button size={"sm"} onClick={() => { @@ -264,17 +341,18 @@ export default function Page() { detailUp={setUpdateRainDetail} handleClick={handleClick} rainChkData={rainChkgdn.rainChk} - pageNation={pageNation} - onPageChange={setPageNation} - /> + pageNation={rainPageNation} + onPageChange={rainPageChange} + pageCount={rainChkgdn.rainChkCount} /> ) : ( <RswtChkgdTable detaiRswtlUpdate={setUpdateRSwtDetail} deleteRswtData={setDeleteIdx} handleRswtClick={handleClick} rswtChkData={rswtChkgdn.rswtChkRes} - pageNation={pageNation} - onPageChange={setPageNation} + pageNation={rswtPageNation} + onPageChange={rswtPageChange} + pageCount={rswtChkgdn.rswtChkCount} /> )} </ListDiv> @@ -282,17 +360,28 @@ export default function Page() { </div> <UiModal open={isModal.open} - onOpenChange={() => { - setIsModal({ open: !open, data: {} }); + onOpenChange={(open: boolean) => { + setIsModal((prev) => ({ ...prev, open })); }} bodyClass={"w-[80vw] max-w-none"} - body={<ChkgdModal type={selFcltDiv} obj={isModal.data} />} + body={ + <ChkgdModal + type={selFcltDiv} + obj={isModal.data} + setIsModal={(open) => setIsModal((prev) => ({ ...prev, open }))} + /> + } ></UiModal> <UiModal open={isRegExcel} onOpenChange={setIsRegExcel} bodyClass={"w-[30vw] max-w-none"} - body={<ChkgdExcel type={selFcltDiv}></ChkgdExcel>} + body={ + <ChkgdExcel + type={selFcltDiv} + setIsRegExcel={setIsRegExcel} + ></ChkgdExcel> + } /> <Toaster position="top-center" /> </ContentsDiv> diff --git a/apps/wr/app/ruseFcltMng/prfmncSttn/chkgdnSttn/page.tsx b/apps/wr/app/ruseFcltMng/prfmncSttn/chkgdnSttn/page.tsx index a3dbd70b9..f8669bd2c 100644 --- a/apps/wr/app/ruseFcltMng/prfmncSttn/chkgdnSttn/page.tsx +++ b/apps/wr/app/ruseFcltMng/prfmncSttn/chkgdnSttn/page.tsx @@ -1,6 +1,8 @@ "use client"; import { useWrStore } from "@/app/store"; +import { commonCd, rainPageSize } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn"; +import { rswtPageSize } from "@/app/store/prfmncReg/chkgdnReg/rswrtChkgdn"; import { excelDownload, yearOptions @@ -77,23 +79,36 @@ export default function Page() { } } }; +const rainPageChange = (pageNo: number) => { + if (pageNo > 0) { + setRainPageNation(pageNo); + rainChkSttnInit(pageNo, rainPageSize, searchYear, commonCd); + } + }; + + const rswtPageChange = (pageNo: number) => { + if (pageNo > 0) { + setRswtPageNation(pageNo); + rswtChkSttnInit(pageNo, rswtPageSize, searchYear, commonCd); + } + }; useEffect(() => { if (!rainChkSttn.isInit) { isRainSttn(true) - rainChkSttnInit(new Date().getFullYear().toString(), "0"); + rainChkSttnInit(1, rainPageSize, new Date().getFullYear().toString(), commonCd); setIsModal({ data: {}, open: false }); } - }, [rainChkSttn.isInit, rainChkSttnInit]); + }, [rainChkSttn.isInit, rainChkSttnInit, rainPageSize]); useEffect(() => { - if (!rswtChkSttn.isInit) { setIsModal({ data: {}, open: false }); - rswtChkSttnInit(new Date().getFullYear().toString(), "0"); + rswtChkSttnInit(1, rswtPageSize, new Date().getFullYear().toString(), commonCd); isRswtSttn(true) } - }, [rswtChkSttn.isInit, rswtChkSttnInit]) + }, [rswtChkSttn.isInit, rswtChkSttnInit, rswtPageSize]) + const handleClick = useCallback( @@ -222,7 +237,7 @@ export default function Page() { setIsModal({ open: !open, data: {} }); }} bodyClass={"w-[80vw] max-w-none"} - body={<ChkgdModal type={selFcltDiv} obj={isModal.data} />} + body={<ChkgdModal type={selFcltDiv} obj={isModal.data} setIsModal={() => { }} />} ></UiModal> <Toaster position="top-center" /> </ContentsDiv> diff --git a/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/index.tsx b/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/index.tsx index 4c7f2d514..e256b4ef4 100644 --- a/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/index.tsx +++ b/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/index.tsx @@ -1,26 +1,31 @@ import { buildingType } from "@/app/store/common/code"; +import type { TableUpperProps } from "@/app/store/common/type/header"; import { TEST_UUID } from "@/app/store/ruseFcltMng/fcltReg/rainReg"; import { deleteFectch, fetchType, + getCountFetch, getFetch, postFectch, putFectch, } from "@/components/lib/api"; -import { ToastFormUi } from "@/components/ui/toast/toastFromUi"; import type { SelectDataType } from "@common/business_components/ui"; +import toast from "react-hot-toast"; import { z } from "zod"; import { buildingToExcelType } from "./excel"; import { rainChkgdnHeadListExport, rainChkZod, - rainHeadNmExport, + rainMainHeadNmExport, + rainRuseHeadNmExport, + rainSplHeadNmExport, ruseHeadListExport, splHeadListExport, type chHeadType, } from "./headList"; -import type { TableUpperProps } from "@/app/store/common/type/header"; -import toast from "react-hot-toast"; + +export const rainPageSize = 5; +export const commonCd = "41"; export type rainChkZodType = z.infer<typeof rainChkZod>; const fcltDiv = [ @@ -28,16 +33,20 @@ const fcltDiv = [ { val: "RNB", text: "빗물이용시설" }, ]; -const rainHeadNm = rainHeadNmExport; +const rainRuseHeadNm = rainRuseHeadNmExport; +const rainSplHeadNm = rainSplHeadNmExport; const rainChkgdnHeadList = rainChkgdnHeadListExport; const ruseHeadList = ruseHeadListExport; const splHeadList = splHeadListExport; +const rainMainHeadNm = rainMainHeadNmExport const rainChk: rainChkZodType[] = []; export type rainChkgdnState = { rainChkgdn: { rainChkgdnHeadList: TableUpperProps[]; + rainSplHeadNm:chHeadType[] + rainRuseHeadNm:chHeadType[] rainChk: rainChkZodType[]; rainHeadNm: chHeadType[]; ruseHeadList: TableUpperProps[]; @@ -45,15 +54,18 @@ export type rainChkgdnState = { isInit: boolean; isModalClose: boolean; fcltDiv: SelectDataType[]; + rainMainHeadNm:chHeadType[] excelList: []; isSttn: boolean; + rainChkCount: number; + isLoading: boolean; }; }; type rainChkgdnActions = { rainChkInit: ( - pageNo: string, - cnt: string, + pageNo: number, + cnt: number, sggCd: string, searchTxt: string ) => void; @@ -66,6 +78,13 @@ type rainChkgdnActions = { rainChkExcelDataUPload(excelData: rainChkZodType[]): Promise<void>; rainChkDelete(chkgdnPks: number[]): Promise<void>; rainChkHeadChange: (rainChkgdn: rainChkgdnState, isEdit: boolean) => void; + rainChkNmSearch: ( + pageNo: number, + cnt: number, + year: string, + sggCd: string, + txt: string + ) => void; }; export type rainChkgdnType = rainChkgdnState & rainChkgdnActions; @@ -73,7 +92,9 @@ export type rainChkgdnType = rainChkgdnState & rainChkgdnActions; export const rainChkgdnStateExport: rainChkgdnState = { rainChkgdn: { rainChkgdnHeadList: rainChkgdnHeadList, - rainHeadNm: rainHeadNm, + rainRuseHeadNm: rainRuseHeadNm, + rainSplHeadNm:rainSplHeadNm, + rainMainHeadNm:rainMainHeadNm, rainChk: rainChk, splHeadList: splHeadList, ruseHeadList: ruseHeadList, @@ -82,6 +103,8 @@ export const rainChkgdnStateExport: rainChkgdnState = { excelList: [], fcltDiv: fcltDiv, isSttn: false, + rainChkCount: 1, + isLoading: false, }, }; @@ -103,20 +126,22 @@ const makeMdfrChk = (data: rainChkZodType[], type: string, toDo: string) => { }; const initFetch = async ( - pageNo: string, - cnt: string, - txt: string | undefined, + pageNo: number, + cnt: number, + year: string | undefined, sgg: string | undefined ) => { const currentYear = new Date().getFullYear().toString(); - const rainRes = await getFetch( + + const rainRes = await getCountFetch( "wr", `/reuseChk/rainy` + (pageNo ? `/${pageNo}` : "/1") + - (cnt ? `/${cnt}` : "/20") + - (txt ? `/${txt}` : `/${currentYear}`) + + (cnt ? `/${cnt}` : "/10") + + (year ? `/${year}` : `/${currentYear}`) + (sgg ? `/${sgg}` : "/0") ); + return rainRes; }; @@ -142,16 +167,24 @@ export const rainChkgdnActionsExport: (set: any) => rainChkgdnActions = ( })); }, rainChkExcelDataUPload: async (excelData: rainChkZodType[]) => { + set((state: rainChkgdnType) => ( + { rainChkgdn: { ...state.rainChkgdn, isLoading: true } })); try { const res = await postFectch("/reuseChk/rainy", excelData); if (res.status === 200) { const currentYear = new Date().getFullYear().toString(); - const rainChkRes = await initFetch("1", "20", currentYear, "0"); - return set((state: rainChkgdnState) => ({ + const rainChkRes = await initFetch( + 1, + rainPageSize, + currentYear, + commonCd + ); + set((state: rainChkgdnState) => ({ ...state, rainChkgdn: { ...state.rainChkgdn, - rainChk: rainChkRes, + rainChk: rainChkRes.data, + rainChkCount: rainChkRes.count, }, })); toast.success("지도점검 등록이 완료되었습니다."); @@ -161,22 +194,62 @@ export const rainChkgdnActionsExport: (set: any) => rainChkgdnActions = ( } catch (error) { console.log(error); toast.error("지도점검 등록이 실패되었습니다."); + } finally { + set((state: rainChkgdnType) => ({ rainChkgdn: { ...state.rainChkgdn, isLoading: false } })); } }, rainChkInit: async ( - pageNo: string, - cnt: string, - searchTxt: string, + pageNo: number, + cnt: number, + year: string, sggCd: string ) => { + set((state: rainChkgdnType) => ({ rainChkgdn: { ...state.rainChkgdn, isLoading: true } })); try { - const rainChkRes = await initFetch(pageNo, cnt, searchTxt, sggCd); - + const rainChkRes = await initFetch(pageNo, cnt, year, sggCd); + set((state: rainChkgdnState) => ({ + ...state, + rainChkgdn: { + ...state.rainChkgdn, + rainChk: rainChkRes.data, + rainChkCount: rainChkRes.count, + isInit: true, // 무한 루프 방지를 위해 isInit을 true로 설정 + }, + })); + } catch (error) { + console.log(error); + // 에러가 발생하더라도 무한 루프에 빠지지 않도록 isInit을 true로 설정합니다. + set((state: rainChkgdnType) => ({ + rainChkgdn: { ...state.rainChkgdn, isInit: true }, + })); + } finally { + set((state: rainChkgdnType) => ({ rainChkgdn: { ...state.rainChkgdn, isLoading: false } })); + } + }, + rainChkNmSearch: async ( + pageNo: number, + cnt: number, + year: string, + sggCd: string, + txt: string + ) => { + try { + const currentYear = new Date().getFullYear(); + const rainChkRes = await getCountFetch( + "wr", + `/reuseChk/rainy` + + (pageNo ? `/${pageNo}` : "/1") + + (cnt ? `/${cnt}` : "/10") + + (year ? `/${year}` : `/${currentYear}`) + + (sggCd ? `/${sggCd}` : "/0") + + (txt ? `/${txt}` : "") + ); return set((state: rainChkgdnState) => ({ ...state, rainChkgdn: { ...state.rainChkgdn, - rainChk: rainChkRes, + rainChk: rainChkRes.data, + rainChkCount: rainChkRes.count, }, })); } catch (error) { @@ -187,26 +260,32 @@ export const rainChkgdnActionsExport: (set: any) => rainChkgdnActions = ( try { const chData = await makeMdfrChk(rainChk, "rain", "rainDetil"); - const res = await postFectch("/reuseChk/rainy", chData); - if (res.staus === 200) { + const res = await putFectch("/reuseChk/rainy/list", chData); + if (res) { toast.success( - "지도점검 보급수 및 밋물이용량 수정이 완료되었습니다. " + "지도점검 대체수 및 밋물이용량 수정이 완료되었습니다. " ); const currentYear = new Date().getFullYear().toString(); - const rainChkRes = await initFetch("1", "20", currentYear, "11"); + const rainChkRes = await initFetch( + 1, + rainPageSize, + currentYear, + "41" + ); return set((state: rainChkgdnState) => ({ ...state, rainChkgdn: { ...state.rainChkgdn, - rainChk: rainChkRes, + rainChk: rainChkRes.data, + rainChkCount: rainChkRes.count, }, })); } else { - toast.error("지도점검 보급수 및 밋물이용량 수정 오류입니다."); + toast.error("지도점검 대체수 및 밋물이용량 수정 오류입니다."); } } catch (error) { console.log(error); - toast.error("지도점검 보급수 및 밋물이용량 수정 오류입니다."); + toast.error("지도점검 대체수 및 밋물이용량 수정 오류입니다."); } }, rainChkGuidUpdate: async (rainChk: rainChkZodType) => { @@ -217,15 +296,21 @@ export const rainChkgdnActionsExport: (set: any) => rainChkgdnActions = ( toast.success("지도점검 수질 및 과태료 수정이 완료되었습니다. "); const currentYear = new Date().getFullYear().toString(); - const rainChkRes = await initFetch("1", "20", currentYear, "11"); + const rainChkRes = await initFetch( + 1, + rainPageSize, + currentYear, + "41" + ); return set((state: rainChkgdnState) => ({ ...state, rainChkgdn: { ...state.rainChkgdn, - rainChk: rainChkRes, + rainChk: rainChkRes.data, + rainChkCount: rainChkRes.count, }, })); - }else{ + } else { toast.error("지도점검 수질 및 과태료 수정 오류입니다."); } @@ -241,15 +326,21 @@ export const rainChkgdnActionsExport: (set: any) => rainChkgdnActions = ( toast.success("삭제가 완료되었습니다. "); const currentYear = new Date().getFullYear().toString(); - const rainChkRes = await initFetch("1", "20", currentYear, "11"); + const rainChkRes = await initFetch( + 1, + rainPageSize, + currentYear, + "41" + ); return set((state: rainChkgdnState) => ({ ...state, rainChkgdn: { ...state.rainChkgdn, - rainChk: rainChkRes, + rainChk: rainChkRes.data, + rainChkCount: rainChkRes.count, }, })); - }else{ + } else { toast.error("삭제시 오류가 발생하였습니다.. "); } diff --git a/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/index.tsx b/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/index.tsx index 37e385ca2..52f46e259 100644 --- a/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/index.tsx +++ b/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/index.tsx @@ -1,18 +1,24 @@ +import { buildingType } from "@/app/store/common/code"; +import type { TableUpperProps } from "@/app/store/common/type/header"; import { TEST_UUID, } from "@/app/store/ruseFcltMng/fcltReg/rainReg"; -import type { - SelectDataType, - SheetParameter, -} from "@common/business_components"; import { deleteFectch, fetchType, + getCountFetch, getFetch, postFectch, putFectch, } from "@/components/lib/api"; -import { buildingType } from "@/app/store/common/code"; +import { ToastFormUi } from "@/components/ui/toast/toastFromUi"; +import type { + SelectDataType, + SheetParameter, +} from "@common/business_components"; +import toast from "react-hot-toast"; +import { z } from "zod"; +import { type chHeadType } from "../rainChkgdn/headList"; import { rswtMakeExcelSample } from "./excel"; import { rswtChkgdZod, @@ -20,15 +26,19 @@ import { rswtDetailHeadListExport, rswtHeadNmExport, rswtrDetailsZod, + rswtRuseHeadNmExport, + rswtSplHeadNmExport, } from "./headList"; -import { z } from "zod"; -import { ToastFormUi } from "@/components/ui/toast/toastFromUi"; -import { type chHeadType } from "../rainChkgdn/headList"; -import type { TableUpperProps } from "@/app/store/common/type/header"; + +export const rswtPageSize = 5; const rswtChkHeadList = rswtChkHeadListExport; const rswtDetailHeadList = rswtDetailHeadListExport; const rswtHeadNm = rswtHeadNmExport; +const rswtRuseHeadNm =rswtRuseHeadNmExport +const rswtSplHeadNm =rswtSplHeadNmExport + + export type rswtChkZodType = z.infer<typeof rswtChkgdZod>; export type rswtrChkDetailsZodType = z.infer<typeof rswtrDetailsZod>; export type rswtChkgdnState = { @@ -40,13 +50,29 @@ export type rswtChkgdnState = { excelList: SheetParameter[]; rswtChkRes: rswtChkZodType[]; rswtChkPolGuide: rswtChkZodType[]; + rswtSplHeadNm:chHeadType[] + rswtRuseHeadNm:chHeadType[] isInit: boolean; - isSttn: boolean + isSttn: boolean; + isLoading:boolean; + rswtChkCount: number; }; }; type rswtChkgdnActions = { - rswtChkgdnInit: (pageNo: string, cnt: string, year: string, sggCd: string) => void; + rswtChkgdnInit: ( + pageNo: number, + cnt: number, + year: string, + sggCd: string + ) => void; + rswtChkNmSearch: ( + pageNo: number, + cnt: number, + year: string, + sggCd: string, + txt: string + ) => void; rswtrChkExcelDownload( sidoCode: SelectDataType[], sggCode: SelectDataType[] @@ -67,21 +93,35 @@ export const rswtChkgdnStateExport: rswtChkgdnState = { rswtChkHeadList: rswtChkHeadList, rswtDetailHeadList: rswtDetailHeadList, rswtHeadNm: rswtHeadNm, - excelList: [{ - data: [[]], headerNum: 0, headerRange: "A1:V3", + rswtRuseHeadNm:rswtRuseHeadNm, + rswtSplHeadNm:rswtSplHeadNm, + + excelList: [ + { + data: [[]], + headerNum: 0, + headerRange: "A1:V3", sheetName: "", - startCol: '1' - }], + startCol: "1", + }, + ], rswtChkRes: [], rswtChkPolGuide: [], isInit: false, isSttn: false, + isLoading:false, + rswtChkCount: 1, }, }; -const initFetch = async (pageNo: string, cnt: string, year: string | undefined, sgg: string | undefined) => { +const initFetch = async ( + pageNo: number, + cnt: number, + year: string | undefined, + sgg: string | undefined +) => { const currentYear = new Date().getFullYear().toString(); - const rswtChkRes = await getFetch( + const rswtChkRes = await getCountFetch( "wr", `/reuseChk/reuseWater` + (pageNo ? `/${pageNo}` : "/1") + @@ -139,7 +179,13 @@ export const rswtChkgdnActionsExport: (set: any) => rswtChkgdnActions = ( }, })); }, - rswtChkgdnInit: async (pageNo: string, cnt: string, year: string, sggCd: string) => { + rswtChkgdnInit: async ( + pageNo: number, + cnt: number, + year: string, + sggCd: string + ) => { + set((state: rswtChkgdnType) => ({ rswtChkgdn: { ...state.rswtChkgdn, isLoading: true } })); try { const rswtChkRes = await initFetch(pageNo, cnt, year, sggCd); @@ -174,16 +220,18 @@ export const rswtChkgdnActionsExport: (set: any) => rswtChkgdnActions = ( "wr", `/reuseChk/reuseWater` + (pageNo ? `/${pageNo}` : "/1") + - (cnt ? `/${cnt}` : "/5") + + (cnt ? `/${cnt}` : "/20") + (year ? `/${year}` : `/${currentYear}`) + (sggCd ? `/${sggCd}` : "/0") + (txt ? `/${txt}` : "") ); + return set((state: rswtChkgdnState) => ({ ...state, rswtChkgdn: { ...state.rswtChkgdn, - rswtChkRes: rswtChkRes, + rswtChkRes: rswtChkRes.data, + rswtChkCount: rswtChkRes.count, isInit: false, }, })); @@ -193,6 +241,8 @@ export const rswtChkgdnActionsExport: (set: any) => rswtChkgdnActions = ( }, rswtChkExceDataUpload: async (excelData: rswtChkZodType[]) => { try { + set((state: rswtChkgdnType) => ( + { rswtChkgdn: { ...state.rswtChkgdn, isLoading: true } })); const res = await postFectch("/reuseChk/reuseWater", excelData); if (res.ok) { ToastFormUi({ @@ -201,65 +251,66 @@ export const rswtChkgdnActionsExport: (set: any) => rswtChkgdnActions = ( }); } const currentYear = new Date().getFullYear().toString(); - const rswtChkRes = await initFetch("1", "20", currentYear, "0"); + const rswtChkRes = await initFetch(1, rswtPageSize, currentYear, "0"); return set((state: rswtChkgdnState) => ({ ...state, rswtChkgdn: { ...state.rswtChkgdn, - rswtChkRes: rswtChkRes + rswtChkRes: rswtChkRes.data, + rswtChkCount: rswtChkRes.count, }, })); } catch (error) { console.log(error); + }finally{ + set((state: rswtChkgdnType) => ( + { rswtChkgdn: { ...state.rswtChkgdn, isLoading: false } })); } + }, rswtChkDetilsUpdate: async (rswtChk: rswtrChkDetailsZodType[]) => { try { const chData = await makeMdfrChk(rswtChk); - const res = await putFectch("/reuseChk/reuseWater", chData); if (res.ok) { - ToastFormUi({ - body: "지도점검 과태료 수정 수정이 완료되었습니다. ", - title: "지도점검 수정", - }); + toast.success("지도점검 과태료 수정이 완료되었습니다. ",) } const currentYear = new Date().getFullYear().toString(); - const rswtChkRes = await initFetch("1", "20", currentYear, "0"); + const rswtChkRes = await initFetch(1, rswtPageSize, currentYear, "0"); return set((state: rswtChkgdnState) => ({ ...state, rswtChkgdn: { ...state.rswtChkgdn, - rswtChkRes: rswtChkRes, + rswtChkRes: rswtChkRes.data, + rswtChkCount: rswtChkRes.count, }, })); } catch (error) { console.log(error); - alert("수정에 실패하였습니다."); + toast.error("지도점검 과태료 수정 오류입니다. ",) + } }, rswtChkDelete: async (chkgdnPks: number[]) => { try { const res = await deleteFectch(`/reuseChk/reuseWater`, chkgdnPks); - if (res.ok) { - ToastFormUi({ - body: "삭제가 완료되었습니다. ", - title: "지도점검 삭제", - }); + if (res) { + toast.success("삭제가 완료되었습니다.") } const currentYear = new Date().getFullYear().toString(); - const rswtChkRes = await initFetch("1", "20", currentYear, "0"); + const rswtChkRes = await initFetch(1, rswtPageSize, currentYear, "0"); return set((state: rswtChkgdnState) => ({ ...state, rswtChkgdn: { ...state.rswtChkgdn, - rswtChkRes: rswtChkRes, + rswtChkRes: rswtChkRes.data, + rswtChkCount: rswtChkRes.count, }, })); } catch (error) { - ToastFormUi({ body: "삭제를 실패했습니다.. ", title: "지도점검 삭제" }); + toast.error("삭제 오류입니다.") } }, rswtChkPolAndGuide: async (chkGdnPk: number) => { @@ -276,7 +327,11 @@ export const rswtChkgdnActionsExport: (set: any) => rswtChkgdnActions = ( rswtChkPolGuide: rswtChkPolGuide, }, })); - } catch (error) { } + } catch (error) { + + + + } }, rswtChkPolInit: () => { try { @@ -287,7 +342,7 @@ export const rswtChkgdnActionsExport: (set: any) => rswtChkgdnActions = ( rswtChkPolGuide: undefined, }, })); - } catch (error) { } + } catch (error) {} }, rwstPolAndGuideUpdate: async (rswtChk: rswtChkZodType[]) => { try { @@ -297,13 +352,10 @@ export const rswtChkgdnActionsExport: (set: any) => rswtChkgdnActions = ( chData[0] ); if (res.ok) { - ToastFormUi({ - body: "지도점검 연간 수질 및 보급수/중수도 수정이 완료되었습니다. ", - title: "지도점검 수정", - }); + toast.success("지도점검 연간 수질 및 대체수/중수도 수정이 완료되었습니다.") } const currentYear = new Date().getFullYear().toString(); - const rswtChkRes = await initFetch("1", "20", currentYear, "0"); + const rswtChkRes = await initFetch(1, rswtPageSize, currentYear, "0"); return set((state: rswtChkgdnState) => ({ ...state, rswtChkgdn: { @@ -313,10 +365,9 @@ export const rswtChkgdnActionsExport: (set: any) => rswtChkgdnActions = ( })); } catch (error) { console.log(error); - ToastFormUi({ - body: "지도점검 연간 수질 및 보급수/중수도 수정이 실패하였습니다.. ", - title: "지도점검 수정", - }); + + toast.error("지도점검 연간 수질 및 대체수/중수도 수정 오류입니다.") + } }, rswtChkHeadChange: async (rswtChkgdn: rswtChkgdnState, isEdit: boolean) => { @@ -337,7 +388,7 @@ export const rswtChkgdnActionsExport: (set: any) => rswtChkgdnActions = ( rswtDetailHeadList: rswtDetailHeadList, }, })); - } catch (e) { } + } catch (e) {} }, }; }; diff --git a/apps/wr/app/store/prfmncSttn/chkgdnSttn/rainChkgdnSttn/index.tsx b/apps/wr/app/store/prfmncSttn/chkgdnSttn/rainChkgdnSttn/index.tsx index 738354b04..e51c912d9 100644 --- a/apps/wr/app/store/prfmncSttn/chkgdnSttn/rainChkgdnSttn/index.tsx +++ b/apps/wr/app/store/prfmncSttn/chkgdnSttn/rainChkgdnSttn/index.tsx @@ -1,10 +1,10 @@ -import type { rainChkZodType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn"; -import { getFetch } from "@/components/lib/api"; -import type { SelectDataType } from "@common/business_components/ui"; -import { rainChkgdnHeadSttnExport, ruseSttnHeadListExport, splSttnHeadListExport } from "./headList"; -import { rainHeadNmExport, type chHeadType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList"; -import { buildingExcelRainSttn } from "./excel"; import type { TableUpperProps } from "@/app/store/common/type/header"; +import type { rainChkZodType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn"; +import { rainMainHeadNmExport, type chHeadType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList"; +import { getCountFetch } from "@/components/lib/api"; +import type { SelectDataType } from "@common/business_components/ui"; +import { buildingExcelRainSttn } from "./excel"; +import { rainChkgdnHeadSttnExport, ruseSttnHeadListExport, splSttnHeadListExport } from "./headList"; const fcltDiv = [ { val: "RST", text: "중수도시설" }, @@ -15,7 +15,7 @@ const fcltDiv = [ const rainChkgdnHeadList = rainChkgdnHeadSttnExport; const ruseHeadList = ruseSttnHeadListExport; const splHeadList = splSttnHeadListExport; -const rainHeadNm = rainHeadNmExport +const rainHeadNm = rainMainHeadNmExport const rainChk: rainChkZodType[] = []; @@ -29,7 +29,8 @@ export type rainChkSttnState = { splHeadList: TableUpperProps[]; sttnExcelList:[] isInit: boolean; - isSttn:boolean; + isSttn: boolean; + rainChkCount: number; }; }; @@ -51,16 +52,21 @@ export const rainChkSttnStateExport: rainChkSttnState = { splHeadList: splHeadList, rainHeadNm:rainHeadNm, isInit: false, - sttnExcelList:[], - isSttn:false, + sttnExcelList: [], + isSttn: false, + rainChkCount: 1, }, }; -const initFetch = async (txt: string | undefined, sgg: string | undefined) => { +const initFetch = async (pageNo: number, cnt: number, txt: string | undefined, sgg: string | undefined) => { const currentYear = new Date().getFullYear().toString(); - const rainRes = await getFetch( + const rainRes = await getCountFetch( "wr", - `/reuseChk/rainy/` + (txt ? `${txt}` : currentYear) + (sgg ? `/${sgg}` : "/0") + `/reuseChk/rainy` + + (pageNo ? `/${pageNo}` : "/1") + + (cnt ? `/${cnt}` : "/5") + + (txt ? `/${txt}` : `/${currentYear}`) + + (sgg ? `/${sgg}` : "/0") ); return rainRes; }; @@ -68,15 +74,17 @@ export const rainChkSttnActionsExport: (set: any) => rainChkSttnActions = ( set: any ) => { return { - rainChkSttnInit: async (searchTxt: string, sggCd: string) => { + rainChkSttnInit: async (pageNo: number, cnt: number, searchTxt: string, sggCd: string) => { try { - const rainChkRes = await initFetch(searchTxt, sggCd); + const rainChkRes = await initFetch(pageNo, cnt, searchTxt, sggCd); return set((state: rainChkSttnState) => ({ ...state, rainChkSttn: { ...state.rainChkSttn, - rainChkRes:rainChkRes + rainChkRes: rainChkRes.data, + rainChkCount: rainChkRes.count, + isSttn: true }, })); } catch (error) { diff --git a/apps/wr/components/ui/modal/chkgd/chkgdModal/chkgdModal.tsx b/apps/wr/components/ui/modal/chkgd/chkgdModal/chkgdModal.tsx index 965bc7275..f3643a094 100644 --- a/apps/wr/components/ui/modal/chkgd/chkgdModal/chkgdModal.tsx +++ b/apps/wr/components/ui/modal/chkgd/chkgdModal/chkgdModal.tsx @@ -1,14 +1,12 @@ import { useWrStore } from "@/app/store"; -import { ScrollArea, Title } from "@common/business_components/ui"; -import { Button } from "@common/components/ui"; -import { useEffect, useState } from "react"; -import { headMakeprfmnc } from "@/app/ruseFcltMng/prfmncReg/chkgdnReg/TableHead"; -import { GuideDetailsTable } from "@/components/ui/table/chkgdTable/guideDetailTable/guideDetailTable"; +import type { TableUpperProps } from "@/app/store/common/type/header"; import type { splRuseWaterType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList"; import type { rswtrPolutionsZode } from "@/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/headList"; +import { GuideDetailsTable } from "@/components/ui/table/chkgdTable/guideDetailTable/guideDetailTable"; +import { ScrollArea, Title } from "@common/business_components/ui"; +import { Button } from "@common/components/ui"; +import React, { useEffect, useState } from "react"; import type { z } from "zod"; -import React from "react"; -import type { TableUpperProps } from "@/app/store/common/type/header"; export default function ChkgdModal({ type, obj }: { type: string; obj: any }) { const { @@ -114,7 +112,6 @@ export default function ChkgdModal({ type, obj }: { type: string; obj: any }) { }); } - console.log("result ", result); setGuidData(result as splRuseWaterType[]); if (obj.rswtrPollutions) { @@ -217,12 +214,14 @@ export default function ChkgdModal({ type, obj }: { type: string; obj: any }) { obj.guideDetails = realGuideDetail; if (type === "RNB") { rainChkGuidUpdate(obj); + setIsModal(false) } else { const polution = Object.values(upPolutionData) .flat() .filter((item) => item.utztnUsgCd !== "평균"); obj.rswtrPollutions = polution; rwstPolAndGuideUpdate([obj]); + setIsModal(false) } }; @@ -248,7 +247,8 @@ export default function ChkgdModal({ type, obj }: { type: string; obj: any }) { </div> <div className="flex justify-between"> <h3 className="text-2xl font-bold text-gray-900 mt-2 ml-6"> - {`(${obj.bdstNm})`} + {type === "RNB" + ? `(${obj.bdstNm})` : `(${obj.building.bdstNm})`} </h3> {!rainChkSttn.isSttn ? ( <Button className="" onClick={allDataUpdate}> @@ -262,12 +262,13 @@ export default function ChkgdModal({ type, obj }: { type: string; obj: any }) { <ScrollArea className="h-[60vh]"> <div className="card-header"> <h3 className="text-2xl font-medium text-label"> - {`연간보급수이용량`} + {`연간대체수이용량`} </h3> </div> <GuideDetailsTable chgRes={guidData || []} - headList={headList.ruseHeadList} + headList={headList.splHeadList} + upheadList={type === "RNB" ? rainChkgdn.rainSplHeadNm : rswtChkgdn.rswtSplHeadNm} type={""} setChGuidData={(data) => { setChGuidData(data); @@ -280,7 +281,8 @@ export default function ChkgdModal({ type, obj }: { type: string; obj: any }) { </div> <GuideDetailsTable chgRes={guidData || []} - headList={headList.splHeadList} + headList={headList.ruseHeadList} + upheadList={type === "RNB" ? rainChkgdn.rainRuseHeadNm : rswtChkgdn.rswtRuseHeadNm} type={""} setChGuidData={(data) => { setChGuidData(data); @@ -302,6 +304,7 @@ export default function ChkgdModal({ type, obj }: { type: string; obj: any }) { <GuideDetailsTable chgRes={value} headList={headList?.rswtDetailHeadList} + upheadList={rswtChkgdn.rswtSplHeadNm} type={""} setChGuidData={(data) => { getPolutionData(data); diff --git a/apps/wr/components/ui/table/chkgdTable/rainChkgd/rainChkgdTable.tsx b/apps/wr/components/ui/table/chkgdTable/rainChkgd/rainChkgdTable.tsx index 720a73ab4..2525cfd27 100644 --- a/apps/wr/components/ui/table/chkgdTable/rainChkgd/rainChkgdTable.tsx +++ b/apps/wr/components/ui/table/chkgdTable/rainChkgd/rainChkgdTable.tsx @@ -1,22 +1,22 @@ "use client"; import { headMakeprfmnc } from "@/app/ruseFcltMng/prfmncReg/chkgdnReg/TableHead"; import { useWrStore } from "@/app/store"; -import type { rainChkZodType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn"; -import type { chHeadType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList"; import type { TableUpperProps } from "@/app/store/common/type/header"; +import { type rainChkZodType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn"; +import type { chHeadType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList"; import { tableData } from "@/components/lib/func/commom"; import { chRowspanTable, - headVisibleChange, rainChkTableData } from "@/components/lib/func/TableData"; +import LoadingOverlay from "@common/business_components/ui/loadingBar"; +import UiPageNations from "@common/business_components/ui/table/uiPageNations"; import { Table, TableBody, TableHeader, } from "@common/components/ui"; import { useCallback, useEffect, useState } from "react"; -import UiPageNations from "@common/business_components/ui/table/uiPageNations"; export default function RainChkgdTable({ detailUp, handleClick = () => { }, @@ -24,6 +24,7 @@ export default function RainChkgdTable({ rainChkData, pageNation, onPageChange, + pageCount, }: { detailUp: (data: rainChkZodType[]) => void; handleClick?: (data: any) => void; @@ -31,6 +32,7 @@ export default function RainChkgdTable({ rainChkData?: rainChkZodType[]; pageNation: number; onPageChange: (page: number) => void; + pageCount: number; }) { const { code, rainChkgdn, rainChkSttn, getRainSttnExcel } = useWrStore((state) => state); const [makeTableData, setMakeTableData] = useState<any[]>([]); @@ -70,16 +72,12 @@ export default function RainChkgdTable({ }, [makeTableData, handleClick]); - useEffect(() =>{ - - - },[rainChkgdn.rainChk]) useEffect(() => { setSelectedIds([])// 셀렉트 초기화를 위한 작업 - if (rainChkData) { + if (rainChkData && rainChkData.length > 0) { - const rainChg = rainChkData.map((item: any) => { + const rainChg = rainChkData?.map((item: any) => { const bud = tableData([item.building], { code }); return { ...item, @@ -89,6 +87,8 @@ export default function RainChkgdTable({ }); setMakeTableData(rainChkTableData(rainChg)); + } else { + setMakeTableData([]); } }, [rainChkData, code]); @@ -167,16 +167,15 @@ export default function RainChkgdTable({ } else { setHeadList({ headList: rainChkgdn.rainChkgdnHeadList, - upHeadList: rainChkgdn.rainHeadNm + upHeadList: rainChkgdn.rainMainHeadNm }) } - }, [rainChkSttn.isSttn, rainChkgdn.rainChkgdnHeadList, rainChkSttn.rainChkgdnHeadList]); - return ( <> + <LoadingOverlay isLoading={rainChkgdn.isLoading}></LoadingOverlay> <Table variant="secondary"> <TableHeader className="font-bold"> {headMakeprfmnc( @@ -197,9 +196,9 @@ export default function RainChkgdTable({ </TableBody> </Table> <UiPageNations - total={rainChkData?.length ?? 0} + total={pageCount} cur={pageNation} - pageSize={20} + pageSize={rainPageSize} onPageChange={onPageChange} /> </> diff --git a/apps/wr/components/ui/table/chkgdTable/rswtChkgd/rswtChkgdTable.tsx b/apps/wr/components/ui/table/chkgdTable/rswtChkgd/rswtChkgdTable.tsx index 31891a595..bc8581b75 100644 --- a/apps/wr/components/ui/table/chkgdTable/rswtChkgd/rswtChkgdTable.tsx +++ b/apps/wr/components/ui/table/chkgdTable/rswtChkgd/rswtChkgdTable.tsx @@ -1,20 +1,22 @@ "use client"; import { headMakeprfmnc } from "@/app/ruseFcltMng/prfmncReg/chkgdnReg/TableHead"; import { useWrStore } from "@/app/store"; -import type { chHeadType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList"; -import type { - rswtChkZodType, - rswtrChkDetailsZodType, -} from "@/app/store/prfmncReg/chkgdnReg/rswrtChkgdn"; import type { TableUpperProps } from "@/app/store/common/type/header"; +import type { chHeadType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList"; +import { + rswtPageSize, + type rswtChkZodType, + type rswtrChkDetailsZodType, +} from "@/app/store/prfmncReg/chkgdnReg/rswrtChkgdn"; import { tableData } from "@/components/lib/func/commom"; import { chRowspanTable, rswtChkTableData, } from "@/components/lib/func/TableData"; +import LoadingOverlay from "@common/business_components/ui/loadingBar"; +import UiPageNations from "@common/business_components/ui/table/uiPageNations"; import { Table, TableBody, TableHeader } from "@common/components/ui"; import { useCallback, useEffect, useState } from "react"; -import UiPageNations from "@common/business_components/ui/table/uiPageNations"; export default function RswtChkgdTable({ detaiRswtlUpdate, @@ -23,6 +25,7 @@ export default function RswtChkgdTable({ rswtChkData, pageNation, onPageChange, + pageCount, }: { detaiRswtlUpdate: (data: rswtrChkDetailsZodType[]) => void; handleRswtClick?: (data: any) => void; @@ -30,6 +33,7 @@ export default function RswtChkgdTable({ rswtChkData: rswtChkZodType[]; pageNation: number; onPageChange: (page: number) => void; + pageCount: number; }) { const { code, rswtChkgdn, rswtChkSttn, rswtChkgdnInit, rswtChkPolAndGuide, getRswtSttnExcel } = useWrStore( (state) => state @@ -133,6 +137,7 @@ export default function RswtChkgdTable({ return ( <> + <LoadingOverlay isLoading={rswtChkgdn.isLoading}></LoadingOverlay> <Table variant="secondary"> <TableHeader className="font-bold"> {headMakeprfmnc(headList?.headList, headList?.upHeadList)} @@ -150,9 +155,9 @@ export default function RswtChkgdTable({ </TableBody> </Table> <UiPageNations - total={rswtChkData?.length ?? 0} + total={pageCount} cur={pageNation} - pageSize={20} + pageSize={rswtPageSize} onPageChange={onPageChange} /> </> From 011b87784d2cc8e4f17e79acf91eea6c1c7d3775 Mon Sep 17 00:00:00 2001 From: truthwing <truthplumage@gmail.com> Date: Fri, 4 Jul 2025 16:12:08 +0900 Subject: [PATCH 070/108] djlee product --- .../prfmncReg/chkgdnReg/TableHead.tsx | 6 ++--- .../chkgdnReg/rainChkgdn/headList.tsx | 16 ++++++++++++-- .../prfmncReg/chkgdnReg/rainChkgdn/index.tsx | 6 ++--- .../chkgdnReg/rswrtChkgdn/headList.tsx | 22 +++++++++++++++++-- .../prfmncReg/chkgdnReg/rswrtChkgdn/index.tsx | 6 ++--- .../chkgdnSttn/rainChkgdnSttn/index.tsx | 8 +++---- .../chkgdnSttn/rswtChkgdnSttn/index.tsx | 8 +++---- .../chkgdTable/rainChkgd/rainChkgdTable.tsx | 2 +- .../src/ui/loadingBar/index.tsx | 20 +++++++++++++++++ 9 files changed, 71 insertions(+), 23 deletions(-) create mode 100644 packages/common/business_components/src/ui/loadingBar/index.tsx diff --git a/apps/wr/app/ruseFcltMng/prfmncReg/chkgdnReg/TableHead.tsx b/apps/wr/app/ruseFcltMng/prfmncReg/chkgdnReg/TableHead.tsx index 10a0bf85f..28ba6c512 100644 --- a/apps/wr/app/ruseFcltMng/prfmncReg/chkgdnReg/TableHead.tsx +++ b/apps/wr/app/ruseFcltMng/prfmncReg/chkgdnReg/TableHead.tsx @@ -26,10 +26,10 @@ export const headMakeprfmnc = ( key={head.id} rowSpan={1} colSpan={ - makeHeadNm.find((ch) => ch.headNm === head.upName)?.length + makeHeadNm?.find((ch) => ch.headNm === head.upName)?.length } > - {makeHeadNm.find((ch) => ch.headNm === head.upName)?.text} + {makeHeadNm?.find((ch) => ch.headNm === head.upName)?.text} </TableHead> ) : null )} @@ -39,7 +39,7 @@ export const headMakeprfmnc = ( {upHeadList .filter((data) => data.visible != false) .map((head: TableUpperProps) => - makeHeadNm.find((ch) => ch.headNm === head.upName) ? ( + makeHeadNm?.find((ch) => ch.headNm === head.upName) ? ( <TableHead className={`border border-gray-600 px-2 py-1 `} key={head.id} diff --git a/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList.tsx b/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList.tsx index d7335e129..3cf1b5b11 100644 --- a/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList.tsx +++ b/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList.tsx @@ -114,10 +114,22 @@ export type splRuseWaterType = { ruse12: number; }; -export const rainHeadNmExport: chHeadType[] = [ +export const rainMainHeadNmExport: chHeadType[] = [ + { headNm: "useQuilty", text: "이용수질(필요시)", length: 3 }, + { headNm: "feeHist", text: "과태료 부과내역", length: 3 }, +]; + +export const rainRuseHeadNmExport: chHeadType[] = [ + { headNm: "useQuilty", text: "이용수질(필요시)", length: 3 }, + { headNm: "feeHist", text: "과태료 부과내역", length: 3 }, + { headNm: "ruseWater", text: "연간대체수 이용량(m³)", length: 13 }, + { headNm: "detailQulity", text: "분기", length: 11 }, +]; + + +export const rainSplHeadNmExport: chHeadType[] = [ { headNm: "useQuilty", text: "이용수질(필요시)", length: 3 }, { headNm: "feeHist", text: "과태료 부과내역", length: 3 }, - { headNm: "ruseWater", text: "연간보급수 이용량(m³)", length: 13 }, { headNm: "detailQulity", text: "분기", length: 11 }, { headNm: "splWater", text: "연간빗물 이용량(m³)", length: 13 }, ]; diff --git a/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/index.tsx b/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/index.tsx index e256b4ef4..36e8672f5 100644 --- a/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/index.tsx +++ b/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/index.tsx @@ -4,8 +4,8 @@ import { TEST_UUID } from "@/app/store/ruseFcltMng/fcltReg/rainReg"; import { deleteFectch, fetchType, - getCountFetch, getFetch, + getFetchWithCount, postFectch, putFectch, } from "@/components/lib/api"; @@ -133,7 +133,7 @@ const initFetch = async ( ) => { const currentYear = new Date().getFullYear().toString(); - const rainRes = await getCountFetch( + const rainRes = await getFetchWithCount( "wr", `/reuseChk/rainy` + (pageNo ? `/${pageNo}` : "/1") + @@ -235,7 +235,7 @@ export const rainChkgdnActionsExport: (set: any) => rainChkgdnActions = ( ) => { try { const currentYear = new Date().getFullYear(); - const rainChkRes = await getCountFetch( + const rainChkRes = await getFetchWithCount( "wr", `/reuseChk/rainy` + (pageNo ? `/${pageNo}` : "/1") + diff --git a/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/headList.tsx b/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/headList.tsx index 0e690dbb9..c9fc9f078 100644 --- a/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/headList.tsx +++ b/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/headList.tsx @@ -1,6 +1,6 @@ +import type { TableUpperProps } from "@/app/store/common/type/header"; import { z } from "zod"; import { guideDetail, type chHeadType } from "../rainChkgdn/headList"; -import type { TableUpperProps } from "@/app/store/common/type/header"; export type polutionType = { chkgdnDtlFk: number; @@ -109,6 +109,24 @@ export const rswtChkgdZod = z.object({ rswtrPollutions: z.array(rswtrPolutionsZode), }); + + +export const rswtRuseHeadNmExport: chHeadType[] = [ + { headNm: "useQuilty", text: "이용수질(필요시)", length: 3 }, + { headNm: "feeHist", text: "과태료 부과내역", length: 3 }, + { headNm: "ruseWater", text: "연간중수 이용량(m³)", length: 13 }, + { headNm: "detailQulity", text: "분기", length: 11 }, +]; + +export const rswtSplHeadNmExport: chHeadType[] = [ + { headNm: "useQuilty", text: "이용수질(필요시)", length: 3 }, + { headNm: "feeHist", text: "과태료 부과내역", length: 3 }, + { headNm: "detailQulity", text: "분기", length: 11 }, + { headNm: "splWater", text: "연간대체수 이용량(m³)", length: 13 }, +]; + + + export const rswtHeadNmExport: chHeadType[] = [ { headNm: "useQuilty", text: "연간 평균 이용수질", length: 10 }, { headNm: "feeHist", text: "과태료 부과내역", length: 3 }, @@ -126,7 +144,7 @@ export const rswtChkHeadListExport: TableUpperProps[] = [ { id: "spwtPositn", title: "채수지점" ,mergeCell: true}, { id: "chckYmd", title: "점검일" ,mergeCell: true}, { id: "utztnUsgCd", title: "이용용도" }, - { id: "splyUswtrUtztnQnt", title: "연간보급수이용량(m³)" ,type: "number"}, + { id: "splyUswtrUtztnQnt", title: "연간대체수이용량(m³)" ,type: "number"}, { id: "ruseFcltUtztnQnt", title: "연간중수도이용량(m³)",type: "number"}, { diff --git a/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/index.tsx b/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/index.tsx index 52f46e259..49e1c0906 100644 --- a/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/index.tsx +++ b/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/index.tsx @@ -6,8 +6,8 @@ import { import { deleteFectch, fetchType, - getCountFetch, getFetch, + getFetchWithCount, postFectch, putFectch, } from "@/components/lib/api"; @@ -121,7 +121,7 @@ const initFetch = async ( sgg: string | undefined ) => { const currentYear = new Date().getFullYear().toString(); - const rswtChkRes = await getCountFetch( + const rswtChkRes = await getFetchWithCount( "wr", `/reuseChk/reuseWater` + (pageNo ? `/${pageNo}` : "/1") + @@ -216,7 +216,7 @@ export const rswtChkgdnActionsExport: (set: any) => rswtChkgdnActions = ( ) => { try { const currentYear = new Date().getFullYear().toString(); - const rswtChkRes = await getCountFetch( + const rswtChkRes = await getFetchWithCount( "wr", `/reuseChk/reuseWater` + (pageNo ? `/${pageNo}` : "/1") + diff --git a/apps/wr/app/store/prfmncSttn/chkgdnSttn/rainChkgdnSttn/index.tsx b/apps/wr/app/store/prfmncSttn/chkgdnSttn/rainChkgdnSttn/index.tsx index e51c912d9..059433593 100644 --- a/apps/wr/app/store/prfmncSttn/chkgdnSttn/rainChkgdnSttn/index.tsx +++ b/apps/wr/app/store/prfmncSttn/chkgdnSttn/rainChkgdnSttn/index.tsx @@ -1,7 +1,7 @@ import type { TableUpperProps } from "@/app/store/common/type/header"; import type { rainChkZodType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn"; import { rainMainHeadNmExport, type chHeadType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList"; -import { getCountFetch } from "@/components/lib/api"; +import { getFetchWithCount } from "@/components/lib/api"; import type { SelectDataType } from "@common/business_components/ui"; import { buildingExcelRainSttn } from "./excel"; import { rainChkgdnHeadSttnExport, ruseSttnHeadListExport, splSttnHeadListExport } from "./headList"; @@ -17,8 +17,6 @@ const ruseHeadList = ruseSttnHeadListExport; const splHeadList = splSttnHeadListExport; const rainHeadNm = rainMainHeadNmExport -const rainChk: rainChkZodType[] = []; - export type rainChkSttnState = { rainChkSttn: { rainChkRes: rainChkZodType[]; @@ -60,7 +58,7 @@ export const rainChkSttnStateExport: rainChkSttnState = { const initFetch = async (pageNo: number, cnt: number, txt: string | undefined, sgg: string | undefined) => { const currentYear = new Date().getFullYear().toString(); - const rainRes = await getCountFetch( + const rainRes = await getFetchWithCount( "wr", `/reuseChk/rainy` + (pageNo ? `/${pageNo}` : "/1") + @@ -100,7 +98,7 @@ export const rainChkSttnActionsExport: (set: any) => rainChkSttnActions = ( ) => { try { const currentYear = new Date().getFullYear(); - const rainChkRes = await getCountFetch( + const rainChkRes = await getFetchWithCount( "wr", `/reuseChk/rainy` + (pageNo ? `/${pageNo}` : "/1") + diff --git a/apps/wr/app/store/prfmncSttn/chkgdnSttn/rswtChkgdnSttn/index.tsx b/apps/wr/app/store/prfmncSttn/chkgdnSttn/rswtChkgdnSttn/index.tsx index 251ff1fa2..9db75de07 100644 --- a/apps/wr/app/store/prfmncSttn/chkgdnSttn/rswtChkgdnSttn/index.tsx +++ b/apps/wr/app/store/prfmncSttn/chkgdnSttn/rswtChkgdnSttn/index.tsx @@ -1,10 +1,10 @@ -import { rswtHeadNmExport } from "@/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/headList"; -import { rswtChkSttnHeadListExport, rswtDetailSttnHeadListExport } from "./headList"; import type { TableUpperProps } from "@/app/store/common/type/header"; import type { chHeadType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList"; import type { rswtChkZodType } from "@/app/store/prfmncReg/chkgdnReg/rswrtChkgdn"; -import { getFetch } from "@/components/lib/api"; +import { rswtHeadNmExport } from "@/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/headList"; +import { getFetch, getFetchWithCount } from "@/components/lib/api"; import { buildingExcelRswtSttn } from "./excel"; +import { rswtChkSttnHeadListExport, rswtDetailSttnHeadListExport } from "./headList"; const rswtChkHeadList = rswtChkSttnHeadListExport; const rswtDetailHeadList = rswtDetailSttnHeadListExport; @@ -113,7 +113,7 @@ export const rswtChkSttnActionsExport: (set: any) => rswtChkSttnActions = ( ) => { try { const currentYear = new Date().getFullYear().toString(); - const rswtChkRes = await getCountFetch( + const rswtChkRes = await getFetchWithCount( "wr", `/reuseChk/reuseWater` + (pageNo ? `/${pageNo}` : "/1") + diff --git a/apps/wr/components/ui/table/chkgdTable/rainChkgd/rainChkgdTable.tsx b/apps/wr/components/ui/table/chkgdTable/rainChkgd/rainChkgdTable.tsx index 2525cfd27..aba225687 100644 --- a/apps/wr/components/ui/table/chkgdTable/rainChkgd/rainChkgdTable.tsx +++ b/apps/wr/components/ui/table/chkgdTable/rainChkgd/rainChkgdTable.tsx @@ -2,7 +2,7 @@ import { headMakeprfmnc } from "@/app/ruseFcltMng/prfmncReg/chkgdnReg/TableHead"; import { useWrStore } from "@/app/store"; import type { TableUpperProps } from "@/app/store/common/type/header"; -import { type rainChkZodType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn"; +import { rainPageSize, type rainChkZodType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn"; import type { chHeadType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList"; import { tableData } from "@/components/lib/func/commom"; import { diff --git a/packages/common/business_components/src/ui/loadingBar/index.tsx b/packages/common/business_components/src/ui/loadingBar/index.tsx new file mode 100644 index 000000000..4d637dd25 --- /dev/null +++ b/packages/common/business_components/src/ui/loadingBar/index.tsx @@ -0,0 +1,20 @@ +import { LoadingWithAnimation } from '@common/components/ui/loading'; +import React from 'react'; + +interface LoadingOverlayProps { + isLoading: boolean; +} + +const LoadingOverlay: React.FC<LoadingOverlayProps> = ({ isLoading }) => { + if (!isLoading) return null; + + return ( + <div className="fixed inset-0 flex items-center justify-center bg-black bg-opacity-50 z-50"> + <div className="relative w-16 h-16"> + <LoadingWithAnimation color="primary" /> + </div> + </div> + ); +}; + +export default LoadingOverlay; \ No newline at end of file From 5b62171b01d201329486c4dbc3c0bb01d87e8cb1 Mon Sep 17 00:00:00 2001 From: swcho <swcho@mind-one.co.kr> Date: Fri, 4 Jul 2025 17:26:10 +0900 Subject: [PATCH 071/108] =?UTF-8?q?feat:=20-=20=ED=8C=8C=EC=9D=BC=20?= =?UTF-8?q?=ED=99=95=EC=9E=A5=EC=9E=90=20=EA=B2=80=EC=A6=9D=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EA=B5=AC=ED=98=84=20=EB=B0=8F=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9=20-=20=ED=99=98=EA=B2=BD=EC=B2=AD,=20?= =?UTF-8?q?=EC=8B=9C=EB=8F=84,=20=EC=8B=9C=EA=B5=B0=EA=B5=AC,=20=ED=95=98?= =?UTF-8?q?=EC=88=98=EC=B2=98=EB=A6=AC=EC=9E=A5=20=EC=9E=85=EB=A0=A5=20?= =?UTF-8?q?=EA=B2=80=EC=A6=9D=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20=EB=B0=8F=20=EC=A0=81=EC=9A=A9=20-=20RDII?= =?UTF-8?q?=20=EB=B6=84=EC=84=9D=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=ED=95=98?= =?UTF-8?q?=EC=88=98=EC=B2=98=EB=A6=AC=EC=9E=A5=20=EC=84=A0=ED=83=9D=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EC=97=90=EB=9F=AC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/(with-base-layout)/fileValidation.tsx | 48 ++++++ .../(with-base-layout)/ii/irsetting/page.tsx | 66 +++++-- .../app/(with-base-layout)/ii/rdii/page.tsx | 77 +++++---- .../selectionValidation.tsx | 161 ++++++++++++++++++ 4 files changed, 303 insertions(+), 49 deletions(-) create mode 100644 apps/sn/app/(with-base-layout)/fileValidation.tsx create mode 100644 apps/sn/app/(with-base-layout)/selectionValidation.tsx diff --git a/apps/sn/app/(with-base-layout)/fileValidation.tsx b/apps/sn/app/(with-base-layout)/fileValidation.tsx new file mode 100644 index 000000000..c0711a7d2 --- /dev/null +++ b/apps/sn/app/(with-base-layout)/fileValidation.tsx @@ -0,0 +1,48 @@ +/** + * 파일 확장자 검증 유틸리티 + */ + +// 파일 확장자 검증 함수 +export const validateFileExtension = (file: File, allowedExtensions: string[]): boolean => { + const fileName = file.name.toLowerCase(); + return allowedExtensions.some(ext => fileName.endsWith(ext.toLowerCase())); +}; + +// 파일 확장자 검증 결과 타입 +export interface FileValidationResult { + isValid: boolean; + errorMessage?: string; +} + +// 파일 검증 설정 타입 +export interface FileValidationConfig { + file: File; + allowedExtensions: string[]; + fileDescription: string; +} + +/** + * 여러 파일의 확장자를 한 번에 검증하는 함수 + * @param validationConfigs 검증할 파일들의 설정 배열 + * @returns 검증 결과. 실패 시 첫 번째 에러 메시지 반환 + */ +export const validateMultipleFiles = (validationConfigs: FileValidationConfig[]): FileValidationResult => { + for (const config of validationConfigs) { + if (!validateFileExtension(config.file, config.allowedExtensions)) { + const allowedExtStr = config.allowedExtensions.join(', '); + return { + isValid: false, + errorMessage: `${config.fileDescription} 파일은 ${allowedExtStr} 확장자만 업로드 가능합니다.` + }; + } + } + + return { isValid: true }; +}; + +// 자주 사용되는 확장자 상수 +export const FILE_EXTENSIONS = { + INP: ['.inp'], + EXCEL: ['.xls', '.xlsx', '.xlsm', '.xltx'], + ALL_OFFICE: ['.xls', '.xlsx', '.xlsm', '.xltx', '.doc', '.docx', '.ppt', '.pptx'] +}; \ No newline at end of file diff --git a/apps/sn/app/(with-base-layout)/ii/irsetting/page.tsx b/apps/sn/app/(with-base-layout)/ii/irsetting/page.tsx index edee9b4ce..0e7a5c331 100644 --- a/apps/sn/app/(with-base-layout)/ii/irsetting/page.tsx +++ b/apps/sn/app/(with-base-layout)/ii/irsetting/page.tsx @@ -6,11 +6,13 @@ import { DateMonthPicker, filePicked } from '@common/business_components/ui' import ListDiv from '@common/business_components/ui/list_div' import { Input } from '@common/components/ui' import { zodResolver } from '@hookform/resolvers/zod' -import { useState, useEffect } from 'react' +import { useState } from 'react' import { useForm } from 'react-hook-form' import { z } from 'zod' import { excelToFlowRateData, excelToMatchingResultData, excelToTimePatternData } from '@/components/lib/utils' import BaseLayout from '../BaseLayout' +import { validateMultipleFiles, FILE_EXTENSIONS, type FileValidationConfig } from '../../fileValidation'; +import { validateBasicSelectionsWithAlert } from '../../selectionValidation'; const formSchema = z.object({ managenm: z.string(), @@ -148,6 +150,17 @@ const IrSetting = () => { const handleAnalysisClick = async () => { setIsLoadingResult(true); // 로딩 상태 시작 + // 기본 선택 항목들 검증 (환경청, 시도, 시군구, 하수처리장) + if (!validateBasicSelectionsWithAlert(select1, select2, select3, select4, { + requireEnv: true, + requireSido: true, + requireSigungu: true, + requireTreatmentPlant: true + })) { + setIsLoadingResult(false); + return; + } + console.log(uploadedFilesData); const file1 = uploadedFilesData[0]; // 오수관망도 const file2 = uploadedFilesData[1]; // 수용가-맨홀 매칭결과 @@ -167,6 +180,42 @@ const IrSetting = () => { return; } + // 파일 확장자 검증 (분리된 유틸리티 사용) + const validationConfigs: FileValidationConfig[] = [ + { + file: file1.file, + allowedExtensions: FILE_EXTENSIONS.INP, + fileDescription: '오수관망도' + }, + { + file: file2.file, + allowedExtensions: FILE_EXTENSIONS.EXCEL, + fileDescription: '수용가-맨홀 매칭결과' + }, + { + file: file3.file, + allowedExtensions: FILE_EXTENSIONS.EXCEL, + fileDescription: '오수량 시간패턴' + } + ]; + + // 유량자료 파일이 있는 경우 검증 목록에 추가 + if (file4?.file) { + validationConfigs.push({ + file: file4.file, + allowedExtensions: FILE_EXTENSIONS.EXCEL, + fileDescription: '유량자료' + }); + } + + // 파일 확장자 검증 실행 + const validationResult = validateMultipleFiles(validationConfigs); + if (!validationResult.isValid) { + setIsLoadingResult(false); + alert(validationResult.errorMessage); + return; + } + // 오수전환율 값 0 ~ 1 사이인지 검증 if (input1 === '' || input1 === null || input1 === undefined) { setIsLoadingResult(false); @@ -394,21 +443,6 @@ const IrSetting = () => { </div> </ListDiv> </div>]} - {/* <div className="flex items-center gap-4"> - <FormSection - name='region' form={form} titleNm='▷ 유량자료 등록'> - <FileUploadForm - key={`file-upload-3-${formResetKey}`} // 폼 초기화를 위한 key - onFileSelect={(file, cd, id) => handleFileSelection(3, file, cd, id)} - showButton={false} - showPreview={true} - accept={".xls, .xlsx, .xlsm, .xltx"} - fileIdx={3} /> - </FormSection> - <div className="flex justify-center mt-1"> - <Button size="sm" type="submit" onClick={handleAnalysisClick}>침입수분석 실행</Button> - </div> - </div> */} </BaseLayout> </> ) diff --git a/apps/sn/app/(with-base-layout)/ii/rdii/page.tsx b/apps/sn/app/(with-base-layout)/ii/rdii/page.tsx index 0ce49c797..970c6d1a9 100644 --- a/apps/sn/app/(with-base-layout)/ii/rdii/page.tsx +++ b/apps/sn/app/(with-base-layout)/ii/rdii/page.tsx @@ -13,6 +13,8 @@ import { useFieldArray, useForm } from "react-hook-form" import { z } from "zod" import { excelToMatchingResultData, excelToRainData } from '@/components/lib/utils' import { EditableFormTable, type EditableTableHeadItem } from '../../../components/ui/EditableFormTable' +import { validateMultipleFiles, FILE_EXTENSIONS, type FileValidationConfig } from '../../fileValidation'; +import { validateBasicSelectionsWithAlert } from '../../selectionValidation'; // zod 스키마 정의 @@ -167,7 +169,18 @@ export default function RTKForm() { // 침입수 분석 실행 버튼 클릭 핸들러 const handleAnalysisClick = async () => { setIsLoadingResult(true); // 로딩 상태 시작 - + + // 기본 선택 항목들 검증 (환경청, 시도, 시군구, 하수처리장) + if (!validateBasicSelectionsWithAlert(select1, select2, select3, select4, { + requireEnv: true, + requireSido: true, + requireSigungu: true, + requireTreatmentPlant: true + })) { + setIsLoadingResult(false); + return; + } + const formData = form.getValues(); console.log(uploadedFilesData); const file1 = uploadedFilesData[0]; // 오수관망도 @@ -185,6 +198,33 @@ export default function RTKForm() { return; } + // 파일 확장자 검증 (분리된 유틸리티 사용) + const validationConfigs: FileValidationConfig[] = [ + { + file: file1.file, + allowedExtensions: FILE_EXTENSIONS.INP, + fileDescription: '오수관망도' + }, + { + file: file2.file, + allowedExtensions: FILE_EXTENSIONS.EXCEL, + fileDescription: '수용가-맨홀 매칭결과' + }, + { + file: file3.file, + allowedExtensions: FILE_EXTENSIONS.EXCEL, + fileDescription: '오수량 시간패턴' + } + ]; + + // 파일 확장자 검증 실행 + const validationResult = validateMultipleFiles(validationConfigs); + if (!validationResult.isValid) { + setIsLoadingResult(false); + alert(validationResult.errorMessage); + return; + } + // 오수전환율 값 0 ~ 1 사이인지 검증 const rateValue = form.getValues().rate; if (rateValue === '' || rateValue === null || rateValue === undefined) { @@ -193,7 +233,7 @@ export default function RTKForm() { alert('오수전환율을 입력해주세요.'); return; } - + const numericInput1 = parseFloat(rateValue); if (isNaN(numericInput1) || numericInput1 < 0 || numericInput1 > 1) { setIsLoadingResult(false); @@ -341,35 +381,6 @@ export default function RTKForm() { console.log('Page changed:', page) } - // 선택된 값을 처리하는 공통 함수 - const handleSelectValueChange = ( - val: string | number, - setter: (value: string | number) => void - ) => { - // val이 0이 아닐 경우 .을 기준으로 슬라이싱해서 두번째 값을 설정 - if (val && val !== '0' && val !== 0) { - const valString = String(val); - const parts = valString.split('.'); - if (parts.length > 1 && parts[1] !== undefined) { - setter(parts[1]); // 두 번째 값 (인덱스 1) - } else { - setter(val); // .이 없으면 원래 값 사용 - } - } else { - setter('0'); // val이 0이면 '0' 설정 - } - }; - - // 하수처리장 변경 핸들러 - const handleSwageTreatmentPlantChange = (val: string | number) => { - handleSelectValueChange(val, setSelect4); - }; - - // 하수처리구역 변경 핸들러 - const handleSwageProcessingZoneChange = (val: string | number) => { - handleSelectValueChange(val, setSelect5); - }; - return ( // <BaseLayout searchBox={{ title: '분석 대상지역 선택' }} titleName={{ mainTitle: 'RDII 분석', firstCardTitle: 'RDII 분석자료 입력', secondCardTitle: '실측자료 등록' }} form={form} onSubmitEvent={() => { }} graphBtn={false} > <BaseLayout @@ -386,9 +397,9 @@ export default function RTKForm() { sigungu={select3} setSigungu={setSelect3} selectedSwageTreatmentPlant={select4} - onSwageTreatmentPlantChange={handleSwageTreatmentPlantChange} + onSwageTreatmentPlantChange={setSelect4} selectedSwageProcessingZone={select5} - onSwageProcessingZoneChange={handleSwageProcessingZoneChange} + onSwageProcessingZoneChange={setSelect5} isLoading={isLoadingResult} onAnalysis={handleAnalysisClick} > diff --git a/apps/sn/app/(with-base-layout)/selectionValidation.tsx b/apps/sn/app/(with-base-layout)/selectionValidation.tsx new file mode 100644 index 000000000..8cc506d0e --- /dev/null +++ b/apps/sn/app/(with-base-layout)/selectionValidation.tsx @@ -0,0 +1,161 @@ +/** + * 선택 항목 검증 유틸리티 + */ + +export interface ApiSelectOption { + text: string; + val: string; +} + +// 선택 항목 검증 설정 타입 +export interface SelectionValidationConfig { + value: string | number | ApiSelectOption; + description: string; + required: boolean; +} + +// 선택 항목 검증 결과 타입 +export interface SelectionValidationResult { + isValid: boolean; + errorMessage?: string; +} + +/** + * 단일 선택 항목이 유효한지 검증하는 함수 + * @param value 검증할 값 + * @param isRequired 필수 선택 여부 + * @returns 유효한지 여부 + */ +export const isValidSelection = ( + value: string | number | ApiSelectOption, + isRequired: boolean = true +): boolean => { + if (!isRequired) return true; + + if (typeof value === 'object' && value !== null) { + // ApiSelectOption 객체인 경우 + return value.val !== '0' && value.val !== '' && value.val !== null && value.val !== undefined; + } + + // 문자열 또는 숫자인 경우 + return value !== '0' && value !== 0 && value !== '' && value !== null && value !== undefined; +}; + +/** + * 여러 선택 항목을 한 번에 검증하는 함수 + * @param validationConfigs 검증할 선택 항목들의 설정 배열 + * @returns 검증 결과. 실패 시 첫 번째 에러 메시지 반환 + */ +export const validateMultipleSelections = ( + validationConfigs: SelectionValidationConfig[] +): SelectionValidationResult => { + for (const config of validationConfigs) { + if (!isValidSelection(config.value, config.required)) { + return { + isValid: false, + errorMessage: `${config.description}을(를) 선택해주세요.` + }; + } + } + + return { isValid: true }; +}; + +/** + * 여러 선택 항목을 한 번에 검증하고 alert 표시하는 함수 + * @param validationConfigs 검증할 선택 항목들의 설정 배열 + * @returns 검증 결과 + */ +export const validateMultipleSelectionsWithAlert = ( + validationConfigs: SelectionValidationConfig[] +): boolean => { + const result = validateMultipleSelections(validationConfigs); + if (!result.isValid && result.errorMessage) { + alert(result.errorMessage); + return false; + } + return true; +}; + +/** + * 기본 선택 항목들 검증 (환경청, 시도, 시군구, 하수처리장) + * @param select1 환경청코드 + * @param select2 시도코드 + * @param select3 시군구 객체 + * @param select4 하수처리장코드 + * @param options 각 항목의 필수 여부 설정 + * @returns 검증 결과 + */ +export const validateBasicSelections = ( + select1: string, + select2: string, + select3: ApiSelectOption, + select4: string | number, + options: { + requireEnv?: boolean; + requireSido?: boolean; + requireSigungu?: boolean; + requireTreatmentPlant?: boolean; + } = {} +): SelectionValidationResult => { + const { + requireEnv = true, + requireSido = true, + requireSigungu = true, + requireTreatmentPlant = true + } = options; + + const validationConfigs: SelectionValidationConfig[] = [ + { + value: select1, + description: '환경청', + required: requireEnv + }, + { + value: select2, + description: '시도', + required: requireSido + }, + { + value: select3, + description: '시군구', + required: requireSigungu + }, + { + value: select4, + description: '하수처리장', + required: requireTreatmentPlant + } + ]; + + return validateMultipleSelections(validationConfigs); +}; + +/** + * 기본 선택 항목들 검증 및 alert 표시 + * @param select1 환경청코드 + * @param select2 시도코드 + * @param select3 시군구 객체 + * @param select4 하수처리장코드 + * @param options 각 항목의 필수 여부 설정 + * @returns 검증 결과 + */ +export const validateBasicSelectionsWithAlert = ( + select1: string, + select2: string, + select3: ApiSelectOption, + select4: string | number, + options: { + requireEnv?: boolean; + requireSido?: boolean; + requireSigungu?: boolean; + requireTreatmentPlant?: boolean; + } = {} +): boolean => { + const result = validateBasicSelections(select1, select2, select3, select4, options); + if (!result.isValid && result.errorMessage) { + alert(result.errorMessage); + return false; + } + return true; +}; \ No newline at end of file From 76e9651a91cb67ebfdcb43adbd43b5d6a912fdda Mon Sep 17 00:00:00 2001 From: JIeunhuh <entks301@naver.com> Date: Fri, 4 Jul 2025 17:40:43 +0900 Subject: [PATCH 072/108] =?UTF-8?q?featureData=EC=97=86=EC=9D=84=EB=95=8C?= =?UTF-8?q?=EB=8F=84=20getProperties=EC=86=8D=EC=84=B1=20=EB=84=98?= =?UTF-8?q?=EA=B8=B0=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../business_components/src/ui/map/Gis.js | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/packages/common/business_components/src/ui/map/Gis.js b/packages/common/business_components/src/ui/map/Gis.js index dd8135d1a..ef5c21d5b 100644 --- a/packages/common/business_components/src/ui/map/Gis.js +++ b/packages/common/business_components/src/ui/map/Gis.js @@ -46,7 +46,7 @@ function Gis({ const gisCore = useMemo(() => new GisApp.core({ targetId: mapRef.current }), []); const mapInstanceRef = useRef(null); const checkedLayerIds = useRef([]); - const [viewMode, setViewMode] = useState("detail"); + // 팝업 데이터를 저장할 상태와 오버레이 참조를 저장할 ref 추가 // const [facilityInfoPopupData, setFacilityInfoPopupData] = useState([]); @@ -347,7 +347,7 @@ function Gis({ setCheckedLayers(checkedIds); setCheckedLayersData(checkedNodesData); onCheckedLayersChange?.(checkedIds, checkedNodesData); - }, 0); + }, 0); }, [onCheckedLayersChange]); return ( @@ -359,7 +359,12 @@ function Gis({ visible={tabVisible} setVisible={setTabVisible} onCheckedChange={handleCheckedLayersChange} - onLayerIconClick={(nodeNm)=>{setLayerNm(nodeNm); setIsOpen(true); setViewMode("detail")}} + onLayerIconClick={(nodeNm) => { + setLayerNm(nodeNm); setIsOpen(true); const dummyFeature = { + getProperties: () => ({}), // 최소 구조 + }; + onFeatureSelect(dummyFeature); + }} /> {/* <GisContent onFeatureSelect={handleFeatureSelect} isSearch={isSearch} isSearchModalOpen={isSearchModalOpen} setIsSearchModalOpen={setIsSearchModalOpen} /> */} @@ -367,20 +372,19 @@ function Gis({ {/* DetailView가 존재할 때만 렌더링 isSearchModalOpen이 true면 visible=false로 팝업 숨김 */} {isOpen && !isSearchModalOpen && ( <DetailView - featureData={featureData} + featureData={featureData || { getProperties: () => ({}) }} seletectedLayerNm={layerNm} - viewMode={viewMode} onClose={closePopup} // 닫기 콜백 전달 /> )} {/* SearchView가 존재할 때만 렌더링 */} - {SearchView && ( - <SearchView - open={isSearchModalOpen} - onOpenChange={setIsSearchModalOpen} - onFeatureSelect={handleFeatureSelect} - /> - )} + {SearchView && ( + <SearchView + open={isSearchModalOpen} + onOpenChange={setIsSearchModalOpen} + onFeatureSelect={handleFeatureSelect} + /> + )} <GisLonLat /> <GisToolBar /> <GisMapToggle /> From 60c06bd50544069170f7bc50bc304cda1239ca69 Mon Sep 17 00:00:00 2001 From: jyha00 <jyha@mind-one.co.kr> Date: Fri, 4 Jul 2025 18:13:07 +0900 Subject: [PATCH 073/108] =?UTF-8?q?=EC=A7=80=EB=8F=84=EC=A0=90=EA=B2=80?= =?UTF-8?q?=EA=B2=B0=EA=B3=BC=20=EB=93=B1=EB=A1=9D=20=EB=B0=8F=20=ED=98=84?= =?UTF-8?q?=ED=99=A9=20merge=20=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../prfmncSttn/chkgdnSttn/page.tsx | 4 +- .../prfmncReg/chkgdnReg/rainChkgdn/index.tsx | 41 +++++++++---------- .../prfmncReg/chkgdnReg/rswrtChkgdn/index.tsx | 17 ++++---- .../chkgdnSttn/rainChkgdnSttn/index.tsx | 12 +++--- .../chkgdnSttn/rswtChkgdnSttn/index.tsx | 22 ++++++---- .../ui/modal/chkgd/chkgdExcel/chkgdExcel.tsx | 21 ++++------ .../ui/modal/chkgd/chkgdModal/chkgdModal.tsx | 2 +- .../guideDetailTable/guideDetailTable.tsx | 27 ++++++------ .../chkgdTable/rswtChkgd/rswtChkgdTable.tsx | 1 - 9 files changed, 77 insertions(+), 70 deletions(-) diff --git a/apps/wr/app/ruseFcltMng/prfmncSttn/chkgdnSttn/page.tsx b/apps/wr/app/ruseFcltMng/prfmncSttn/chkgdnSttn/page.tsx index f8669bd2c..a191de9ee 100644 --- a/apps/wr/app/ruseFcltMng/prfmncSttn/chkgdnSttn/page.tsx +++ b/apps/wr/app/ruseFcltMng/prfmncSttn/chkgdnSttn/page.tsx @@ -79,7 +79,8 @@ export default function Page() { } } }; -const rainPageChange = (pageNo: number) => { + + const rainPageChange = (pageNo: number) => { if (pageNo > 0) { setRainPageNation(pageNo); rainChkSttnInit(pageNo, rainPageSize, searchYear, commonCd); @@ -96,6 +97,7 @@ const rainPageChange = (pageNo: number) => { useEffect(() => { if (!rainChkSttn.isInit) { isRainSttn(true) + console.log("여기", rainPageSize) rainChkSttnInit(1, rainPageSize, new Date().getFullYear().toString(), commonCd); setIsModal({ data: {}, open: false }); } diff --git a/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/index.tsx b/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/index.tsx index 36e8672f5..069f1f760 100644 --- a/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/index.tsx +++ b/apps/wr/app/store/prfmncReg/chkgdnReg/rainChkgdn/index.tsx @@ -45,16 +45,15 @@ const rainChk: rainChkZodType[] = []; export type rainChkgdnState = { rainChkgdn: { rainChkgdnHeadList: TableUpperProps[]; - rainSplHeadNm:chHeadType[] - rainRuseHeadNm:chHeadType[] + rainSplHeadNm: chHeadType[] + rainRuseHeadNm: chHeadType[] rainChk: rainChkZodType[]; - rainHeadNm: chHeadType[]; ruseHeadList: TableUpperProps[]; splHeadList: TableUpperProps[]; isInit: boolean; isModalClose: boolean; fcltDiv: SelectDataType[]; - rainMainHeadNm:chHeadType[] + rainMainHeadNm: chHeadType[] excelList: []; isSttn: boolean; rainChkCount: number; @@ -93,8 +92,8 @@ export const rainChkgdnStateExport: rainChkgdnState = { rainChkgdn: { rainChkgdnHeadList: rainChkgdnHeadList, rainRuseHeadNm: rainRuseHeadNm, - rainSplHeadNm:rainSplHeadNm, - rainMainHeadNm:rainMainHeadNm, + rainSplHeadNm: rainSplHeadNm, + rainMainHeadNm: rainMainHeadNm, rainChk: rainChk, splHeadList: splHeadList, ruseHeadList: ruseHeadList, @@ -136,10 +135,10 @@ const initFetch = async ( const rainRes = await getFetchWithCount( "wr", `/reuseChk/rainy` + - (pageNo ? `/${pageNo}` : "/1") + - (cnt ? `/${cnt}` : "/10") + - (year ? `/${year}` : `/${currentYear}`) + - (sgg ? `/${sgg}` : "/0") + (pageNo ? `/${pageNo}` : "/1") + + (cnt ? `/${cnt}` : "/10") + + (year ? `/${year}` : `/${currentYear}`) + + (sgg ? `/${sgg}` : "/0") ); return rainRes; @@ -204,7 +203,7 @@ export const rainChkgdnActionsExport: (set: any) => rainChkgdnActions = ( year: string, sggCd: string ) => { - set((state: rainChkgdnType) => ({ rainChkgdn: { ...state.rainChkgdn, isLoading: true } })); + // set((state: rainChkgdnType) => ({ rainChkgdn: { ...state.rainChkgdn, isLoading: true } })); try { const rainChkRes = await initFetch(pageNo, cnt, year, sggCd); set((state: rainChkgdnState) => ({ @@ -213,17 +212,17 @@ export const rainChkgdnActionsExport: (set: any) => rainChkgdnActions = ( ...state.rainChkgdn, rainChk: rainChkRes.data, rainChkCount: rainChkRes.count, - isInit: true, // 무한 루프 방지를 위해 isInit을 true로 설정 + isInit: false, // 무한 루프 방지를 위해 isInit을 true로 설정 }, })); } catch (error) { console.log(error); // 에러가 발생하더라도 무한 루프에 빠지지 않도록 isInit을 true로 설정합니다. - set((state: rainChkgdnType) => ({ - rainChkgdn: { ...state.rainChkgdn, isInit: true }, - })); + // set((state: rainChkgdnType) => ({ + // rainChkgdn: { ...state.rainChkgdn, isInit: true }, + // })); } finally { - set((state: rainChkgdnType) => ({ rainChkgdn: { ...state.rainChkgdn, isLoading: false } })); + // set((state: rainChkgdnType) => ({ rainChkgdn: { ...state.rainChkgdn, isLoading: false } })); } }, rainChkNmSearch: async ( @@ -238,11 +237,11 @@ export const rainChkgdnActionsExport: (set: any) => rainChkgdnActions = ( const rainChkRes = await getFetchWithCount( "wr", `/reuseChk/rainy` + - (pageNo ? `/${pageNo}` : "/1") + - (cnt ? `/${cnt}` : "/10") + - (year ? `/${year}` : `/${currentYear}`) + - (sggCd ? `/${sggCd}` : "/0") + - (txt ? `/${txt}` : "") + (pageNo ? `/${pageNo}` : "/1") + + (cnt ? `/${cnt}` : "/10") + + (year ? `/${year}` : `/${currentYear}`) + + (sggCd ? `/${sggCd}` : "/0") + + (txt ? `/${txt}` : "") ); return set((state: rainChkgdnState) => ({ ...state, diff --git a/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/index.tsx b/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/index.tsx index 49e1c0906..4f647da1e 100644 --- a/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/index.tsx +++ b/apps/wr/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/index.tsx @@ -185,10 +185,12 @@ export const rswtChkgdnActionsExport: (set: any) => rswtChkgdnActions = ( year: string, sggCd: string ) => { - set((state: rswtChkgdnType) => ({ rswtChkgdn: { ...state.rswtChkgdn, isLoading: true } })); + // set((state: rswtChkgdnType) => ({ rswtChkgdn: { ...state.rswtChkgdn, isLoading: true } })); try { const rswtChkRes = await initFetch(pageNo, cnt, year, sggCd); + console.log("rswtChkRes", rswtChkRes) + set((state: rswtChkgdnState) => ({ ...state, rswtChkgdn: { @@ -199,12 +201,12 @@ export const rswtChkgdnActionsExport: (set: any) => rswtChkgdnActions = ( }, })); } catch (error) { - console.log(error); - set((state: rswtChkgdnType) => ({ - rswtChkgdn: { ...state.rswtChkgdn, isInit: true }, - })); + // console.log(error); + // set((state: rswtChkgdnType) => ({ + // rswtChkgdn: { ...state.rswtChkgdn, isInit: true }, + // })); } finally { - set((state: rswtChkgdnType) => ({ rswtChkgdn: { ...state.rswtChkgdn, isLoading: false } })); + // set((state: rswtChkgdnType) => ({ rswtChkgdn: { ...state.rswtChkgdn, isLoading: false } })); } }, rswtChkNmSearch: async ( @@ -215,6 +217,7 @@ export const rswtChkgdnActionsExport: (set: any) => rswtChkgdnActions = ( txt: string ) => { try { + console.log("여기") const currentYear = new Date().getFullYear().toString(); const rswtChkRes = await getFetchWithCount( "wr", @@ -232,7 +235,7 @@ export const rswtChkgdnActionsExport: (set: any) => rswtChkgdnActions = ( ...state.rswtChkgdn, rswtChkRes: rswtChkRes.data, rswtChkCount: rswtChkRes.count, - isInit: false, + // isInit: false, }, })); } catch (error) { diff --git a/apps/wr/app/store/prfmncSttn/chkgdnSttn/rainChkgdnSttn/index.tsx b/apps/wr/app/store/prfmncSttn/chkgdnSttn/rainChkgdnSttn/index.tsx index 059433593..e99bdb7a8 100644 --- a/apps/wr/app/store/prfmncSttn/chkgdnSttn/rainChkgdnSttn/index.tsx +++ b/apps/wr/app/store/prfmncSttn/chkgdnSttn/rainChkgdnSttn/index.tsx @@ -22,10 +22,10 @@ export type rainChkSttnState = { rainChkRes: rainChkZodType[]; fcltDiv: SelectDataType[]; rainChkgdnHeadList: TableUpperProps[]; - rainHeadNm:chHeadType[]; + rainHeadNm: chHeadType[]; ruseHeadList: TableUpperProps[]; splHeadList: TableUpperProps[]; - sttnExcelList:[] + sttnExcelList: [] isInit: boolean; isSttn: boolean; rainChkCount: number; @@ -48,7 +48,7 @@ export const rainChkSttnStateExport: rainChkSttnState = { rainChkgdnHeadList: rainChkgdnHeadList, ruseHeadList: ruseHeadList, splHeadList: splHeadList, - rainHeadNm:rainHeadNm, + rainHeadNm: rainHeadNm, isInit: false, sttnExcelList: [], isSttn: false, @@ -125,13 +125,13 @@ export const rainChkSttnActionsExport: (set: any) => rainChkSttnActions = ( ...state, rainChkSttn: { ...state.rainChkSttn, - isSttn:isSttn + isSttn: isSttn }, })); } catch (error) { console.log(error); } - },getRainSttnExcel: async (excelList:rainChkZodType[]) => { + }, getRainSttnExcel: async (excelList: rainChkZodType[]) => { try { const excelData = buildingExcelRainSttn(excelList) @@ -140,7 +140,7 @@ export const rainChkSttnActionsExport: (set: any) => rainChkSttnActions = ( ...state, rainChkSttn: { ...state.rainChkSttn, - sttnExcelList:excelData + sttnExcelList: excelData }, })); } catch (error) { diff --git a/apps/wr/app/store/prfmncSttn/chkgdnSttn/rswtChkgdnSttn/index.tsx b/apps/wr/app/store/prfmncSttn/chkgdnSttn/rswtChkgdnSttn/index.tsx index 9db75de07..7891d73fb 100644 --- a/apps/wr/app/store/prfmncSttn/chkgdnSttn/rswtChkgdnSttn/index.tsx +++ b/apps/wr/app/store/prfmncSttn/chkgdnSttn/rswtChkgdnSttn/index.tsx @@ -20,11 +20,12 @@ export type rswtChkSttnState = { rswtChkRes: rswtChkZodType[]; isInit: boolean; isSttn: boolean; + rswtChkCount: number; }; }; type rswtChkSttnActions = { - rswtChkSttnInit: (searchTxt: string, sggCd: string) => void; + rswtChkSttnInit: (pageNo: number, cnt: number, searchTxt: string, sggCd: string) => void; isRswtSttn: (isSttn: boolean) => void; getRswtSttnExcel: (excelList: rswtChkZodType[]) => void; rswtChkNmSttnSearch: (pageNo: number, cnt: number, year: string, sggCd: string, txt: string) => void; @@ -41,33 +42,36 @@ export const rswtChkStateExport: rswtChkSttnState = { rswtChkRes: [], isInit: false, isSttn: false, + rswtChkCount: 1, }, }; -const initFetch = async (year: string | undefined, sgg: string | undefined) => { +const initFetch = async (pageNo: number, cnt: number, year: string | undefined, sgg: string | undefined) => { const currentYear = new Date().getFullYear().toString(); - const rswtChkRes = await getFetch( + const rswtChkRes = await getFetchWithCount( "wr", - `/reuseChk/reuseWater/` + - (year ? `${year}` : currentYear) + + `/reuseChk/reuseWater` + + (pageNo ? `/${pageNo}` : "/1") + + (cnt ? `/${cnt}` : "/20") + + (year ? `/${year}` : `/${currentYear}`) + (sgg ? `/${sgg}` : "/0") ); return rswtChkRes; }; - export const rswtChkSttnActionsExport: (set: any) => rswtChkSttnActions = ( set: any ) => { return { - rswtChkSttnInit: async (searchTxt: string, sggCd: string) => { + rswtChkSttnInit: async (pageNo: number, cnt: number, searchTxt: string, sggCd: string) => { try { - const rswtChkRes = await initFetch(searchTxt, sggCd); + const rswtChkRes = await initFetch(pageNo, cnt, searchTxt, sggCd); return set((state: rswtChkSttnState) => ({ ...state, rswtChkSttn: { ...state.rswtChkSttn, - rswtChkRes: rswtChkRes, + rswtChkRes: rswtChkRes.data, + rswtChkCount: rswtChkRes.count, isInit: false, }, })); diff --git a/apps/wr/components/ui/modal/chkgd/chkgdExcel/chkgdExcel.tsx b/apps/wr/components/ui/modal/chkgd/chkgdExcel/chkgdExcel.tsx index 8828420b8..5aa67fd3a 100644 --- a/apps/wr/components/ui/modal/chkgd/chkgdExcel/chkgdExcel.tsx +++ b/apps/wr/components/ui/modal/chkgd/chkgdExcel/chkgdExcel.tsx @@ -7,11 +7,9 @@ import { excelToRswtGuideData, excelToRswtPolution, } from "@/app/store/prfmncReg/chkgdnReg/rswrtChkgdn/excel"; -import { ToastFormUi } from "@/components/ui/toast/toastFromUi"; import { filePicked, filePickedArray, - FileUpload, Form, FormField, FormUi, @@ -26,7 +24,7 @@ import { useForm } from "react-hook-form"; import toast from "react-hot-toast"; import type { z } from "zod"; -export default function ChkgdExcel({ type }: { type: string }) { +export default function ChkgdExcel({ type,setIsRegExcel }: { type: string, setIsRegExcel:(open:boolean) =>void }) { const { rswtChkgdn, rainChkExcelDataUPload, rswtChkExceDataUpload } = useWrStore((state) => { return state; @@ -146,29 +144,28 @@ export default function ChkgdExcel({ type }: { type: string }) { const onSubmit = (values: z.infer<typeof chkgdRegExcel>) => { if (values.chckYr === "") { - ToastFormUi({ body: "등록년도를 작성해주세요", title: "지도점검 등록" }); + toast.error("등록년도를 작성해주세요"); return; } if (type === "RNB") { if (excelData === undefined || excelData?.length === 0) { - ToastFormUi({ - body: "빈 파일 및 파일 오류 입니다.", - title: "지도점검 등록", - }); + toast.error("빈 파일 및 파일 오류 입니다."); + return; } else { rainChkExcelDataUPload(excelData as rainChkZodType[]); + setIsRegExcel(false) } } else if (type === "RST") { if (excelRswtData === undefined || excelRswtData?.length === 0) { - ToastFormUi({ - body: "빈 파일 및 파일 오류 입니다.", - title: "중수지도점검 등록", - }); + toast.error("빈 파일 및 파일 오류 입니다."); + return; } else { rswtChkExceDataUpload(excelRswtData as rswtChkZodType[]); + setIsRegExcel(false) + } } }; diff --git a/apps/wr/components/ui/modal/chkgd/chkgdModal/chkgdModal.tsx b/apps/wr/components/ui/modal/chkgd/chkgdModal/chkgdModal.tsx index f3643a094..edbd9e570 100644 --- a/apps/wr/components/ui/modal/chkgd/chkgdModal/chkgdModal.tsx +++ b/apps/wr/components/ui/modal/chkgd/chkgdModal/chkgdModal.tsx @@ -8,7 +8,7 @@ import { Button } from "@common/components/ui"; import React, { useEffect, useState } from "react"; import type { z } from "zod"; -export default function ChkgdModal({ type, obj }: { type: string; obj: any }) { +export default function ChkgdModal({ type, obj, setIsModal }: { type: string; obj: any, setIsModal: (open: boolean) => void }) { const { rainChkgdn, rswtChkgdn, diff --git a/apps/wr/components/ui/table/chkgdTable/guideDetailTable/guideDetailTable.tsx b/apps/wr/components/ui/table/chkgdTable/guideDetailTable/guideDetailTable.tsx index 94be4ebb6..627537dcb 100644 --- a/apps/wr/components/ui/table/chkgdTable/guideDetailTable/guideDetailTable.tsx +++ b/apps/wr/components/ui/table/chkgdTable/guideDetailTable/guideDetailTable.tsx @@ -4,6 +4,7 @@ import { useWrStore } from "@/app/store"; import type { TableUpperProps } from "@/app/store/common/type/header"; import type { } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn"; +import type { chHeadType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn/headList"; import { InputTableBody } from "@/components/lib/func/TableData"; import { Input, @@ -13,16 +14,18 @@ import { TableHeader, TableRow, } from "@common/components/ui"; -import { useEffect, useState, type Dispatch, type SetStateAction } from "react"; +import { useEffect, useState, type Dispatch, type SetStateAction } from "react"; export function GuideDetailsTable({ chgRes, headList, + upheadList, type, setChGuidData, }: { chgRes: any[]; - headList: TableUpperProps[]; + headList: TableUpperProps[]; + upheadList: chHeadType[]; type: string; setChGuidData: (chgRes: any[]) => void; }) { @@ -35,21 +38,21 @@ export function GuideDetailsTable({ useEffect(() => { - setChGuidData(inputData??[] ); + setChGuidData(inputData ?? []); }, [inputData]); - + return ( <> - <div className={`card !m-[1.2rem] !p-0r `}> - <Table variant="secondary"> - <TableHeader className="font-bold">{headMakeprfmnc(headList, rainChkgdn?.rainHeadNm)}</TableHeader> - <TableBody > - {InputTableBody(inputData ?? [],headList, setInputData) } - </TableBody> - </Table> - </div> + <div className={`card !m-[1.2rem] !p-0r `}> + <Table variant="secondary"> + <TableHeader className="font-bold">{headMakeprfmnc(headList, upheadList)}</TableHeader> + <TableBody > + {InputTableBody(inputData ?? [], headList, setInputData)} + </TableBody> + </Table> + </div> </> ); } diff --git a/apps/wr/components/ui/table/chkgdTable/rswtChkgd/rswtChkgdTable.tsx b/apps/wr/components/ui/table/chkgdTable/rswtChkgd/rswtChkgdTable.tsx index bc8581b75..7a43df3d8 100644 --- a/apps/wr/components/ui/table/chkgdTable/rswtChkgd/rswtChkgdTable.tsx +++ b/apps/wr/components/ui/table/chkgdTable/rswtChkgd/rswtChkgdTable.tsx @@ -76,7 +76,6 @@ export default function RswtChkgdTable({ }, [rswtChkgdn.rswtChkPolGuide]); useEffect(() => { - console.log("makeTableData ", makeTableData) getRswtSttnExcel(makeTableData) const selectedData = makeTableData .map((item) => { From f4f3d2a3e9b662f1ef1478848078088a1f5116a8 Mon Sep 17 00:00:00 2001 From: yschoi <yschoi> Date: Fri, 4 Jul 2025 18:36:54 +0900 Subject: [PATCH 074/108] =?UTF-8?q?=EB=AF=B8=EB=8B=88=EB=A7=B5=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wr/app/map/htdflSttn/page.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/wr/app/map/htdflSttn/page.tsx b/apps/wr/app/map/htdflSttn/page.tsx index e74d055d8..7b804b7cd 100644 --- a/apps/wr/app/map/htdflSttn/page.tsx +++ b/apps/wr/app/map/htdflSttn/page.tsx @@ -124,7 +124,7 @@ const Map = () => { treeData={treeData} isSearch={false} gisMaps={gisMaps} - isMiniMap={true} + isMiniMap={false} searchView={undefined} // searchView={(props: any) => ( // <MapSearchBar From 7c34c87aafb0aea6b7d2537c607d71d258f3ccae Mon Sep 17 00:00:00 2001 From: leedj312 <dlehd467@gmail.com> Date: Fri, 4 Jul 2025 19:29:10 +0900 Subject: [PATCH 075/108] =?UTF-8?q?=EC=98=A8=EB=B0=B0=EC=88=98=20=EC=99=84?= =?UTF-8?q?=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/ruseFcltMng/fcltReg/htdflReg/page.tsx | 28 +++++++--- .../ruseFcltMng/fcltReg/htdflReg/index.tsx | 53 ++++++++++++++----- .../ruseFcltMng/fcltReg/rswtReg/index.tsx | 2 +- .../ui/form/fcltReg/bdstReg/bdstReg.tsx | 10 ++-- .../ui/form/fcltReg/htdflReg/htdflRegForm.tsx | 2 - .../ui/form/fcltReg/rswtrReg/rswtrRegForm.tsx | 19 ++++++- .../fcltReg/bdstRegModal/bdstRegModal.tsx | 2 +- 7 files changed, 86 insertions(+), 30 deletions(-) diff --git a/apps/wr/app/ruseFcltMng/fcltReg/htdflReg/page.tsx b/apps/wr/app/ruseFcltMng/fcltReg/htdflReg/page.tsx index 33cfa3bba..9b3688e61 100644 --- a/apps/wr/app/ruseFcltMng/fcltReg/htdflReg/page.tsx +++ b/apps/wr/app/ruseFcltMng/fcltReg/htdflReg/page.tsx @@ -21,9 +21,10 @@ import type { chgBuilding } from "@/app/store/ruseFcltMng/fcltReg/rainReg"; import BdstChgModal from "@/components/ui/modal/fcltReg/bdstChgModal/bdstChgModal"; import type { customHtdflBdst } from "@/app/store/ruseFcltMng/fcltReg/htdflReg"; import { BdstReg } from "@/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal"; +import { Toaster } from "react-hot-toast"; export default function Home() { - const { code, htdfl, initHtdfl, getHtdflBdstData } = useWrStore( + const { code, htdfl, initHtdflReg, getHtdflBdstData ,htdflNmSearch} = useWrStore( (state) => state ); const [searchTxt, setSearchTxt] = useState(""); @@ -42,18 +43,27 @@ export default function Home() { useEffect(() => { if (!htdfl.isInit) { + initHtdflReg('41') } - }, [htdfl.isInit, initHtdfl]); + }, [htdfl.isInit, initHtdflReg]); useEffect(() => { - if (code.sggCd.length > 0) { + if (htdflRes&&htdflRes.length>0) { + + console.log('htdflRes ', htdflRes) const htdflData = tableData(htdflRes, { code }).map( (item: customHtdflBdst) => { + let archArea = item?.archArea.split('.')[0]!=='-1'?item?.archArea.split('.')[0]:'확인불가' + return { ...item, + archArea:archArea + }; } ); setbdstTable(htdflData); + }else{ + setbdstTable([]); } }, [code, htdflRes]); @@ -79,7 +89,7 @@ export default function Home() { // setBdstChgProps({ isOpen: true, chgBuilding: chgBuilding }); // }; useEffect(() => { - getHtdflBdstData(pageNation, "0"); + getHtdflBdstData(pageNation, "41"); }, [pageNation]); return ( @@ -104,7 +114,12 @@ export default function Home() { <Button size={"sm"} onClick={() => { - initHtdfl(searchTxt || ""); + if(searchTxt&&searchTxt.length>0){ + htdflNmSearch(String(41),searchTxt) + }else{ + initHtdflReg(String(41) ); + + } }} > 조회 @@ -160,7 +175,8 @@ export default function Home() { }} /> - {/* <UiModal +<Toaster position="top-center" /> +{/* <UiModal bodyClass={""} open={bdstChgProps.isOpen} body={ diff --git a/apps/wr/app/store/ruseFcltMng/fcltReg/htdflReg/index.tsx b/apps/wr/app/store/ruseFcltMng/fcltReg/htdflReg/index.tsx index f2875e232..317f68e10 100644 --- a/apps/wr/app/store/ruseFcltMng/fcltReg/htdflReg/index.tsx +++ b/apps/wr/app/store/ruseFcltMng/fcltReg/htdflReg/index.tsx @@ -76,7 +76,7 @@ export const hdtflZod = z.object({ .date() .default(() => new Date()) .nullish(), - archArea: z.number().default(0), + archArea: z.string().default(''), archChgArea: z.number().nullish().default(0), rgtr: z.string().nullish().default(null), mdfr: z.string().nullish().default(null), @@ -99,7 +99,8 @@ export type htdflFacState = { }; }; type htdflFacSActions = { - initHtdfl: (code: string) => void; + initHtdflReg: (code: string) => void; + htdflNmSearch: (code: string | number, fcltNm:string) => void; htdflBdstUpdate: (data: customHtdflBdst, code: string | number) => void; htdflBdstReg: (data: customHtdflBdst, code: string | number) => void; getHtdflBdstData: (pageNation: number, code: string | number) => void; @@ -116,8 +117,16 @@ export const htdflFacStateExport: htdflFacState = { }, }; -const initFetch = async (code: string | number) => { - const rswtRes = await getFetch("wr", `/drain/facilityList/1/20/${code}`); +const initFetch = async (code: string) => { + + + console.log(' initFetch code ', code) + let url = `/drain/facilityList/1/20` + + url+=`/${code}` + + + const rswtRes = await getFetch("wr", url); return rswtRes; }; export const htdflFacActionsExport: (set: any) => htdflFacSActions = ( @@ -142,9 +151,9 @@ export const htdflFacActionsExport: (set: any) => htdflFacSActions = ( console.log(e); } }, - initHtdfl: async (code: string) => { + initHtdflReg: async (code: string) => { try { - const htdflRes = await initFetch(code); + const htdflRes = await initFetch( String(code)); set((state: htdflFacState) => { return { ...state, @@ -162,17 +171,18 @@ export const htdflFacActionsExport: (set: any) => htdflFacSActions = ( htdflBdstUpdate: async (data: customHtdflBdst, code: string | number) => { try { - console.log('data ', data) const chData = await makeMdfr(data); - console.log('chData ', chData) - const res = await putFectch("/drain/facility", chData); - if (res.ok) { + + if (res.status) { toast.success("온배수 건축물 수정이 완료되었습니다."); } - const htdflRes = await initFetch(code); + const htdflRes = await getFetch( + "wr", + `/drain/facilityList/1/20/${code}` + ); set((state: htdflFacState) => ({ ...state, htdfl: { @@ -195,7 +205,7 @@ export const htdflFacActionsExport: (set: any) => htdflFacSActions = ( if (res.status ===200) { toast.success("온배수 건축물 등록이 완료되었습니다."); } - const htdflRes = await initFetch(code); + const htdflRes = await initFetch(String(code)); set((state: htdflFacState) => ({ ...state, htdfl: { @@ -208,6 +218,23 @@ export const htdflFacActionsExport: (set: any) => htdflFacSActions = ( toast.error("온배수 건축물 등록 오류입니다.."); } - } + },htdflNmSearch :async(code:string | number, fcltNm:string) =>{ + try { + + const htdflRes = await getFetch("wr", `/drain/facilityList/1/20/${code}/${fcltNm}`); + set((state: htdflFacState) => { + return { + ...state, + htdfl: { + ...state.htdfl, + htdflRes: htdflRes, + isInit: false, + }, + }; + }); + } catch (error) { + console.log(error); + } + }, }; }; diff --git a/apps/wr/app/store/ruseFcltMng/fcltReg/rswtReg/index.tsx b/apps/wr/app/store/ruseFcltMng/fcltReg/rswtReg/index.tsx index 178badd3b..df9c3b543 100644 --- a/apps/wr/app/store/ruseFcltMng/fcltReg/rswtReg/index.tsx +++ b/apps/wr/app/store/ruseFcltMng/fcltReg/rswtReg/index.tsx @@ -11,7 +11,7 @@ import { makeMdfr, makeRstr } from "../bdstReg"; wtrUsqty: z.number({ required_error: validationMessages.required("물사용량(m³)") }).positive({ message: validationMessages.required("물사용량(m³)") }), oblgTrgtWtrUsqty: z.number().nullish().default(0), orgwtKndCd: z.string({ required_error: validationMessages.requiredSelect("원수 종류") }).min(1, { message: validationMessages.requiredSelect("원수 종류") }), - filtrTrtmfctCpct: z.number({ required_error: validationMessages.required("처리시설 용량") }).positive({ message: validationMessages.required("처리시설 용량") }), + filtrTrtmfctCpct: z.number({ required_error: validationMessages.required("처리시설 용량") }), filtrTrtmfctProcs: z.string({ required_error: validationMessages.required("주요처리공정") }).min(1, { message: validationMessages.required("주요처리공정") }), filtrTrtmfctUsgCd: z.string().default("").nullish(), rswtrUsqty: z.number().default(0), diff --git a/apps/wr/components/ui/form/fcltReg/bdstReg/bdstReg.tsx b/apps/wr/components/ui/form/fcltReg/bdstReg/bdstReg.tsx index 8ae9ad4dd..04b36cbc5 100644 --- a/apps/wr/components/ui/form/fcltReg/bdstReg/bdstReg.tsx +++ b/apps/wr/components/ui/form/fcltReg/bdstReg/bdstReg.tsx @@ -89,7 +89,7 @@ export default function BdstReg({ form, type }: { form: any; type?: string }) { return ( <> - <h1 className={formH1Class}>건축물 기본 정보</h1> + <h1 className={formH1Class}> { type !== "HTB"?"건축물 기본 정보": '발전소 기본 정보'}</h1> <div className="grid grid-cols-6 gap-2 mt-4"> <div className="col-span-3"> <FormField @@ -140,10 +140,10 @@ export default function BdstReg({ form, type }: { form: any; type?: string }) { control={form.control} name="bdstNm" render={({ field }) => ( - <FormUi label="건축물명"> + <FormUi label={ type !== "HTB"?"건축물명": '발전소명'}> <Input className={selectClass} - placeholder="건축물 명 입력하세요" + placeholder= {type !== "HTB"?"건축물 명 입력하세요":'발전소명 입력하세요' } type="text" {...field} /> @@ -156,7 +156,7 @@ export default function BdstReg({ form, type }: { form: any; type?: string }) { control={form.control} name="daddr" render={({ field }) => ( - <FormUi label="건축물 주소"> + <FormUi label= { type !== "HTB"?"건축물 주소": '발전소 주소'} > <Input className={selectClass} placeholder="위치를 입력해주세요" @@ -173,7 +173,7 @@ export default function BdstReg({ form, type }: { form: any; type?: string }) { control={form.control} name="frstArchPrmsnDt" render={({ field }) => ( - <FormUi label="최초건축허가일"> + <FormUi label= { type !== "HTB"?"최초건축허가일": '준공년도'} > <DatePicker mode={"single"} field={{ diff --git a/apps/wr/components/ui/form/fcltReg/htdflReg/htdflRegForm.tsx b/apps/wr/components/ui/form/fcltReg/htdflReg/htdflRegForm.tsx index 5547f057b..83051672d 100644 --- a/apps/wr/components/ui/form/fcltReg/htdflReg/htdflRegForm.tsx +++ b/apps/wr/components/ui/form/fcltReg/htdflReg/htdflRegForm.tsx @@ -58,8 +58,6 @@ export default function HtdflRegForm({ if (setIsModal) setIsModal(false); } else { - console.log(' isModal ' ,isModal) - console.log(' values ' ,values) htdflBdstUpdate(values, "41"); } }; diff --git a/apps/wr/components/ui/form/fcltReg/rswtrReg/rswtrRegForm.tsx b/apps/wr/components/ui/form/fcltReg/rswtrReg/rswtrRegForm.tsx index ed0e51af6..8c71d77a9 100644 --- a/apps/wr/components/ui/form/fcltReg/rswtrReg/rswtrRegForm.tsx +++ b/apps/wr/components/ui/form/fcltReg/rswtrReg/rswtrRegForm.tsx @@ -62,7 +62,11 @@ export default function RswtrRegForm({ rswtBdstUpdate(values, "41"); } }; - const cleanRswtData = (raw: any): z.infer<typeof rswtZod> => { + + const filtrTrtmfctCpct = form.watch('reuseBuilding.0.rswtrObj.filtrTrtmfctCpct') + const cleanRswtData = (raw: any): z.infer<typeof rswtZod> => { + + console.log('raw ', raw) return { ...raw, frstArchPrmsnDt: raw?.frstArchPrmsnDt @@ -82,11 +86,20 @@ export default function RswtrRegForm({ rswtrObj: { ...item?.rswtrObj, filtrTrtmfctYn: item?.rainyObj?.filtrTrtmfctYn ?? "Y", + filtrTrtmfctCpct: item?.rswtrObj?.filtrTrtmfctCpct ?? null, + filtrTrtmfctProcs: item?.rswtrObj?.filtrTrtmfctProcs || code.rswtPrscCdMthdCd[0]?.val, + orgwtKndCd: item?.rswtrObj?.orgwtKndCd || code.orgwtKndCd[0]?.val, }, })), }; }; - + // useEffect(() =>{ + // console.log(' formform ', form.watch()) + // },[form.formState]) + useEffect(() =>{ + console.log(' filtrTrtmfctCpct ', filtrTrtmfctCpct) + },[filtrTrtmfctCpct]) + useEffect(() => { console.log("form errors:", form.formState.errors); }, [form.formState.errors]); @@ -227,6 +240,8 @@ export default function RswtrRegForm({ onFocus={() => { // 입력 시 자동으로 확인불가 해제 if (isUnknown && !readonly) { + + console.log('value') field.onChange(""); // 초기화 } }} diff --git a/apps/wr/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal.tsx b/apps/wr/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal.tsx index bee445114..4abe29d4e 100644 --- a/apps/wr/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal.tsx +++ b/apps/wr/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal.tsx @@ -148,7 +148,7 @@ export function BdstReg<T extends MoType>({ moType,setIsModal,isModal }: BdstReg <RainRegForm rainForm={rainBdstForm as customRainBdst} isModal={isModal} setIsModal={setIsModal} /> :moType==='RST' ? <RswtrRegForm rswtForm={rainBdstForm as customRswtBdst} isModal={isModal} setIsModal={(open) =>{setIsModal}}></RswtrRegForm> - :<HtdflRegForm htdflForm={rainBdstForm as unknown as customHtdflBdst} isModal={true} setIsModal={(open) =>{setIsModal}}></HtdflRegForm> + :<HtdflRegForm htdflForm={rainBdstForm as unknown as customHtdflBdst} isModal={true} setIsModal={(open) =>{setIsModal && setIsModal(open)}}></HtdflRegForm> } </div> From 76a511a5e0bdef23705be53a9467498d8be697b5 Mon Sep 17 00:00:00 2001 From: leedj312 <dlehd467@gmail.com> Date: Fri, 4 Jul 2025 20:06:07 +0900 Subject: [PATCH 076/108] =?UTF-8?q?=EC=A4=91=EC=88=98=EB=8F=84=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/ruseFcltMng/fcltReg/rainReg/page.tsx | 3 +++ .../app/ruseFcltMng/fcltReg/rswtReg/page.tsx | 18 ++++++++++++++++-- .../ui/form/fcltReg/bdstReg/bdstReg.tsx | 4 ++-- .../ui/form/fcltReg/rswtrReg/rswtrRegForm.tsx | 14 +++++++------- .../fcltReg/bdstRegModal/bdstRegModal.tsx | 2 +- 5 files changed, 29 insertions(+), 12 deletions(-) diff --git a/apps/wr/app/ruseFcltMng/fcltReg/rainReg/page.tsx b/apps/wr/app/ruseFcltMng/fcltReg/rainReg/page.tsx index b468fad03..8ecc1221f 100644 --- a/apps/wr/app/ruseFcltMng/fcltReg/rainReg/page.tsx +++ b/apps/wr/app/ruseFcltMng/fcltReg/rainReg/page.tsx @@ -51,8 +51,11 @@ export default function Home() { if (code.sggCd.length > 0) { const rainData = tableData(rainRes, { code }).map( (item: customRainBdst) => { + let archArea = item?.archArea.split('.')[0]!=='-1'?item?.archArea.split('.')[0]:'확인불가' return { ...item, + archArea:archArea, + bdstChgYn: item.chgBuilding.length > 0 ? "Y" : "N", }; } diff --git a/apps/wr/app/ruseFcltMng/fcltReg/rswtReg/page.tsx b/apps/wr/app/ruseFcltMng/fcltReg/rswtReg/page.tsx index ea0dea649..94224d40d 100644 --- a/apps/wr/app/ruseFcltMng/fcltReg/rswtReg/page.tsx +++ b/apps/wr/app/ruseFcltMng/fcltReg/rswtReg/page.tsx @@ -20,6 +20,7 @@ import { import ListDiv from "@common/business_components/ui/list_div"; import { Button } from "@common/components"; import { useEffect, useState } from "react"; +import { Toaster } from "react-hot-toast"; export default function Home() { const { code, rswt, initRswt, rswtBdstSearch, getRswtBdstData } = useWrStore( @@ -45,8 +46,13 @@ export default function Home() { if (code.sggCd.length > 0 && rswtRes?.length > 0) { const rswtData = tableData(rswtRes, { code })?.map( (item: customRswtBdst) => { + let archArea = + item?.archArea.split(".")[0] !== "-1" + ? item?.archArea.split(".")[0] + : "확인불가"; return { ...item, + archArea: archArea, bdstChgYn: item.chgBuilding[0]?.bdstChgNm != null ? "Y" : "N", bdstUsgCd: item.reuseBuilding[0]?.bdstUsgCd, }; @@ -151,7 +157,13 @@ export default function Home() { </div> <UiModal - body={<BdstReg moType="RST" setIsModal={setIsNewReg} isModal={issNewReg}></BdstReg>} + body={ + <BdstReg + moType="RST" + setIsModal={setIsNewReg} + isModal={issNewReg} + ></BdstReg> + } bodyClass="w-[68vw] max-w-none h-[90vh]" open={issNewReg} onOpenChange={(open) => { @@ -162,12 +174,14 @@ export default function Home() { bodyClass={""} open={bdstChgProps.isOpen} body={ - <BdstChgModal chgBuilding={bdstChgProps.chgBuilding} ></BdstChgModal> + <BdstChgModal chgBuilding={bdstChgProps.chgBuilding}></BdstChgModal> } onOpenChange={(open) => { setBdstChgProps({ ...bdstChgProps, isOpen: open }); }} /> + + <Toaster position="top-center" /> </ContentsDiv> </> ); diff --git a/apps/wr/components/ui/form/fcltReg/bdstReg/bdstReg.tsx b/apps/wr/components/ui/form/fcltReg/bdstReg/bdstReg.tsx index 04b36cbc5..31e8dbacb 100644 --- a/apps/wr/components/ui/form/fcltReg/bdstReg/bdstReg.tsx +++ b/apps/wr/components/ui/form/fcltReg/bdstReg/bdstReg.tsx @@ -223,9 +223,9 @@ export default function BdstReg({ form, type }: { form: any; type?: string }) { placeholder="면적을 입력하세요" type="text" onChange={(e) => { - handleCommaNumberChange(e, field.onChange); + field.onChange(e.target.value) }} - value={formatCommaNumber(field.value)} + value={field.value} /> </FormUi> )} diff --git a/apps/wr/components/ui/form/fcltReg/rswtrReg/rswtrRegForm.tsx b/apps/wr/components/ui/form/fcltReg/rswtrReg/rswtrRegForm.tsx index 8c71d77a9..0fbffbf59 100644 --- a/apps/wr/components/ui/form/fcltReg/rswtrReg/rswtrRegForm.tsx +++ b/apps/wr/components/ui/form/fcltReg/rswtrReg/rswtrRegForm.tsx @@ -81,11 +81,11 @@ export default function RswtrRegForm({ ? new Date(item.instlCmptnDt) : new Date(), // item?.instlCmptnDt가 유효하지 않으면 현재 날짜로 초기화 operMngCd: item?.operMngCd ?? "", // null → "" - instlCstSprtYn: item?.instlCstSprtYn ?? "Y", // null → "Y" - grntchBdstCertYn: item?.grntchBdstCertYn ?? "Y", + instlCstSprtYn: item?.instlCstSprtYn || code.yn[0]?.val, // null → "Y" + grntchBdstCertYn: item?.grntchBdstCertYn|| code.yn[0]?.val, rswtrObj: { ...item?.rswtrObj, - filtrTrtmfctYn: item?.rainyObj?.filtrTrtmfctYn ?? "Y", + filtrTrtmfctYn: item?.rainyObj?.filtrTrtmfctYn || code.yn[0]?.val, filtrTrtmfctCpct: item?.rswtrObj?.filtrTrtmfctCpct ?? null, filtrTrtmfctProcs: item?.rswtrObj?.filtrTrtmfctProcs || code.rswtPrscCdMthdCd[0]?.val, orgwtKndCd: item?.rswtrObj?.orgwtKndCd || code.orgwtKndCd[0]?.val, @@ -349,7 +349,7 @@ export default function RswtrRegForm({ if (readonly) return; handleMinusNumberChange(e, field.onChange); }} - disabled={instlCstSprtYn !== "Y"} + disabled={instlCstSprtYn === "N"||instlCstSprtYn === "D"} /> </FormUi> ); @@ -374,7 +374,7 @@ export default function RswtrRegForm({ label={""} selectArray={code.bdstIncenYn} onSelectValue={readonly ? () => {} : field.onChange} - value={field.value ?? code.yn[0]?.val} + value={field.value || code.yn[0]?.val} readOnly={readonly} /> </FormUi> @@ -410,7 +410,7 @@ export default function RswtrRegForm({ field.onChange(e.target.value); } }} - disabled={grntchBdstCertYn !== "Y"} + disabled={grntchBdstCertYn == "N" || grntchBdstCertYn == "D"} max={4} /> </FormUi> @@ -447,7 +447,7 @@ export default function RswtrRegForm({ field.onChange(e.target.value); } }} - disabled={grntchBdstCertYn !== "Y"} + disabled={grntchBdstCertYn == "N" || grntchBdstCertYn == "D"} diff --git a/apps/wr/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal.tsx b/apps/wr/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal.tsx index 4abe29d4e..c534c38af 100644 --- a/apps/wr/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal.tsx +++ b/apps/wr/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal.tsx @@ -147,7 +147,7 @@ export function BdstReg<T extends MoType>({ moType,setIsModal,isModal }: BdstReg { moType === 'RNB'? <RainRegForm rainForm={rainBdstForm as customRainBdst} isModal={isModal} setIsModal={setIsModal} /> :moType==='RST' - ? <RswtrRegForm rswtForm={rainBdstForm as customRswtBdst} isModal={isModal} setIsModal={(open) =>{setIsModal}}></RswtrRegForm> + ? <RswtrRegForm rswtForm={rainBdstForm as customRswtBdst} isModal={isModal} setIsModal={(open) =>{setIsModal && setIsModal(open)}}></RswtrRegForm> :<HtdflRegForm htdflForm={rainBdstForm as unknown as customHtdflBdst} isModal={true} setIsModal={(open) =>{setIsModal && setIsModal(open)}}></HtdflRegForm> } From 1d9111ee4c98291f7f7fd8329b6a92e62e2949d6 Mon Sep 17 00:00:00 2001 From: jyha00 <jyha@mind-one.co.kr> Date: Fri, 4 Jul 2025 20:10:52 +0900 Subject: [PATCH 077/108] =?UTF-8?q?=EB=B9=97=EB=AC=BC/=EC=A4=91=EC=88=98?= =?UTF-8?q?=EB=8F=84/=EC=98=A8=EB=B0=B0=EC=88=98=EC=8B=9C=EC=84=A4?= =?UTF-8?q?=ED=98=84=ED=99=A9=20=EC=A1=B0=ED=9A=8C=EC=A1=B0=EA=B1=B4=20?= =?UTF-8?q?=EC=88=98=EC=A0=95,=20=ED=8E=98=EC=9D=B4=EC=A7=95=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC,=20=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ruseFcltMng/fcltSttn/htdflSttn/page.tsx | 195 ++++++++++++------ .../ruseFcltMng/fcltSttn/rainSttn/page.tsx | 86 +++----- .../ruseFcltMng/fcltSttn/rswtSttn/page.tsx | 178 +++++++++++----- .../ruseFcltMng/fcltSttn/htdflSttn/index.tsx | 41 +++- .../ruseFcltMng/fcltSttn/rainSttn/index.tsx | 99 +++++---- .../ruseFcltMng/fcltSttn/rswtSttn/index.tsx | 28 ++- 6 files changed, 385 insertions(+), 242 deletions(-) diff --git a/apps/wr/app/ruseFcltMng/fcltSttn/htdflSttn/page.tsx b/apps/wr/app/ruseFcltMng/fcltSttn/htdflSttn/page.tsx index e43d5b04f..959367202 100644 --- a/apps/wr/app/ruseFcltMng/fcltSttn/htdflSttn/page.tsx +++ b/apps/wr/app/ruseFcltMng/fcltSttn/htdflSttn/page.tsx @@ -8,6 +8,7 @@ import type { } from "@/app/store/ruseFcltMng/fcltReg/rainReg"; import { excelDownload, + getCity, getCityOrSgg, tableData, } from "@/components/lib/func/commom"; @@ -16,16 +17,23 @@ import SttnDetailModal from "@/components/ui/modal/sttn/sttnDetailModal/sttnDeta import { ContentsDiv, SearchBaseForm, + SearchDiv, + SearchFormLeft, + SearchFormRight, + SearchInput, + SelectBox, Title, UiModal, UiTable, type ReturnType, + type SelectDataType, } from "@common/business_components/ui"; import ListDiv from "@common/business_components/ui/list_div"; -import { Button } from "@common/components/ui"; +import { Button, Label } from "@common/components/ui"; import { useCallback, useEffect, useRef, useState } from "react"; import { excelHtdflSttnParsing } from "@/app/store/ruseFcltMng/fcltSttn/htdflSttn/excel"; import { HeadMakeColSpan } from "@common/business_components/ui/table/HeadMakeColSpan"; +import type { objSearchType } from "@/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal"; export default function HtdflSttn() { const { code, htdflSttn, htdflSttnSearch, initHtdfl } = useWrStore( @@ -38,6 +46,8 @@ export default function HtdflSttn() { const [pageNation, setPageNation] = useState(1); const [isChHead, setIsChHead] = useState(false); const [bdstTable, setBdstTable] = useState<customRainBdst[]>(); + const [selectSgg, setSelectSgg] = useState<SelectDataType[] | []>() + const [objSearch, setObjSearch] = useState<objSearchType>({ sgg: "", city: "" }); const [detailModal, setDetailModal] = useState<{ isOpen: boolean; data: customRainBdst | undefined; @@ -46,17 +56,47 @@ export default function HtdflSttn() { isOpen: boolean; chBuilding: chgBuilding[]; }>({ isOpen: false, chBuilding: [] }); - - const codeData = getCityOrSgg(ref); + const [fcltNm, setFcltNm] = useState<string>(""); const excelClick = useCallback(() => { - if (codeData) htdflSttnSearch(1, "10000", codeData); - }, [htdflSttnSearch, codeData]); + // 최신 검색 조건 반영 + const con = getConValue(); + htdflSttnSearch(1, "10000", String(con), fcltNm, code.cityCd, code.sggCd); + }, [htdflSttnSearch, objSearch, code.cityCd, code.sggCd]); useEffect(() => { - if (codeData != null) htdflSttnSearch(pageNation, "20", codeData); + htdflSttnSearch(pageNation, "20", String(getConValue()), fcltNm, code.cityCd, code.sggCd); }, [pageNation]); + const getSelCity = (val: string | number) => { + const cityValue = String(val); + + if (cityValue === "0") { + setSelectSgg([]); + setObjSearch({ city: cityValue, sgg: "" }); + } else { + const data = getCity(cityValue, code.sggCd); + setSelectSgg(data); + + 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 = () => { + if (!objSearch.city || objSearch.city === "0") { + return "0"; + } + if (objSearch.sgg && objSearch.sgg !== "0") { + return objSearch.sgg; + } + return objSearch.city; + }; + const cellClick = (index: number | undefined, headId: string) => { if ( headId !== "chBuildingYn" && @@ -67,42 +107,34 @@ export default function HtdflSttn() { } }; useEffect(() => { - const excelDown = async () => { - if (htdflSttn.excelList.length > 0) { - if (htdflSttn.excelList.length === 0) return; - const excelData = await tableData(htdflSttn.excelList, { code }); - const htdflExcel = excelHtdflSttnParsing(excelData, code.cityCd, code.sggCd); - excelDownload( - htdflExcel as [[]], - "온배수시설 현황", - "sample_htdflSttn.xlsx", - "A1:S3" - ); - } - }; - excelDown(); + if (htdflSttn.excelList.length > 0) { + if (htdflSttn.excelList.length === 0) return; + excelDownload( + htdflSttn.excelList, + "온배수시설 현황", + "sample_htdflSttn.xlsx", + "A1:S3" + ); + } }, [htdflSttn.excelList, code]); useEffect(() => { if (!htdflSttn.isInit) { - initHtdfl(codeData ?? "0"); + initHtdfl(1, "20", "0", fcltNm, code.cityCd, code.sggCd); } }, [htdflSttn.isInit, initHtdfl]); useEffect(() => { - if (htdflSttnRes.length > 0 && code.cityCd.length > 0) { - const data = tableData(htdflSttnRes, { code }); - const chData = data - .map((item: any) => ({ - ...item, - ...item.reuseBuilding[0], - ...item.reuseBuilding[0].htdflObj, - chBuildingYn: item.chgBuilding[0]?.bdstChgNm !== null ? "Y" : "N", - })) - .filter((item: any) => item.htdflObj !== undefined); - - setBdstTable(chData); - } + const data = tableData(htdflSttnRes, { code }); + const chData = data + .map((item: any) => ({ + ...item, + ...item.reuseBuilding[0], + ...item.reuseBuilding[0].htdflObj, + chBuildingYn: item.chgBuilding[0]?.bdstChgNm !== null ? "Y" : "N", + })) + .filter((item: any) => item.htdflObj !== undefined); + setBdstTable(chData); }, [code, htdflSttnRes, htdflSttn.htdflSttHeadList]); return ( @@ -111,43 +143,76 @@ export default function HtdflSttn() { <div className="grid grid-cols-4 gap-4 "> <div className="col-span-4"> <Title title={"온배수 시설 현황"} /> - <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; - } - htdflSttnSearch(pageNation, "20", con); - } } - excelClick={excelClick} - initClick={function (): void { } } - btnClass={btnClass} - ref={ref} isAdmin={false} /> + + <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={code.cityCd} + onSelectValue={getSelCity} + /> + <SelectBox + className="col-span-2 w-1/6 h-full text-2xl ml-4" + label="시군구" + selectClass={""} + selectArray={selectSgg || []} + value={objSearch.sgg} // value props 추가 + onSelectValue={getSgg} + /> + <div className="col-span-2 flex flex-row space-x-2"> + <Label className="w-28 mt-2 text-xl text-gray-600"> + {"시설명"} + </Label> + <SearchInput + className={"w-full h-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 + size={"sm"} + onClick={() => { + const con = getConValue(); + htdflSttnSearch(pageNation, "20", String(con), fcltNm, code.cityCd, code.sggCd); + }} + > + 조회 + </Button> + <Button + size={"sm"} + className={`${btnClass} green`} + onClick={excelClick} + > + 다운로드 + </Button> + <Button + size={"sm"} + 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> <UiTable - headName={[]} + headName={undefined} tableData={bdstTable || []} children={ <HeadMakeColSpan upHeadList={htdflSttn.htdflSttNodeList} /> diff --git a/apps/wr/app/ruseFcltMng/fcltSttn/rainSttn/page.tsx b/apps/wr/app/ruseFcltMng/fcltSttn/rainSttn/page.tsx index 7e068bcd0..0b6dffe07 100644 --- a/apps/wr/app/ruseFcltMng/fcltSttn/rainSttn/page.tsx +++ b/apps/wr/app/ruseFcltMng/fcltSttn/rainSttn/page.tsx @@ -24,7 +24,7 @@ import { type SelectDataType, } from "@common/business_components/ui"; import ListDiv from "@common/business_components/ui/list_div"; -import { Button } from "@common/components/ui"; +import { Button, Label } from "@common/components/ui"; import { useCallback, useEffect, useRef, useState } from "react"; import type { chgBuilding, @@ -52,8 +52,7 @@ export default function RainSttn() { isOpen: boolean; chBuilding: chgBuilding[]; }>({ isOpen: false, chBuilding: [] }); - - const codeData = getCityOrSgg(ref); + const [fcltNm, setFcltNm] = useState<string>(""); const cellClick = (index: number | undefined, headId: string) => { @@ -82,7 +81,6 @@ export default function RainSttn() { }; const getSgg = (val: string | number) => { - console.log("val", val) setObjSearch({ ...objSearch, sgg: String(val) }) }; @@ -96,11 +94,9 @@ export default function RainSttn() { return objSearch.city; }; - - useEffect(() => { if (!rainSttn.isInit) { - rainSttnInit(codeData ?? "0"); + rainSttnInit(1, "20", "0", fcltNm, code.cityCd, code.sggCd); } }, [rainSttn.isInit, rainSttnInit]); @@ -108,8 +104,6 @@ export default function RainSttn() { const data = tableData(rainSttnRes, { code }); const chData = data .map((item: any) => { - console.log("item", item); - console.log("왜안돼", item.reuseBuilding[0].rainyObj.dtfcltCpct) return { ...item, ...item.reuseBuilding[0], @@ -124,15 +118,21 @@ export default function RainSttn() { ? "확인불가" : item.reuseBuilding[0].instlCstSprtAmt, chBuildingYn: item.chgBuilding[0]?.bdstChgNm !== null ? "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 ? "확인불가" : item.archArea, }; }) .filter((item: any) => item.rainyObj !== undefined); - console.log("chData", chData) setBdstTable(chData); }, [code, rainSttnRes, rainSttn.rainSttHeadList]); useEffect(() => { - if (codeData != null) rainSttnSearch(pageNation, "20", codeData, code.cityCd, code.sggCd); + rainSttnSearch(pageNation, "20", String(getConValue()), fcltNm, code.cityCd, code.sggCd); }, [pageNation]); useEffect(() => { @@ -147,13 +147,10 @@ export default function RainSttn() { } }, [rainSttn.excelList]); - // const excelClick = useCallback(() => { - // if (codeData) rainSttnSearch(1, "10000", codeData, code.cityCd, code.sggCd); - // }, [rainSttnSearch, codeData, code.cityCd, code.sggCd]); const excelClick = useCallback(() => { // 최신 검색 조건 반영 const con = getConValue(); - rainSttnSearch(1, "10000", String(con), code.cityCd, code.sggCd); + rainSttnSearch(1, "10000", String(con), fcltNm, code.cityCd, code.sggCd); }, [rainSttnSearch, objSearch, code.cityCd, code.sggCd]); return ( @@ -162,8 +159,6 @@ export default function RainSttn() { <div className="grid grid-cols-4 gap-4 "> <div className="col-span-4"> <Title title={"빗물재이용 시설 현황"} /> - - <SearchDiv> <SearchFormLeft> <div className="col-span-10 flex flex-row"> @@ -182,6 +177,17 @@ export default function RainSttn() { value={objSearch.sgg} // value props 추가 onSelectValue={getSgg} /> + <div className="col-span-2 flex flex-row space-x-2"> + <Label className="w-28 mt-2 text-xl text-gray-600"> + {"시설명"} + </Label> + <SearchInput + className={"w-full h-full text-2xl"} + label={""} + textValue={fcltNm || ""} + setText={setFcltNm} + /> + </div> </div> </SearchFormLeft> <SearchFormRight @@ -191,7 +197,7 @@ export default function RainSttn() { size={"sm"} onClick={() => { const con = getConValue(); - rainSttnSearch(1, "20", String(con), code.cityCd, code.sggCd); + rainSttnSearch(pageNation, "20", String(con), fcltNm, code.cityCd, code.sggCd); }} > 조회 @@ -215,50 +221,6 @@ export default function RainSttn() { </div> </SearchFormRight> </SearchDiv> - - - - - - - - - - - {/* <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, code.cityCd, code.sggCd); - }} - excelClick={excelClick} - initClick={function (): void { }} - btnClass={btnClass} - ref={ref} - isAdmin={false} - /> */} </div> </div> <div className="grid grid-cols-6 gap-4 "> diff --git a/apps/wr/app/ruseFcltMng/fcltSttn/rswtSttn/page.tsx b/apps/wr/app/ruseFcltMng/fcltSttn/rswtSttn/page.tsx index f9b3c6187..ee7a344f8 100644 --- a/apps/wr/app/ruseFcltMng/fcltSttn/rswtSttn/page.tsx +++ b/apps/wr/app/ruseFcltMng/fcltSttn/rswtSttn/page.tsx @@ -5,23 +5,31 @@ import type { chgBuilding } from "@/app/store/ruseFcltMng/fcltReg/rainReg"; import type { customRswtBdst } from "@/app/store/ruseFcltMng/fcltReg/rswtReg"; import { excelDownload, + getCity, getCityOrSgg, tableData, } from "@/components/lib/func/commom"; import BdstChgModal from "@/components/ui/modal/fcltReg/bdstChgModal/bdstChgModal"; +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, SearchBaseForm, + SearchDiv, + SearchFormLeft, + SearchFormRight, + SearchInput, + SelectBox, Title, UiModal, UiTable, type ReturnType, + 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 } from "@common/components/ui"; +import { Button, Label } from "@common/components/ui"; import { useCallback, useEffect, useRef, useState } from "react"; export default function RswtSttn() { @@ -34,13 +42,14 @@ export default function RswtSttn() { const [isChHead, setIsChHead] = useState(false); const ref = useRef<ReturnType>({} as ReturnType); const [bdstTable, setBdstTable] = useState<customRswtBdst[]>(); + const [selectSgg, setSelectSgg] = useState<SelectDataType[] | []>() + const [objSearch, setObjSearch] = useState<objSearchType>({ sgg: "", city: "" }); 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 codeCityOrSgg = getCityOrSgg(ref); + const [fcltNm, setFcltNm] = useState<string>(""); const cellClick = (index: number | undefined, headId: string) => { @@ -50,37 +59,67 @@ export default function RswtSttn() { if ( bdstTable[index] && bdstTable[index]?.chBuildingYn === 'Y' - ) { - setBdstChgProps({ isOpen: true, chBuilding: bdstTable[index].chgBuilding }); } } } + const getSelCity = (val: string | number) => { + const cityValue = String(val); + + if (cityValue === "0") { + setSelectSgg([]); + setObjSearch({ city: cityValue, sgg: "" }); + } else { + const data = getCity(cityValue, code.sggCd); + setSelectSgg(data); + + 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 = () => { + if (!objSearch.city || objSearch.city === "0") { + return "0"; + } + if (objSearch.sgg && objSearch.sgg !== "0") { + return objSearch.sgg; + } + return objSearch.city; + }; + useEffect(() => { if (!rswtSttn.isInit) { - rswtSttnInit("0"); + rswtSttnInit(1, "20", "0", fcltNm, code.cityCd, code.sggCd); } }, [rswtSttn.isInit, rswtSttnInit]); useEffect(() => { - if (rswtSttnRes?.length > 0 && code.cityCd.length > 0) { - const data = tableData(rswtSttnRes, { code }); - const chData = data - .map((item: customRswtBdst) => ({ + const data = tableData(rswtSttnRes, { code }); + const chData = data + .map((item: customRswtBdst) => { + return { ...item, ...item.reuseBuilding[0], ...item.reuseBuilding[0]?.rswtrObj, - chBuildingYn: item?.chgBuilding[0]?.bdstChgNm !== null && item?.chgBuilding.length > 0 ? 'Y' : 'N' - })) - .filter( - (item: customRswtBdst) => - item.reuseBuilding[0]?.rswtrObj !== undefined - ); + chBuildingYn: item?.chgBuilding[0]?.bdstChgNm !== null && item?.chgBuilding.length > 0 ? 'Y' : 'N', + instlCst: item.reuseBuilding[0]?.instlCst === -1 ? "확인불가" : item.reuseBuilding[0]?.instlCst, + instlCstSprtAmt: item.reuseBuilding[0]?.instlCstSprtAmt === -1 ? "확인불가" : item.reuseBuilding[0]?.instlCstSprtAmt, + archArea : item?.archArea.split('.')[0]!=='-1'?item?.archArea.split('.')[0]:'확인불가' + } + }) + .filter( + (item: customRswtBdst) => + item.reuseBuilding[0]?.rswtrObj !== undefined + ); - setBdstTable(chData); - } + setBdstTable(chData); }, [code, rswtSttnRes]); useEffect(() => { @@ -95,16 +134,14 @@ export default function RswtSttn() { } }, [rswtSttn.excelList]); - const excelClick = useCallback( - (returnType: ReturnType) => { - if (codeCityOrSgg) - rswtSttnSearch(1, "10000", codeCityOrSgg, code.cityCd, code.sggCd); - }, - [rswtSttnSearch, code.cityCd, code.sggCd, codeCityOrSgg] - ); + const excelClick = useCallback(() => { + // 최신 검색 조건 반영 + const con = getConValue(); + rswtSttnSearch(1, "10000", String(con), fcltNm, code.cityCd, code.sggCd); + }, [rswtSttnSearch, objSearch, code.cityCd, code.sggCd]); useEffect(() => { - if (codeCityOrSgg != null) rswtSttnSearch(pageNation, "20", codeCityOrSgg, code.cityCd, code.sggCd); + rswtSttnSearch(pageNation, "20", String(getConValue()), fcltNm, code.cityCd, code.sggCd); }, [pageNation]); return ( @@ -113,38 +150,75 @@ export default function RswtSttn() { <div className="grid grid-cols-4 gap-4 "> <div className="col-span-4"> <Title title={"중수도시설 현황"} /> - <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', …}' - if (codeCityOrSgg) - rswtSttnSearch(1, "20", codeCityOrSgg, code.cityCd, code.sggCd); - } } - excelClick={excelClick} - initClick={function (): void { } } - btnClass={btnClass} - ref={ref} isAdmin={false} /> + <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={code.cityCd} + onSelectValue={getSelCity} + /> + <SelectBox + className="col-span-2 w-1/6 h-full text-2xl ml-4" + label="시군구" + selectClass={""} + selectArray={selectSgg || []} + value={objSearch.sgg} // value props 추가 + onSelectValue={getSgg} + /> + <div className="col-span-2 flex flex-row space-x-2"> + <Label className="w-28 mt-2 text-xl text-gray-600"> + {"시설명"} + </Label> + <SearchInput + className={"w-full h-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 + size={"sm"} + onClick={() => { + const con = getConValue(); + rswtSttnSearch(pageNation, "20", String(con), fcltNm, code.cityCd, code.sggCd); + }} + > + 조회 + </Button> + <Button + size={"sm"} + className={`${btnClass} green`} + onClick={excelClick} + > + 다운로드 + </Button> + <Button + size={"sm"} + 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> <UiTable - headName={[]} + headName={undefined} tableData={bdstTable || []} children={ <HeadMakeColSpan upHeadList={rswtSttn.rswtSttNodeList} /> diff --git a/apps/wr/app/store/ruseFcltMng/fcltSttn/htdflSttn/index.tsx b/apps/wr/app/store/ruseFcltMng/fcltSttn/htdflSttn/index.tsx index dccc8719c..eb37d0d73 100644 --- a/apps/wr/app/store/ruseFcltMng/fcltSttn/htdflSttn/index.tsx +++ b/apps/wr/app/store/ruseFcltMng/fcltSttn/htdflSttn/index.tsx @@ -5,6 +5,7 @@ import type { z } from "zod"; import { controlHeadView } from "../rainSttn"; import type { customHtdflBdst } from "../../fcltReg/htdflReg"; import { fetchType, getFetch } from "@/components/lib/api"; +import type { SelectDataType } from "@common/business_components/ui"; const htdflSttHeadList = htdflSttHeadListExport; const htdflSttNodeList = htdflSttNodeListExport; @@ -20,8 +21,12 @@ export type htdflSttnState = { }; }; type htdflSttnActions = { - initHtdfl: (con: string) => void; - htdflSttnSearch: (page: number, total: string, con: string) => void; + initHtdfl: (page: number, total: string, con: string, txt: string, + sidoCode: SelectDataType[], + sggCode: SelectDataType[]) => void; + htdflSttnSearch: (page: number, total: string, con: string, txt: string, + sidoCode: SelectDataType[], + sggCode: SelectDataType[]) => void; htdflChSttnHead: ( headList: z.infer<typeof sttnHeadListType>, ) => void; @@ -38,10 +43,17 @@ export const htdflSttnStateExport: htdflSttnState = { isInit: false, }, }; -const initFetch = async (code: string | number) => { +const initFetch = async (page: number, total: string, con: string, txt: string, + sidoCode: SelectDataType[], + sggCode: SelectDataType[]) => { const htdflRes = await getFetch( "wr", - `/drain/facilityList/1/20/${code}`) + `/drain/facilityList` + + (page ? `/${page}` : "/1") + + (total ? `/${total}` : "/20") + + (con ? `/${con}` : "/0") + + (txt ? `/${txt}` : "") + ); return htdflRes; }; @@ -50,9 +62,11 @@ export const htdflSttnActionsExport: (set: any) => htdflSttnActions = ( set: any ) => { return { - initHtdfl: async (code: string | number) => { + initHtdfl: async (page: number, total: string, con: string, txt: string, + sidoCode: SelectDataType[], + sggCode: SelectDataType[]) => { try { - const htdflSttnRes = await initFetch(code); + const htdflSttnRes = await initFetch(page, total, con, txt, sidoCode, sggCode); set((state: htdflSttnState) => { return { ...state, @@ -67,17 +81,22 @@ export const htdflSttnActionsExport: (set: any) => htdflSttnActions = ( console.log(error); } }, - htdflSttnSearch: async (page: number, total: string, con: string) => { + htdflSttnSearch: async (page: number, total: string, con: string, txt: string, + sidoCode: SelectDataType[], + sggCode: SelectDataType[]) => { try { + console.log("page", page) const htdflSttnRes = await getFetch( fetchType.WR, - `/drain/facilityList/${page}/${total}/${con}` + `/drain/facilityList` + + (page ? `/${page}` : "/1") + + (total ? `/${total}` : "/20") + + (con ? `/${con}` : "/0") + + (txt ? `/${txt}` : "") ); + if (Number(total) > 9000) { - - set((state: htdflSttnState) => { - return { ...state, htdflSttn: { diff --git a/apps/wr/app/store/ruseFcltMng/fcltSttn/rainSttn/index.tsx b/apps/wr/app/store/ruseFcltMng/fcltSttn/rainSttn/index.tsx index 42d158d4e..eb2c08d37 100644 --- a/apps/wr/app/store/ruseFcltMng/fcltSttn/rainSttn/index.tsx +++ b/apps/wr/app/store/ruseFcltMng/fcltSttn/rainSttn/index.tsx @@ -1,4 +1,4 @@ -import type { rainChkZodType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn"; +import type { rainChkZodType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn"; import { fetchType, getFetch } from "@/components/lib/api"; import { type customRainBdst, @@ -16,34 +16,34 @@ import type { CommonTableProps } from "@/app/store/common/type/header"; import type { SelectDataType } from "@common/business_components/ui"; // 테이블 헤드 리스트 -const rainSttHeadList = rainSttHeadListExport; +const rainSttHeadList = rainSttHeadListExport; const rainSttNodeList = rainSttNodeListExport; const rainSttnDetailNodeList = rainSttnDetailNodeListExport; const rainSttnDetailHeadList = rainSttnDetailHeadListExport; const rainSttnRes: customRainBdst[] = []; -const rainChkRes : rainChkZodType ={} as rainChkZodType +const rainChkRes: rainChkZodType = {} as rainChkZodType -const visibleHeadList = (falseHeads:CommonTableProps[], orgNodeList:CommonTableProps[])=>{ +const visibleHeadList = (falseHeads: CommonTableProps[], orgNodeList: CommonTableProps[]) => { const visibleHeads = orgNodeList.map((node) => { - const copyNode = {...node} - falseHeads.forEach((head)=>{ - if(copyNode.id ===head.id) - copyNode.visible = head.visible + const copyNode = { ...node } + falseHeads.forEach((head) => { + if (copyNode.id === head.id) + copyNode.visible = head.visible }) return copyNode; }) return visibleHeads } -const getColspanReductionMap = (falseHeads:CommonTableProps[], orgList:CommonTableProps[]):Record<string, number> => { - const reduceMap:Record<string, number> = {}; +const getColspanReductionMap = (falseHeads: CommonTableProps[], orgList: CommonTableProps[]): Record<string, number> => { + const reduceMap: Record<string, number> = {}; falseHeads.forEach(head => { const node = orgList.find(n => n.id === head.id); if (node?.upName) { reduceMap[node.upName] = (reduceMap[node.upName] || 0) + 1; - if (node.row&& node.row >= 3) { + if (node.row && node.row >= 3) { const upperUpName = orgList.find(n => n.id === node.upName)?.upName; if (upperUpName) { reduceMap[upperUpName] = (reduceMap[upperUpName] || 0) + 1; @@ -78,35 +78,37 @@ const applyColspanReduction = ( }); }; // 일단 헤더가 3개 이상인경우 visible 처리를 위해서 공통함수로 작업 -export const controlHeadView = (headList:CommonTableProps[], orgNodeList:CommonTableProps[] ) =>{ +export const controlHeadView = (headList: CommonTableProps[], orgNodeList: CommonTableProps[]) => { const falseHeads = headList.filter((item) => item.visible == false); const viewHeadList = visibleHeadList(falseHeads, orgNodeList) - const reduceMap= getColspanReductionMap( falseHeads,viewHeadList) - return applyColspanReduction(viewHeadList,reduceMap); + const reduceMap = getColspanReductionMap(falseHeads, viewHeadList) + return applyColspanReduction(viewHeadList, reduceMap); } - - export type rainSttnState = { rainSttn: { rainSttnRes: customRainBdst[]; rainSttHeadList: CommonTableProps[]; rainSttNodeList: CommonTableProps[]; - rainSttnDetailHeadList:CommonTableProps[] - rainSttnDetailNodeList:CommonTableProps[] - rainChkRes:rainChkZodType + rainSttnDetailHeadList: CommonTableProps[] + rainSttnDetailNodeList: CommonTableProps[] + rainChkRes: rainChkZodType excelList: []; isInit: boolean; }; }; type rainSttnActions = { - rainSttnInit:(con:string) => void - rainSttnSearch: (page: number, total: string, con: string, sidoCode: SelectDataType[], sggCode: SelectDataType[]) => void; + rainSttnInit: (page: number, total: string, con: string, txt: string, + sidoCode: SelectDataType[], + sggCode: SelectDataType[]) => void + rainSttnSearch: (page: number, total: string, con: string, txt: string, + sidoCode: SelectDataType[], + sggCode: SelectDataType[]) => void; rainChSttnHead: ( headList: z.infer<typeof sttnHeadListType>, ) => void; - rainSttnChkSearch:(rusePk:string) => void; + rainSttnChkSearch: (rusePk: string) => void; }; export type rainSttnType = rainSttnState & rainSttnActions; @@ -115,10 +117,10 @@ export const rainSttnStateExport: rainSttnState = { rainSttn: { rainSttHeadList: rainSttHeadList, rainSttNodeList: rainSttNodeList, - rainSttnDetailHeadList:rainSttnDetailHeadList, - rainSttnDetailNodeList:rainSttnDetailNodeList, + rainSttnDetailHeadList: rainSttnDetailHeadList, + rainSttnDetailNodeList: rainSttnDetailNodeList, rainSttnRes: rainSttnRes, - rainChkRes:rainChkRes, + rainChkRes: rainChkRes, excelList: [], isInit: false, }, @@ -128,12 +130,18 @@ export const rainSttnActionsExport: (set: any) => rainSttnActions = ( set: any ) => { return { - rainSttnInit: async( con:string)=>{ - try{ + rainSttnInit: async (page: number, total: string, con: string, txt: string, + sidoCode: SelectDataType[], + sggCode: SelectDataType[]) => { + try { - const rainSttnRes = await getFetch( + const rainSttnRes = await getFetch( fetchType.WR, - `/reuseFc/rainyInfoList/1/20/${con}` + `/reuseFc/rainyInfoList` + + (page ? `/${page}` : "/1") + + (total ? `/${total}` : "/20") + + (con ? `/${con}` : "/0") + + (txt ? `/${txt}` : "") ); set((state: rainSttnState) => { return { @@ -145,21 +153,22 @@ export const rainSttnActionsExport: (set: any) => rainSttnActions = ( }, }; }); - }catch(e){ + } catch (e) { console.log(e) } }, - rainSttnSearch: async (page: number, total: string, con: string, - sidoCode: SelectDataType[], - sggCode: SelectDataType[]) => { + rainSttnSearch: async (page: number, total: string, con: string, txt: string, + sidoCode: SelectDataType[], + sggCode: SelectDataType[]) => { try { - let a = `/reuseFc/rainyInfoList/${page}/${total}/${con}` - console.log("주소", a) const rainSttnRes = await getFetch( fetchType.WR, - `/reuseFc/rainyInfoList/${page}/${total}/${con}` + `/reuseFc/rainyInfoList` + + (page ? `/${page}` : "/1") + + (total ? `/${total}` : "/20") + + (con ? `/${con}` : "/0") + + (txt ? `/${txt}` : "") ); - console.log("rainSttnRes", rainSttnRes) if (Number(total) > 9000) { const rainExcel = excelRainSttnParsing(rainSttnRes, sidoCode, sggCode); set((state: rainSttnState) => { @@ -189,7 +198,7 @@ export const rainSttnActionsExport: (set: any) => rainSttnActions = ( rainChSttnHead: async ( headList: z.infer<typeof sttnHeadListType> ) => { - const changeNodeList= controlHeadView(headList,rainSttNodeList) + const changeNodeList = controlHeadView(headList, rainSttNodeList) return set((state: rainSttnState) => ({ rainSttn: { @@ -199,21 +208,21 @@ export const rainSttnActionsExport: (set: any) => rainSttnActions = ( }, })); }, - rainSttnChkSearch: async( - rusePk:string - )=>{ + rainSttnChkSearch: async ( + rusePk: string + ) => { const rainChkRes = await getFetch( fetchType.WR, `/reuseChk/rainy/${rusePk}` ); - - return set((state: rainSttnState) =>({ + + return set((state: rainSttnState) => ({ rainSttn: { ...state.rainSttn, - rainChkRes:rainChkRes + rainChkRes: rainChkRes }, - }) ) + })) } }; diff --git a/apps/wr/app/store/ruseFcltMng/fcltSttn/rswtSttn/index.tsx b/apps/wr/app/store/ruseFcltMng/fcltSttn/rswtSttn/index.tsx index 301cd965e..8fbe2c26d 100644 --- a/apps/wr/app/store/ruseFcltMng/fcltSttn/rswtSttn/index.tsx +++ b/apps/wr/app/store/ruseFcltMng/fcltSttn/rswtSttn/index.tsx @@ -28,9 +28,12 @@ export type rswtSttnState = { }; type rswtSttnActions = { - rswtSttnInit: (con: string) => void; - rswtSttnSearch: (page: number, total: string, con: string, sidoCode: SelectDataType[], - sggCode: SelectDataType[]) => void; + rswtSttnInit: (page: number, total: string, con: string, txt: string, + sidoCode: SelectDataType[], + sggCode: SelectDataType[]) => void; + rswtSttnSearch: (page: number, total: string, con: string, txt: string, + sidoCode: SelectDataType[], + sggCode: SelectDataType[]) => void; rswtChSttnHead: ( headList: z.infer<typeof sttnHeadListType>, ) => void; @@ -54,12 +57,18 @@ export const rswtSttnActionsExport: (set: any) => rswtSttnActions = ( set: any ) => { return { - rswtSttnInit: async (con: string) => { + rswtSttnInit: async (page: number, total: string, con: string, txt: string, + sidoCode: SelectDataType[], + sggCode: SelectDataType[]) => { try { const rswtSttnRes = await getFetch( fetchType.WR, - `/reuseFc/waterreuseFc/1/20/${con}` + `/reuseFc/waterreuseFc` + + (page ? `/${page}` : "/1") + + (total ? `/${total}` : "/20") + + (con ? `/${con}` : "/0") + + (txt ? `/${txt}` : "") ); set((state: rswtSttnState) => { return { @@ -75,13 +84,18 @@ export const rswtSttnActionsExport: (set: any) => rswtSttnActions = ( console.log(e) } }, - rswtSttnSearch: async (page: number, total: string, con: string, + rswtSttnSearch: async (page: number, total: string, con: string, txt: string, sidoCode: SelectDataType[], sggCode: SelectDataType[]) => { try { + console.log("헐", page) const rswtSttnRes = await getFetch( fetchType.WR, - `/reuseFc/waterreuseFc/${page}/${total}/${con}` + `/reuseFc/waterreuseFc` + + (page ? `/${page}` : "/1") + + (total ? `/${total}` : "/20") + + (con ? `/${con}` : "/0") + + (txt ? `/${txt}` : "") ); if (Number(total) > 9000) { const rswtExcel = excelRSwtSttnParsing(rswtSttnRes, sidoCode, sggCode); From db74c07df89d5bea899af9b1379eadc306f6996c Mon Sep 17 00:00:00 2001 From: leedj312 <dlehd467@gmail.com> Date: Fri, 4 Jul 2025 22:26:55 +0900 Subject: [PATCH 078/108] =?UTF-8?q?=EA=B2=B0=ED=95=A8=EB=8C=80=EC=9E=A5=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wr/app/matMng/coMat/coMatRvw/page.tsx | 33 ++++--- .../app/ruseFcltMng/fcltReg/pbnstReg/page.tsx | 90 +++++++++++++------ .../ruseFcltMng/fcltReg/pbnstReg/utilsZod.tsx | 38 ++++++++ .../prfmncReg/pbnstPrfmncReg/page.tsx | 39 +++++--- .../wr/app/store/matDmd/coMatRvw/headList.tsx | 4 +- apps/wr/components/lib/func/commom.tsx | 33 ++++--- .../ui/form/coMat/conCostForm/conCostForm.tsx | 11 ++- .../pbnPrfExcelModal/pbnPrfExcelModal.tsx | 49 ++++++---- .../fcltReg/pbnstRegModal/pbnstRegModal.tsx | 81 ++++++++++++++--- 9 files changed, 282 insertions(+), 96 deletions(-) create mode 100644 apps/wr/app/ruseFcltMng/fcltReg/pbnstReg/utilsZod.tsx diff --git a/apps/wr/app/matMng/coMat/coMatRvw/page.tsx b/apps/wr/app/matMng/coMat/coMatRvw/page.tsx index c1e601320..46aeacbf3 100644 --- a/apps/wr/app/matMng/coMat/coMatRvw/page.tsx +++ b/apps/wr/app/matMng/coMat/coMatRvw/page.tsx @@ -9,6 +9,7 @@ import { } from "@/app/store/matDmd/coMatRvw/type"; import { formH1Class, formTableName } from "@/components/lib/class/comClass"; import { + formatPhoneNumber, getCity, getdmdMatFlag, tableData, @@ -33,7 +34,7 @@ import { import ListDiv from "@common/business_components/ui/list_div"; import { Button, Input, Label } from "@common/components/ui"; import { useEffect, useRef, useState } from "react"; -import { useForm } from "react-hook-form"; +import { useForm, useWatch } from "react-hook-form"; import toast, { Toaster } from "react-hot-toast"; import { z } from "zod"; import moment from "moment"; @@ -68,6 +69,7 @@ export default function CoMatRvw() { const [selectData, setSelectData] = useState<DmdCoReqType>( {} as DmdCoReqType ); + const [selectionVersion, setSelectionVersion] = useState(0); const getSearch = ( startYear: number, @@ -91,39 +93,46 @@ export default function CoMatRvw() { const form = useForm<z.infer<typeof DmdCoReqSchema>>({ resolver: zodResolver(DmdCoReqSchema), + shouldUnregister: true }); + const TableCellClick = (index: number) => { console.log(coMatRvwRes); if (coMatRvwRes && coMatRvwRes[index]) { - setSelectData(coMatRvwRes[index]); + setSelectionVersion((v) => v + 1); // 행을 클릭할 때마다 버전을 1씩 증가 } }; + const useUsgCdWatch = useWatch({ control: form.control, name: "useUsgCd" }); useEffect(() => { - if (form?.getValues("useUsgCd")) { + + console.log(' useUsgCdWatch page ' ,useUsgCdWatch) + if (useUsgCdWatch) { form.setValue( "demandCst.usgPrcsProcs", usageProcData?.find( - (item) => item.usage === (form?.getValues("useUsgCd") as string) + (item) => item.usage === useUsgCdWatch )?.process ?? "" ); } - }, [form.watch("useUsgCd")]); + }, [useUsgCdWatch, form,selectionVersion]); useEffect(() => { if (coMatRvwRes.length > 0) { const data = coMatRvwRes?.map((item, index) => { let flag = ""; - flag = getdmdMatFlag(item?.demandMatching?.splyFcltFlg ??""); + + flag = getdmdMatFlag(item?.demandMatching?.splyFcltFlg ||undefined); + return { ...item, ctpvCd: item.demandCo?.ctpvCd, sggCd: item.demandCo?.sggCd, dmdCoNm: item.demandCo?.dmdCoNm, daddr: item.demandCo?.daddr, - telno: item.demandCo?.telno, - regDt: moment(item?.demandCo?.regDt).format("YYYY-MM-DD"), + telno: formatPhoneNumber(item?.demandCo?.telno||''), + dmndTmng: item?.dmndTmng, splyFcltFlg: flag, }; }); @@ -151,8 +160,8 @@ export default function CoMatRvw() { useEffect(() => { if (selectData) { - console.log(selectData); form.reset(selectData); + setSelectionVersion((v) => v + 1); // 강제로 바뀌었다고 알리기 } }, [selectData, form]); return ( @@ -269,7 +278,7 @@ export default function CoMatRvw() { ></UiTable> </ListDiv> <hr /> - <ScrollArea className="h-[49vh]"> + {/* <ScrollArea className="h-[49vh]"> */} <ListDiv> <h3 className="mt-4 ml-2 text-2xl font-medium text-label"> 수요신청 상세현황 @@ -660,9 +669,9 @@ export default function CoMatRvw() { </div> </div> </Form> - <ConCostForm form={form} type={"mat"} /> + <ConCostForm form={form} type={"mat"} selectionVersion={selectionVersion} /> </ListDiv> - </ScrollArea> + {/* </ScrollArea> */} </div> </div> </ContentsDiv> diff --git a/apps/wr/app/ruseFcltMng/fcltReg/pbnstReg/page.tsx b/apps/wr/app/ruseFcltMng/fcltReg/pbnstReg/page.tsx index 174f268d0..8d7e32c66 100644 --- a/apps/wr/app/ruseFcltMng/fcltReg/pbnstReg/page.tsx +++ b/apps/wr/app/ruseFcltMng/fcltReg/pbnstReg/page.tsx @@ -35,6 +35,10 @@ import { useFieldArray, useForm, type FieldErrors } from "react-hook-form"; // F import { zodResolver } from "@hookform/resolvers/zod"; import { z } from "zod"; +import { createDefaultPbnstItem } from "./utilsZod"; + + + export default function PublicReuseFaciltyInsert() { const { code, @@ -100,7 +104,7 @@ export default function PublicReuseFaciltyInsert() { fcOpers: fclt.fcOpers, }; }); - await pbnstRuseFcltUpdate(pbnst as customPbnst[]); + await pbnstRuseFcltUpdate(pbnst as customPbnst[]) await pbnstRuseBizUpdate(bizs); }; @@ -115,21 +119,43 @@ export default function PublicReuseFaciltyInsert() { path: (string | number)[] = [] ): { message: string; path: (string | number)[] }[] => { if (!errorObject) return []; - - return Object.keys(errorObject).flatMap((key) => { - const value = errorObject[key]; - const currentKey = !isNaN(parseInt(key)) ? parseInt(key) : key; - const newPath = [...path, currentKey]; - if (value && typeof value === "object") { - if (typeof value.message === "string") { - return [{ message: value.message, path: newPath }]; // 에러 메시지와 경로 반환 - } - return findAllErrors(value, newPath); + + // Zod에서 배열 에러는 바로 배열로 전달됨 (예: items: [ {}, {...}, ... ]) + if (Array.isArray(errorObject)) { + return errorObject.flatMap((entry, index) => + findAllErrors(entry, [...path, index]) + ); + } + + if (typeof errorObject === "object") { + const entries: { message: string; path: (string | number)[] }[] = []; + + if (typeof errorObject.message === "string") { + entries.push({ message: errorObject.message, path }); } - return []; - }); + + if (Array.isArray(errorObject._errors)) { + for (const msg of errorObject._errors) { + entries.push({ message: msg, path }); + } + } + + const keys = Object.keys(errorObject).filter( + (k) => k !== "message" && k !== "_errors" + ); + + for (const key of keys) { + entries.push(...findAllErrors(errorObject[key], [...path, key])); + } + + return entries; + } + + return []; }; const errorList = findAllErrors(errors); + + console.log(' errors ', errors) if (errorList.length > 0) { errorList.forEach((errorInfo) => { const { message, path } = errorInfo; @@ -160,6 +186,9 @@ export default function PublicReuseFaciltyInsert() { }); } }; + useEffect(() => { + console.log("form errors:", form.formState.errors); + }, [form.formState.errors]); useEffect(() => { if (code.sggCd.length && pbnstHasuRes.length > 0) { @@ -184,6 +213,7 @@ export default function PublicReuseFaciltyInsert() { useEffect(() => { if (pbnst.pbnstRes && pbnst.pbnstRes?.length > 0) { + form.reset({ items: pbnstRegArrayZod.parse(pbnst.pbnstRes) }); } else { form.reset({ items: [] }); @@ -204,21 +234,23 @@ export default function PublicReuseFaciltyInsert() { if (pbnstRes && pbnstRes.length > 0) { const lastPbnst = pbnstRes[pbnstRes.length - 1]; if (lastPbnst && selecTableRow) { - append( - pbnstRegZod.parse({ - pbnstRuseFcltPk: - String(selecTableRow.bscFctCd) + String(pbnstRes.length + 1), - ruseUtztnFcltNm: "", - fcBizs: [ - fcBizSchema.parse({ - pbnstRuseFcltFk: - String(lastPbnst.pbnstRuseFcltPk) + - String(pbnstRes.length + 1), - cycl: 1, - }), - ] as fcBizType[], - }) - ); + // append( + // pbnstRegZod.parse({ + // pbnstRuseFcltPk: + // String(selecTableRow.bscFctCd) + String(pbnstRes.length + 1), + // ruseUtztnFcltNm: null, + // fcBizs: [ + // fcBizSchema.parse({ + // pbnstRuseFcltFk: + // String(lastPbnst.pbnstRuseFcltPk) + + // String(pbnstRes.length + 1), + // cycl: 1, + // }), + // ] as fcBizType[], + // }) + // ); + append(createDefaultPbnstItem(selecTableRow, pbnstRes.length)); + setIsRuseReg({ isModal: true }); } } else { @@ -396,7 +428,7 @@ export default function PublicReuseFaciltyInsert() { form={form} // form prop 전달 setPbnstInsert={(data) => { if (data) { - pbnstInsert(data); // pbnstInsert 함수 호출 + // pbnstInsert(data); // pbnstInsert 함수 호출 setIsRuseReg({ isModal: false }); } }} diff --git a/apps/wr/app/ruseFcltMng/fcltReg/pbnstReg/utilsZod.tsx b/apps/wr/app/ruseFcltMng/fcltReg/pbnstReg/utilsZod.tsx new file mode 100644 index 000000000..14224fce2 --- /dev/null +++ b/apps/wr/app/ruseFcltMng/fcltReg/pbnstReg/utilsZod.tsx @@ -0,0 +1,38 @@ +import type { fcBizType, pubHasuType } from "@/app/store/ruseFcltMng/fcltReg/pbnstReg/type"; + +export function createDefaultPbnstItem( + selecTableRow: pubHasuType, + index: number + ) { + const pk = String(selecTableRow.bscFctCd) + String(index + 1); + + return { + pbnstRuseFcltPk: pk, + swtrmtFcltFk: "", + ruseUtztnFcltNm: "", // 필수 입력이므로 빈 문자열로 + fcltCpct: 0, + ruseCpct: 0, + splyExtraCpct: 0, + regDt: null, + rgtr: "test", + mdfcnDt: null, + mdfr: "test", + fcFiles: [], + fcBizs: [ + createDefaultBiz(pk, 1), // 첫 차수 + ], + fcOpers: [], + }; + } + + + export function createDefaultBiz( + pbnstRuseFcltFk: string, + cycl: number + ): fcBizType { + return { + pbnstRuseFcltFk, + cycl, + // 필요한 값 있으면 여기에 추가 + } as fcBizType; + } \ No newline at end of file diff --git a/apps/wr/app/ruseFcltMng/prfmncReg/pbnstPrfmncReg/page.tsx b/apps/wr/app/ruseFcltMng/prfmncReg/pbnstPrfmncReg/page.tsx index 10dd7ccb6..f86367a17 100644 --- a/apps/wr/app/ruseFcltMng/prfmncReg/pbnstPrfmncReg/page.tsx +++ b/apps/wr/app/ruseFcltMng/prfmncReg/pbnstPrfmncReg/page.tsx @@ -37,6 +37,7 @@ import { type PrfType, } from "@/app/store/prfmncReg/pbnstPrfmncReg/type"; import { excelToPbnPrfData } from "@/app/store/prfmncReg/pbnstPrfmncReg/excel"; +import data from "@common/components/ui/icon-finder/data"; export default function prfmncPrfmncReg() { const { code, prfmnc, prfmncHasuInit, prfmnRegData } = useWrStore( @@ -78,9 +79,6 @@ export default function prfmncPrfmncReg() { } }, [inputData, searchYear, pbnstRuseFcltPk, prfmncHasuInit, pageNation]); - - - const cellClick = useCallback( (index: number | undefined, headId: string) => { if (index !== undefined) { @@ -88,16 +86,22 @@ export default function prfmncPrfmncReg() { isModal: false, data: prfmncRes[index] || ({} as FcPrfType), }); - const inUsageData = structuredClone(initValInUsage).map(row => ({ + const inUsageData = structuredClone(initValInUsage).map((row) => ({ ...row, 구분: "장내용수", })); - const outUsageData = structuredClone(initValOutUsage).map(row => ({ + const outUsageData = structuredClone(initValOutUsage).map((row) => ({ ...row, 구분: "장외용수", })); - const inSubtotal = { ...structuredClone(subTotalObj), 구분: "장내용수" }; - const outSubtotal = { ...structuredClone(subTotalObj), 구분: "장외용수" }; + const inSubtotal = { + ...structuredClone(subTotalObj), + 구분: "장내용수", + }; + const outSubtotal = { + ...structuredClone(subTotalObj), + 구분: "장외용수", + }; if (prfmncRes[index]?.prfList) { prfmncRes[index].prfList?.forEach((prfItem) => { const month = Number(prfItem?.ym?.slice(4)); // "MM" @@ -133,7 +137,11 @@ export default function prfmncPrfmncReg() { }); inUsageData.push(inSubtotal); outUsageData.push(outSubtotal); - const usageTotalObj: { [key: string]: any } = { ...structuredClone(totalObj), 구분: "총합계", 용수명: "" }; + const usageTotalObj: { [key: string]: any } = { + ...structuredClone(totalObj), + 구분: "총합계", + 용수명: "", + }; for (const key in usageTotalObj) { if (["usage", "gubun", "edit"].includes(key)) continue; const val = @@ -156,7 +164,7 @@ export default function prfmncPrfmncReg() { useEffect(() => { if (!prfmnc.isInit) { - prfmncHasuInit('1', "41"); + prfmncHasuInit("1", "41"); } }, [prfmnc.isInit, prfmncHasuInit]); useEffect(() => { @@ -203,7 +211,7 @@ export default function prfmncPrfmncReg() { <SearchFormRight className="w-2/12 mx-5 flex flex-col items-center justify-center space-x-2 "> <div className="space-y"> <div className="flex flex-row space-x-2"> - <Button size={"sm"} onClick={(e) => { }}> + <Button size={"sm"} onClick={(e) => {}}> 조회 </Button> <Button @@ -218,7 +226,7 @@ export default function prfmncPrfmncReg() { size={"sm"} onClick={() => { if (cellNum === undefined) { - toast.error("하수처리재이용시설을 선택해주세요.") + toast.error("하수처리재이용시설을 선택해주세요."); return; } else { setIsExcelOPen({ ...isExcelOpen, isModal: true }); @@ -279,7 +287,14 @@ export default function prfmncPrfmncReg() { </ContentsDiv> <UiModal bodyClass={"w-[30vw] max-w-none"} - body={<PbnPrfExcelModal data={isExcelOpen.data}></PbnPrfExcelModal>} + body={ + <PbnPrfExcelModal + data={isExcelOpen.data} + setIsExcelOPen={(open) => { + setIsExcelOPen({ ...isExcelOpen, isModal: open }) + }} + ></PbnPrfExcelModal> + } open={isExcelOpen.isModal} onOpenChange={() => { setIsExcelOPen({ isModal: false, data: {} as FcPrfType }); diff --git a/apps/wr/app/store/matDmd/coMatRvw/headList.tsx b/apps/wr/app/store/matDmd/coMatRvw/headList.tsx index 7b43e4b00..6b783ea68 100644 --- a/apps/wr/app/store/matDmd/coMatRvw/headList.tsx +++ b/apps/wr/app/store/matDmd/coMatRvw/headList.tsx @@ -29,12 +29,12 @@ export const coRvwHeadListExport: CommonTableProps[]= [ visible: true, }, { - id: "regDt", //바꾸기 + id: "dmndTmng", //바꾸기 title: "신청일자", visible: true, }, { - id: "state", //바꾸기 + id: "splyFcltFlg", //바꾸기 title: "진행상태", visible: true, }, diff --git a/apps/wr/components/lib/func/commom.tsx b/apps/wr/components/lib/func/commom.tsx index 8569d4eb3..6b253bdba 100644 --- a/apps/wr/components/lib/func/commom.tsx +++ b/apps/wr/components/lib/func/commom.tsx @@ -165,18 +165,29 @@ export const yearOptions: { val: string; text: string }[] = Array.from( }) ); -export const getdmdMatFlag = (flag: string|undefined) => { - let flagMat = ""; +export const getdmdMatFlag = (flag: string | undefined | null) => { + const cleanFlag = (flag ?? "").trim(); - if(flag === undefined || flag === null) { - flagMat = "신청"; - }else if(flag.trim() === "Sub") { - flagMat = "검토"; - }else if(flag.trim() === "matched") { - flagMat = "매칭완료"; - }else { - flagMat = "신청"; + console.log('cleanFlag ', cleanFlag) + switch (cleanFlag) { + case "Sub": + return "검토"; + case "matched": + return "매칭완료"; + default: + return "신청"; } - return flagMat; }; + + +export const formatPhoneNumber=(tel:string) => { + const onlyNums = tel.replace(/[^0-9]/g, ''); + if (onlyNums.length === 10) { + return onlyNums.replace(/(\d{2})(\d{4})(\d{4})/, '$1-$2-$3'); + } else if (onlyNums.length === 11) { + return onlyNums.replace(/(\d{3})(\d{4})(\d{4})/, '$1-$2-$3'); + } else { + return tel; // 유효하지 않으면 빈 문자열 + } +} \ No newline at end of file diff --git a/apps/wr/components/ui/form/coMat/conCostForm/conCostForm.tsx b/apps/wr/components/ui/form/coMat/conCostForm/conCostForm.tsx index b22f9474e..aaa7c46b9 100644 --- a/apps/wr/components/ui/form/coMat/conCostForm/conCostForm.tsx +++ b/apps/wr/components/ui/form/coMat/conCostForm/conCostForm.tsx @@ -31,9 +31,11 @@ import toast from "react-hot-toast"; export default function ConCostForm({ form, type, + selectionVersion, }: { form: UseFormReturn<DmdCoReqType>; type: string; + selectionVersion: number; }) { const { constCostRes, @@ -70,7 +72,14 @@ export default function ConCostForm({ const dmndQtyWatch = form.watch("dmndQty"); const totalConstCost = form.watch("totalConstCost"); const cstrnCst = form.watch("demandCst.cstrnCst"); + const useUsgCdWatch = form.watch("useUsgCd"); + useEffect(() =>{ + console.log('usgPrcsProcsWatch ', usgPrcsProcsWatch) + + console.log('useUsgCdWatch ' , useUsgCdWatch) + + },[usgPrcsProcsWatch, useUsgCdWatch, selectionVersion]) const calculateConstCost = useCallback(() => { const usgPrcsProcs = usgPrcsProcsWatch; const dmndQtyYear = dmndQtyWatch * 365; @@ -255,7 +264,7 @@ export default function ConCostForm({ placeholder="" type="text" onChange={field.onChange} - value={form.getValues("demandCst.usgPrcsProcs") ?? ""} + value={field.value ?? ""} className="mt-2" /> </FormUi> diff --git a/apps/wr/components/ui/modal/chkgd/pbnPrfExcelModal/pbnPrfExcelModal.tsx b/apps/wr/components/ui/modal/chkgd/pbnPrfExcelModal/pbnPrfExcelModal.tsx index fb6ba9b22..25fe5d273 100644 --- a/apps/wr/components/ui/modal/chkgd/pbnPrfExcelModal/pbnPrfExcelModal.tsx +++ b/apps/wr/components/ui/modal/chkgd/pbnPrfExcelModal/pbnPrfExcelModal.tsx @@ -18,12 +18,13 @@ import { Button, Input } from "@common/components/ui"; import { zodResolver } from "@hookform/resolvers/zod"; import { useEffect, useState } from "react"; import { useForm } from "react-hook-form"; +import toast from "react-hot-toast"; import type { z } from "zod"; -export default function PbnPrfExcelModal({ data }: { data: FcPrfType }) { +export default function PbnPrfExcelModal({ data ,setIsExcelOPen }: { data: FcPrfType ,setIsExcelOPen:(open:boolean )=>void }) { const { prfmnRegData } = useWrStore((state) => state); - const [files, setFiles] = useState<File[] | null>(null); + const [file, setFile] = useState<File | null>(null); const [excelData, setExcelData] = useState<PrfType[]>(); const form = useForm<z.infer<typeof pbnPrfRegExcelZod>>({ resolver: zodResolver(pbnPrfRegExcelZod), @@ -31,10 +32,18 @@ export default function PbnPrfExcelModal({ data }: { data: FcPrfType }) { ym: String(new Date().getFullYear()), }, }); + + const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => { + const selectedFile = e.target.files?.[0]; + if (selectedFile) { + setFile(selectedFile); + } + }; const onSubmit = (values: z.infer<typeof pbnPrfRegExcelZod>) => { if (excelData) { prfmnRegData(excelData as unknown as PrfType); + setIsExcelOPen(false) } @@ -46,31 +55,24 @@ export default function PbnPrfExcelModal({ data }: { data: FcPrfType }) { form.setValue("pbnstRuseFcltNm", data.ruseUtztnFcltNm); }, [data]); - useEffect(() => { - console.log("excelData", excelData); - }, [excelData]); useEffect(() => { - if (files) { - console.log("이런") - const file = files[0]; + if (file) { + const isFile = file if (form.getValues("ym").length < 1) { - ToastFormUi({ - body: "파일업로드 이전에 등록년도를 작성해주세요 ", - title: "지도점검 등록", - }); - setFiles(null); + toast.error("파일업로드 이전에 등록년도를 작성해주세요 " ); + setFile(null); return; } - if (file) { - filePicked(file, 0).then((res) => { + if (isFile) { + filePicked(isFile, 0).then((res) => { if (res !== undefined) setExcelData(excelToPbnPrfData(res as unknown as JSON[], form.getValues("ym"), form.getValues('pbnstRuseFcltFk')) as PrfType[]) }); } } - }, [files]); + }, [file]); return ( <> @@ -144,14 +146,25 @@ export default function PbnPrfExcelModal({ data }: { data: FcPrfType }) { name="file" render={({ field }) => ( <FormUi label="점검결과 파일"> - <FileUpload + {/* <FileUpload files={files || []} setFiles={(files: File[] | null) => { if (files) setFiles(files); }} fileTypes="xlsx" maxFiles={1} - ></FileUpload> + + + + ></FileUpload> */} + +<div className="space-y-4 p-4"> + <Input + type="file" + onChange={handleFileChange} + accept=".csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel" + /> + </div> </FormUi> )} /> diff --git a/apps/wr/components/ui/modal/fcltReg/pbnstRegModal/pbnstRegModal.tsx b/apps/wr/components/ui/modal/fcltReg/pbnstRegModal/pbnstRegModal.tsx index cc0eb80bf..a7987577b 100644 --- a/apps/wr/components/ui/modal/fcltReg/pbnstRegModal/pbnstRegModal.tsx +++ b/apps/wr/components/ui/modal/fcltReg/pbnstRegModal/pbnstRegModal.tsx @@ -1,6 +1,7 @@ "use client"; import { useWrStore } from "@/app/store"; import { + pbnstRegZod, type customPbnst, type PbnstRegArrrayType, type pubHasuType, @@ -16,17 +17,23 @@ import { } from "@common/components/ui"; import { useEffect, useState } from "react"; import { PbnstBuseForm } from "@/components/ui/form/fcltReg/pbnstReg/buseReg/bugRegForm"; -import { useFieldArray, type UseFormReturn } from "react-hook-form"; +import { + useFieldArray, + type FieldErrors, + type UseFormReturn, +} from "react-hook-form"; import { writeRgtrAndMdfr } from "@/app/store/ruseFcltMng/fcltReg/pbnstReg"; +import toast from "react-hot-toast"; +import type { z } from "zod"; export function PbnstRegModal({ form, - setPbnstInsert + setPbnstInsert, }: { form: UseFormReturn<{ items: PbnstRegArrrayType }>; - setPbnstInsert: (data: customPbnst) => void + setPbnstInsert: (data: customPbnst) => void; }) { - const { pbnstInsert } = useWrStore((state) => { + const { pbnstInsert } = useWrStore((state) => { return state; }); const [pbnstBdstForm, setPbnstBdstForm] = useState<customPbnst>( @@ -35,13 +42,65 @@ export function PbnstRegModal({ const [currentTab, setCurrentTab] = useState<string>("mng"); const [indexMng, setIndexMng] = useState<number>(0); const [indexBiz, setIndexBiz] = useState<number>(0); - const onSubmit = async (valuse: { items: PbnstRegArrrayType }) => { - const item = valuse.items[indexMng]; - const data = item ? writeRgtrAndMdfr([item]) : []; + const onSubmit = async (valuse: { items: PbnstRegArrrayType }) => { + const item = valuse.items[indexMng]; + const data = item ? writeRgtrAndMdfr([item]) : []; + if (data && data[0]) { + setPbnstInsert(data[0]); + } + }; + const onInvalid = ( + errors: FieldErrors<{ items: z.infer<typeof pbnstRegZod>[] }> + ) => { + // 재귀 함수를 사용해 중첩된 에러 객체에서 모든 에러 메시지와 경로를 찾습니다. + const findAllErrors = ( + errorObject: any, + path: (string | number)[] = [] + ): { message: string; path: (string | number)[] }[] => { + if (!errorObject) return []; - if (data && data[0]) { - // pbnstInsert(data[0]); - setPbnstInsert(data[0]) + return Object.keys(errorObject).flatMap((key) => { + const value = errorObject[key]; + const currentKey = !isNaN(parseInt(key)) ? parseInt(key) : key; + const newPath = [...path, currentKey]; + if (value && typeof value === "object") { + if (typeof value.message === "string") { + return [{ message: value.message, path: newPath }]; // 에러 메시지와 경로 반환 + } + return findAllErrors(value, newPath); + } + return []; + }); + }; + const errorList = findAllErrors(errors); + if (errorList.length > 0) { + errorList.forEach((errorInfo) => { + const { message, path } = errorInfo; + let prefix = ""; + const itemsIndex = path.indexOf("items"); + + if (itemsIndex > -1 && path.length > itemsIndex + 1) { + const itemIdx = path[itemsIndex + 1] as number; + const facilityName = form.getValues( + `items.${itemIdx}.ruseUtztnFcltNm` + ); + if (facilityName) { + prefix += `[${facilityName}] `; + } + + const fcBizsIndex = path.indexOf("fcBizs"); + if (fcBizsIndex > -1 && path.length > fcBizsIndex + 1) { + const bizIdx = path[fcBizsIndex + 1] as number; + const cycle = form.getValues( + `items.${itemIdx}.fcBizs.${bizIdx}.cycl` + ); + if (cycle) { + prefix += `[${cycle}차수] `; + } + } + } + toast.error(`${prefix}${message}`); + }); } }; @@ -76,7 +135,7 @@ export function PbnstRegModal({ 차수별정보 </TabsTrigger> <Button - onClick={form.handleSubmit(onSubmit)} + onClick={form.handleSubmit(onSubmit, onInvalid)} size={"sm"} className="!justify-end" > From 5fd8c6e6a62d056d29682a81143ab43649f5e3a8 Mon Sep 17 00:00:00 2001 From: leedj312 <dlehd467@gmail.com> Date: Fri, 4 Jul 2025 22:27:55 +0900 Subject: [PATCH 079/108] =?UTF-8?q?=EA=B2=B0=ED=95=A8=EB=8C=80=EC=9E=A5=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ruseFcltMng/fcltReg/pbnstReg/index.tsx | 12 ++++ .../ruseFcltMng/fcltReg/pbnstReg/type.tsx | 15 +++-- .../ruseFcltMng/fcltReg/rainReg/index.tsx | 2 +- .../ruseFcltMng/fcltReg/rswtReg/index.tsx | 60 +++++++++---------- 4 files changed, 53 insertions(+), 36 deletions(-) diff --git a/apps/wr/app/store/ruseFcltMng/fcltReg/pbnstReg/index.tsx b/apps/wr/app/store/ruseFcltMng/fcltReg/pbnstReg/index.tsx index ea7eee5c8..e4bedff60 100644 --- a/apps/wr/app/store/ruseFcltMng/fcltReg/pbnstReg/index.tsx +++ b/apps/wr/app/store/ruseFcltMng/fcltReg/pbnstReg/index.tsx @@ -54,6 +54,7 @@ export type pubFacState = { pbnsUpdateIndex: pbnstInsertIndex; fcOrgwtsNmList:pubHasuType[]; dmdComList: any[]; + apiMessage:string; }; }; type pubFacActions = { @@ -94,6 +95,7 @@ export const pubFacStateExport: pubFacState = { pbnsUpdateIndex: {} as pbnstInsertIndex, fcOrgwtsNmList:[], dmdComList: [], + apiMessage:'' }, }; @@ -269,8 +271,13 @@ export const pubFacActionsExport: (set: any) => pubFacActions = (set: any) => { ruseRes ); + if(response){ + + toast.success(` 하수재이용 시설 정보가 수정되었습니다.`); + } } catch (error) { console.error("Error pbnstRuseFcltUpdate:", error); + toast.error(` 하수재이용 시설 정보가 수정오류입니다.`); } } ,pbnstRuseBizUpdate : async(ruseRes:fcBizType[])=>{ try { @@ -280,6 +287,10 @@ export const pubFacActionsExport: (set: any) => pubFacActions = (set: any) => { `/reuseFc/pubFcBiz`, ruseRes ); + + if(response){ + toast.success(` 하수재이용 시설 사업이 수정되었습니다.`); + } const pbnstRes = await hasuListFetch("11", "11"); return set((state: pubFacState) => ({ @@ -292,6 +303,7 @@ export const pubFacActionsExport: (set: any) => pubFacActions = (set: any) => { })); } catch (error) { console.error("Error pbnstRuseFcltUpdate:", error); + toast.error(` 하수재이용 시설 사업이 수정이 실패되엇습니다..`); } } , getBizCycle: async (pbnstObj: customPbnst) => { diff --git a/apps/wr/app/store/ruseFcltMng/fcltReg/pbnstReg/type.tsx b/apps/wr/app/store/ruseFcltMng/fcltReg/pbnstReg/type.tsx index b908750fd..a111b6c10 100644 --- a/apps/wr/app/store/ruseFcltMng/fcltReg/pbnstReg/type.tsx +++ b/apps/wr/app/store/ruseFcltMng/fcltReg/pbnstReg/type.tsx @@ -236,10 +236,17 @@ export const fcBizSchema = z.object({ export const pbnstRegZod = z.object({ pbnstRuseFcltPk: z.string().default(""), swtrmtFcltFk: z.string().default(""), - ruseUtztnFcltNm: z.string().default(""), - fcltCpct: zNumDefaultZero, - ruseCpct: zNumDefaultZero, - splyExtraCpct: zNumDefaultZero, + ruseUtztnFcltNm: z + .preprocess( + (v) => (v != null ? String(v).trim() : ""), + z.string() + ) + .refine((val) => val.length > 0, { + message: "시설 이름을 입력하세요", + }), + fcltCpct: z.number(), + ruseCpct: z.number(), + splyExtraCpct: z.number(), regDt: zDateDefaultNow, rgtr: z.string().default("test"), mdfcnDt: zDateDefaultNow, diff --git a/apps/wr/app/store/ruseFcltMng/fcltReg/rainReg/index.tsx b/apps/wr/app/store/ruseFcltMng/fcltReg/rainReg/index.tsx index d4cfebfca..6ceb75531 100644 --- a/apps/wr/app/store/ruseFcltMng/fcltReg/rainReg/index.tsx +++ b/apps/wr/app/store/ruseFcltMng/fcltReg/rainReg/index.tsx @@ -34,7 +34,7 @@ export const rainZod = z.object({ bdstNm: z.string().default(""), frstArchPrmsnDt: z.date().default(() => new Date()), archChgPrmsnDt: z.coerce.date().nullish().default(null), - archArea: z.number().default(0), + archArea: z.string().default(''), archChgArea: z.number().default(0), rgtr:z.string().nullish(), mdfr:z.string().nullish(), diff --git a/apps/wr/app/store/ruseFcltMng/fcltReg/rswtReg/index.tsx b/apps/wr/app/store/ruseFcltMng/fcltReg/rswtReg/index.tsx index df9c3b543..d39412d8d 100644 --- a/apps/wr/app/store/ruseFcltMng/fcltReg/rswtReg/index.tsx +++ b/apps/wr/app/store/ruseFcltMng/fcltReg/rswtReg/index.tsx @@ -5,11 +5,12 @@ import type { TableUpperProps } from "@/app/store/common/type/header"; import { z } from "zod"; import { baseReuseBuildingSchema, chgBuildingSchema, validationMessages } from "../rainReg/type"; import { makeMdfr, makeRstr } from "../bdstReg"; +import toast from "react-hot-toast"; const rswtrObjSchema = z.object({ bdstRuseFcltFk: z.number().nullish(), - wtrUsqty: z.number({ required_error: validationMessages.required("물사용량(m³)") }).positive({ message: validationMessages.required("물사용량(m³)") }), - oblgTrgtWtrUsqty: z.number().nullish().default(0), + //wtrUsqty: z.number({ required_error: validationMessages.required("물사용량(m³)") }).positive({ message: validationMessages.required("물사용량(m³)") }), + //oblgTrgtWtrUsqty: z.number().nullish().default(0), orgwtKndCd: z.string({ required_error: validationMessages.requiredSelect("원수 종류") }).min(1, { message: validationMessages.requiredSelect("원수 종류") }), filtrTrtmfctCpct: z.number({ required_error: validationMessages.required("처리시설 용량") }), filtrTrtmfctProcs: z.string({ required_error: validationMessages.required("주요처리공정") }).min(1, { message: validationMessages.required("주요처리공정") }), @@ -36,7 +37,7 @@ export const rswtZod = z.object({ frstArchPrmsnDt: z.date().default(() => new Date()), archChgPrmsnDt: z.coerce.date().nullish().default(null), chBuildingYn: z.string().nullish(), - archArea: z.number().default(0), + archArea: z.string().default(''), archChgArea: z.number().default(0), rgtr: z.string().nullish().default(null), mdfr: z.string().nullish().default(null), @@ -152,8 +153,8 @@ export const rswtFacActionsExport: (set: any) => rswtFacSActions = ( try { const chData = await makeMdfr(data); const res = await putFectch("/reuseFc/waterreuseFc", chData); - if (res.ok) { - alert("등록이 완료되었습니다."); + if (res.status ===200) { + toast.success("중수도 건축물 수정이 완료되었습니다."); const rswtRes = await initFetch(code); set((state: rswtFacState) => ({ ...state, @@ -165,10 +166,8 @@ export const rswtFacActionsExport: (set: any) => rswtFacSActions = ( })); } } catch (errorUpdate) { - alert( - "업데이트 오류입니다. " + - (errorUpdate instanceof Error ? errorUpdate.message : "") - ); + toast.error("중수도 건축물 수정 오류입니다. "); + } finally { } }, @@ -176,29 +175,28 @@ export const rswtFacActionsExport: (set: any) => rswtFacSActions = ( try { const chData = await makeRstr(data); const res = await postFectch("/reuseFc/waterreuseFc", chData); - if (res.ok) { - if (data.chgBuilding.length > 0) { - await res - .json() - .then((resData: any) => { - const chPK = resData?.data?.bdstInfoPk; - const makeCh = resData.data.chgBuilding.map( - (item: chgBuilding) => ({ - ...item, - bdstInfoFk: chPK, - }) - ); - const chch = postFectch("/reuseFc/chgBuilding", makeCh); - return chch; - }) - .then((chch: any) => { - if (chch.ok) { - alert("등록이 완료되었습니다."); - } - }); + if (res.status===200) { + if(data.chgBuilding.length > 0){ + + const chPK = res?.data?.bdstInfoPk + const makeCh= res.data.chgBuilding.map((item: chgBuilding) => + ( { + ...item, + bdstInfoFk: chPK, + }) + ); + const chch= await postFectch("/reuseFc/chgBuilding", makeCh); + + if(chch.status ===200) { + toast.success("건축물 등록이 완료되었습니다."); + }else{ + toast.error("건축물 증축/개축/중축 오류입니다."); + } } else { - alert("등록이 완료되었습니다."); + toast.success("건축물 등록이 완료되었습니다."); } + }else{ + toast.error("건축물 등록이 완료되었습니다."); } const rainRes = await initFetch(code); set((state: rswtFacState) => ({ @@ -211,7 +209,7 @@ export const rswtFacActionsExport: (set: any) => rswtFacSActions = ( })); } catch (e) { console.log("e", e); - alert("등록 오류입니다."); + toast.error("등록시 오류가 발생하였습니다."); } finally { } }, From 5a681420465af0ffe153b9fce121484947c53b9c Mon Sep 17 00:00:00 2001 From: jyha00 <jyha@mind-one.co.kr> Date: Fri, 4 Jul 2025 22:35:45 +0900 Subject: [PATCH 080/108] =?UTF-8?q?=EC=9B=90=EC=88=98,=EC=B7=A8=EC=88=98?= =?UTF-8?q?=20=EB=AA=A8=EB=8B=AC=EC=B0=BD=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../fcltReg/pbnstReg/buseReg/bugRegForm.tsx | 10 +- .../fcOrgwtsRegModal/fcOrgwtsRegModal.tsx | 99 ++++++++++++------- 2 files changed, 71 insertions(+), 38 deletions(-) diff --git a/apps/wr/components/ui/form/fcltReg/pbnstReg/buseReg/bugRegForm.tsx b/apps/wr/components/ui/form/fcltReg/pbnstReg/buseReg/bugRegForm.tsx index 8ea07535d..a1d433f2d 100644 --- a/apps/wr/components/ui/form/fcltReg/pbnstReg/buseReg/bugRegForm.tsx +++ b/apps/wr/components/ui/form/fcltReg/pbnstReg/buseReg/bugRegForm.tsx @@ -111,9 +111,9 @@ export function PbnstBuseForm({ const fcOrgwts = form.watch(`items.${indexMng}.fcBizs.${indexBiz}.fcOrgwts`); useEffect(() => { - console.log("fcOrgwts ", fcOrgwts); + console.log("fcOrgwts", fcOrgwts); if (fcOrgwts && fcOrgwts?.length > 0) { - const swtrmtFcltFkList = fcOrgwts.map((item) => item.swtrmtFcltFk); + const swtrmtFcltFkList = fcOrgwts?.map((item) => item.swtrmtFcltFk); console.log("swtrmtFcltFkList ", swtrmtFcltFkList); @@ -128,8 +128,6 @@ export function PbnstBuseForm({ useEffect(() => { if (fcOrgwtsNmList && fcOrgwtsNmList.length > 0) { - console.log("fcOrgwtsNmList ", fcOrgwtsNmList); - console.log("fcOrgwts ", fcOrgwts); fcOrgwts?.forEach((item, index) => { const orgwtNm = fcOrgwtsNmList.find( @@ -421,6 +419,7 @@ export function PbnstBuseForm({ </div> <RuseMngTable key={`fcOrgwts-${indexMng}-${indexBiz}`} // 이게 핵심! + tableData={ form.getValues( `items.${indexMng}.fcBizs.${indexBiz}.fcOrgwts` @@ -724,6 +723,9 @@ export function PbnstBuseForm({ bodyClass={"w-[70vw] max-w-none max-h-[90vh] overflow-y-auto"} body={ <FcOrgwtsRegModal + initTableData={form.getValues( + `items.${indexMng}.fcBizs.${indexBiz}.fcOrgwts` + ) ?? []} closeModal={() => { setIsFcOrgwts(false); }} diff --git a/apps/wr/components/ui/modal/fcltReg/fcOrgwtsRegModal/fcOrgwtsRegModal.tsx b/apps/wr/components/ui/modal/fcltReg/fcOrgwtsRegModal/fcOrgwtsRegModal.tsx index 9c8f2cdd6..69ec1afcc 100644 --- a/apps/wr/components/ui/modal/fcltReg/fcOrgwtsRegModal/fcOrgwtsRegModal.tsx +++ b/apps/wr/components/ui/modal/fcltReg/fcOrgwtsRegModal/fcOrgwtsRegModal.tsx @@ -14,13 +14,16 @@ import { Button } from "@common/business_components/ui/table/lib/button"; import { Input } from "@common/components/ui"; import { ArrowLeft, ArrowRight } from "lucide-react"; import { useEffect, useRef, useState } from "react"; +import toast from "react-hot-toast"; export default function FcOrgwtsRegModal({ onRightSet, closeModal, + initTableData, }: { onRightSet?: (rightData: fcOrgwtType[]) => void; closeModal?: (val: boolean) => void; + initTableData?: any[]; }) { const { code, pbnst, getSwtrmtFcltNm, searchFclts } = useWrStore( (state) => state @@ -31,6 +34,30 @@ export default function FcOrgwtsRegModal({ const leftRef = useRef<pubHasuType[] | null>([] as pubHasuType[]); const [rightData, setRightData] = useState<pubHasuType[]>(); const [inputVal, setInputVal] = useState<string>(""); + const saveData = () => { + if (rightData && rightData?.length > 0) { + if (onRightSet) + onRightSet( + (rightData as pubHasuType[])?.map((item) => { + return { + ...item, + swtrmtFcltFk: item.bscFctCd, // 임시값, 실제 데이터에 맞게 수정 + orgwtWtritQnt: Number(item.fctCpc), // 임시값, 실제 데이터에 맞게 수정 + tmsYn: "N", // 임시값, 실제 데이터에 맞게 수정 + dschgFmYn: "N", // 임시값, 실제 데이터에 맞게 수정 + dtlPstnExpln: item.addr, // 임시값, 실제 데이터에 맞게 수정 + choose: false, // 임시값, 실제 데이터에 맞게 수정 + } + // 필요한 경우 다른 속성들도 추가 + }) as unknown as fcOrgwtType[] + ); + + closeModal?.(false) + } + else { + toast.error("취수하수처리장 목록을 선택해주세요.") + } + } useEffect(() => { if (Array.isArray(searchFclts)) { @@ -48,46 +75,50 @@ export default function FcOrgwtsRegModal({ }, [searchFclts]); useEffect(() => { if (code.sggCd.length > 0 && pbnstHasuRes.length > 0) { - setbdstTable(tableData(pbnstHasuRes, { code })); - - console.log("bdstTable ", bdstTable); + const data = pbnstHasuRes.filter((item) => !initTableData?.find((initItem) => initItem.ruseUtztnFcltNm === item.bscFctNm)) + leftRef.current?.filter((item) => !initTableData?.find((initItem) => initItem.ruseUtztnFcltNm === item.bscFctNm)); + setbdstTable(tableData(data, { code })); } - }, [code, pbnstHasuRes]); + }, [code, pbnstHasuRes, initTableData]); + useEffect(() => { - console.log("rightData ", rightData); - if (onRightSet) - onRightSet( - (rightData as pubHasuType[])?.map((item) => ({ - swtrmtFcltFk: item.bscFctCd, // 임시값, 실제 데이터에 맞게 수정 - orgwtWtritQnt: Number(item.fctCpc), // 임시값, 실제 데이터에 맞게 수정 - tmsYn: "N", // 임시값, 실제 데이터에 맞게 수정 - dschgFmYn: "N", // 임시값, 실제 데이터에 맞게 수정 - dtlPstnExpln: item.addr, // 임시값, 실제 데이터에 맞게 수정 - choose: false, // 임시값, 실제 데이터에 맞게 수정 - // 필요한 경우 다른 속성들도 추가 - })) as unknown as fcOrgwtType[] - ); - }, [rightData]); + if (initTableData && Array.isArray(initTableData)) { + setRightData(initTableData.map(item => ({ + ...item, + choose: item.choose, + sido: item.dtlPstnExpln?.split(" ")[0] || "", + gugun: item.dtlPstnExpln?.split(" ")[1] || "", + bscFctNm: item.ruseUtztnFcltNm, + addr: item.dtlPstnExpln, + orgwtWtritQnt: String(item.orgwtWtritQnt), + fctCpc: String(item.orgwtWtritQnt), + swtrmtFcltFk: item.swtrmtFcltFk, + }))); + rightRef.current = initTableData.map(item => ({ ...item })); + } + }, []); return ( <div className="flex flex-col h-[80vh]"> <div className="shrink-0 p-4 border-b z-10 bg-white"> <div className="flex items-center"> <Title title="취수하수처리장 추가" /> - {/* <Button size="sm" onClick={() => closeModal?.(false)}>저장</Button> */} + <div className="flex items-center gap-2 mt-8"> - <Label className="text-xl text-gray-600">시설명</Label> - <Input - className="w-48 text-xl" - type="text" - value={inputVal} - onChange={(e) => setInputVal(e.target.value)} - /> - <Button size="sm" onClick={() => getSwtrmtFcltNm(inputVal)}> - 조회 - </Button> + + <Label className="text-xl text-gray-600">시설명</Label> + <Input + className="w-48 text-xl" + type="text" + value={inputVal} + onChange={(e) => setInputVal(e.target.value)} + /> + <Button size="sm" onClick={() => getSwtrmtFcltNm(inputVal)}> + 조회 + </Button> + <Button size="sm" onClick={saveData}>저장</Button> </div> </div> </div> @@ -107,10 +138,10 @@ export default function FcOrgwtsRegModal({ {/* 테이블 영역 */} <div className="flex flex-row gap-4 flex-grow p-4 overflow-x-auto"> - {/* 왼쪽 테이블 */} + {/* 왼쪽 테이블 */} - <div className="w-5/12 min-w-[400px]"> - <Label className="text-2xl text-gray-600 mb-2 block text-right"> + <div className="w-5/12 min-w-[400px]"> + <Label className="text-2xl text-gray-600 mb-2 block text-right"> 공공하수처리시설 목록 </Label> <RuseMngTable @@ -123,7 +154,7 @@ export default function FcOrgwtsRegModal({ {/* 중간 이동 버튼 */} <div className="flex flex-col justify-center items-center gap-4 w-[200px] min-w-[120px]"> - <ArrowRight + <ArrowRight onClick={() => { const checked = leftRef.current?.filter((item) => item.choose); const unchecked = leftRef.current?.filter((item) => !item.choose); @@ -159,7 +190,7 @@ export default function FcOrgwtsRegModal({ {/* 오른쪽 테이블 */} <div className="w-5/12 min-w-[400px]"> - <Label className="text-2xl text-gray-600 mb-2 block text-right"> + <Label className="text-2xl text-gray-600 mb-2 block text-right"> 취수하수처리장 목록 </Label> <RuseMngTable From 7d56ba0243f84e6f83c571f2fcd4097399300aa3 Mon Sep 17 00:00:00 2001 From: leedj312 <dlehd467@gmail.com> Date: Sun, 6 Jul 2025 00:46:43 +0900 Subject: [PATCH 081/108] =?UTF-8?q?=ED=95=98=EC=88=98=EC=9E=AC=EC=9D=B4?= =?UTF-8?q?=EC=9A=A9=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/ruseFcltMng/fcltReg/pbnstReg/page.tsx | 91 +++++++++---------- .../ruseFcltMng/fcltReg/pbnstReg/utilsZod.tsx | 8 +- .../ruseFcltMng/fcltReg/pbnstReg/type.tsx | 21 +++-- .../fcltReg/pbnstRegModal/pbnstRegModal.tsx | 11 +++ 4 files changed, 68 insertions(+), 63 deletions(-) diff --git a/apps/wr/app/ruseFcltMng/fcltReg/pbnstReg/page.tsx b/apps/wr/app/ruseFcltMng/fcltReg/pbnstReg/page.tsx index 8d7e32c66..0c3ad9dce 100644 --- a/apps/wr/app/ruseFcltMng/fcltReg/pbnstReg/page.tsx +++ b/apps/wr/app/ruseFcltMng/fcltReg/pbnstReg/page.tsx @@ -70,6 +70,8 @@ export default function PublicReuseFaciltyInsert() { // defaultValues: { items: pbnstRegArrayZod.parse([]) }, mode: "onChange", }); + const { isDirty } = form.formState; + const { fields: items, append, @@ -87,7 +89,11 @@ export default function PublicReuseFaciltyInsert() { }, [pbnst.isInit, pubHasuInit]); const onSubmit = async (valuse: { items: PbnstRegArrrayType }) => { - const data = writeRgtrAndMdfr(valuse.items); + + if (!isDirty) { + toast.error("변경사항이 없습니다."); // 변경사항이 없으면 메시지 표시 + return; + } const data = writeRgtrAndMdfr(valuse.items); let bizs = [] as fcBizType[]; data.forEach((fclt) => { bizs = [...bizs, ...fclt.fcBizs]; @@ -110,78 +116,58 @@ export default function PublicReuseFaciltyInsert() { }; // 폼 유효성 검사 실패 시 호출될 함수 - const onInvalid = ( - errors: FieldErrors<{ items: z.infer<typeof pbnstRegZod>[] }> - ) => { - // 재귀 함수를 사용해 중첩된 에러 객체에서 모든 에러 메시지와 경로를 찾습니다. + const onInvalid = (errors: FieldErrors<{ items: z.infer<typeof pbnstRegZod>[] }>) => { + console.log("errors:", errors); + const findAllErrors = ( errorObject: any, path: (string | number)[] = [] ): { message: string; path: (string | number)[] }[] => { if (!errorObject) return []; - - // Zod에서 배열 에러는 바로 배열로 전달됨 (예: items: [ {}, {...}, ... ]) - if (Array.isArray(errorObject)) { - return errorObject.flatMap((entry, index) => - findAllErrors(entry, [...path, index]) - ); - } - - if (typeof errorObject === "object") { - const entries: { message: string; path: (string | number)[] }[] = []; - - if (typeof errorObject.message === "string") { - entries.push({ message: errorObject.message, path }); - } - - if (Array.isArray(errorObject._errors)) { - for (const msg of errorObject._errors) { - entries.push({ message: msg, path }); + + return Object.keys(errorObject).flatMap((key) => { + const value = errorObject[key]; + const currentKey = !isNaN(parseInt(key)) ? parseInt(key) : key; + const newPath = [...path, currentKey]; + + if (value && typeof value === "object") { + if (value.message) { + return [{ message: value.message, path: newPath }]; } + + return findAllErrors(value, newPath); } - - const keys = Object.keys(errorObject).filter( - (k) => k !== "message" && k !== "_errors" - ); - - for (const key of keys) { - entries.push(...findAllErrors(errorObject[key], [...path, key])); - } - - return entries; - } - - return []; + return []; + }); }; + const errorList = findAllErrors(errors); - - console.log(' errors ', errors) + if (errorList.length > 0) { errorList.forEach((errorInfo) => { const { message, path } = errorInfo; let prefix = ""; + const itemsIndex = path.indexOf("items"); - + if (itemsIndex > -1 && path.length > itemsIndex + 1) { const itemIdx = path[itemsIndex + 1] as number; - const facilityName = form.getValues( - `items.${itemIdx}.ruseUtztnFcltNm` - ); + const facilityName = form.getValues(`items.${itemIdx}.ruseUtztnFcltNm`); if (facilityName) { prefix += `[${facilityName}] `; } - + const fcBizsIndex = path.indexOf("fcBizs"); if (fcBizsIndex > -1 && path.length > fcBizsIndex + 1) { const bizIdx = path[fcBizsIndex + 1] as number; - const cycle = form.getValues( - `items.${itemIdx}.fcBizs.${bizIdx}.cycl` - ); + const cycle = form.getValues(`items.${itemIdx}.fcBizs.${bizIdx}.cycl`); if (cycle) { prefix += `[${cycle}차수] `; } } } + + // 최종 오류 메시지 출력 toast.error(`${prefix}${message}`); }); } @@ -214,7 +200,14 @@ export default function PublicReuseFaciltyInsert() { useEffect(() => { if (pbnst.pbnstRes && pbnst.pbnstRes?.length > 0) { - form.reset({ items: pbnstRegArrayZod.parse(pbnst.pbnstRes) }); + // form.reset({ + // items: pbnstRegArrayZod.parse(pbnst.pbnstRes), + // }); + + form.reset({ items: pbnst.pbnstRes }); + + //form.reset({ items: pbnst.pbnstRes }); + } else { form.reset({ items: [] }); } @@ -249,7 +242,7 @@ export default function PublicReuseFaciltyInsert() { // ] as fcBizType[], // }) // ); - append(createDefaultPbnstItem(selecTableRow, pbnstRes.length)); + append(createDefaultPbnstItem(selecTableRow, pbnstRes.length)); setIsRuseReg({ isModal: true }); } @@ -428,7 +421,7 @@ export default function PublicReuseFaciltyInsert() { form={form} // form prop 전달 setPbnstInsert={(data) => { if (data) { - // pbnstInsert(data); // pbnstInsert 함수 호출 + pbnstInsert(data); // pbnstInsert 함수 호출 setIsRuseReg({ isModal: false }); } }} diff --git a/apps/wr/app/ruseFcltMng/fcltReg/pbnstReg/utilsZod.tsx b/apps/wr/app/ruseFcltMng/fcltReg/pbnstReg/utilsZod.tsx index 14224fce2..3433a62c5 100644 --- a/apps/wr/app/ruseFcltMng/fcltReg/pbnstReg/utilsZod.tsx +++ b/apps/wr/app/ruseFcltMng/fcltReg/pbnstReg/utilsZod.tsx @@ -10,12 +10,12 @@ export function createDefaultPbnstItem( pbnstRuseFcltPk: pk, swtrmtFcltFk: "", ruseUtztnFcltNm: "", // 필수 입력이므로 빈 문자열로 - fcltCpct: 0, - ruseCpct: 0, - splyExtraCpct: 0, + fcltCpct: 0, + ruseCpct: 0, + splyExtraCpct: 0, regDt: null, rgtr: "test", - mdfcnDt: null, + mdfcnDt: null, mdfr: "test", fcFiles: [], fcBizs: [ diff --git a/apps/wr/app/store/ruseFcltMng/fcltReg/pbnstReg/type.tsx b/apps/wr/app/store/ruseFcltMng/fcltReg/pbnstReg/type.tsx index a111b6c10..2ef1c6d2f 100644 --- a/apps/wr/app/store/ruseFcltMng/fcltReg/pbnstReg/type.tsx +++ b/apps/wr/app/store/ruseFcltMng/fcltReg/pbnstReg/type.tsx @@ -238,22 +238,23 @@ export const pbnstRegZod = z.object({ swtrmtFcltFk: z.string().default(""), ruseUtztnFcltNm: z .preprocess( - (v) => (v != null ? String(v).trim() : ""), - z.string() - ) - .refine((val) => val.length > 0, { - message: "시설 이름을 입력하세요", - }), - fcltCpct: z.number(), - ruseCpct: z.number(), - splyExtraCpct: z.number(), + (v) => (v !== null && v !== undefined ? String(v).trim() : ""), // null/undefined → "", 공백 제거 + z.string().min(1, "시설 이름을 입력하세요")), + fcltCpct: zNumDefaultZero, + ruseCpct: zNumDefaultZero, + splyExtraCpct: zNumDefaultZero, regDt: zDateDefaultNow, rgtr: z.string().default("test"), mdfcnDt: zDateDefaultNow, mdfr: z.string().default("test"), fcFiles: z.array(fcFileSchema).default([]).nullish(), fcBizs: z.array(fcBizSchema).default([]), - fcOpers: z.array(fcOperSchema).default([]).nullish(), + fcOpers: z + .array(fcOperSchema) + .refine((val) => val.length >= 1, { + message: "재이용시설의 운영정보를 최소 하나를 등록해주세요", + }) + .default([]), // 기본값 설정 }); export const pbnstRegArrayZod = z.array(pbnstRegZod); diff --git a/apps/wr/components/ui/modal/fcltReg/pbnstRegModal/pbnstRegModal.tsx b/apps/wr/components/ui/modal/fcltReg/pbnstRegModal/pbnstRegModal.tsx index a7987577b..34bc71a46 100644 --- a/apps/wr/components/ui/modal/fcltReg/pbnstRegModal/pbnstRegModal.tsx +++ b/apps/wr/components/ui/modal/fcltReg/pbnstRegModal/pbnstRegModal.tsx @@ -42,6 +42,8 @@ export function PbnstRegModal({ const [currentTab, setCurrentTab] = useState<string>("mng"); const [indexMng, setIndexMng] = useState<number>(0); const [indexBiz, setIndexBiz] = useState<number>(0); + + const onSubmit = async (valuse: { items: PbnstRegArrrayType }) => { const item = valuse.items[indexMng]; const data = item ? writeRgtrAndMdfr([item]) : []; @@ -53,6 +55,8 @@ export function PbnstRegModal({ errors: FieldErrors<{ items: z.infer<typeof pbnstRegZod>[] }> ) => { // 재귀 함수를 사용해 중첩된 에러 객체에서 모든 에러 메시지와 경로를 찾습니다. + + const findAllErrors = ( errorObject: any, path: (string | number)[] = [] @@ -72,13 +76,18 @@ export function PbnstRegModal({ return []; }); }; + + const errorList = findAllErrors(errors); + + if (errorList.length > 0) { errorList.forEach((errorInfo) => { const { message, path } = errorInfo; let prefix = ""; const itemsIndex = path.indexOf("items"); + if (itemsIndex > -1 && path.length > itemsIndex + 1) { const itemIdx = path[itemsIndex + 1] as number; const facilityName = form.getValues( @@ -99,6 +108,8 @@ export function PbnstRegModal({ } } } + + toast.error(`${prefix}${message}`); }); } From c5f7d6290862204309f9b28d6a7bbdaec129d990 Mon Sep 17 00:00:00 2001 From: leedj312 <dlehd467@gmail.com> Date: Sun, 6 Jul 2025 14:31:10 +0900 Subject: [PATCH 082/108] =?UTF-8?q?=EC=82=AD=EC=A0=9C=EB=90=9C=20=EB=B6=80?= =?UTF-8?q?=EB=B6=84=EC=9D=84=20=EC=88=98=EC=A0=95=ED=95=98=EA=B3=A0=20?= =?UTF-8?q?=EC=8B=A4=EC=A0=81=20=EC=98=A4=EB=A5=98=EC=82=AC=ED=95=AD=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wr/app/matMng/coMat/coMatRvw/page.tsx | 4 +- apps/wr/app/matMng/coMng/coSttn/page.tsx | 11 ++- .../prfmncReg/pbnstPrfmncReg/page.tsx | 37 +++++++--- .../wr/app/store/matDmd/coMatRvw/headList.tsx | 4 +- apps/wr/app/store/matDmd/coMatRvw/index.tsx | 4 +- apps/wr/app/store/matDmd/coMatRvw/type.tsx | 74 ++++++++----------- apps/wr/app/store/matDmd/coSttn/headList.tsx | 5 ++ apps/wr/app/store/matDmd/coSttn/index.tsx | 2 +- .../store/prfmncReg/pbnstPrfmncReg/index.tsx | 23 ++++-- apps/wr/components/lib/func/TableData.tsx | 48 ++++++------ .../ui/form/coMat/conCostForm/conCostForm.tsx | 20 ++--- .../pbnPrfExcelModal/pbnPrfExcelModal.tsx | 6 +- 12 files changed, 136 insertions(+), 102 deletions(-) diff --git a/apps/wr/app/matMng/coMat/coMatRvw/page.tsx b/apps/wr/app/matMng/coMat/coMatRvw/page.tsx index 46aeacbf3..acfdb6585 100644 --- a/apps/wr/app/matMng/coMat/coMatRvw/page.tsx +++ b/apps/wr/app/matMng/coMat/coMatRvw/page.tsx @@ -119,7 +119,7 @@ export default function CoMatRvw() { }, [useUsgCdWatch, form,selectionVersion]); useEffect(() => { - if (coMatRvwRes.length > 0) { + if (coMatRvwRes&&coMatRvwRes.length > 0) { const data = coMatRvwRes?.map((item, index) => { let flag = ""; @@ -190,7 +190,7 @@ export default function CoMatRvw() { <div className="col-span-7 w-full"> <div className="flex flex-row items-center"> <Label className="text-xl text-gray-600 mx-6"> - 하수 재이용년도 + 수요신청년도 </Label> <SelectBox className="h-full w-32 text-2xl" diff --git a/apps/wr/app/matMng/coMng/coSttn/page.tsx b/apps/wr/app/matMng/coMng/coSttn/page.tsx index 181a5d82f..e2c8b8195 100644 --- a/apps/wr/app/matMng/coMng/coSttn/page.tsx +++ b/apps/wr/app/matMng/coMng/coSttn/page.tsx @@ -5,6 +5,7 @@ import { btnClass } from "@/app/store/common/code"; import { fetchType, getFetch } from "@/components/lib/api"; import { excelDownload, + formatPhoneNumber, getCity, tableData } from "@/components/lib/func/commom"; @@ -26,6 +27,7 @@ import ListDiv from "@common/business_components/ui/list_div"; import SdmInsertForm from "@common/business_components/ui/sdmInsertForm"; import type { DemandCompType } from "@common/business_components/ui/sdmInsertForm/form"; import { Button } from "@common/components/ui"; +import moment from "moment"; import { useCallback, useEffect, useState } from "react"; import toast, { Toaster } from "react-hot-toast"; @@ -83,6 +85,9 @@ export default function CoSttn() { const data = tableData(coSttnRes, { code }); const chData = data.map((item: any) => ({ ...item, + telno:formatPhoneNumber(item.telno||undefined), + regDt: moment(item.regDt).format("YYYY-MM-DD") + })); // .filter((item: any) => item !== undefined); setBdstTable(chData); @@ -162,7 +167,7 @@ export default function CoSttn() { </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"> - <Button size={"sm"} onClick={searchClick}> + <Button className="mt-2" size={"sm"} onClick={searchClick}> 조회 </Button> <Button @@ -182,7 +187,7 @@ export default function CoSttn() { onClick={() => { /* 저장 로직 */ if (!upadateData) { - <ToastFormUi body={"수요처 변경을 원하는 행을 선택해주세요"} title={"수요처관리"}></ToastFormUi>; + toast.error("수요처 변경을 원하는 행을 선택해주세요") }else{ setIsDmdModal({ ...isDmdModal, @@ -190,7 +195,7 @@ export default function CoSttn() { } }} > - 저장 + 수정 </Button> <Button size={"sm"} diff --git a/apps/wr/app/ruseFcltMng/prfmncReg/pbnstPrfmncReg/page.tsx b/apps/wr/app/ruseFcltMng/prfmncReg/pbnstPrfmncReg/page.tsx index f86367a17..c54725ff2 100644 --- a/apps/wr/app/ruseFcltMng/prfmncReg/pbnstPrfmncReg/page.tsx +++ b/apps/wr/app/ruseFcltMng/prfmncReg/pbnstPrfmncReg/page.tsx @@ -53,10 +53,11 @@ export default function prfmncPrfmncReg() { const [inputData, setInputData] = useState<any[]>(); const [pbnstRuseFcltPk, setPbnstRuseFcltPk] = useState<string>(); const [pageNation, setPageNation] = useState<number>(1); - const [cellNum, setCellNum] = useState<number>(); + const [cellNum, setCellNum] = useState<number>(1); const [searchYear, setSearchYear] = useState<string>( String(new Date().getFullYear()) ); + const excelClick = useCallback(() => { excelDownload( [[]], @@ -66,21 +67,22 @@ export default function prfmncPrfmncReg() { ); }, [prfmnc.excelList]); - const onSave = useCallback(() => { + const onSave = useCallback( + async() => { if (pbnstRuseFcltPk) { - prfmnRegData( + await prfmnRegData(pageNation,'41', excelToPbnPrfData( inputData as unknown as JSON[], searchYear, pbnstRuseFcltPk ) as unknown as PrfType ); - prfmncHasuInit(String(pageNation), "41"); + // cellClick(cellNum) } - }, [inputData, searchYear, pbnstRuseFcltPk, prfmncHasuInit, pageNation]); + }, [inputData, searchYear, pbnstRuseFcltPk, pageNation,cellNum]); const cellClick = useCallback( - (index: number | undefined, headId: string) => { + (index: number | undefined) => { if (index !== undefined) { setIsExcelOPen({ isModal: false, @@ -152,7 +154,6 @@ export default function prfmncPrfmncReg() { } const tableData = [...inUsageData, ...outUsageData, usageTotalObj]; setPbnstRuseFcltPk(prfmncRes[index].pbnstRuseFcltPk); - setInputData(tableData); } else { setInputData([]); @@ -164,12 +165,17 @@ export default function prfmncPrfmncReg() { useEffect(() => { if (!prfmnc.isInit) { - prfmncHasuInit("1", "41"); + prfmncHasuInit(1, "41"); } }, [prfmnc.isInit, prfmncHasuInit]); useEffect(() => { setbdstTable(prfmncRes); - }, [prfmncRes]); + + // cellNum이 0일 경우(첫 번째 행)도 유효한 인덱스이므로 undefined 여부만 체크합니다. + if (cellNum !== undefined) { + cellClick(cellNum); + } + }, [prfmncRes, cellNum, cellClick]); return ( <> @@ -250,13 +256,14 @@ export default function prfmncPrfmncReg() { <ListDiv className="min-h-0"> <ScrollArea className="h-[60vh]"> <UiTable + key={'leftTable'} tableData={bdstTable || []} headlist={pbnstPrfmncHeadList} pageSize={10} total={10000} headName={"하수재이용시설 목록"} cellClick={(index: number | undefined, headId: string) => { - cellClick(index, headId); + cellClick(index); setCellNum(index ?? 0); }} onPageChange={setPageNation} @@ -277,7 +284,7 @@ export default function prfmncPrfmncReg() { <TableHeader className="font-bold"> {headMakeprfmnc(monthHeadList, [])} </TableHeader> - <TableBody> + <TableBody key={prfmnc.pageKey+'pageKey'}> {InputTableBody(inputData ?? [], monthHeadList, setInputData)} </TableBody> </Table> @@ -292,7 +299,15 @@ export default function prfmncPrfmncReg() { data={isExcelOpen.data} setIsExcelOPen={(open) => { setIsExcelOPen({ ...isExcelOpen, isModal: open }) + }} + setModalExcelData={ async(excelData ) =>{ + await prfmnRegData(pageNation,'41', + excelData, + ); + // 데이터 저장 후 UI 업데이트는 useEffect가 감지하여 처리하므로 여기서는 호출하지 않습니다. + } + } ></PbnPrfExcelModal> } open={isExcelOpen.isModal} diff --git a/apps/wr/app/store/matDmd/coMatRvw/headList.tsx b/apps/wr/app/store/matDmd/coMatRvw/headList.tsx index 6b783ea68..6d9bf8862 100644 --- a/apps/wr/app/store/matDmd/coMatRvw/headList.tsx +++ b/apps/wr/app/store/matDmd/coMatRvw/headList.tsx @@ -29,12 +29,12 @@ export const coRvwHeadListExport: CommonTableProps[]= [ visible: true, }, { - id: "dmndTmng", //바꾸기 + id: "dmndTmng", title: "신청일자", visible: true, }, { - id: "splyFcltFlg", //바꾸기 + id: "splyFcltFlg", title: "진행상태", visible: true, }, diff --git a/apps/wr/app/store/matDmd/coMatRvw/index.tsx b/apps/wr/app/store/matDmd/coMatRvw/index.tsx index 89836957b..4140905cf 100644 --- a/apps/wr/app/store/matDmd/coMatRvw/index.tsx +++ b/apps/wr/app/store/matDmd/coMatRvw/index.tsx @@ -102,7 +102,9 @@ export const coRvwActionsExport: (set: any) => coRvwActions = (set: any) => { `/reuseFc/request/cst/${splyCd}/${dmdCd}`, data ); - + if(isUpdate){ + toast.error("수요 매칭 검토 검토로 저장되었습니다."); + } const coMatRvwRes = coMatInit( 1, 10, diff --git a/apps/wr/app/store/matDmd/coMatRvw/type.tsx b/apps/wr/app/store/matDmd/coMatRvw/type.tsx index 9f146c0fb..979e24d55 100644 --- a/apps/wr/app/store/matDmd/coMatRvw/type.tsx +++ b/apps/wr/app/store/matDmd/coMatRvw/type.tsx @@ -1,24 +1,24 @@ import { z } from "zod"; const DemandInfoSchema = z.object({ - dmdInfoSn: z.number(), - dmdInfoFk: z.string(), - dmndTmng: z.coerce.date(), - dmndQty: z.number(), - wntUntprc: z.number(), - grssClfg: z.number(), - combResdch: z.number(), - trbt: z.number(), + dmdInfoSn: z.number().nullish(), + dmdInfoFk: z.string().nullish(), + dmndTmng: z.coerce.date().nullish(), + dmndQty: z.number().nullish(), + wntUntprc: z.number().nullish(), + grssClfg: z.number().nullish(), + combResdch: z.number().nullish(), + trbt: z.number().nullish(), blgcOxygDmndQnt: z.number(), - smll: z.number(), + smll: z.number().nullish(), crmty: z.number().nullish(), - tn: z.number(), - tph: z.number(), + tn: z.number().nullish(), + tph: z.number().nullish(), ph: z.number(), - clIonQnt: z.number(), - cdtnv: z.number(), - rmrk: z.string(), - useUsgCd: z.string(), + clIonQnt: z.number().nullish(), + cdtnv: z.number().nullish(), + rmrk: z.string().nullish(), + useUsgCd: z.string().nullish(), mtchnCmptnYmd: z.string().nullish(), rvwCmptnYmd: z.string().nullish() }); @@ -38,28 +38,28 @@ import { z } from "zod"; }); const DemandCoSchema = z.object({ - dmdInfoPk: z.string(), - dmdCoNm: z.string(), - ctpvCd: z.string(), - sggCd: z.string(), - daddr: z.string(), - splyBgngDay: z.coerce.date(), - splyEndDay: z.coerce.date(), - splyCpct: z.number(), + dmdInfoPk: z.string().nullish(), + dmdCoNm: z.string().nullish(), + ctpvCd: z.string().nullish(), + sggCd: z.string().nullish(), + daddr: z.string().nullish(), + splyBgngDay: z.coerce.date().nullish(), + splyEndDay: z.coerce.date().nullish(), + splyCpct: z.number().nullish(), splyFmYn: z.string().nullish(), - telno: z.string(), + telno: z.string().nullish(), useUsgCd: z.string().nullish(), regDt: z.coerce.date().nullish(), rgtr: z.string().nullish(), mdfcnDt: z.coerce.date().nullish(), mdfr: z.string().nullish(), - dmdAplyPswd: z.string(), + dmdAplyPswd: z.string().nullish(), demandInfo: z.array(DemandInfoSchema.nullish()).nullish(), // 안쓰는 거임 demandMatchings: z.array(DemandMatchingSchema.nullish()).nullish()// 안쓰는 거임 }); const DemandCstSchema = z.object({ - dmdInfoSn: z.number(), + dmdInfoSn: z.number().nullish(), usgPrcsProcs: z.string().nullish(), cstrnCst: z.number(), fcltSbsdCstrnCst: z.number(), @@ -69,9 +69,9 @@ import { z } from "zod"; export const DmdCoReqSchema = z.object({ cityText:z.string().nullish(), sggText:z.string().nullish(), - dmdInfoSn: z.number(), - dmdInfoFk: z.string(), - dmndTmng: z.coerce.date(), + dmdInfoSn: z.number().nullish(), + dmdInfoFk: z.string().nullish(), + dmndTmng: z.coerce.date().nullish(), dmndQty: z.number(), wntUntprc: z.number().nullish(), grssClfg: z.number().nullish(), @@ -139,23 +139,7 @@ export type RvwHistTableType = { } -export type RvwHistTableType = { - cityText:string, - sggText:string, - dmdCoNm:string, - daddr:string, - telno:string, - regDt:string, - rvwCmptnYmd:string, - mtchnCmptnYmd:string, - ruseUtztnFcltNm:string, - dmndQty:string, - useUsgCd:string, - splyFcltFlg:string - completeYn:boolean - dmdInfoSn :string -} diff --git a/apps/wr/app/store/matDmd/coSttn/headList.tsx b/apps/wr/app/store/matDmd/coSttn/headList.tsx index 80fed0d76..62c3002ac 100644 --- a/apps/wr/app/store/matDmd/coSttn/headList.tsx +++ b/apps/wr/app/store/matDmd/coSttn/headList.tsx @@ -44,6 +44,11 @@ export const coSttHeadListExport: z.infer<typeof sttnHeadListType> = [ title: "전화번호", visible: true, }, + { + id: "regDt", + title: "등록일자", + visible: true, + }, ]; export const coSttNodeListExport: CommonTableProps[] = [ diff --git a/apps/wr/app/store/matDmd/coSttn/index.tsx b/apps/wr/app/store/matDmd/coSttn/index.tsx index 0de37bbfc..5e8baf859 100644 --- a/apps/wr/app/store/matDmd/coSttn/index.tsx +++ b/apps/wr/app/store/matDmd/coSttn/index.tsx @@ -2,7 +2,7 @@ import type { CommonTableProps } from "@/app/store/common/type/header"; import { fetchType, getFetch, getFetchWithCount } from "@/components/lib/api"; import { coSttHeadListExport, coSttNodeListExport } from "./headList"; import { excelCoSttnParsing } from "./excel"; -import type { DemandCompType } from "@common/business_components/ui"; +import type { DemandCompType } from "@common/business_components/ui/sdmInsertForm/form"; // 테이블 헤드 리스트 const coSttNodeList = coSttNodeListExport; diff --git a/apps/wr/app/store/prfmncReg/pbnstPrfmncReg/index.tsx b/apps/wr/app/store/prfmncReg/pbnstPrfmncReg/index.tsx index 82f2ed59c..387f56eb7 100644 --- a/apps/wr/app/store/prfmncReg/pbnstPrfmncReg/index.tsx +++ b/apps/wr/app/store/prfmncReg/pbnstPrfmncReg/index.tsx @@ -16,6 +16,7 @@ export type pbnstPrfmncState = { monthHeadList: TableUpperProps[]; prfmncRes: FcPrfType[]; excelList: Number[]; + pageKey:number }; }; @@ -26,21 +27,22 @@ export const pbnstPrfmncStateExport: pbnstPrfmncState = { monthHeadList: monthHeadList, prfmncRes: [], excelList: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], + pageKey:1 }, }; export type pbnstPrfmncType = pbnstPrfmncState & pbnstPrfmncActions; type pbnstPrfmncActions = { - prfmncHasuInit: (page: string, sggOrCity: string) => void; - prfmnRegData: (data: PrfType) => void; + prfmncHasuInit: (page: number, sggOrCity: string) => void; + prfmnRegData: (page:number,sggOrCity:string, data: PrfType) => void; }; const checkWrRegList = async () => { const wrRegList = await getFetch("wr", `/reuseFc/pubDetailList`); return wrRegList; }; -const hasuListFetch = async (page: string, sggOrCity: string) => { +const hasuListFetch = async (page: number, sggOrCity: string) => { const checkWrRegListRes = await checkWrRegList(); const rainRes = await postFectch( `/prcsFclt/ruseInfo/${page ? page : 1}/10/A/${sggOrCity ? sggOrCity : "11"}/0/1/SW`, @@ -74,7 +76,7 @@ export const pbnstPrfmncActionsExport: (set: any) => pbnstPrfmncActions = ( set: any ) => { return { - prfmncHasuInit: async (page: string, sggOrCity: string) => { + prfmncHasuInit: async (page: number, sggOrCity: string) => { try { const prfmncRes = await hasuListFetch(page, sggOrCity); return set((state: pbnstPrfmncState) => ({ @@ -89,7 +91,7 @@ export const pbnstPrfmncActionsExport: (set: any) => pbnstPrfmncActions = ( console.log(errorHasu); } }, - prfmnRegData: async (data: PrfType) => { + prfmnRegData: async (page:number,sggOrCity:string,data: PrfType) => { try { const ressponse = await postFectch( "/reuseFc/pubFcPrf", @@ -100,6 +102,17 @@ export const pbnstPrfmncActionsExport: (set: any) => pbnstPrfmncActions = ( if (ressponse.status === 200) { toast.success("하수재이용 시설 실적 등록이 완료되었습니다."); + const prfmncRes = await hasuListFetch(page, sggOrCity); + return set((state: pbnstPrfmncState) => ({ + ...state, + prfmnc: { + ...state.prfmnc, + prfmncRes: prfmncRes, + isInit: false, + pageKey: state.prfmnc.pageKey + 1 + }, + })); + } } catch (e) { console.log(e); diff --git a/apps/wr/components/lib/func/TableData.tsx b/apps/wr/components/lib/func/TableData.tsx index 7a35e02c4..8bed17187 100644 --- a/apps/wr/components/lib/func/TableData.tsx +++ b/apps/wr/components/lib/func/TableData.tsx @@ -2,6 +2,7 @@ import type { TableUpperProps } from "@/app/store/common/type/header"; import type { rainChkZodType } from "@/app/store/prfmncReg/chkgdnReg/rainChkgdn"; import type { rswtChkZodType } from "@/app/store/prfmncReg/chkgdnReg/rswrtChkgdn"; import { Checkbox, Input, TableCell, TableRow } from "@common/components/ui"; +import { useEffect } from "react"; export const rainChkTableData = (chkData: rainChkZodType[]) => { // 1. chkgdnFk + utztnUsgCd 기준 누계 정보 만들기 @@ -54,27 +55,32 @@ export const rainChkTableData = (chkData: rainChkZodType[]) => { export const rswtChkTableData = (chkData: rswtChkZodType[]) => { // 1. chkgdnFk + utztnUsgCd 기준 누계 정보 만들기 const makeRswtData: any[] = []; - chkData?.forEach((root) => { - root.rswtrDetails.forEach((detail) => { - let flatRswtChk = {}; - const findGuide = root.guideDetails.find( - (guide) => guide.utztnUsgCd === detail.utztnUsgCd - ); - const findPoluttion = root.rswtrPollutions.find( - (polution) => polution.utztnUsgCd === detail.utztnUsgCd - ); - const operMngCd = root?.reuseBuilding.operMngCd; - flatRswtChk = { - ...root, - ...detail, - ...findGuide, - ...findPoluttion, - ...flatRswtChk, - operMngCd: operMngCd, - }; - makeRswtData.push(flatRswtChk); + + + if(chkData&&chkData.length>0){ + + chkData?.forEach((root) => { + root.rswtrDetails.forEach((detail) => { + let flatRswtChk = {}; + const findGuide = root.guideDetails.find( + (guide) => guide.utztnUsgCd === detail.utztnUsgCd + ); + const findPoluttion = root.rswtrPollutions.find( + (polution) => polution.utztnUsgCd === detail.utztnUsgCd + ); + const operMngCd = root?.reuseBuilding.operMngCd; + flatRswtChk = { + ...root, + ...detail, + ...findGuide, + ...findPoluttion, + ...flatRswtChk, + operMngCd: operMngCd, + }; + makeRswtData.push(flatRswtChk); + }); }); - }); + } return makeRswtData; }; @@ -93,6 +99,7 @@ export const chRowspanTable = ( let order = 0 // rowspan된 셀 렌더링 여부 추적용 const renderedMap = new Map<string, Set<number>>(); + return ( <> {makeTableData.map((row, rowIndex) => ( @@ -228,7 +235,6 @@ export const InputTableBody = ( } return span; }; - return ( <> {chgRes.map((data, dataIndex) => { diff --git a/apps/wr/components/ui/form/coMat/conCostForm/conCostForm.tsx b/apps/wr/components/ui/form/coMat/conCostForm/conCostForm.tsx index aaa7c46b9..2653f4be9 100644 --- a/apps/wr/components/ui/form/coMat/conCostForm/conCostForm.tsx +++ b/apps/wr/components/ui/form/coMat/conCostForm/conCostForm.tsx @@ -138,7 +138,7 @@ export default function ConCostForm({ if (type === "mat") { const data: DedMatUpdateType = { dmdInfoSn: form.getValues("dmdInfoSn"), - usgPrcsProcs: form.getValues("demandCst.usgPrcsProcs"), + usgPrcsProcs: form.getValues("demandCst.usgPrcsProcs")|| "", cstrnCst: form.getValues("demandCst.cstrnCst"), fcltSbsdCstrnCst: form.getValues("demandCst.fcltSbsdCstrnCst"), prcsFcltCpctCstrnCst: form.getValues("demandCst.prcsFcltCpctCstrnCst"), @@ -294,7 +294,9 @@ export default function ConCostForm({ <div className="col-span-12 w-full"> <h1 className={formH1Class}>공사비 입력사항</h1> </div> - <div className="col-span-3 mt-4"> + + + <div className="col-span-2 mt-4"> <div className="flex items-center justify-between"> <PopoverUi childrenTriger={ @@ -333,7 +335,7 @@ export default function ConCostForm({ )} /> </div> - <div className="col-span-3 mt-4"> + <div className="col-span-2 mt-4"> <div className="flex items-center justify-between"> <PopoverUi childrenTriger={ @@ -372,12 +374,12 @@ export default function ConCostForm({ )} /> </div> - <div className="col-span-3 mt-4"> + <div className="col-span-2 mt-4"> <PopoverUi childrenTriger={ <> <Label className="underline"> - 총사업비 (백만원) <i className="diveicon di-search" /> + 처리시설 총사업비(백만원) <i className="diveicon di-search" /> </Label> </> } @@ -414,7 +416,7 @@ export default function ConCostForm({ )} /> </div> - <div className="col-span-3 mt-4"> + {/* <div className="col-span-3 mt-4"> <div className="flex items-center justify-between"> <Label>공급처 선정</Label> <Button size={"sm"} onClick={(e) => {}} className="bg-none"> @@ -437,8 +439,8 @@ export default function ConCostForm({ </FormUi> )} /> - </div> - <div className="col-span-4 mt-4"> + </div> */} + <div className="col-span-3 mt-4"> <FormField control={form.control} name="demandCst.cstrnCst" @@ -457,7 +459,7 @@ export default function ConCostForm({ )} /> </div> - <div className="col-span-4 mt-4 mb-3"> + <div className="col-span-3 mt-4 mb-3"> <Label className="text-xl"> 처리시설 + 공급관로 총사업비(백만원) </Label> diff --git a/apps/wr/components/ui/modal/chkgd/pbnPrfExcelModal/pbnPrfExcelModal.tsx b/apps/wr/components/ui/modal/chkgd/pbnPrfExcelModal/pbnPrfExcelModal.tsx index 25fe5d273..b4b87663e 100644 --- a/apps/wr/components/ui/modal/chkgd/pbnPrfExcelModal/pbnPrfExcelModal.tsx +++ b/apps/wr/components/ui/modal/chkgd/pbnPrfExcelModal/pbnPrfExcelModal.tsx @@ -21,7 +21,7 @@ import { useForm } from "react-hook-form"; import toast from "react-hot-toast"; import type { z } from "zod"; -export default function PbnPrfExcelModal({ data ,setIsExcelOPen }: { data: FcPrfType ,setIsExcelOPen:(open:boolean )=>void }) { +export default function PbnPrfExcelModal({ data ,setIsExcelOPen ,setModalExcelData}: { data: FcPrfType ,setIsExcelOPen:(open:boolean )=>void,setModalExcelData:(excelData :PrfType)=>void }) { const { prfmnRegData } = useWrStore((state) => state); const [file, setFile] = useState<File | null>(null); @@ -42,7 +42,9 @@ export default function PbnPrfExcelModal({ data ,setIsExcelOPen }: { data: FcPrf const onSubmit = (values: z.infer<typeof pbnPrfRegExcelZod>) => { if (excelData) { - prfmnRegData(excelData as unknown as PrfType); + // prfmnRegData(1,'41', excelData as unknown as PrfType); + + setModalExcelData(excelData as unknown as PrfType) setIsExcelOPen(false) } From 103c5d3cb1102d6994ad2a842e42d245fac511e9 Mon Sep 17 00:00:00 2001 From: leedj312 <dlehd467@gmail.com> Date: Sun, 6 Jul 2025 16:15:08 +0900 Subject: [PATCH 083/108] =?UTF-8?q?=EC=88=98=EC=9A=94=EB=A7=A4=EC=B9=AD?= =?UTF-8?q?=EA=B2=80=ED=86=A0=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wr/app/matMng/coMat/coMatRvw/page.tsx | 18 +- apps/wr/app/matMng/coMng/coSttn/page.tsx | 37 ++- apps/wr/app/store/common/searchCode/index.ts | 19 +- apps/wr/app/store/matDmd/coMatRvw/index.tsx | 69 +++-- apps/wr/app/store/matDmd/coMatRvw/type.tsx | 268 +++++++++--------- .../ui/form/coMat/conCostForm/conCostForm.tsx | 45 ++- 6 files changed, 248 insertions(+), 208 deletions(-) diff --git a/apps/wr/app/matMng/coMat/coMatRvw/page.tsx b/apps/wr/app/matMng/coMat/coMatRvw/page.tsx index acfdb6585..0bb06770a 100644 --- a/apps/wr/app/matMng/coMat/coMatRvw/page.tsx +++ b/apps/wr/app/matMng/coMat/coMatRvw/page.tsx @@ -93,12 +93,12 @@ export default function CoMatRvw() { const form = useForm<z.infer<typeof DmdCoReqSchema>>({ resolver: zodResolver(DmdCoReqSchema), - shouldUnregister: true + shouldUnregister: false, + // defaultValues: DmdCoReqSchema.parse({}) }); const TableCellClick = (index: number) => { - console.log(coMatRvwRes); if (coMatRvwRes && coMatRvwRes[index]) { setSelectData(coMatRvwRes[index]); setSelectionVersion((v) => v + 1); // 행을 클릭할 때마다 버전을 1씩 증가 @@ -107,7 +107,6 @@ export default function CoMatRvw() { const useUsgCdWatch = useWatch({ control: form.control, name: "useUsgCd" }); useEffect(() => { - console.log(' useUsgCdWatch page ' ,useUsgCdWatch) if (useUsgCdWatch) { form.setValue( "demandCst.usgPrcsProcs", @@ -160,7 +159,15 @@ export default function CoMatRvw() { useEffect(() => { if (selectData) { - form.reset(selectData); + + try { + DmdCoReqSchema.parse(selectData); + } catch (e) { + if (e instanceof z.ZodError) { + console.log(e.flatten()); + } + } + form.reset( selectData); setSelectionVersion((v) => v + 1); // 강제로 바뀌었다고 알리기 } }, [selectData, form]); @@ -168,6 +175,7 @@ export default function CoMatRvw() { <> <ContentsDiv> <div className="grid grid-cols-4 gap-4"> + <div className="col-span-4"> <Title title={"수요매칭검토"} /> <SearchDiv> @@ -288,7 +296,7 @@ export default function CoMatRvw() { <div className="col-span-3 mt-4"> <FormField control={form.control} - name="demandCo.regDt" + name="dmndTmng" render={({ field }) => ( <FormUi label="요구시기"> <Input diff --git a/apps/wr/app/matMng/coMng/coSttn/page.tsx b/apps/wr/app/matMng/coMng/coSttn/page.tsx index e2c8b8195..e828f4226 100644 --- a/apps/wr/app/matMng/coMng/coSttn/page.tsx +++ b/apps/wr/app/matMng/coMng/coSttn/page.tsx @@ -61,17 +61,29 @@ export default function CoSttn() { const [seartchTxt, setSearchTxt] = useState<string>(); const [upadateData, setUpdateData] = useState<DemandCompType>(); - const getSelCity = (val: string | number) => { - const cityValue = String(val); // Ensure val is treated as a string - const data = getCity(cityValue, code.sggCd); - setSelectSgg(data); - setObjSearch({ city: cityValue, sgg: "" }); - }; + const getSelCity = useCallback((val: string | number) => { + const cityValue = String(val); + const data = getCity(cityValue, code.sggCd); + setSelectSgg(data); + // 시/도를 변경하면 시/군/구는 '전체'("0")로 초기화합니다. + setObjSearch({ city: cityValue, sgg: "0" }); + }, [code.sggCd]); - const getSgg = (val: string | number) => { - setObjSearch({ ...objSearch, sgg: String(val) }); - }; + const getSgg = useCallback((val: string | number) => { + setObjSearch((prev) => ({ ...prev, sgg: String(val) })); + }, []); + useEffect(() => { + // code.cityCd 데이터가 로드되었고, 아직 시/도 선택이 안된 초기 상태일 때 실행 + if (code.cityCd && code.cityCd.length > 0 && !objSearch.city) { + const firstCityVal = code.cityCd[0]?.val; + if (firstCityVal) { + // 첫 번째 시/도 값으로 상태를 초기화합니다. + getSelCity(firstCityVal); + } + } + }, [code.cityCd, objSearch.city, getSelCity]); + useEffect(() => { if (!coSttn.isInit) { coSttnInit("1", "13", "0"); @@ -124,8 +136,11 @@ export default function CoSttn() { const searchClick = () => { const con = (objSearch.sgg && objSearch.sgg !== "0") ? objSearch.sgg : objSearch.city; + if (con) { if (seartchTxt && seartchTxt?.length > 0) { + + console.log(' seartchTxt ', seartchTxt) coSttnTxtSearch("1", "13", String(con), seartchTxt ?? ""); } else { coSttnSearch("1", "13", String(con)); @@ -147,13 +162,15 @@ export default function CoSttn() { selectClass={""} selectArray={code.cityCd || []} onSelectValue={getSelCity} + value={objSearch.city} /> <SelectBox className="col-span-2 w-1/6 h-full text-2xl" label="시군구" selectClass={""} - selectArray={selectSgg?selectSgg : [{text:"전체",val:"0"}]} + selectArray={selectSgg || []} onSelectValue={getSgg} + value={objSearch.sgg} /> <SearchInput className={"!text-3xl"} diff --git a/apps/wr/app/store/common/searchCode/index.ts b/apps/wr/app/store/common/searchCode/index.ts index 51ebda8a5..893e6ee10 100644 --- a/apps/wr/app/store/common/searchCode/index.ts +++ b/apps/wr/app/store/common/searchCode/index.ts @@ -94,10 +94,13 @@ export const comFcltSearchAction: (set: any) => comFcltActions = (set: any) => { `/reuseFc/request/${searchNm}/${cnt}` ); - console.log(coList); - set((state: comFcltState) => { - return (state.demandCompList = [...coList]); - }); + + if(coList&&coList.length>0){ + + set((state: comFcltState) => { + return (state.demandCompList = [...coList]); + }); + } }, regDemandCompany: async (data: DemandCompType) => { try { @@ -112,9 +115,10 @@ export const comFcltSearchAction: (set: any) => comFcltActions = (set: any) => { } else { message = "fail"; } + + set((state: comFcltState) => { - state.dmdIsSucees = message; - return state; + return state.dmdIsSucees = message; }); } catch (err) { console.log(err); @@ -132,8 +136,7 @@ export const comFcltSearchAction: (set: any) => comFcltActions = (set: any) => { } set((state: comFcltState) => { - state.dmdIsSucees = message; - return state; + return state.dmdIsSucees = message; }); } catch (err) { console.log(err); diff --git a/apps/wr/app/store/matDmd/coMatRvw/index.tsx b/apps/wr/app/store/matDmd/coMatRvw/index.tsx index 4140905cf..a1a7c187d 100644 --- a/apps/wr/app/store/matDmd/coMatRvw/index.tsx +++ b/apps/wr/app/store/matDmd/coMatRvw/index.tsx @@ -1,13 +1,18 @@ -import { constCostHeadListExport, coRvwHeadListExport, coRvwNodeListExport, rateFeeHeadListExport } from "./headList"; +import { + constCostHeadListExport, + coRvwHeadListExport, + coRvwNodeListExport, + rateFeeHeadListExport, +} from "./headList"; import type { CommonTableProps } from "../../common/type/header"; import type { DedMatUpdateType, DmdCoReqType } from "./type"; import { fetchType, getFetch, putFectch } from "@/components/lib/api"; -import toast from "react-hot-toast/headless"; +import toast from "react-hot-toast"; const coRvwHeadList = coRvwHeadListExport; const coRvwNodeList = coRvwNodeListExport; -const rateFeeHeadList =rateFeeHeadListExport; -const constCostHeadList=constCostHeadListExport +const rateFeeHeadList = rateFeeHeadListExport; +const constCostHeadList = constCostHeadListExport; export type coRvwState = { coRvw: { isInit: boolean; @@ -25,8 +30,8 @@ export const coRvwStateExport: coRvwState = { coMatRvwRes: [], coRvwHeadList: coRvwHeadList, coRvwNodeList: coRvwNodeList, - rateFeeHeadList:rateFeeHeadList, - constCostHeadList:constCostHeadList + rateFeeHeadList: rateFeeHeadList, + constCostHeadList: constCostHeadList, }, }; @@ -37,7 +42,7 @@ type coRvwActions = { pageNo: number, pageCnt: number, startYear: string, - endYear: string, + endYear: string ) => void; coMatRvwUpdate: ( splyCd: string, @@ -50,7 +55,7 @@ type coRvwActions = { startYear: string, endYear: string, sggCd: string, - dmdCoNm: string, + dmdCoNm: string ) => void; coMatRvwInqua: ( pageNo: number, @@ -65,7 +70,7 @@ const coMatInit = async ( pageNo: number, pageCnt: number, startYear: string, - endYear: string, + endYear: string ) => { const url = `/reuseFc/request/infoList/sub/${pageNo}/${pageCnt}/${startYear}/${endYear}`; const coMatRvwRes = await getFetch(fetchType.WR, url); @@ -79,7 +84,7 @@ export const coRvwActionsExport: (set: any) => coRvwActions = (set: any) => { pageNo: number, pageCnt: number, startYear: string, - endYear: string, + endYear: string ) => { const coMatRvwRes = await coMatInit(pageNo, pageCnt, startYear, endYear); @@ -98,27 +103,29 @@ export const coRvwActionsExport: (set: any) => coRvwActions = (set: any) => { data: DedMatUpdateType ) => { try { - const isUpdate = await putFectch( + const response = await putFectch( `/reuseFc/request/cst/${splyCd}/${dmdCd}`, data ); - if(isUpdate){ - toast.error("수요 매칭 검토 검토로 저장되었습니다."); + if (response.status === 200) { + toast.success("매칭 검토로 저장되었습니다."); + const coMatRvwRes = await coMatInit( + 1, + 10, + new Date().getFullYear().toString(), + new Date().getFullYear().toString() + ); + return set((state: coRvwState) => ({ + ...state, + coRvw: { + ...state.coRvw, + coMatRvwRes: coMatRvwRes, + init: false, + }, + })); + } else { + toast.error("매칭 검토 저장에 실패했습니다."); } - const coMatRvwRes = coMatInit( - 1, - 10, - new Date().getFullYear().toString(), - new Date().getFullYear().toString(), - ); - return set((state: coRvwState) => ({ - ...state, - coRvw: { - ...state.coRvw, - init: false, - coMatRvwRes: coMatRvwRes, - }, - })); } catch (e) { toast.error("매칭 검토 저장 오류입니다."); } @@ -129,7 +136,7 @@ export const coRvwActionsExport: (set: any) => coRvwActions = (set: any) => { startYear: string, endYear: string, sggCd: string, - dmdCoNm: string, + dmdCoNm: string ) => { let url = `/reuseFc/request/infoList/sub/${pageNo}/${pageCnt}/${startYear}/${endYear}`; @@ -144,7 +151,7 @@ export const coRvwActionsExport: (set: any) => coRvwActions = (set: any) => { url += `/0`; } - console.log(' url ', url) + console.log(" url ", url); const coMatRvwRes = await getFetch(fetchType.WR, url); return set((state: coRvwState) => ({ @@ -166,7 +173,7 @@ export const coRvwActionsExport: (set: any) => coRvwActions = (set: any) => { let url = `/reuseFc/request/infoList/sub/${pageNo}/${pageCnt}/${startYear}/${endYear}`; if (sggCd.length > 0) { url += `/${sggCd}/0`; - }else if(sggCd ==='0'){ + } else if (sggCd === "0") { url += `/0/0`; } const coMatRvwRes = await getFetch(fetchType.WR, url); @@ -180,6 +187,6 @@ export const coRvwActionsExport: (set: any) => coRvwActions = (set: any) => { } catch (e) { console.log(" error ", e); } - } + }, }; }; diff --git a/apps/wr/app/store/matDmd/coMatRvw/type.tsx b/apps/wr/app/store/matDmd/coMatRvw/type.tsx index 979e24d55..9b2e65cc4 100644 --- a/apps/wr/app/store/matDmd/coMatRvw/type.tsx +++ b/apps/wr/app/store/matDmd/coMatRvw/type.tsx @@ -1,157 +1,147 @@ import { z } from "zod"; - const DemandInfoSchema = z.object({ - dmdInfoSn: z.number().nullish(), - dmdInfoFk: z.string().nullish(), - dmndTmng: z.coerce.date().nullish(), - dmndQty: z.number().nullish(), - wntUntprc: z.number().nullish(), - grssClfg: z.number().nullish(), - combResdch: z.number().nullish(), - trbt: z.number().nullish(), - blgcOxygDmndQnt: z.number(), - smll: z.number().nullish(), - crmty: z.number().nullish(), - tn: z.number().nullish(), - tph: z.number().nullish(), - ph: z.number(), - clIonQnt: z.number().nullish(), - cdtnv: z.number().nullish(), - rmrk: z.string().nullish(), - useUsgCd: z.string().nullish(), - mtchnCmptnYmd: z.string().nullish(), - rvwCmptnYmd: z.string().nullish() - }); - - const DemandMatchingSchema = z.object({ - dmdInfoFk: z.string().nullish(), - splyFcltFk: z.string().nullish(), - splyFcltFlg: z.string().nullish(), - pbnstRuseFcltBizFk: z.number().nullish(), - splyBgngDay: z.coerce.date().nullish(), - splyEndDay: z.coerce.date().nullish(), - splyCpct: z.number().nullish(), - splyFmYn: z.string().nullish(), - useUsgCd: z.string().nullish(), - dmdInfoSn: z.number().nullish(), - splyCndtList:z.array(z.string().nullish() ).nullish() - }); - - const DemandCoSchema = z.object({ - dmdInfoPk: z.string().nullish(), - dmdCoNm: z.string().nullish(), - ctpvCd: z.string().nullish(), - sggCd: z.string().nullish(), - daddr: z.string().nullish(), - splyBgngDay: z.coerce.date().nullish(), - splyEndDay: z.coerce.date().nullish(), - splyCpct: z.number().nullish(), - splyFmYn: z.string().nullish(), - telno: z.string().nullish(), - useUsgCd: z.string().nullish(), - regDt: z.coerce.date().nullish(), - rgtr: z.string().nullish(), - mdfcnDt: z.coerce.date().nullish(), - mdfr: z.string().nullish(), - dmdAplyPswd: z.string().nullish(), - demandInfo: z.array(DemandInfoSchema.nullish()).nullish(), // 안쓰는 거임 - demandMatchings: z.array(DemandMatchingSchema.nullish()).nullish()// 안쓰는 거임 - }); - - const DemandCstSchema = z.object({ - dmdInfoSn: z.number().nullish(), - usgPrcsProcs: z.string().nullish(), - cstrnCst: z.number(), - fcltSbsdCstrnCst: z.number(), - prcsFcltCpctCstrnCst: z.number() - }); - - export const DmdCoReqSchema = z.object({ - cityText:z.string().nullish(), - sggText:z.string().nullish(), - dmdInfoSn: z.number().nullish(), - dmdInfoFk: z.string().nullish(), - dmndTmng: z.coerce.date().nullish(), - dmndQty: z.number(), - wntUntprc: z.number().nullish(), - grssClfg: z.number().nullish(), - combResdch: z.number().nullish(), - trbt: z.number().nullish(), - blgcOxygDmndQnt: z.number(), - smll: z.number().nullish(), - crmty: z.number().nullish(), - tn: z.number().nullish(), - tph: z.number().nullish(), - ph: z.number().nullish(), - clIonQnt: z.number().nullish(), - cdtnv: z.number(), - rmrk: z.string().nullish(), - useUsgCd: z.string().nullish(), - mtchnCmptnYmd: z.string().nullish(), - rvwCmptnYmd: z.string().nullish(), - demandCo: DemandCoSchema.nullish(), - demandMatching: DemandMatchingSchema.nullish(), - demandCst: DemandCstSchema, - ruseUtztnFcltNm:z.string().nullish(), - totalConstCost:z.number().nullish(), +const DemandInfoSchema = z.object({ + dmdInfoSn: z.number(), + dmdInfoFk: z.string().nullish(), + dmndTmng: z.coerce.date().nullish(), + dmndQty: z.number().nullish(), + wntUntprc: z.number().nullish(), + grssClfg: z.number().nullish(), + combResdch: z.number().nullish(), + trbt: z.number().nullish(), + blgcOxygDmndQnt: z.number(), + smll: z.number().nullish(), + crmty: z.number().nullish(), + tn: z.number().nullish(), + tph: z.number().nullish(), + ph: z.number(), + clIonQnt: z.number().nullish(), + cdtnv: z.number().nullish(), + rmrk: z.string().nullish(), + useUsgCd: z.string().nullish(), + mtchnCmptnYmd: z.string().nullish(), + rvwCmptnYmd: z.string().nullish(), +}); - }); +const DemandMatchingSchema = z.object({ + dmdInfoFk: z.string().nullish(), + splyFcltFk: z.string().nullish(), + splyFcltFlg: z.string().nullish(), + pbnstRuseFcltBizFk: z.number().nullish(), + splyBgngDay: z.coerce.date().nullish(), + splyEndDay: z.coerce.date().nullish(), + splyCpct: z.number().nullish(), + splyFmYn: z.string().nullish(), + useUsgCd: z.string().nullish(), + dmdInfoSn: z.number().nullish(), + splyCndtList: z.array(z.string().nullish()).nullish(), +}); +const DemandCoSchema = z.object({ + dmdInfoPk: z.string().nullish(), + dmdCoNm: z.string().nullish(), + ctpvCd: z.string().nullish(), + sggCd: z.string().nullish(), + daddr: z.string().nullish(), + splyBgngDay: z.coerce.date().nullish(), + splyEndDay: z.coerce.date().nullish(), + splyCpct: z.number().nullish(), + splyFmYn: z.string().nullish(), + telno: z.string().nullish(), + useUsgCd: z.string().nullish(), + regDt: z.coerce.date().nullish(), + rgtr: z.string().nullish(), + mdfcnDt: z.coerce.date().nullish(), + mdfr: z.string().nullish(), + dmdAplyPswd: z.string().nullish(), + demandInfo: z.array(DemandInfoSchema.nullish()).nullish(), // 안쓰는 거임 + demandMatchings: z.array(DemandMatchingSchema.nullish()).nullish(), // 안쓰는 거임 +}); + +const DemandCstSchema = z.object({ + dmdInfoSn: z.number().nullish(), + usgPrcsProcs: z.string().nullish(), + cstrnCst: z.number().default(0), + fcltSbsdCstrnCst: z.number().default(0), + prcsFcltCpctCstrnCst: z.number().default(0), +}); + +export const DmdCoReqSchema = z.object({ + cityText: z.string().nullish(), + sggText: z.string().nullish(), + dmdInfoSn: z.number().default(0), + dmdInfoFk: z.string().default(''), + dmndTmng: z.coerce.date().nullish(), + dmndQty: z.number().nullish(), // null 또는 undefined 허용 + blgcOxygDmndQnt: z.number().nullish(), + wntUntprc: z.number().nullish(), + grssClfg: z.number().nullish(), + combResdch: z.number().nullish(), + trbt: z.number().nullish(), + smll: z.number().nullish(), + crmty: z.number().nullish(), + tn: z.number().nullish(), + tph: z.number().nullish(), + ph: z.number().nullish(), + clIonQnt: z.number().nullish(), + cdtnv: z.number().nullish(), + rmrk: z.string().nullish(), + useUsgCd: z.string().nullish(), + mtchnCmptnYmd: z.string().nullish(), + rvwCmptnYmd: z.string().nullish(), + demandCo: DemandCoSchema.nullish(), + demandMatching: DemandMatchingSchema.nullish(), + demandCst: DemandCstSchema.nullish(), + ruseUtztnFcltNm: z.string().nullish(), + totalConstCost: z.number().nullish(), +}); export type DmdCoReqType = z.infer<typeof DmdCoReqSchema>; export type DmdMatTableType = { - cityText:string, - sggText:string, - dmdCoNm:string, - daddr:string, - telno:string, - regDt:string, - splyFcltFlg:string -} + cityText: string; + sggText: string; + dmdCoNm: string; + daddr: string; + telno: string; + regDt: string; + splyFcltFlg: string; +}; - -export type DedMatUpdateType = -{ +export type DedMatUpdateType = { dmdInfoSn: number; usgPrcsProcs: string; cstrnCst: number; fcltSbsdCstrnCst: number; prcsFcltCpctCstrnCst: number; rgtr: string; -} - +}; export type RvwHistTableType = { - cityText:string, - sggText:string, - dmdCoNm:string, - daddr:string, - telno:string, - regDt:string, - rvwCmptnYmd:string, - mtchnCmptnYmd:string, - ruseUtztnFcltNm:string, - dmndQty:string, - useUsgCd:string, - splyFcltFlg:string - completeYn:boolean - dmdInfoSn :string + cityText: string; + sggText: string; + dmdCoNm: string; + daddr: string; + telno: string; + regDt: string; + rvwCmptnYmd: string; + mtchnCmptnYmd: string; + ruseUtztnFcltNm: string; + dmndQty: string; + useUsgCd: string; + splyFcltFlg: string; + completeYn: boolean; + dmdInfoSn: string; +}; -} - - - - - -export type conCostFeeType= { - yr: string, - fctCpc: string, - fctCd: string, - stdCost: string -} +export type conCostFeeType = { + yr: string; + fctCpc: string; + fctCd: string; + stdCost: string; +}; export type rateFeeType = { - yr: string, - incExpCd: string, - stdCost: number, - rt: number -} \ No newline at end of file + yr: string; + incExpCd: string; + stdCost: number; + rt: number; +}; diff --git a/apps/wr/components/ui/form/coMat/conCostForm/conCostForm.tsx b/apps/wr/components/ui/form/coMat/conCostForm/conCostForm.tsx index 2653f4be9..f50474d79 100644 --- a/apps/wr/components/ui/form/coMat/conCostForm/conCostForm.tsx +++ b/apps/wr/components/ui/form/coMat/conCostForm/conCostForm.tsx @@ -74,14 +74,19 @@ export default function ConCostForm({ const cstrnCst = form.watch("demandCst.cstrnCst"); const useUsgCdWatch = form.watch("useUsgCd"); - useEffect(() =>{ - console.log('usgPrcsProcsWatch ', usgPrcsProcsWatch) + - console.log('useUsgCdWatch ' , useUsgCdWatch) - - },[usgPrcsProcsWatch, useUsgCdWatch, selectionVersion]) const calculateConstCost = useCallback(() => { const usgPrcsProcs = usgPrcsProcsWatch; + if(!dmndQtyWatch){ + toast.error('요구수량(M3/d)을 확인해주세요 계산할수 없습니다. ') + return + } + if(usgPrcsProcs === null || usgPrcsProcs === undefined){ + toast.error('용도에 따른 처리공정을 확인할수 없습니다. 용도를 확인해주세요. ') + return + } + const dmndQtyYear = dmndQtyWatch * 365; if (usgPrcsProcs && usgPrcsProcs?.length > 0) { const firstProc = getConstFctCode(usgPrcsProcs?.split("+")[0] || "") || ""; @@ -131,20 +136,23 @@ export default function ConCostForm({ }, [constCostRes, rateFeeRes, usgPrcsProcsWatch, dmndQtyWatch]); const onSubmit = (value: DmdCoReqType) => { - if( !form.getValues('demandCst.prcsFcltCpctCstrnCst') ){ + if(value.demandCst&& !value.demandCst.prcsFcltCpctCstrnCst ){ toast.error('공사비 계산 진행 후 저장을 눌러주새요') return } if (type === "mat") { - const data: DedMatUpdateType = { - dmdInfoSn: form.getValues("dmdInfoSn"), - usgPrcsProcs: form.getValues("demandCst.usgPrcsProcs")|| "", - cstrnCst: form.getValues("demandCst.cstrnCst"), - fcltSbsdCstrnCst: form.getValues("demandCst.fcltSbsdCstrnCst"), - prcsFcltCpctCstrnCst: form.getValues("demandCst.prcsFcltCpctCstrnCst"), - rgtr: TEST_UUID, - }; - coMatRvwUpdate("11000SW001R1", form.getValues("dmdInfoFk"), data); + + if(value.demandCst ){ + const data: DedMatUpdateType = { + dmdInfoSn: value.dmdInfoSn, + usgPrcsProcs: value.demandCst.usgPrcsProcs|| "", + cstrnCst: value.demandCst.cstrnCst ||0, + fcltSbsdCstrnCst: value.demandCst.fcltSbsdCstrnCst ||0, + prcsFcltCpctCstrnCst: value.demandCst.prcsFcltCpctCstrnCst ||0, + rgtr: TEST_UUID, + }; + coMatRvwUpdate("11000SW001R1", form.getValues("dmdInfoFk"), data); + } } else if (type === "hist") { delete value.totalConstCost @@ -159,6 +167,13 @@ export default function ConCostForm({ console.log(" form ", form.formState.errors); }, [form.formState]); + + useEffect(() => { + form.register("dmdInfoSn"); + form.register("dmdInfoFk"); + form.register("ruseUtztnFcltNm"); + }, []); + return ( <> <hr className="mt-8" /> From ad7b78688f79da1cc4ba77990ef7620f5caf8648 Mon Sep 17 00:00:00 2001 From: leedj312 <dlehd467@gmail.com> Date: Sun, 6 Jul 2025 16:34:47 +0900 Subject: [PATCH 084/108] =?UTF-8?q?[=ED=95=98=EC=88=98=EC=9E=AC=EC=9D=B4?= =?UTF-8?q?=EC=9A=A9=EC=8B=9C=EC=84=A4=20=EB=93=B1=EB=A1=9D=20=EC=88=98?= =?UTF-8?q?=EC=A0=95]=20onVild=20=ED=8C=8C=EB=9D=BC=EB=AF=B8=ED=84=B0?= =?UTF-8?q?=EB=A1=9C=20=EB=AA=A8=EB=8B=AC=EC=97=90=EA=B2=8C=20=EB=84=98?= =?UTF-8?q?=EA=B2=A8=EC=A3=BC=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/ruseFcltMng/fcltReg/pbnstReg/page.tsx | 8 +- .../ruseFcltMng/fcltReg/pbnstReg/type.tsx | 2 +- .../fcltReg/pbnstRegModal/pbnstRegModal.tsx | 104 +++++++++--------- 3 files changed, 57 insertions(+), 57 deletions(-) diff --git a/apps/wr/app/ruseFcltMng/fcltReg/pbnstReg/page.tsx b/apps/wr/app/ruseFcltMng/fcltReg/pbnstReg/page.tsx index 0c3ad9dce..9f243627c 100644 --- a/apps/wr/app/ruseFcltMng/fcltReg/pbnstReg/page.tsx +++ b/apps/wr/app/ruseFcltMng/fcltReg/pbnstReg/page.tsx @@ -200,14 +200,10 @@ export default function PublicReuseFaciltyInsert() { useEffect(() => { if (pbnst.pbnstRes && pbnst.pbnstRes?.length > 0) { - // form.reset({ - // items: pbnstRegArrayZod.parse(pbnst.pbnstRes), - // }); + form.reset({ items: pbnst.pbnstRes }); - //form.reset({ items: pbnst.pbnstRes }); - } else { form.reset({ items: [] }); } @@ -425,6 +421,8 @@ export default function PublicReuseFaciltyInsert() { setIsRuseReg({ isModal: false }); } }} + + onInvalid ={onInvalid} ></PbnstRegModal> } bodyClass="w-[68vw] max-w-none h-[90vh]" diff --git a/apps/wr/app/store/ruseFcltMng/fcltReg/pbnstReg/type.tsx b/apps/wr/app/store/ruseFcltMng/fcltReg/pbnstReg/type.tsx index 2ef1c6d2f..ad3496a54 100644 --- a/apps/wr/app/store/ruseFcltMng/fcltReg/pbnstReg/type.tsx +++ b/apps/wr/app/store/ruseFcltMng/fcltReg/pbnstReg/type.tsx @@ -163,7 +163,7 @@ export const fcBizSchema = z.object({ etc: z.string().default(""), totalMoney: zNumNullish.default(0), rprcsFcltCpct: zNumDefaultZero, - majorPrcsMsrcCd: z.string().default(""), + majorPrcsMsrcCd: z.string().nullish(), addr: z.string().default(""), rprcsMthdCd: z.string().default(""), inoutYn: z.enum(["Y", "N"]).default("Y"), diff --git a/apps/wr/components/ui/modal/fcltReg/pbnstRegModal/pbnstRegModal.tsx b/apps/wr/components/ui/modal/fcltReg/pbnstRegModal/pbnstRegModal.tsx index 34bc71a46..846e427c6 100644 --- a/apps/wr/components/ui/modal/fcltReg/pbnstRegModal/pbnstRegModal.tsx +++ b/apps/wr/components/ui/modal/fcltReg/pbnstRegModal/pbnstRegModal.tsx @@ -29,9 +29,11 @@ import type { z } from "zod"; export function PbnstRegModal({ form, setPbnstInsert, + onInvalid }: { form: UseFormReturn<{ items: PbnstRegArrrayType }>; setPbnstInsert: (data: customPbnst) => void; + onInvalid:(errors: FieldErrors<{ items: z.infer<typeof pbnstRegZod>[] }>) => void }) { const { pbnstInsert } = useWrStore((state) => { return state; @@ -51,69 +53,69 @@ export function PbnstRegModal({ setPbnstInsert(data[0]); } }; - const onInvalid = ( - errors: FieldErrors<{ items: z.infer<typeof pbnstRegZod>[] }> - ) => { - // 재귀 함수를 사용해 중첩된 에러 객체에서 모든 에러 메시지와 경로를 찾습니다. + // const onInvalid = ( + // errors: FieldErrors<{ items: z.infer<typeof pbnstRegZod>[] }> + // ) => { + // // 재귀 함수를 사용해 중첩된 에러 객체에서 모든 에러 메시지와 경로를 찾습니다. - const findAllErrors = ( - errorObject: any, - path: (string | number)[] = [] - ): { message: string; path: (string | number)[] }[] => { - if (!errorObject) return []; + // const findAllErrors = ( + // errorObject: any, + // path: (string | number)[] = [] + // ): { message: string; path: (string | number)[] }[] => { + // if (!errorObject) return []; - return Object.keys(errorObject).flatMap((key) => { - const value = errorObject[key]; - const currentKey = !isNaN(parseInt(key)) ? parseInt(key) : key; - const newPath = [...path, currentKey]; - if (value && typeof value === "object") { - if (typeof value.message === "string") { - return [{ message: value.message, path: newPath }]; // 에러 메시지와 경로 반환 - } - return findAllErrors(value, newPath); - } - return []; - }); - }; + // return Object.keys(errorObject).flatMap((key) => { + // const value = errorObject[key]; + // const currentKey = !isNaN(parseInt(key)) ? parseInt(key) : key; + // const newPath = [...path, currentKey]; + // if (value && typeof value === "object") { + // if (typeof value.message === "string") { + // return [{ message: value.message, path: newPath }]; // 에러 메시지와 경로 반환 + // } + // return findAllErrors(value, newPath); + // } + // return []; + // }); + // }; - const errorList = findAllErrors(errors); + // const errorList = findAllErrors(errors); - if (errorList.length > 0) { - errorList.forEach((errorInfo) => { - const { message, path } = errorInfo; - let prefix = ""; - const itemsIndex = path.indexOf("items"); + // if (errorList.length > 0) { + // errorList.forEach((errorInfo) => { + // const { message, path } = errorInfo; + // let prefix = ""; + // const itemsIndex = path.indexOf("items"); - if (itemsIndex > -1 && path.length > itemsIndex + 1) { - const itemIdx = path[itemsIndex + 1] as number; - const facilityName = form.getValues( - `items.${itemIdx}.ruseUtztnFcltNm` - ); - if (facilityName) { - prefix += `[${facilityName}] `; - } + // if (itemsIndex > -1 && path.length > itemsIndex + 1) { + // const itemIdx = path[itemsIndex + 1] as number; + // const facilityName = form.getValues( + // `items.${itemIdx}.ruseUtztnFcltNm` + // ); + // if (facilityName) { + // prefix += `[${facilityName}] `; + // } - const fcBizsIndex = path.indexOf("fcBizs"); - if (fcBizsIndex > -1 && path.length > fcBizsIndex + 1) { - const bizIdx = path[fcBizsIndex + 1] as number; - const cycle = form.getValues( - `items.${itemIdx}.fcBizs.${bizIdx}.cycl` - ); - if (cycle) { - prefix += `[${cycle}차수] `; - } - } - } + // const fcBizsIndex = path.indexOf("fcBizs"); + // if (fcBizsIndex > -1 && path.length > fcBizsIndex + 1) { + // const bizIdx = path[fcBizsIndex + 1] as number; + // const cycle = form.getValues( + // `items.${itemIdx}.fcBizs.${bizIdx}.cycl` + // ); + // if (cycle) { + // prefix += `[${cycle}차수] `; + // } + // } + // } - toast.error(`${prefix}${message}`); - }); - } - }; + // toast.error(`${prefix}${message}`); + // }); + // } + // }; const handleTabChange = (value: string) => { setCurrentTab(value); From 59a517583201fffc28426cd3350220583955c2f4 Mon Sep 17 00:00:00 2001 From: leedj312 <dlehd467@gmail.com> Date: Sun, 6 Jul 2025 17:20:21 +0900 Subject: [PATCH 085/108] =?UTF-8?q?=ED=95=98=EC=88=98=EC=9E=AC=EC=9D=B4?= =?UTF-8?q?=EC=9A=A9=EC=8B=9C=EC=84=A4=20=EC=85=80=EB=A0=89=ED=8A=B8?= =?UTF-8?q?=EB=B0=95=EC=8A=A4=20=EC=84=A0=ED=83=9D=EB=90=98=EA=B2=8C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pbnstReg/mngReg/mngRegFormDetail/mngRegFormDetail.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/wr/components/ui/form/fcltReg/pbnstReg/mngReg/mngRegFormDetail/mngRegFormDetail.tsx b/apps/wr/components/ui/form/fcltReg/pbnstReg/mngReg/mngRegFormDetail/mngRegFormDetail.tsx index fb50e1b89..234efa7c7 100644 --- a/apps/wr/components/ui/form/fcltReg/pbnstReg/mngReg/mngRegFormDetail/mngRegFormDetail.tsx +++ b/apps/wr/components/ui/form/fcltReg/pbnstReg/mngReg/mngRegFormDetail/mngRegFormDetail.tsx @@ -101,7 +101,7 @@ export default function MngRegFormDetail({ : [] } onSelectValue={field.onChange} - value={field.value?.trim() ??""} + value={field.value?.trim() ?? (Array.isArray(potentialOptions) && potentialOptions[0]?.val )} readOnly={formHead.option === "readOnly"} /> ); From d2c7bbb78bed1d6c094f007d7aa3eb18f5a2412a Mon Sep 17 00:00:00 2001 From: leedj312 <dlehd467@gmail.com> Date: Sun, 6 Jul 2025 18:30:07 +0900 Subject: [PATCH 086/108] =?UTF-8?q?=ED=95=98=EC=88=98=EC=9E=AC=EC=9D=B4?= =?UTF-8?q?=EC=9A=A9=EC=8B=9C=EC=84=A4=20=EB=93=B1=EB=A1=9D=20=EC=9B=90?= =?UTF-8?q?=EC=88=98=EC=B7=A8=EC=88=98=20=EC=A1=B0=ED=9A=8C=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wr/app/store/common/searchCode/index.ts | 1 + .../fcltReg/fcOrgwtsRegModal/fcOrgwtsRegModal.tsx | 14 +++++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/apps/wr/app/store/common/searchCode/index.ts b/apps/wr/app/store/common/searchCode/index.ts index 893e6ee10..656bc33d9 100644 --- a/apps/wr/app/store/common/searchCode/index.ts +++ b/apps/wr/app/store/common/searchCode/index.ts @@ -70,6 +70,7 @@ export type comFcltState = { export const comFcltSearchAction: (set: any) => comFcltActions = (set: any) => { return { getSwtrmtFcltNm: async (searchNm: string) => { + const fcltNm = await getFetch( "common", `/prcsFclt/info/1/11/${searchNm}` diff --git a/apps/wr/components/ui/modal/fcltReg/fcOrgwtsRegModal/fcOrgwtsRegModal.tsx b/apps/wr/components/ui/modal/fcltReg/fcOrgwtsRegModal/fcOrgwtsRegModal.tsx index 69ec1afcc..5b1a6f0f6 100644 --- a/apps/wr/components/ui/modal/fcltReg/fcOrgwtsRegModal/fcOrgwtsRegModal.tsx +++ b/apps/wr/components/ui/modal/fcltReg/fcOrgwtsRegModal/fcOrgwtsRegModal.tsx @@ -115,7 +115,19 @@ export default function FcOrgwtsRegModal({ value={inputVal} onChange={(e) => setInputVal(e.target.value)} /> - <Button size="sm" onClick={() => getSwtrmtFcltNm(inputVal)}> + <Button size="sm" onClick={() => { + + if(inputVal){ + + getSwtrmtFcltNm(inputVal) + }else{ + const filterData = pbnstHasuRes.filter((item) => ! rightData?.find(( right) => right.bscFctNm ===item.bscFctNm)) + setbdstTable(tableData(filterData, { code })); + + } + } + + }> 조회 </Button> <Button size="sm" onClick={saveData}>저장</Button> From e332162984c445ceca02129d692be4ebcd2cc10b Mon Sep 17 00:00:00 2001 From: leedj312 <dlehd467@gmail.com> Date: Sun, 6 Jul 2025 22:44:31 +0900 Subject: [PATCH 087/108] =?UTF-8?q?=EC=88=98=EC=9A=94=EB=A7=A4=EC=B9=AD=20?= =?UTF-8?q?=EA=B2=80=ED=86=A0=20=EB=B0=8F=20=EC=9D=B4=EB=A0=A5=20=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wr/app/matMng/coMat/coMatRvw/page.tsx | 4 +- .../wr/app/matMng/coMat/ruseRvwHstry/page.tsx | 98 ++++++++----- apps/wr/app/store/matDmd/coMatRvw/index.tsx | 28 ++-- apps/wr/app/store/matDmd/coMatRvw/type.tsx | 5 +- .../store/matDmd/ruseRvwHstry/headList.tsx | 2 +- .../app/store/matDmd/ruseRvwHstry/index.tsx | 39 +++-- apps/wr/components/lib/func/commom.tsx | 5 +- .../ui/form/coMat/conCostForm/conCostForm.tsx | 133 +++++++++++------- 8 files changed, 191 insertions(+), 123 deletions(-) diff --git a/apps/wr/app/matMng/coMat/coMatRvw/page.tsx b/apps/wr/app/matMng/coMat/coMatRvw/page.tsx index 0bb06770a..15bff0b32 100644 --- a/apps/wr/app/matMng/coMat/coMatRvw/page.tsx +++ b/apps/wr/app/matMng/coMat/coMatRvw/page.tsx @@ -46,7 +46,7 @@ import type { objSearchType } from "@/components/ui/modal/fcltReg/bdstRegModal/b export default function CoMatRvw() { const { code, coRvw, coMatRvwInit, coMatRvwSearch, coMatRvwInqua } = useWrStore((state) => state); - const { coMatRvwRes } = coRvw; + const { coMatRvwRes,coMatRvwCount } = coRvw; //init, sub, match const initRef = useRef<boolean>(false); const { coRvwHeadList } = coRvw; @@ -276,7 +276,7 @@ export default function CoMatRvw() { tableData={dmdMatData || []} headlist={coRvwHeadList} pageSize={20} - total={100000} + total={coMatRvwCount} cellClick={(index: number | undefined, headId: string) => { if (index !== undefined && index >= 0) { TableCellClick(index); diff --git a/apps/wr/app/matMng/coMat/ruseRvwHstry/page.tsx b/apps/wr/app/matMng/coMat/ruseRvwHstry/page.tsx index 2d339b869..d0a6bcb93 100644 --- a/apps/wr/app/matMng/coMat/ruseRvwHstry/page.tsx +++ b/apps/wr/app/matMng/coMat/ruseRvwHstry/page.tsx @@ -7,6 +7,7 @@ import { type RvwHistTableType, } from "@/app/store/matDmd/coMatRvw/type"; import { + formatPhoneNumber, getCity, getdmdMatFlag, yearOptions, @@ -43,7 +44,7 @@ export default function RuseRvwHstry() { ruseRvwInqua, ruseRvwComplete, } = useWrStore((state) => state); - const { rvwHistRes, ruseHistHeadList } = ruseHist; + const { rvwHistRes, ruseHistHeadList,rvwHistCount } = ruseHist; const [pageNation, setPageNatiion] = useState<number>(1); const [dmdCoNm, setDmdCoNm] = useState<string>(""); @@ -69,6 +70,36 @@ export default function RuseRvwHstry() { setObjSearch({ city: cityValue, sgg: "0" }); }; + useEffect(() =>{ + getButtonSearch() + },[pageNation]) + + const getButtonSearch=()=>{ + if (fromDate && toDate) { + const fromNum = Number(fromDate); + const toNum = Number(toDate); + if (fromNum > toNum) { + toast.error( + "시작년도가 종료년도 이후로 설정할 수 없습니다." + ); + } else { + let con: string | number = ""; + if (String(objSearch.city).length > 0) { + con = String(objSearch.city); + } + if (String(objSearch.sgg).length > 0 && objSearch.sgg !== "0") { + con = String(objSearch.sgg); + } + getSearch( + fromNum, + toNum, + con, + dmdCoNm, + ruseUtztnFcltNm + ); + } + } + } const getSgg = (val: string | number) => { console.log(val); setObjSearch({ ...objSearch, sgg: String(val) }); @@ -163,23 +194,29 @@ export default function RuseRvwHstry() { const data = rvwHistRes?.map((item, index) => { let flag = ""; flag = getdmdMatFlag(item.demandMatching?.splyFcltFlg ?? ""); - + const formatDate = (dateStr: string | null | undefined) => { + // null, undefined, 빈 문자열인 경우 '없음'을 반환합니다. + if (!dateStr) return "없음"; + // "YYYYMMDD" 형식으로 날짜를 파싱합니다. + const d = moment(dateStr, "YYYYMMDD"); + // 파싱된 날짜가 유효한 경우에만 포맷하고, 그렇지 않으면 '없음'을 반환합니다. + return d.isValid() ? d.format("YYYY-MM-DD") : "없음"; + }; + return { ...item, ctpvCd: item.demandCo?.ctpvCd, sggCd: item.demandCo?.sggCd, dmdCoNm: item.demandCo?.dmdCoNm, daddr: item.demandCo?.daddr, - telno: item.demandCo?.telno, - regDt: item?.demandCo?.regDt - ? moment(item?.demandCo?.regDt).format("YYYY-MM-DD") - : "없음", - rvwCmptnYmd: item?.rvwCmptnYmd - ? moment(item?.rvwCmptnYmd).format("YYYY-MM-DD") - : "없음", - mtchnCmptnYmd: item?.mtchnCmptnYmd - ? moment(item?.mtchnCmptnYmd).format("YYYY-MM-DD") + telno: formatPhoneNumber(item.demandCo?.telno ||''), + dmndTmng: item?.dmndTmng + ? moment(item?.dmndTmng).format("YYYY-MM-DD") : "없음", + + rvwCmptnYmd: formatDate(item.demandMatching?.rvwCmptnYmd), + mtchnCmptnYmd: formatDate(item.demandMatching?.mtchnCmptnYmd), + ruseUtztnFcltNm: item.ruseUtztnFcltNm, dmndQty: item.dmndQty, splyFcltFlg: flag, @@ -272,30 +309,7 @@ export default function RuseRvwHstry() { <Button size={"sm"} onClick={(e) => { - if (fromDate && toDate) { - const fromNum = Number(fromDate); - const toNum = Number(toDate); - if (fromNum > toNum) { - toast.error( - "시작년도가 종료년도 이후로 설정할 수 없습니다." - ); - } else { - let con: string | number = ""; - if (String(objSearch.city).length > 0) { - con = String(objSearch.city); - } - if (String(objSearch.sgg).length > 0 && objSearch.sgg !== "0") { - con = String(objSearch.sgg); - } - getSearch( - fromNum, - toNum, - con, - dmdCoNm, - ruseUtztnFcltNm - ); - } - } + getButtonSearch() }} > 조회 @@ -318,17 +332,23 @@ export default function RuseRvwHstry() { tableData={rvwHistData || []} headlist={ruseHistHeadList} pageSize={20} - total={100000} + total={rvwHistCount} cellClick={(index: number | undefined, headId: string) => { TableCellClick(index); }} - onPageChange={setPageNatiion} + onPageChange={ + (page)=>{ + setPageNatiion(page) + + } + + } ></UiTable> - <hr /> + {/* <hr /> <h3 className="mt-4 ml-2 text-2xl font-medium text-label"> 수요신청 상세현황 </h3> - <ConCostForm form={form} type={"hist"} /> + <ConCostForm form={form} type={"hist"} selectionVersion={0} /> */} </ListDiv> </div> </div> diff --git a/apps/wr/app/store/matDmd/coMatRvw/index.tsx b/apps/wr/app/store/matDmd/coMatRvw/index.tsx index a1a7c187d..e683b548e 100644 --- a/apps/wr/app/store/matDmd/coMatRvw/index.tsx +++ b/apps/wr/app/store/matDmd/coMatRvw/index.tsx @@ -6,7 +6,7 @@ import { } from "./headList"; import type { CommonTableProps } from "../../common/type/header"; import type { DedMatUpdateType, DmdCoReqType } from "./type"; -import { fetchType, getFetch, putFectch } from "@/components/lib/api"; +import { fetchType, getFetch, getFetchWithCount, putFectch } from "@/components/lib/api"; import toast from "react-hot-toast"; const coRvwHeadList = coRvwHeadListExport; @@ -21,6 +21,7 @@ export type coRvwState = { coRvwNodeList: CommonTableProps[]; rateFeeHeadList: CommonTableProps[]; constCostHeadList: CommonTableProps[]; + coMatRvwCount:number; }; }; @@ -32,6 +33,7 @@ export const coRvwStateExport: coRvwState = { coRvwNodeList: coRvwNodeList, rateFeeHeadList: rateFeeHeadList, constCostHeadList: constCostHeadList, + coMatRvwCount:1 }, }; @@ -72,8 +74,8 @@ const coMatInit = async ( startYear: string, endYear: string ) => { - const url = `/reuseFc/request/infoList/sub/${pageNo}/${pageCnt}/${startYear}/${endYear}`; - const coMatRvwRes = await getFetch(fetchType.WR, url); + const url = `/reuseFc/request/infoList/Sub/${pageNo}/${pageCnt}/${startYear}/${endYear}`; + const coMatRvwRes = await getFetchWithCount(fetchType.WR, url); return coMatRvwRes; }; @@ -93,7 +95,8 @@ export const coRvwActionsExport: (set: any) => coRvwActions = (set: any) => { coRvw: { ...state.coRvw, init: false, - coMatRvwRes: coMatRvwRes, + coMatRvwRes: coMatRvwRes.data, + coMatRvwCount:coMatRvwRes.count }, })); }, @@ -119,7 +122,8 @@ export const coRvwActionsExport: (set: any) => coRvwActions = (set: any) => { ...state, coRvw: { ...state.coRvw, - coMatRvwRes: coMatRvwRes, + coMatRvwRes: coMatRvwRes.data, + coMatRvwCount:coMatRvwRes.count, init: false, }, })); @@ -138,7 +142,7 @@ export const coRvwActionsExport: (set: any) => coRvwActions = (set: any) => { sggCd: string, dmdCoNm: string ) => { - let url = `/reuseFc/request/infoList/sub/${pageNo}/${pageCnt}/${startYear}/${endYear}`; + let url = `/reuseFc/request/infoList/Sub/${pageNo}/${pageCnt}/${startYear}/${endYear}`; if (sggCd.length > 0) { url += `/${sggCd}`; @@ -152,13 +156,14 @@ export const coRvwActionsExport: (set: any) => coRvwActions = (set: any) => { } console.log(" url ", url); - const coMatRvwRes = await getFetch(fetchType.WR, url); + const coMatRvwRes = await getFetchWithCount(fetchType.WR, url); return set((state: coRvwState) => ({ ...state, coRvw: { ...state.coRvw, - coMatRvwRes: coMatRvwRes, + coMatRvwRes: coMatRvwRes.data, + coMatRvwCount:coMatRvwRes.count, }, })); }, @@ -170,18 +175,19 @@ export const coRvwActionsExport: (set: any) => coRvwActions = (set: any) => { sggCd: string ) => { try { - let url = `/reuseFc/request/infoList/sub/${pageNo}/${pageCnt}/${startYear}/${endYear}`; + let url = `/reuseFc/request/infoList/Sub/${pageNo}/${pageCnt}/${startYear}/${endYear}`; if (sggCd.length > 0) { url += `/${sggCd}/0`; } else if (sggCd === "0") { url += `/0/0`; } - const coMatRvwRes = await getFetch(fetchType.WR, url); + const coMatRvwRes = await getFetchWithCount(fetchType.WR, url); return set((state: coRvwState) => ({ ...state, coRvw: { ...state.coRvw, - coMatRvwRes: coMatRvwRes, + coMatRvwRes: coMatRvwRes.data, + coMatRvwCount:coMatRvwRes.count, }, })); } catch (e) { diff --git a/apps/wr/app/store/matDmd/coMatRvw/type.tsx b/apps/wr/app/store/matDmd/coMatRvw/type.tsx index 9b2e65cc4..2121c49fe 100644 --- a/apps/wr/app/store/matDmd/coMatRvw/type.tsx +++ b/apps/wr/app/store/matDmd/coMatRvw/type.tsx @@ -19,8 +19,6 @@ const DemandInfoSchema = z.object({ cdtnv: z.number().nullish(), rmrk: z.string().nullish(), useUsgCd: z.string().nullish(), - mtchnCmptnYmd: z.string().nullish(), - rvwCmptnYmd: z.string().nullish(), }); const DemandMatchingSchema = z.object({ @@ -35,6 +33,8 @@ const DemandMatchingSchema = z.object({ useUsgCd: z.string().nullish(), dmdInfoSn: z.number().nullish(), splyCndtList: z.array(z.string().nullish()).nullish(), + mtchnCmptnYmd: z.string().nullish(), + rvwCmptnYmd: z.string().nullish(), }); const DemandCoSchema = z.object({ @@ -114,6 +114,7 @@ export type DedMatUpdateType = { fcltSbsdCstrnCst: number; prcsFcltCpctCstrnCst: number; rgtr: string; + flg?:string; }; export type RvwHistTableType = { diff --git a/apps/wr/app/store/matDmd/ruseRvwHstry/headList.tsx b/apps/wr/app/store/matDmd/ruseRvwHstry/headList.tsx index 90c793c2f..a90f5831b 100644 --- a/apps/wr/app/store/matDmd/ruseRvwHstry/headList.tsx +++ b/apps/wr/app/store/matDmd/ruseRvwHstry/headList.tsx @@ -28,7 +28,7 @@ export const ruseHstryHeadListExport:TableUpperProps[]= [ visible: true, }, { - id: "regDt", //바꾸기 + id: "dmndTmng", //바꾸기 title: "신청일자", visible: true, }, diff --git a/apps/wr/app/store/matDmd/ruseRvwHstry/index.tsx b/apps/wr/app/store/matDmd/ruseRvwHstry/index.tsx index 3338bc467..cfd991b4d 100644 --- a/apps/wr/app/store/matDmd/ruseRvwHstry/index.tsx +++ b/apps/wr/app/store/matDmd/ruseRvwHstry/index.tsx @@ -5,7 +5,7 @@ import type { TableUpperProps, } from "../../common/type/header"; import { ruseHstryHeadListExport } from "./headList"; -import { fetchType, getFetch, postFectch, putFectch } from "@/components/lib/api"; +import { fetchType, getFetch, getFetchWithCount, postFectch, putFectch } from "@/components/lib/api"; import type { DmdCoReqType } from "../coMatRvw/type"; import toast from "react-hot-toast"; import { TEST_UUID } from "../../ruseFcltMng/fcltReg/rainReg"; @@ -16,6 +16,7 @@ export type ruseHstryState = { ruseHist: { isInit: boolean; rvwHistRes: DmdCoReqType[]; + rvwHistCount:number; ruseHistHeadList: TableUpperProps[]; }; }; @@ -25,6 +26,7 @@ export const ruseHstryStateExport: ruseHstryState = { isInit: false, rvwHistRes: [], ruseHistHeadList: ruseHistHeadList, + rvwHistCount:1, }, }; @@ -79,14 +81,14 @@ const irvwHistInit = async ( startYear: string, endYear: string, ) => { - let url = `/reuseFc/request/infoList/matched/${pageNo}/${pageCnt}/${startYear}/${endYear}`; + let url = `/reuseFc/request/infoList/Matched/${pageNo}/${pageCnt}/${startYear}/${endYear}`; // console.log("sggCd ", sggCd); // if (sggCd) { // url += `/${sggCd}`; // } - const rvwHistRes = await getFetch(fetchType.WR, url); + const rvwHistRes = await getFetchWithCount(fetchType.WR, url); return rvwHistRes; }; @@ -115,7 +117,8 @@ export const ruseHstryActionsExport: (set: any) => ruseHstryActions = ( ruseHist: { ...state.ruseHist, init: false, - rvwHistRes: rvwHistRes, + rvwHistRes: rvwHistRes.data, + rvwHistCount:rvwHistRes.count }, })); }, @@ -140,8 +143,8 @@ export const ruseHstryActionsExport: (set: any) => ruseHstryActions = ( ruseHist: { ...state.ruseHist, init: false, - rvwHistRes: rvwHistRes, - }, + rvwHistRes: rvwHistRes.data, + rvwHistCount:rvwHistRes.count }, })); } } catch (e) { @@ -158,7 +161,7 @@ export const ruseHstryActionsExport: (set: any) => ruseHstryActions = ( dmdCoNm: string, ruseUtztnFcltNm: string ) => { - let url = `/reuseFc/request/infoList/matched/${pageNo}/${pageCnt}/${startYear}/${endYear}`; + let url = `/reuseFc/request/infoList/Matched/${pageNo}/${pageCnt}/${startYear}/${endYear}`; if (sggCd.length > 0) { url += `/${sggCd}`; @@ -175,14 +178,14 @@ export const ruseHstryActionsExport: (set: any) => ruseHstryActions = ( } else { url += `/0`; } - const rvwHistRes = await getFetch(fetchType.WR, url); + const rvwHistRes = await getFetchWithCount(fetchType.WR, url); return set((state: ruseHstryState) => ({ ...state, ruseHist: { ...state.ruseHist, - rvwHistRes: rvwHistRes, - }, + rvwHistRes: rvwHistRes.data, + rvwHistCount:rvwHistRes.count }, })); }, ruseRvwInqua: async ( @@ -193,16 +196,17 @@ export const ruseHstryActionsExport: (set: any) => ruseHstryActions = ( sggCd: string ) => { try { - let url = `/reuseFc/request/infoList/matched/${pageNo}/${pageCnt}/${startYear}/${endYear}`; + let url = `/reuseFc/request/infoList/Matched/${pageNo}/${pageCnt}/${startYear}/${endYear}`; if (sggCd.length > 0) { url += `/${sggCd}/0`; } - const rvwHistRes = await getFetch(fetchType.WR, url); + const rvwHistRes = await getFetchWithCount(fetchType.WR, url); return set((state: ruseHstryState) => ({ ...state, ruseHist: { ...state.ruseHist, - rvwHistRes: rvwHistRes, + rvwHistRes: rvwHistRes.data, + rvwHistCount:rvwHistRes.count }, })); } catch (e) { @@ -224,6 +228,15 @@ export const ruseHstryActionsExport: (set: any) => ruseHstryActions = ( new Date().getFullYear().toString(), new Date().getFullYear().toString(), ); + + return set((state: ruseHstryState) => ({ + ...state, + ruseHist: { + ...state.ruseHist, + rvwHistRes: rvwHistRes.data, + rvwHistCount:rvwHistRes.count + }, + })); } } catch (e) { console.log(" error ", e); diff --git a/apps/wr/components/lib/func/commom.tsx b/apps/wr/components/lib/func/commom.tsx index 6b253bdba..7d8c2ffcc 100644 --- a/apps/wr/components/lib/func/commom.tsx +++ b/apps/wr/components/lib/func/commom.tsx @@ -169,12 +169,13 @@ export const getdmdMatFlag = (flag: string | undefined | null) => { const cleanFlag = (flag ?? "").trim(); - console.log('cleanFlag ', cleanFlag) switch (cleanFlag) { case "Sub": return "검토"; - case "matched": + case "Matched": return "매칭완료"; + case "Checked": + return '검토완료'; default: return "신청"; } diff --git a/apps/wr/components/ui/form/coMat/conCostForm/conCostForm.tsx b/apps/wr/components/ui/form/coMat/conCostForm/conCostForm.tsx index f50474d79..bb70cb7ec 100644 --- a/apps/wr/components/ui/form/coMat/conCostForm/conCostForm.tsx +++ b/apps/wr/components/ui/form/coMat/conCostForm/conCostForm.tsx @@ -22,8 +22,7 @@ import { UiTable, } from "@common/business_components/ui"; import PopoverUi from "@common/business_components/ui/popover_ui"; -import { Button, Input, Label } from "@common/components/ui"; -import type { read } from "fs"; +import { Button, Checkbox, Input, Label } from "@common/components/ui"; import { useCallback, useEffect, useRef, useState } from "react"; import { type UseFormReturn } from "react-hook-form"; import toast from "react-hot-toast"; @@ -48,6 +47,8 @@ export default function ConCostForm({ const ref = useRef<boolean>(false); + const [checkFinish, setCheckFinish] = useState<boolean>(false); + useEffect(() => { if (!ref.current) { const currentYear = new Date().getFullYear(); @@ -57,6 +58,13 @@ export default function ConCostForm({ } }, [ref, getConstCostFee, getRateFee]); + + useEffect(() => { + // 다른 행을 선택하면(selectionVersion 변경) 체크박스 상태를 초기화합니다. + setCheckFinish(false); + }, [selectionVersion]); + + const zipDowonLoad = () => { fetch(`/2024_constarctFeeProgram.zip`, {}).then(async (response) => { const blobRet = await response.blob(); @@ -74,36 +82,38 @@ export default function ConCostForm({ const cstrnCst = form.watch("demandCst.cstrnCst"); const useUsgCdWatch = form.watch("useUsgCd"); - - const calculateConstCost = useCallback(() => { const usgPrcsProcs = usgPrcsProcsWatch; - if(!dmndQtyWatch){ - toast.error('요구수량(M3/d)을 확인해주세요 계산할수 없습니다. ') - return + if (!dmndQtyWatch) { + toast.error("요구수량(M3/d)을 확인해주세요 계산할수 없습니다. "); + return; + } + if (usgPrcsProcs === null || usgPrcsProcs === undefined) { + toast.error( + "용도에 따른 처리공정을 확인할수 없습니다. 용도를 확인해주세요. " + ); + return; } - if(usgPrcsProcs === null || usgPrcsProcs === undefined){ - toast.error('용도에 따른 처리공정을 확인할수 없습니다. 용도를 확인해주세요. ') - return - } const dmndQtyYear = dmndQtyWatch * 365; if (usgPrcsProcs && usgPrcsProcs?.length > 0) { - const firstProc = getConstFctCode(usgPrcsProcs?.split("+")[0] || "") || ""; - const secondProc = getConstFctCode(usgPrcsProcs?.split("+")[1] || "") || ""; + const firstProc = + getConstFctCode(usgPrcsProcs?.split("+")[0] || "") || ""; + const secondProc = + getConstFctCode(usgPrcsProcs?.split("+")[1] || "") || ""; const { lower: firstLower, upper: firstUpper } = findBounds( constCostRes, firstProc || "", - 'fctCd', - 'fctCpc', + "fctCd", + "fctCpc", Number(dmndQtyYear) ); const { lower: secondLower, upper: secondUpper } = findBounds( constCostRes, secondProc || "", - 'fctCd', - 'fctCpc', + "fctCd", + "fctCpc", Number(dmndQtyYear) ); @@ -122,52 +132,56 @@ export default function ConCostForm({ const groupCodes = ["BD", "CE", "SE", "WD"]; const groupedBounds: { [key: string]: { lower?: any; upper?: any } } = {}; for (const code of groupCodes) { - - groupedBounds[code] = findBounds(rateFeeRes, code, 'incExpCd', 'stdCost',result); + groupedBounds[code] = findBounds( + rateFeeRes, + code, + "incExpCd", + "stdCost", + result + ); } const resultRate = transformAndInterpolate(groupedBounds, result); - const fcltSbsdCstrnCst = (result* (resultRate.BD + resultRate.CE + resultRate.SE + resultRate.WD) / 100); + const fcltSbsdCstrnCst = + (result * + (resultRate.BD + resultRate.CE + resultRate.SE + resultRate.WD)) / + 100; const realResult = result + fcltSbsdCstrnCst; - form.setValue("demandCst.prcsFcltCpctCstrnCst", Math.round(result) ); - form.setValue("demandCst.fcltSbsdCstrnCst", Math.round(fcltSbsdCstrnCst)) ; - form.setValue("totalConstCost", Math.round(realResult)); + form.setValue("demandCst.prcsFcltCpctCstrnCst", Math.round(result)); + form.setValue("demandCst.fcltSbsdCstrnCst", Math.round(fcltSbsdCstrnCst)); + form.setValue("totalConstCost", Math.round(realResult)); } }, [constCostRes, rateFeeRes, usgPrcsProcsWatch, dmndQtyWatch]); const onSubmit = (value: DmdCoReqType) => { - - if(value.demandCst&& !value.demandCst.prcsFcltCpctCstrnCst ){ - toast.error('공사비 계산 진행 후 저장을 눌러주새요') - return + if (value.demandCst && !value.demandCst.prcsFcltCpctCstrnCst) { + toast.error("공사비 계산 진행 후 저장을 눌러주새요"); + return; } if (type === "mat") { - - if(value.demandCst ){ + if (value.demandCst) { const data: DedMatUpdateType = { dmdInfoSn: value.dmdInfoSn, - usgPrcsProcs: value.demandCst.usgPrcsProcs|| "", - cstrnCst: value.demandCst.cstrnCst ||0, - fcltSbsdCstrnCst: value.demandCst.fcltSbsdCstrnCst ||0, - prcsFcltCpctCstrnCst: value.demandCst.prcsFcltCpctCstrnCst ||0, + usgPrcsProcs: value.demandCst.usgPrcsProcs || "", + cstrnCst: value.demandCst.cstrnCst || 0, + fcltSbsdCstrnCst: value.demandCst.fcltSbsdCstrnCst || 0, + prcsFcltCpctCstrnCst: value.demandCst.prcsFcltCpctCstrnCst || 0, + flg: checkFinish?'Checked':'Sub', rgtr: TEST_UUID, }; - coMatRvwUpdate("11000SW001R1", form.getValues("dmdInfoFk"), data); + coMatRvwUpdate("11000SW001R1", form.getValues("dmdInfoFk"), data); } } else if (type === "hist") { - - delete value.totalConstCost - delete value.ruseUtztnFcltNm - + delete value.totalConstCost; + delete value.ruseUtztnFcltNm; ruseRvwCalUpdate(value); } }; useEffect(() => { - console.log(" form ", form.formState.errors); + console.log(" form ", form.formState.errors); }, [form.formState]); - useEffect(() => { form.register("dmdInfoSn"); form.register("dmdInfoFk"); @@ -187,18 +201,30 @@ export default function ConCostForm({ <Button className="mx-3" size={"sm"} onClick={calculateConstCost}> 계산 </Button> - <Button className="mr-3" size={"sm"} onClick={form.handleSubmit(onSubmit)}> + <Button + className="mr-3" + size={"sm"} + onClick={form.handleSubmit(onSubmit)} + > 저장 </Button> - <Label>관로 표준공사비 산정 프로그램</Label> - <Button - color="green" - size={"sm"} - onClick={zipDowonLoad} - className="bg-none ml-6" - > - 다운로드 - </Button> + <Label>검토완료</Label> + <Checkbox + className="ml-3 mr-6" + checked={checkFinish} + onCheckedChange={(checked) => + // 'indeterminate' 상태를 무시하고 순수 boolean 값으로만 상태를 업데이트합니다. + setCheckFinish(checked === true)} + ></Checkbox> + <Label>관로 표준공사비 산정 프로그램</Label> + <Button + color="green" + size={"sm"} + onClick={zipDowonLoad} + className="bg-none ml-6" + > + 다운로드 + </Button> </div> <div className="col-span-12 w-full"> <h1 className={formH1Class}>기본사항</h1> @@ -310,14 +336,14 @@ export default function ConCostForm({ <h1 className={formH1Class}>공사비 입력사항</h1> </div> - <div className="col-span-2 mt-4"> <div className="flex items-center justify-between"> <PopoverUi childrenTriger={ <> <Label className="underline"> - 처리시설용량에 따른 공사비(백만원) <i className="diveicon di-search" /> + 처리시설용량에 따른 공사비(백만원){" "} + <i className="diveicon di-search" /> </Label> </> } @@ -394,7 +420,8 @@ export default function ConCostForm({ childrenTriger={ <> <Label className="underline"> - 처리시설 총사업비(백만원) <i className="diveicon di-search" /> + 처리시설 총사업비(백만원){" "} + <i className="diveicon di-search" /> </Label> </> } From fb8812303baa27bf0f38fb3b60da43a8ab716e25 Mon Sep 17 00:00:00 2001 From: leedj312 <dlehd467@gmail.com> Date: Sun, 6 Jul 2025 23:01:04 +0900 Subject: [PATCH 088/108] =?UTF-8?q?=EA=B2=80=ED=86=A0=EC=99=84=EB=A3=8C=20?= =?UTF-8?q?=EB=A9=94=EC=8B=9C=EC=A7=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wr/app/store/matDmd/coMatRvw/index.tsx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/apps/wr/app/store/matDmd/coMatRvw/index.tsx b/apps/wr/app/store/matDmd/coMatRvw/index.tsx index e683b548e..21239fce3 100644 --- a/apps/wr/app/store/matDmd/coMatRvw/index.tsx +++ b/apps/wr/app/store/matDmd/coMatRvw/index.tsx @@ -111,7 +111,14 @@ export const coRvwActionsExport: (set: any) => coRvwActions = (set: any) => { data ); if (response.status === 200) { + + if(data.flg ==='Sub'){ + toast.success("매칭 검토로 저장되었습니다."); + }else{ + toast.success("매칭 검토완료로 저장되었습니다."); + } + const coMatRvwRes = await coMatInit( 1, 10, From c79b4ca18d59746941c70b6f0122d24393086fd7 Mon Sep 17 00:00:00 2001 From: jyha00 <jyha@mind-one.co.kr> Date: Mon, 7 Jul 2025 09:19:37 +0900 Subject: [PATCH 089/108] =?UTF-8?q?=ED=95=98=EC=88=98=EC=9E=AC=EC=9D=B4?= =?UTF-8?q?=EC=9A=A9=EC=8B=A4=EC=A0=81=20=ED=98=84=ED=99=A9=20=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=84=B0=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../prfmncSttn/pbnstPrfmncSttn/page.tsx | 121 +++++++++--------- .../prfmncSttn/pbnstPrfmncSttn/index.tsx | 34 ++--- 2 files changed, 77 insertions(+), 78 deletions(-) diff --git a/apps/wr/app/ruseFcltMng/prfmncSttn/pbnstPrfmncSttn/page.tsx b/apps/wr/app/ruseFcltMng/prfmncSttn/pbnstPrfmncSttn/page.tsx index f689eb745..3e860d744 100644 --- a/apps/wr/app/ruseFcltMng/prfmncSttn/pbnstPrfmncSttn/page.tsx +++ b/apps/wr/app/ruseFcltMng/prfmncSttn/pbnstPrfmncSttn/page.tsx @@ -68,8 +68,6 @@ export default function PbnstPrfmncSttn() { chartData5: lineChartDataProps[]; chartData6: lineChartDataProps[]; chartData7: lineChartDataProps[]; - chartData8: lineChartDataProps[]; - chartData9: lineChartDataProps[]; }>(); const [outLineData, setOutLineData] = useState<{ chartData1: lineChartDataProps[]; @@ -78,6 +76,8 @@ export default function PbnstPrfmncSttn() { chartData4: lineChartDataProps[]; chartData5: lineChartDataProps[]; chartData6: lineChartDataProps[]; + chartData7: lineChartDataProps[]; + chartData8: lineChartDataProps[]; }>(); type dnChartForm = { label: string, val: number, fill?: string }; @@ -228,16 +228,18 @@ export default function PbnstPrfmncSttn() { indstUswtr += element.indstUswtr; // 월별 합계 - inUsageSum = element.wshwtr + inUsageSum = + element.wshwtr + element.coolUswtr + element.cleanUswtr + element.drwtUswtr + element.dltnUswtr + element.ithlRswtr + element.ithlEtcUswtr - + element.cleanToilUswtr - + element.resn; - outUsageSum = element.lndwtr + outUsageSum = + element.cleanToilUswtr + + element.resn + + element.lndwtr + element.hydUswtr + element.ugwtChr + element.farmUswtr @@ -258,9 +260,10 @@ export default function PbnstPrfmncSttn() { dltnUswtrData.push({ label: `${month}월`, val: element.dltnUswtr, color: "purple" }); ithlRswtrData.push({ label: `${month}월`, val: element.ithlRswtr, color: "green" }); ithlEtcUswtrData.push({ label: `${month}월`, val: element.ithlEtcUswtr, color: "blue" }); + + cleanToilUswtrData.push({ label: `${month}월`, val: element.cleanToilUswtr, color: "magenta" }); resnData.push({ label: `${month}월`, val: element.resn, color: "skyblue" }); - lndwtrData.push({ label: `${month}월`, val: element.lndwtr, color: "red" }); hydUswtrData.push({ label: `${month}월`, val: element.hydUswtr, color: "orange" }); ugwtChrData.push({ label: `${month}월`, val: element.ugwtChr, color: "yellow" }); @@ -271,22 +274,18 @@ export default function PbnstPrfmncSttn() { } }); + let dnTotalIn = 0; + let dnTotalOut = 0; + inUsageArr.forEach((item) => { + dnTotalIn += item + }) + outUsageArr.forEach((item) => { + dnTotalOut += item + }) + // 도넛차트 데이터 - dnData.push({ label: "세척수", val: wshwtr, fill: "#F7CFD8" }); - dnData.push({ label: "냉각수", val: coolUswtr, fill: "#F4F8D3" }); - dnData.push({ label: "청소수", val: cleanUswtr, fill: "#A6D6D6" }); - dnData.push({ label: "식수대", val: drwtUswtr, fill: "#8E7DBE" }); - dnData.push({ label: "희석용수", val: dltnUswtr, fill: "#FF8383" }); - dnData.push({ label: "중수도", val: ithlRswtr, fill: "#FFF574" }); - dnData.push({ label: "기타", val: ithlEtcUswtr, fill: "#A1D6CB" }); - dnData.push({ label: "청소화장실용수", val: cleanToilUswtr, fill: "#A19AD3" }); - dnData.push({ label: "세척살수용수", val: resn, fill: "#B1F0F7" }); - dnData.push({ label: "조경용수", val: lndwtr, fill: "#81BFDA" }); - dnData.push({ label: "친수용수", val: hydUswtr, fill: "#F5F0CD" }); - dnData.push({ label: "지하수충전", val: ugwtChr, fill: "#FADA7A" }); - dnData.push({ label: "농업용수", val: farmUswtr, fill: "#A6AEBF" }); - dnData.push({ label: "하천등유지용수", val: rvrKeepUswtr, fill: "#C5D3E8" }); - dnData.push({ label: "공업용수", val: indstUswtr, fill: "#D0E8C5" }); + dnData.push({ label: "장내용수", val: dnTotalIn, fill: "#8DD8FF" }); + dnData.push({ label: "장외용수", val: dnTotalOut, fill: "#FFF085" }); // 장내용수 도넛 dnInData.push({ label: "세척수", val: wshwtr, fill: "#F7CFD8" }); @@ -296,10 +295,10 @@ export default function PbnstPrfmncSttn() { dnInData.push({ label: "희석용수", val: dltnUswtr, fill: "#FF8383" }); dnInData.push({ label: "중수도", val: ithlRswtr, fill: "#FFF574" }); dnInData.push({ label: "기타", val: ithlEtcUswtr, fill: "#A1D6CB" }); - dnInData.push({ label: "청소화장실용수", val: cleanToilUswtr, fill: "#A19AD3" }); - dnInData.push({ label: "세척살수용수", val: resn, fill: "#B1F0F7" }); // 장외용수 도넛 + dnOutData.push({ label: "청소화장실용수", val: cleanToilUswtr, fill: "#A19AD3" }); + dnOutData.push({ label: "세척살수용수", val: resn, fill: "#B1F0F7" }); dnOutData.push({ label: "조경용수", val: lndwtr, fill: "#81BFDA" }); dnOutData.push({ label: "친수용수", val: hydUswtr, fill: "#F5F0CD" }); dnOutData.push({ label: "지하수충전", val: ugwtChr, fill: "#FADA7A" }); @@ -315,17 +314,17 @@ export default function PbnstPrfmncSttn() { chartData4: drwtUswtrData, chartData5: dltnUswtrData, chartData6: ithlRswtrData, - chartData7: ithlEtcUswtrData, - chartData8: cleanToilUswtrData, - chartData9: resnData + chartData7: ithlEtcUswtrData }); setOutLineData({ - chartData1: lndwtrData, - chartData2: hydUswtrData, - chartData3: ugwtChrData, - chartData4: farmUswtrData, - chartData5: rvrKeepUswtrData, - chartData6: indstUswtrData, + chartData1: cleanToilUswtrData, + chartData2: resnData, + chartData3: lndwtrData, + chartData4: hydUswtrData, + chartData5: ugwtChrData, + chartData6: farmUswtrData, + chartData7: rvrKeepUswtrData, + chartData8: indstUswtrData, }); setBarData(barData); setChartData(dnData); @@ -408,15 +407,7 @@ export default function PbnstPrfmncSttn() { chartData7: { label: '기타', color: "blue", - }, - chartData8: { - label: '청소화장실용수', - color: "magenta", - }, - chartData9: { - label: '세척살수용수', - color: "skyblue", - }, + } } satisfies ChartConfig; setLnInChartConfig(newChartConfig); } @@ -427,26 +418,34 @@ export default function PbnstPrfmncSttn() { if (outLineData?.chartData1) { const newChartConfig = { chartData1: { - label: '조경용수', + label: '청소화장실용수', color: "red", }, chartData2: { + label: '세척살수용수', + color: "pink", + }, + chartData3: { + label: '조경용수', + color: "gray", + }, + chartData4: { label: '친수용수', color: "orange", }, - chartData3: { + chartData5: { label: '지하수충전', color: "yellow", }, - chartData4: { + chartData6: { label: '농업용수', color: "green", }, - chartData5: { + chartData7: { label: '하천등유지용수', color: "blue", }, - chartData6: { + chartData8: { label: '공업용수', color: "purple", }, @@ -615,7 +614,7 @@ export default function PbnstPrfmncSttn() { <h1 className="">장내·외 월별 하수재이용실적 현황</h1> <div className="flex"> <div className="w-1/3"> - <MainChart chartConfig={barChartConfig} chartData={barData} chartTitle={"월별 장내·외 용수 총 실적"} chartForm={CHART_FORM.DOUBLEBAR} /> + <MainChart chartConfig={barChartConfig} chartData={barData} chartTitle={"월별 장내·외 용수 총 실적"} chartForm={CHART_FORM.DOUBLEBAR} showLegend={true} /> </div> <div className="w-1/3"> <MainChart chartConfig={lnInChartConfig} chartData={inLineData} chartTitle={"월별 장내 용수 실적"} chartForm={CHART_FORM.LINE} /> @@ -627,25 +626,25 @@ export default function PbnstPrfmncSttn() { <h1 className="">하수재이용실적 현황</h1> <ListDiv className="col-span-8 pr-6 "> {/* <ScrollArea className="h-[60vh]"> */} - {/* <div className="card-header"> + {/* <div className="card-header"> <h3 className="text-2xl font-medium text-label"> {"하수처리수재이용 실적(m3) 등록결과"} </h3> </div> */} - <UiTable - headName={undefined} - tableData={prfData || []} - headlist={monthHeadList} - pageSize={0} - total={0} - cellClick={undefined} - onPageChange={function (page: number): void { - throw new Error("Function not implemented."); - }}> - </UiTable> + <UiTable + headName={undefined} + tableData={prfData || []} + headlist={monthHeadList} + pageSize={0} + total={0} + cellClick={undefined} + onPageChange={function (page: number): void { + throw new Error("Function not implemented."); + }}> + </UiTable> - {/* <ListDiv className="col-span-8 pr-6 "> + {/* <ListDiv className="col-span-8 pr-6 "> <ScrollArea className="h-[60vh]"> <div className="card-header"> <h3 className="text-2xl font-medium text-label"> diff --git a/apps/wr/app/store/prfmncSttn/pbnstPrfmncSttn/index.tsx b/apps/wr/app/store/prfmncSttn/pbnstPrfmncSttn/index.tsx index b513085ae..db8b6a717 100644 --- a/apps/wr/app/store/prfmncSttn/pbnstPrfmncSttn/index.tsx +++ b/apps/wr/app/store/prfmncSttn/pbnstPrfmncSttn/index.tsx @@ -148,7 +148,7 @@ export const pbnstPrfmncSttnActionsExport: (set: any) => pbnstPrfmncSttnActions let usageSum = 0; let year = ""; const yearsData = [] - + res.data.forEach((item) => { if (item.prfList.length === 0) { const year = ""; // 연도 정보가 없으면 빈 문자열, 필요시 item에서 추출 @@ -161,26 +161,26 @@ export const pbnstPrfmncSttnActionsExport: (set: any) => pbnstPrfmncSttnActions item.prfList.forEach((acc) => { year = acc.ym?.substring(0, 4) ?? ""; inUsageSum = - (acc.wshwtr ?? 0) + - (acc.coolUswtr ?? 0) + - (acc.cleanUswtr ?? 0) + - (acc.drwtUswtr ?? 0) + - (acc.dltnUswtr ?? 0) + - (acc.ithlRswtr ?? 0) + - (acc.ithlEtcUswtr ?? 0) + - (acc.cleanToilUswtr ?? 0) + - (acc.resn ?? 0); + (acc.wshwtr ?? 0) + //조경용수 + (acc.coolUswtr ?? 0) + //냉각수 + (acc.cleanUswtr ?? 0) + //청소수 + (acc.drwtUswtr ?? 0) + //식수대 + (acc.dltnUswtr ?? 0) + //희석용수 + (acc.ithlRswtr ?? 0) + //중수도 + (acc.ithlEtcUswtr ?? 0) //기타 outUsageSum = - (acc.lndwtr ?? 0) + - (acc.hydUswtr ?? 0) + - (acc.ugwtChr ?? 0) + - (acc.farmUswtr ?? 0) + - (acc.rvrKeepUswtr ?? 0) + - (acc.indstUswtr ?? 0); + (acc.cleanToilUswtr ?? 0) + //청소화장실용수 + (acc.resn ?? 0)+ //세척살수용도 + (acc.lndwtr ?? 0) + //조경용수 + (acc.hydUswtr ?? 0) + //친수용수 + (acc.ugwtChr ?? 0) + //지하수충전 + (acc.farmUswtr ?? 0) + //농업용수 + (acc.rvrKeepUswtr ?? 0) + //하천등유지용수 + (acc.indstUswtr ?? 0); //공업용수 const yearData = yearsData.find( (yearItem) => yearItem.year === year && - yearItem.pbnstRuseFcltPk == acc.pbnstRuseFcltPk + yearItem.pbnstRuseFcltPk == acc.pbnstRuseFcltFk ); if (yearData) { From c5d2cc53a4883aacb6fdd5349c134d4fe9aba25e Mon Sep 17 00:00:00 2001 From: leedj312 <dlehd467@gmail.com> Date: Mon, 7 Jul 2025 09:26:37 +0900 Subject: [PATCH 090/108] =?UTF-8?q?=EA=B2=80=ED=86=A0=EC=99=84=EB=A3=8C=20?= =?UTF-8?q?=EC=8B=9C=20=EB=A9=94=EC=8B=9C=EC=A7=80=20=ED=91=9C=EC=B6=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/form/coMat/conCostForm/conCostForm.tsx | 53 ++++++++++++++++++- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/apps/wr/components/ui/form/coMat/conCostForm/conCostForm.tsx b/apps/wr/components/ui/form/coMat/conCostForm/conCostForm.tsx index bb70cb7ec..ee06893c9 100644 --- a/apps/wr/components/ui/form/coMat/conCostForm/conCostForm.tsx +++ b/apps/wr/components/ui/form/coMat/conCostForm/conCostForm.tsx @@ -65,6 +65,31 @@ export default function ConCostForm({ }, [selectionVersion]); + const confrirmToast = () => { + + if (checkFinish) { + toast((t) => ( + <span className="flex flex-col items-center"> + 검토완료 변경시 공사비는 변경할수 없습니다. + <br /> + 진행하시겠습니까? + <Button + onClick={() => { + toast.dismiss(t.id); + + + }} + > + 확인 + </Button> + <Button onClick={() => toast.dismiss(t.id)}>취소</Button> + </span> + )); + } else { + + } + }; + const zipDowonLoad = () => { fetch(`/2024_constarctFeeProgram.zip`, {}).then(async (response) => { const blobRet = await response.blob(); @@ -168,12 +193,36 @@ export default function ConCostForm({ flg: checkFinish?'Checked':'Sub', rgtr: TEST_UUID, }; - coMatRvwUpdate("11000SW001R1", form.getValues("dmdInfoFk"), data); + if (checkFinish) { + toast((t) => ( + <span className="flex flex-col items-center text-2xl" > + 검토완료 변경시 공사비는 변경할수 없습니다. + <br /> + 진행하시겠습니까? + <div className="flex gap-2 mt-2"> + <Button + size='sm' + + onClick={() => { + toast.dismiss(t.id); + coMatRvwUpdate("11000SW001R1", form.getValues("dmdInfoFk"), data); + }} + > + 확인 + </Button> + <Button + size='sm' + onClick={() => toast.dismiss(t.id)}>취소</Button> + </div> + </span> + )); + } else { + coMatRvwUpdate("11000SW001R1", form.getValues("dmdInfoFk"), data); + } } } else if (type === "hist") { delete value.totalConstCost; delete value.ruseUtztnFcltNm; - ruseRvwCalUpdate(value); } }; From abda62eebb642e6a9f611ee954f6d630f23b3610 Mon Sep 17 00:00:00 2001 From: jyha00 <jyha@mind-one.co.kr> Date: Mon, 7 Jul 2025 09:28:45 +0900 Subject: [PATCH 091/108] =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/common/business_components/src/ui/chart/DoubleBar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/common/business_components/src/ui/chart/DoubleBar.tsx b/packages/common/business_components/src/ui/chart/DoubleBar.tsx index b201b3bf9..c52937967 100644 --- a/packages/common/business_components/src/ui/chart/DoubleBar.tsx +++ b/packages/common/business_components/src/ui/chart/DoubleBar.tsx @@ -89,7 +89,7 @@ const CustomTooltip = ({ ))} </div> ); -};*/ +};*/ const CustomLegend = (props: any) => { const { payload } = props; if (!payload) return null; From ab13d1436142fe892bfc20362385bed6ae75474f Mon Sep 17 00:00:00 2001 From: jyha00 <jyha@mind-one.co.kr> Date: Mon, 7 Jul 2025 09:32:43 +0900 Subject: [PATCH 092/108] =?UTF-8?q?=EB=8D=94=EB=B8=94=EB=B0=94=20=EC=B0=A8?= =?UTF-8?q?=ED=8A=B8=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/common/business_components/src/ui/chart/DoubleBar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/common/business_components/src/ui/chart/DoubleBar.tsx b/packages/common/business_components/src/ui/chart/DoubleBar.tsx index c52937967..b201b3bf9 100644 --- a/packages/common/business_components/src/ui/chart/DoubleBar.tsx +++ b/packages/common/business_components/src/ui/chart/DoubleBar.tsx @@ -89,7 +89,7 @@ const CustomTooltip = ({ ))} </div> ); -};*/ +};*/ const CustomLegend = (props: any) => { const { payload } = props; if (!payload) return null; From cbb41bb200d06ff29dbe440f0fcc7d5e94a6ae93 Mon Sep 17 00:00:00 2001 From: YangJinSoo94 <jschrome782@gmail.com> Date: Mon, 7 Jul 2025 10:15:09 +0900 Subject: [PATCH 093/108] =?UTF-8?q?=EC=97=91=EC=85=80=20=EC=97=85=EB=A1=9C?= =?UTF-8?q?=EB=93=9C=20=EB=B3=80=EC=88=98=EB=AA=85=20=EC=98=A4=ED=83=80=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wr/app/store/matDmd/ruseCaseMng/excel.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/wr/app/store/matDmd/ruseCaseMng/excel.tsx b/apps/wr/app/store/matDmd/ruseCaseMng/excel.tsx index ebbaa8b7a..bb0c18d6a 100644 --- a/apps/wr/app/store/matDmd/ruseCaseMng/excel.tsx +++ b/apps/wr/app/store/matDmd/ruseCaseMng/excel.tsx @@ -11,7 +11,7 @@ type nameMap = { export const reUseCaseNames: nameMap = { "사업명": "ruseBizNm", "사업시작일": "bizBgngDate", - "사업종료일": "bizEndtDate", + "사업종료일": "bizEndDate", "공급처_시설종류": "splyFcltKnd", "공급처명": "splyFcltNm", "공급처_시설용량(㎥/일)": "splyFcltCpct", From 051c158e22777b2cb9c48c10b4f39e7687f9d71d Mon Sep 17 00:00:00 2001 From: truthwing <truthplumage@gmail.com> Date: Mon, 7 Jul 2025 10:24:11 +0900 Subject: [PATCH 094/108] =?UTF-8?q?=EB=8F=84=EB=84=9B=20=EB=B0=8F=20?= =?UTF-8?q?=EB=9D=BC=EC=9D=B8=20=EC=B0=A8=ED=8A=B8=20=EC=88=98=EC=A0=95?= =?UTF-8?q?=EC=82=AC=ED=95=AD=20=EC=B6=94=EA=B0=80.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/ui/chart/DoughnutChart.tsx | 61 ++++++++++++++++++- .../src/ui/chart/LineChart.tsx | 58 +++++++++++++++++- 2 files changed, 115 insertions(+), 4 deletions(-) diff --git a/packages/common/business_components/src/ui/chart/DoughnutChart.tsx b/packages/common/business_components/src/ui/chart/DoughnutChart.tsx index 39dee3d9e..99395267a 100644 --- a/packages/common/business_components/src/ui/chart/DoughnutChart.tsx +++ b/packages/common/business_components/src/ui/chart/DoughnutChart.tsx @@ -48,6 +48,7 @@ type LegendProps = { }; /** 차트 기본 스타일 */ +/* const customStyles = { legendContainer: { display: "flex", @@ -58,6 +59,23 @@ const customStyles = { marginTop: "10px", } as CSSProperties, }; +*/ + +/** 차트 기본 스타일 (라인차트와 동일하게) */ +const customStyles = { + legendContainer: { + display: "flex", + flexWrap: "wrap", // 여러 줄로 자동 줄바꿈 + justifyContent: "center", + alignItems: "center", + gap: "12px", // 아이템 간 간격 + fontSize: "13px", + fontWeight: "normal", + marginTop: "16px", + marginBottom: "8px", + minHeight: "32px", // 범례 영역 높이 통일 + } as CSSProperties, +}; // 커스텀 툴팁 const CustomTooltip = ({ active, payload, label }: { @@ -82,6 +100,7 @@ const CustomTooltip = ({ active, payload, label }: { }; // 커스텀 범례 +/* const CustomLegend = (props: LegendProps) => { const { payload } = props; if (!payload) return null; @@ -97,12 +116,52 @@ const CustomLegend = (props: LegendProps) => { </div> ); }; +*/ + +const CustomLegend = (props: LegendProps) => { + const { payload } = props; + if (!payload) return null; + + return ( + <div style={customStyles.legendContainer}> + {payload.map((entry: LegendEntry, index: number) => ( + <div + key={`legend-${index}`} + className="flex items-center" + style={{ + minWidth: "90px", + margin: "2px 0", + }} + > + <div + style={{ + width: "14px", + height: "14px", + backgroundColor: entry.fill, + borderRadius: "3px", + marginRight: "8px", + minWidth: "14px", + minHeight: "14px", + }} + ></div> + <span style={{ fontSize: "13px", whiteSpace: "nowrap" }}> + {entry.payload?.label || entry.value} + </span> + </div> + ))} + </div> + ); +}; export default function DoughnutChart(props: { chartConfig: any; chartData: ChartDataItem[] | undefined; chartTitle: string }) { + const legendData = props.chartData?.map(item => ({ + value: item.label, + fill: props.chartConfig?.[item.label]?.fill || 'blue', + })) ?? []; return ( <Card className="flex flex-col"> <CardContent className="flex-1 pb-0"> @@ -130,9 +189,9 @@ export default function DoughnutChart(props: { nameKey="label" innerRadius={60} /> - <CustomLegend /> </PieChart> </ChartContainer> + <CustomLegend payload={legendData} /> </div> </CardContent> </Card> diff --git a/packages/common/business_components/src/ui/chart/LineChart.tsx b/packages/common/business_components/src/ui/chart/LineChart.tsx index 4014702b6..23570912d 100644 --- a/packages/common/business_components/src/ui/chart/LineChart.tsx +++ b/packages/common/business_components/src/ui/chart/LineChart.tsx @@ -1,11 +1,11 @@ "use client" import type { CSSProperties } from "react"; import { CartesianGrid, Legend, Line, LineChart, Tooltip, XAxis, YAxis } from "recharts"; +import type { ContentType } from "recharts/types/component/DefaultLegendContent"; import { ChartContainer, type ChartConfig } from "./lib/chart"; -import type { ContentType } from "recharts/types/component/DefaultLegendContent"; type LineChartProps = { chartConfig: ChartConfig | undefined; @@ -18,6 +18,7 @@ type LineChartProps = { } /** 차트 기본 스타일 */ +/* const customStyles = { legendContainer: { display: "flex", @@ -28,6 +29,23 @@ const customStyles = { marginTop: "10px", } as CSSProperties, }; +*/ + +/** 차트 기본 스타일 */ +const customStyles = { + legendContainer: { + display: "flex", + flexWrap: "wrap", + justifyContent: "center", + alignItems: "center", + gap: "12px", + fontSize: "13px", + fontWeight: "normal", + marginTop: "16px", + marginBottom: "8px", + minHeight: "32px", + } as CSSProperties, +}; type LegendEntry = { value: string; @@ -60,7 +78,7 @@ const CustomTooltip = ({ active, payload, label }: { active?: boolean; payload?: }; // 커스텀 범례 (글씨 크기 키우기 + 스타일 추가) -const CustomLegend = (props: LegendProps) => { +/*const CustomLegend = (props: LegendProps) => { const { payload } = props; if (!payload) return null; @@ -74,6 +92,40 @@ const CustomLegend = (props: LegendProps) => { ))} </div> ); +};*/ + +// 커스텀 범례 (도넛차트와 동일하게) +const CustomLegend = (props: LegendProps) => { + const { payload } = props; + if (!payload) return null; + + return ( + <div style={customStyles.legendContainer}> + {payload.map((entry: LegendEntry, index: number) => ( + <div + key={`legend-${index}`} + className="flex items-center" + style={{ + minWidth: "90px", + margin: "2px 0", + }} + > + <div + style={{ + width: "14px", + height: "14px", + backgroundColor: entry.color, + borderRadius: "3px", + marginRight: "8px", + minWidth: "14px", + minHeight: "14px", + }} + ></div> + <span style={{ fontSize: "13px", whiteSpace: "nowrap" }}>{entry.value}</span> + </div> + ))} + </div> + ); }; type DataType = {label: string; val: string}; @@ -141,7 +193,7 @@ export default function Linechart({ > <CartesianGrid vertical={false} /> <XAxis dataKey="label" fontSize="8px" fontWeight="bold" /> - <YAxis yAxisId="left" className="text-[12px] font-bold" domain={[0, Math.ceil(lineMaxValue * 1.1)]} tickCount={6} orientation="left" label={{ value: yFieldText, className:"text-[12px] font-bold", angle: -90, position: 'insideLeft' }} /> + <YAxis yAxisId="left" className="text-[12px] font-bold" tickFormatter={(value) => value.toLocaleString()} tickCount={6} orientation="left" label={{ value: yFieldText, className:"text-[12px] font-bold", angle: -90, position: 'insideLeft' }} /> <Tooltip content={<CustomTooltip />} /> {showLegend && <Legend content={CustomLegend as ContentType}/>} {Object.keys(chartData).map((key) => { From d842ac599e9f16b786e05ab692d8067a8d94a8ff Mon Sep 17 00:00:00 2001 From: truthwing <truthplumage@gmail.com> Date: Mon, 7 Jul 2025 10:32:35 +0900 Subject: [PATCH 095/108] =?UTF-8?q?=EB=8F=84=EB=84=9B=20=EB=9D=BC=EC=9D=B8?= =?UTF-8?q?=20=EB=B3=80=EA=B2=BD=EC=82=AC=ED=95=AD=20=EB=B3=B5=EA=B5=AC.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/ui/chart/DoughnutChart.tsx | 61 ++++++++++++++++++- .../src/ui/chart/LineChart.tsx | 56 ++++++++++++++++- 2 files changed, 114 insertions(+), 3 deletions(-) diff --git a/packages/common/business_components/src/ui/chart/DoughnutChart.tsx b/packages/common/business_components/src/ui/chart/DoughnutChart.tsx index 39dee3d9e..99395267a 100644 --- a/packages/common/business_components/src/ui/chart/DoughnutChart.tsx +++ b/packages/common/business_components/src/ui/chart/DoughnutChart.tsx @@ -48,6 +48,7 @@ type LegendProps = { }; /** 차트 기본 스타일 */ +/* const customStyles = { legendContainer: { display: "flex", @@ -58,6 +59,23 @@ const customStyles = { marginTop: "10px", } as CSSProperties, }; +*/ + +/** 차트 기본 스타일 (라인차트와 동일하게) */ +const customStyles = { + legendContainer: { + display: "flex", + flexWrap: "wrap", // 여러 줄로 자동 줄바꿈 + justifyContent: "center", + alignItems: "center", + gap: "12px", // 아이템 간 간격 + fontSize: "13px", + fontWeight: "normal", + marginTop: "16px", + marginBottom: "8px", + minHeight: "32px", // 범례 영역 높이 통일 + } as CSSProperties, +}; // 커스텀 툴팁 const CustomTooltip = ({ active, payload, label }: { @@ -82,6 +100,7 @@ const CustomTooltip = ({ active, payload, label }: { }; // 커스텀 범례 +/* const CustomLegend = (props: LegendProps) => { const { payload } = props; if (!payload) return null; @@ -97,12 +116,52 @@ const CustomLegend = (props: LegendProps) => { </div> ); }; +*/ + +const CustomLegend = (props: LegendProps) => { + const { payload } = props; + if (!payload) return null; + + return ( + <div style={customStyles.legendContainer}> + {payload.map((entry: LegendEntry, index: number) => ( + <div + key={`legend-${index}`} + className="flex items-center" + style={{ + minWidth: "90px", + margin: "2px 0", + }} + > + <div + style={{ + width: "14px", + height: "14px", + backgroundColor: entry.fill, + borderRadius: "3px", + marginRight: "8px", + minWidth: "14px", + minHeight: "14px", + }} + ></div> + <span style={{ fontSize: "13px", whiteSpace: "nowrap" }}> + {entry.payload?.label || entry.value} + </span> + </div> + ))} + </div> + ); +}; export default function DoughnutChart(props: { chartConfig: any; chartData: ChartDataItem[] | undefined; chartTitle: string }) { + const legendData = props.chartData?.map(item => ({ + value: item.label, + fill: props.chartConfig?.[item.label]?.fill || 'blue', + })) ?? []; return ( <Card className="flex flex-col"> <CardContent className="flex-1 pb-0"> @@ -130,9 +189,9 @@ export default function DoughnutChart(props: { nameKey="label" innerRadius={60} /> - <CustomLegend /> </PieChart> </ChartContainer> + <CustomLegend payload={legendData} /> </div> </CardContent> </Card> diff --git a/packages/common/business_components/src/ui/chart/LineChart.tsx b/packages/common/business_components/src/ui/chart/LineChart.tsx index 74d17f784..23570912d 100644 --- a/packages/common/business_components/src/ui/chart/LineChart.tsx +++ b/packages/common/business_components/src/ui/chart/LineChart.tsx @@ -18,6 +18,7 @@ type LineChartProps = { } /** 차트 기본 스타일 */ +/* const customStyles = { legendContainer: { display: "flex", @@ -28,6 +29,23 @@ const customStyles = { marginTop: "10px", } as CSSProperties, }; +*/ + +/** 차트 기본 스타일 */ +const customStyles = { + legendContainer: { + display: "flex", + flexWrap: "wrap", + justifyContent: "center", + alignItems: "center", + gap: "12px", + fontSize: "13px", + fontWeight: "normal", + marginTop: "16px", + marginBottom: "8px", + minHeight: "32px", + } as CSSProperties, +}; type LegendEntry = { value: string; @@ -60,7 +78,7 @@ const CustomTooltip = ({ active, payload, label }: { active?: boolean; payload?: }; // 커스텀 범례 (글씨 크기 키우기 + 스타일 추가) -const CustomLegend = (props: LegendProps) => { +/*const CustomLegend = (props: LegendProps) => { const { payload } = props; if (!payload) return null; @@ -74,6 +92,40 @@ const CustomLegend = (props: LegendProps) => { ))} </div> ); +};*/ + +// 커스텀 범례 (도넛차트와 동일하게) +const CustomLegend = (props: LegendProps) => { + const { payload } = props; + if (!payload) return null; + + return ( + <div style={customStyles.legendContainer}> + {payload.map((entry: LegendEntry, index: number) => ( + <div + key={`legend-${index}`} + className="flex items-center" + style={{ + minWidth: "90px", + margin: "2px 0", + }} + > + <div + style={{ + width: "14px", + height: "14px", + backgroundColor: entry.color, + borderRadius: "3px", + marginRight: "8px", + minWidth: "14px", + minHeight: "14px", + }} + ></div> + <span style={{ fontSize: "13px", whiteSpace: "nowrap" }}>{entry.value}</span> + </div> + ))} + </div> + ); }; type DataType = {label: string; val: string}; @@ -141,7 +193,7 @@ export default function Linechart({ > <CartesianGrid vertical={false} /> <XAxis dataKey="label" fontSize="8px" fontWeight="bold" /> - <YAxis yAxisId="left" className="text-[12px] font-bold" domain={[0, Math.ceil(lineMaxValue * 1.1)]} tickCount={6} orientation="left" label={{ value: yFieldText, className:"text-[12px] font-bold", angle: -90, position: 'insideLeft' }} /> + <YAxis yAxisId="left" className="text-[12px] font-bold" tickFormatter={(value) => value.toLocaleString()} tickCount={6} orientation="left" label={{ value: yFieldText, className:"text-[12px] font-bold", angle: -90, position: 'insideLeft' }} /> <Tooltip content={<CustomTooltip />} /> {showLegend && <Legend content={CustomLegend as ContentType}/>} {Object.keys(chartData).map((key) => { From 8b20d7e88e4fc5abe10e1dbc2ce915d456222642 Mon Sep 17 00:00:00 2001 From: swcho <swcho@mind-one.co.kr> Date: Mon, 7 Jul 2025 13:29:45 +0900 Subject: [PATCH 096/108] =?UTF-8?q?feat:=20SELECTBOX=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20hidden=20props=20=EC=B6=94=EA=B0=80?= =?UTF-8?q?=EB=A1=9C=20placeholder=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/ui/select/index.tsx | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/packages/common/business_components/src/ui/select/index.tsx b/packages/common/business_components/src/ui/select/index.tsx index 6fa78ccf2..c4e1264c4 100644 --- a/packages/common/business_components/src/ui/select/index.tsx +++ b/packages/common/business_components/src/ui/select/index.tsx @@ -5,7 +5,8 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from ". /** 차트 기본 스타일 */ interface SelectBox1 { text:string, - val:string|number + val:string|number, + hidden?: boolean } const SelectBox = React.forwardRef< @@ -20,17 +21,27 @@ const SelectBox = React.forwardRef< readOnly?: boolean // readOnly 속성 추가 } >(({ label, className, selectClass, selectArray, onSelectValue, value, readOnly = false }, ref)=>{ - const placeHolder = selectArray?.[0]?.text ?? ""; + // 첫 번째 항목을 placeholder로 사용하고 드롭다운 목록에서는 제외 + const firstItem = selectArray[0]; + const placeHolder = firstItem?.text ?? ""; + + // 첫 번째 항목(placeholder)을 제외한 나머지 항목만 드롭다운에 표시 + const visibleItems = selectArray.filter((_, index) => index !== 0); + + // 현재 값이 첫 번째 항목(placeholder)인 경우 undefined로 설정하여 placeholder가 표시되도록 함 + const currentValue = value !== undefined && String(value) === String(firstItem?.val) + ? undefined + : (value !== undefined ? String(value) : undefined); return ( <div className={className+" flex items-center"} ref={ref}> {label && <Label className="whitespace-nowrap w-20 text-xl text-gray-600">{label}</Label>} - <Select onValueChange={onSelectValue} value={value !== undefined ? String(value) : undefined}> + <Select onValueChange={onSelectValue} value={currentValue} disabled={readOnly}> <SelectTrigger className={selectClass?selectClass:"flex-1 p-2 border rounded"}> <SelectValue placeholder={placeHolder} /> </SelectTrigger> <SelectContent> - {selectArray.map((selectArray) => ( + {visibleItems.map((selectArray) => ( <SelectItem key={selectArray.val} value={String(selectArray.val)}>{selectArray.text}</SelectItem> ))} </SelectContent> From a7035b774e3fc7a24118bee2660ea34d0b585a88 Mon Sep 17 00:00:00 2001 From: swcho <swcho@mind-one.co.kr> Date: Mon, 7 Jul 2025 14:43:13 +0900 Subject: [PATCH 097/108] =?UTF-8?q?feat:=20=EC=B9=A8=EC=9E=85=EC=88=98?= =?UTF-8?q?=EB=B6=84=EC=84=9D,=20RDII=20=EB=B6=84=EC=84=9D=20=EC=97=B0?= =?UTF-8?q?=EB=8F=84=20=EC=A1=B0=ED=9A=8C=20=EC=BF=BC=EB=A6=AC=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../map/components/popupComponent/chart/aplcnYms.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/sn/app/(map-layout)/map/components/popupComponent/chart/aplcnYms.ts b/apps/sn/app/(map-layout)/map/components/popupComponent/chart/aplcnYms.ts index 1cd0d6ded..26d597abf 100644 --- a/apps/sn/app/(map-layout)/map/components/popupComponent/chart/aplcnYms.ts +++ b/apps/sn/app/(map-layout)/map/components/popupComponent/chart/aplcnYms.ts @@ -120,7 +120,7 @@ export const getOptionsInftwtAnlsInfo = (anlsArr: TInftwtAnls[]): { text: string export const selectInftwtAnls = async(aplcnYm:String) => { // 침입수분석 try { - const queryString = aplcnYm ? `aplcnYm=${aplcnYm}` : ''; + const queryString = aplcnYm ? `aplcnYmFrom=${aplcnYm}&aplcnYmTo=${aplcnYm}` : ''; const result = await getData(`v1/inftwtAnls${queryString ? `?${queryString}` : ''}`); if (result && result.numberOfElements > 0) { console.log("selectInftwtAnls", result.content); @@ -219,7 +219,7 @@ export const getOptionsRdiiAnlsInfo = (anlsArr: TRdiiAnls[]): { text: string; va export const selectRdiiAnls = async(aplcnYm:String) => { // 침입수분석 try { - const queryString = aplcnYm ? `aplcnYm=${aplcnYm}` : ''; + const queryString = aplcnYm ? `aplcnYmFrom=${aplcnYm}&aplcnYmTo=${aplcnYm}` : ''; const result = await getData(`v1/rdiiAnls${queryString ? `?${queryString}` : ''}`); if (result && result.numberOfElements > 0) { console.log("selectRdiiAnls", result.content); From 921592a12181119d33726dc1a45a988280691abb Mon Sep 17 00:00:00 2001 From: swcho <swcho@mind-one.co.kr> Date: Mon, 7 Jul 2025 14:51:39 +0900 Subject: [PATCH 098/108] =?UTF-8?q?feat:=20DateMonthPicker=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20startYerar=20props=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/ui/date_month_picker/index.tsx | 3 +++ .../src/ui/date_month_picker/mothPicker.tsx | 12 +++++++++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/packages/common/business_components/src/ui/date_month_picker/index.tsx b/packages/common/business_components/src/ui/date_month_picker/index.tsx index d318b561f..1b97d9ce1 100644 --- a/packages/common/business_components/src/ui/date_month_picker/index.tsx +++ b/packages/common/business_components/src/ui/date_month_picker/index.tsx @@ -17,6 +17,7 @@ type MonthPickerProps = { field: { value: string; onChange: (date: string) => void }; // yyyyMM 문자열 selectClass?: string; readOnly?: boolean; + startYear?: number; // 시작 연도 props 추가 }; // yyyyMM → Date 객체 @@ -37,6 +38,7 @@ export const DateMonthPicker = ({ field, selectClass, readOnly = false, + startYear, }: MonthPickerProps) => { const [open, setOpen] = useState(false); @@ -75,6 +77,7 @@ export const DateMonthPicker = ({ <MonthPicker currentMonth={parsedDate} onMonthChange={handleSelect} + startYear={startYear} /> </PopoverContent> </Popover> diff --git a/packages/common/business_components/src/ui/date_month_picker/mothPicker.tsx b/packages/common/business_components/src/ui/date_month_picker/mothPicker.tsx index acf17fba4..6afbde5e6 100644 --- a/packages/common/business_components/src/ui/date_month_picker/mothPicker.tsx +++ b/packages/common/business_components/src/ui/date_month_picker/mothPicker.tsx @@ -28,9 +28,10 @@ export type MonthPickerProps = { newMonth: Date | undefined, shouldClosePopover?: boolean ) => void; + startYear?: number; }; -export function MonthPicker({ currentMonth, onMonthChange }: MonthPickerProps) { +export function MonthPicker({ currentMonth, onMonthChange, startYear }: MonthPickerProps) { const today = startOfToday(); const currentYear = today.getFullYear(); @@ -46,7 +47,12 @@ const selectedYear = parseInt(yearFromProp); - const yearRange = Array.from({ length: 21 }, (_, i) => currentYear - 20 + i); + // startYear가 없으면 기존과 동일하게 (현재년도 - 20년부터 현재년도까지) + // startYear가 있으면 해당 년도부터 현재년도까지, 내림차순으로 정렬 + const yearRange = startYear + ? Array.from({ length: currentYear - startYear + 1 }, (_, i) => currentYear - i) + : Array.from({ length: 21 }, (_, i) => currentYear - 20 + i); + const months = eachMonthOfInterval({ start: fullYearStart, end: yearEnd }); const getMonthButtonClass = (month: Date) => @@ -77,7 +83,7 @@ const selectedYear = parseInt(yearFromProp); onMonthChange(newDate, false); }} > - <SelectTrigger className="w-[120px] h-7"> + <SelectTrigger className="w-[140px] h-9 text-base"> <SelectValue placeholder="년도" /> </SelectTrigger> <SelectContent> From f7c8518e5786e23e1eded4634e5f50dd42dc60ee Mon Sep 17 00:00:00 2001 From: swcho <swcho@mind-one.co.kr> Date: Mon, 7 Jul 2025 16:20:32 +0900 Subject: [PATCH 099/108] =?UTF-8?q?feat:=20=EC=9D=B4=EC=A0=84=20Commit=20?= =?UTF-8?q?=EB=82=B4=EC=97=AD=20=EC=9E=AC=EC=9E=91=EC=97=85=20-=20UITable,?= =?UTF-8?q?=20UITable2=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=88=AB?= =?UTF-8?q?=EC=9E=90=20=EA=B0=80=EC=9A=B4=EB=8D=B0=20=EC=A0=95=EB=A0=AC=20?= =?UTF-8?q?-=20UiModal=20=EC=9D=B4=EC=A0=84=20=EB=8F=99=EC=9E=91=20?= =?UTF-8?q?=EB=B0=A9=EC=8B=9D=EC=9C=BC=EB=A1=9C=20=EC=82=AC=EC=9A=A9?= =?UTF-8?q?=ED=95=98=EB=8A=94=20props=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/ui/dialog_ui/index.tsx | 58 ++++----- .../src/ui/table/index-2.tsx | 6 +- .../src/ui/table/index.tsx | 116 +++++++++--------- 3 files changed, 89 insertions(+), 91 deletions(-) diff --git a/packages/common/business_components/src/ui/dialog_ui/index.tsx b/packages/common/business_components/src/ui/dialog_ui/index.tsx index bbb27d327..df7fffd3a 100644 --- a/packages/common/business_components/src/ui/dialog_ui/index.tsx +++ b/packages/common/business_components/src/ui/dialog_ui/index.tsx @@ -1,7 +1,7 @@ import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader } from "@common/components"; import { cn } from "../../lib/utils"; -type ModalType = { +type ModalType = { bodyClass: string; body: React.ReactNode; open?: boolean; @@ -10,43 +10,45 @@ type ModalType = { noFooterYn?: 'N' | 'Y'; headerTitle?: string; footerContent?: React.ReactNode; + useDirectRender?: boolean; } -export function UiModal({ - bodyClass, - body, - open, +export function UiModal({ + bodyClass, + body, + open, onOpenChange, noHeaderYn = 'N', noFooterYn = 'N', headerTitle, - footerContent + footerContent, + useDirectRender = false }: ModalType) { - + return ( <> - <Dialog open={open} onOpenChange={onOpenChange}> + <Dialog open={open} onOpenChange={onOpenChange}> <DialogContent className={bodyClass}> - {noHeaderYn === 'N' && ( - <DialogHeader> - <DialogDescription> - {headerTitle} - </DialogDescription> - </DialogHeader> - )} - {(open && body) ? - <div> {body}</div>:<></> - - - - } - {noFooterYn === 'N' && ( - <DialogFooter className="sm:justify-start"> - {footerContent} - </DialogFooter> - )} - </DialogContent> - </Dialog> + {noHeaderYn === 'N' && ( + <DialogHeader> + <DialogDescription> + {headerTitle} + </DialogDescription> + </DialogHeader> + )} + {(open && body) ? + useDirectRender + ? body + : <div>{body}</div> + : <></> + } + {noFooterYn === 'N' && ( + <DialogFooter className="sm:justify-start"> + {footerContent} + </DialogFooter> + )} + </DialogContent> + </Dialog> </> ); } diff --git a/packages/common/business_components/src/ui/table/index-2.tsx b/packages/common/business_components/src/ui/table/index-2.tsx index b6a9750b0..9756d2dfa 100644 --- a/packages/common/business_components/src/ui/table/index-2.tsx +++ b/packages/common/business_components/src/ui/table/index-2.tsx @@ -110,9 +110,7 @@ export function UiTable2({ ? 'text-right' : head.align === 'center' ? 'text-center' - : typeof el[head.id] === 'number' - ? 'text-right' - : 'text-center' + : 'text-center' } ${cellClick && idx === index ? '!font-[1000] cursor-pointer' : cellClick ? 'cursor-pointer' : ''} @@ -141,7 +139,7 @@ export function UiTable2({ </div> {/* 페이지네이션 하단 고정 */} - { ( + {( <div className="sticky left-0 right-0 bottom-0 bg-white z-20 mt-5"> <UiPageNations total={total} diff --git a/packages/common/business_components/src/ui/table/index.tsx b/packages/common/business_components/src/ui/table/index.tsx index cd25bc8db..fe720abc0 100644 --- a/packages/common/business_components/src/ui/table/index.tsx +++ b/packages/common/business_components/src/ui/table/index.tsx @@ -9,28 +9,28 @@ import { boolean } from "zod"; type UiTableProps = { headName: React.ReactNode | undefined; - tableData: { [key: string]:any}[]; + tableData: { [key: string]: any }[]; headlist: UiHeadProps[]; pageSize: number; total: number; - cellClick : ((index:number|undefined, headId:string)=>void )| undefined; + cellClick: ((index: number | undefined, headId: string) => void) | undefined; onPageChange: (page: number) => void; className?: string; resetPageNo?: boolean; children?: React.ReactNode | undefined; boldArray?: number[]; resetSelection?: boolean; - index?:number; + index?: number; } type UiHeadProps = { id: string; title: string; align?: string; - visible?:boolean; + visible?: boolean; } -export function UiTable({ tableData, headlist, pageSize, total, headName, children, cellClick, onPageChange, className = '', resetPageNo = false, boldArray = [], resetSelection = false, index=-1 }: UiTableProps) { +export function UiTable({ tableData, headlist, pageSize, total, headName, children, cellClick, onPageChange, className = '', resetPageNo = false, boldArray = [], resetSelection = false, index = -1 }: UiTableProps) { const [cur, setCur] = useState<number>(1); const [idx, setIdx] = useState<number>(index); const [isCheck, seIsCheck] = useState<boolean>(false); @@ -54,77 +54,75 @@ export function UiTable({ tableData, headlist, pageSize, total, headName, childr onPageChange(page); } - const handleCellClick = (index: number,headId:string )=> { + const handleCellClick = (index: number, headId: string) => { if (cellClick) { - cellClick(index,headId); + cellClick(index, headId); setIdx(index); } }; - + return ( <> {headName ? <div className="card-header"> <h3 className="text-2xl font-medium text-label">{headName}</h3> - </div> : <></>} + </div> : <></>} <div className={`card !m-[1.2rem] !p-0r ${className}`}> <Table variant="secondary"> <TableHeader className="font-bold"> {children ? children : <HeadMakeColSpan upHeadList={headlist} />} </TableHeader> <TableBody className='text-center'> - {tableData.length > 0 ? tableData?.map((el: { [key: string]: any}, index) => ( + {tableData.length > 0 ? tableData?.map((el: { [key: string]: any }, index) => ( <TableRow key={`row-${index}`}> {headlist - .filter((item) => item.visible != false) - .map((head) => ( - head.id && head.id.length > 1 && ( - boldArray.includes(index) - ? - <TableCell - className={`${head.align === 'right' ? 'text-right' : head.align === 'center' ? 'text-center' : typeof el[head.id] === 'number' ? 'text-right' : 'text-center'} ${ - cellClick && idx === index ? '!font-[1000] cursor-pointer' : - cellClick ? 'cursor-pointer' : '' - } !font-bold`} - onClick={() => handleCellClick(index, head.id)} - key={`cell-${head.id}-${index}`} - > - {typeof el[head.id] !== 'number'?String(el[head.id] ?? ''): String(el[head.id]).includes('.') - ? String(el[head.id]).split('.').map((part, index) => - index === 0 ? part.replace(/\B(?=(\d{3})+(?!\d))/g, ",") : part - ).join('.') - : String(el[head.id] ?? '').replace(/\B(?=(\d{3})+(?!\d))/g, ",")} - </TableCell> - : - <TableCell - className={`${head.align === 'right' ? 'text-right' : head.align === 'center' ? 'text-center' : typeof el[head.id] === 'number' ? 'text-right' : 'text-center'} ${ - cellClick && idx === index ? '!font-[1000] cursor-pointer' : - cellClick ? 'cursor-pointer' : '' - }`} - onClick={() => handleCellClick(index, head.id)} - key={`cell-${head.id}-${index}`} - > - {typeof el[head.id] === 'boolean' - ?<Checkbox - checked={el[head.id] === true} - onCheckedChange={(checked) => { - el[head.id]= checked; - }} - > - <Check className="h-6 w-6 text-primary" /> - </Checkbox> - : - typeof el[head.id] !== 'number'? - String(el[head.id] ?? ''): - String(el[head.id]).includes('.') - ? String(el[head.id]).split('.').map((part, index) => - index === 0 ? part.replace(/\B(?=(\d{3})+(?!\d))/g, ",") : part - ).join('.') - : - String(el[head.id] ?? '').replace(/\B(?=(\d{3})+(?!\d))/g, ",")} - </TableCell> - ) - ))} + .filter((item) => item.visible != false) + .map((head) => ( + head.id && head.id.length > 1 && ( + boldArray.includes(index) + ? + <TableCell + className={`${head.align === 'right' ? 'text-right' : head.align === 'center' ? 'text-center' : 'text-center'} ${cellClick && idx === index ? '!font-[1000] cursor-pointer' : + cellClick ? 'cursor-pointer' : '' + } !font-bold`} + onClick={() => handleCellClick(index, head.id)} + key={`cell-${head.id}-${index}`} + > + {typeof el[head.id] !== 'number' ? String(el[head.id] ?? '') : String(el[head.id]).includes('.') + ? String(el[head.id]).split('.').map((part, index) => + index === 0 ? part.replace(/\B(?=(\d{3})+(?!\d))/g, ",") : part + ).join('.') + : String(el[head.id] ?? '').replace(/\B(?=(\d{3})+(?!\d))/g, ",")} + </TableCell> + : + <TableCell + className={`${head.align === 'right' ? 'text-right' : head.align === 'center' ? 'text-center' : 'text-center'} ${cellClick && idx === index ? '!font-[1000] cursor-pointer' : + cellClick ? 'cursor-pointer' : '' + }`} + onClick={() => handleCellClick(index, head.id)} + key={`cell-${head.id}-${index}`} + > + {typeof el[head.id] === 'boolean' + ? <Checkbox + checked={el[head.id] === true} + onCheckedChange={(checked) => { + el[head.id] = checked; + }} + > + <Check className="h-6 w-6 text-primary" /> + </Checkbox> + : + typeof el[head.id] !== 'number' ? + String(el[head.id] ?? '') : + String(el[head.id]).includes('.') + ? String(el[head.id]).split('.').map((part, index) => + index === 0 ? part.replace(/\B(?=(\d{3})+(?!\d))/g, ",") : part + ).join('.') + : + String(el[head.id] ?? '').replace(/\B(?=(\d{3})+(?!\d))/g, ",")} + </TableCell> + ) + ))} </TableRow> )) : <></>} </TableBody> From 4503181684a3ec1feb20df7a1db7531d525fdf52 Mon Sep 17 00:00:00 2001 From: swcho <swcho@mind-one.co.kr> Date: Mon, 7 Jul 2025 19:48:35 +0900 Subject: [PATCH 100/108] =?UTF-8?q?fix:=20SELECTBOX=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20hidden=20=EB=A1=9C=EC=A7=81=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/ui/select/index.tsx | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/packages/common/business_components/src/ui/select/index.tsx b/packages/common/business_components/src/ui/select/index.tsx index c4e1264c4..8843aa04e 100644 --- a/packages/common/business_components/src/ui/select/index.tsx +++ b/packages/common/business_components/src/ui/select/index.tsx @@ -1,3 +1,5 @@ +// selectBox + import { Label } from "@common/components"; import React from "react"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./lib/selectBox"; @@ -21,15 +23,17 @@ const SelectBox = React.forwardRef< readOnly?: boolean // readOnly 속성 추가 } >(({ label, className, selectClass, selectArray, onSelectValue, value, readOnly = false }, ref)=>{ - // 첫 번째 항목을 placeholder로 사용하고 드롭다운 목록에서는 제외 - const firstItem = selectArray[0]; - const placeHolder = firstItem?.text ?? ""; + // hidden이 true인 첫 번째 항목을 placeholder로 사용 + const hiddenItem = selectArray.find(item => item.hidden); + const placeHolder = hiddenItem?.text ?? "선택"; - // 첫 번째 항목(placeholder)을 제외한 나머지 항목만 드롭다운에 표시 - const visibleItems = selectArray.filter((_, index) => index !== 0); + // hidden이 true인 항목만 드롭다운에서 제외 + const visibleItems = selectArray.filter(item => !item.hidden); - // 현재 값이 첫 번째 항목(placeholder)인 경우 undefined로 설정하여 placeholder가 표시되도록 함 - const currentValue = value !== undefined && String(value) === String(firstItem?.val) + // 현재 값이 hidden 항목이거나 빈 값인 경우 undefined로 설정하여 placeholder가 표시되도록 함 + const currentValue = value !== undefined && + hiddenItem && + (String(value) === String(hiddenItem.val) || value === "" || value === "0" || value === 0) ? undefined : (value !== undefined ? String(value) : undefined); From 892c3d1eee06f633536454b1d314a0a3cfb57277 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=B5=9C=EC=A7=80=EC=B2=A0?= <jcchoi@onasys.co.kr> Date: Mon, 7 Jul 2025 20:29:30 +0900 Subject: [PATCH 101/108] =?UTF-8?q?LoginLog=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/cs/app/(with-base-layout)/loginlog/page.tsx | 2 ++ apps/cs/src/pages/loginlog/index.ts | 1 + 2 files changed, 3 insertions(+) create mode 100644 apps/cs/app/(with-base-layout)/loginlog/page.tsx create mode 100644 apps/cs/src/pages/loginlog/index.ts diff --git a/apps/cs/app/(with-base-layout)/loginlog/page.tsx b/apps/cs/app/(with-base-layout)/loginlog/page.tsx new file mode 100644 index 000000000..960f03e05 --- /dev/null +++ b/apps/cs/app/(with-base-layout)/loginlog/page.tsx @@ -0,0 +1,2 @@ +import { LoginlogPage } from "@/pages/loginlog"; +export default LoginlogPage; diff --git a/apps/cs/src/pages/loginlog/index.ts b/apps/cs/src/pages/loginlog/index.ts new file mode 100644 index 000000000..4e5e70ea8 --- /dev/null +++ b/apps/cs/src/pages/loginlog/index.ts @@ -0,0 +1 @@ +export { LoginlogPage } from "./ui/loginlog-page"; From 2dd80be17fd19419f2748d6dd54634e91eb1a576 Mon Sep 17 00:00:00 2001 From: swcho <swcho@mind-one.co.kr> Date: Mon, 7 Jul 2025 20:53:37 +0900 Subject: [PATCH 102/108] =?UTF-8?q?feat:=20-=20=ED=8C=8C=EC=9D=BC=20?= =?UTF-8?q?=ED=99=95=EC=9E=A5=EC=9E=90=20=EA=B2=80=EC=A6=9D=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EA=B5=AC=ED=98=84=20=EB=B0=8F=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9=20-=20=ED=99=98=EA=B2=BD=EC=B2=AD,=20?= =?UTF-8?q?=EC=8B=9C=EB=8F=84,=20=EC=8B=9C=EA=B5=B0=EA=B5=AC,=20=ED=95=98?= =?UTF-8?q?=EC=88=98=EC=B2=98=EB=A6=AC=EC=9E=A5=20=EC=9E=85=EB=A0=A5=20?= =?UTF-8?q?=EA=B2=80=EC=A6=9D=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84=20=EB=B0=8F=20=EC=A0=81=EC=9A=A9=20-=20RDII?= =?UTF-8?q?=20=EB=B6=84=EC=84=9D=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=ED=95=98?= =?UTF-8?q?=EC=88=98=EC=B2=98=EB=A6=AC=EC=9E=A5=20=EC=84=A0=ED=83=9D=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EC=97=90=EB=9F=AC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../business_components/src/ui/map/Gis.js | 50 ++++++++++++++----- 1 file changed, 38 insertions(+), 12 deletions(-) diff --git a/packages/common/business_components/src/ui/map/Gis.js b/packages/common/business_components/src/ui/map/Gis.js index 617f17165..ef5c21d5b 100644 --- a/packages/common/business_components/src/ui/map/Gis.js +++ b/packages/common/business_components/src/ui/map/Gis.js @@ -20,7 +20,22 @@ import GisApp from './lib/gis.core'; export const GisContext = createContext({ gisCore: null, gisMap: null, treeData: null, checkedLayerIds: null }); export const useGisContext = () => useContext(GisContext); -function Gis({ isOpen, gisMaps, isSearchModalOpen, treeData = [], isSearch = false, isMiniMap = false, setIsOpen, setFeatureData, featureData, searchView: SearchView, detailView: DetailView, onFeatureSelect, setIsSearchModalOpen }) { +function Gis({ + isOpen, + gisMaps, + isSearchModalOpen, + treeData = [], + isSearch = false, + isMiniMap = false, + setIsOpen, + setFeatureData, + featureData, + searchView: SearchView, + detailView: DetailView, + onFeatureSelect, + setIsSearchModalOpen, + onCheckedLayersChange +}) { const mapRef = useRef(); const [mapObject, setMapObject] = useState({}); const [tabVisible, setTabVisible] = useState(true); @@ -146,7 +161,7 @@ function Gis({ isOpen, gisMaps, isSearchModalOpen, treeData = [], isSearch = fal gisMaps.current = map - //미니맵 + // 미니맵 추가 if (isMiniMap) { const overviewMap = new OverviewMap({ layers: [GisApp.Module.core.baseMiniMapLayers['base']], @@ -340,29 +355,40 @@ function Gis({ isOpen, gisMaps, isSearchModalOpen, treeData = [], isSearch = fal <GisContext.Provider value={{ gisCore, gisMap: mapInstanceRef.current, treeData, checkedLayerIds }}> <div className="h-[calc(100vh-10rem)] flex relative"> <div ref={mapRef} className="gis-map flex-1 relative" style={{ height: '100%', padding: 0 }} /> - <GisTree visible={tabVisible} setVisible={setTabVisible} /> + <GisTree + visible={tabVisible} + setVisible={setTabVisible} + onCheckedChange={handleCheckedLayersChange} + onLayerIconClick={(nodeNm) => { + setLayerNm(nodeNm); setIsOpen(true); const dummyFeature = { + getProperties: () => ({}), // 최소 구조 + }; + onFeatureSelect(dummyFeature); + }} + /> {/* <GisContent onFeatureSelect={handleFeatureSelect} isSearch={isSearch} isSearchModalOpen={isSearchModalOpen} setIsSearchModalOpen={setIsSearchModalOpen} /> */} - {/* isSearchModalOpen이 true면 visible=false로 팝업 숨김 */} - {isOpen && featureData && !isSearchModalOpen && ( + {/* DetailView가 존재할 때만 렌더링 isSearchModalOpen이 true면 visible=false로 팝업 숨김 */} + {isOpen && !isSearchModalOpen && ( <DetailView featureData={featureData || { getProperties: () => ({}) }} seletectedLayerNm={layerNm} onClose={closePopup} // 닫기 콜백 전달 /> )} - {SearchView &&( - <SearchView - open={isSearchModalOpen} - onOpenChange={setIsSearchModalOpen} - onFeatureSelect={onFeatureSelect} - /> + {/* SearchView가 존재할 때만 렌더링 */} + {SearchView && ( + <SearchView + open={isSearchModalOpen} + onOpenChange={setIsSearchModalOpen} + onFeatureSelect={handleFeatureSelect} + /> )} <GisLonLat /> <GisToolBar /> <GisMapToggle /> - <GisSelect onFeatureSelect={onFeatureSelect} /> + <GisSelect onFeatureSelect={handleFeatureSelect} /> </div> {isSearch && ( From b396a14a5d1579800acabdaa724eff44f5db8b75 Mon Sep 17 00:00:00 2001 From: swcho <swcho@mind-one.co.kr> Date: Mon, 7 Jul 2025 20:59:07 +0900 Subject: [PATCH 103/108] =?UTF-8?q?feat:=20stash=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../popupComponent/chart/aplcnYms.ts | 8 +- .../popupComponent/chart/infilChart.tsx | 113 ++++++++++-------- apps/sn/app/components/lib/utils/date.ts | 20 ++++ .../app/components/ui/operFileUploadForm.tsx | 8 +- 4 files changed, 89 insertions(+), 60 deletions(-) diff --git a/apps/sn/app/(map-layout)/map/components/popupComponent/chart/aplcnYms.ts b/apps/sn/app/(map-layout)/map/components/popupComponent/chart/aplcnYms.ts index 26d597abf..5ac5ee0a8 100644 --- a/apps/sn/app/(map-layout)/map/components/popupComponent/chart/aplcnYms.ts +++ b/apps/sn/app/(map-layout)/map/components/popupComponent/chart/aplcnYms.ts @@ -71,7 +71,7 @@ export const getDcTree = async () => { return result; }; -const getOptionsAplcnYm = (yyyymms: string[]): { text: string; val: string }[] => { +const getOptionsAplcnYm = (yyyymms: string[]): { text: string; val: string, hidden?: boolean }[] => { let yyyymms1 = yyyymms.map(dateStr => { const year = dateStr.slice(0, 4); const month = dateStr.slice(4, 6); @@ -80,7 +80,7 @@ const getOptionsAplcnYm = (yyyymms: string[]): { text: string; val: string }[] = val: dateStr }; }); - yyyymms1.unshift({ val: '0', text: '선택' }); + yyyymms1.unshift({ val: '0', text: '선택', hidden: true }); return yyyymms1; }; const selectAplcnYms = async(gubun:string) => { // 상수사용월 @@ -109,12 +109,12 @@ export const useAplcnYms = (gubun:string) => { return data; }; -export const getOptionsInftwtAnlsInfo = (anlsArr: TInftwtAnls[]): { text: string; val: number }[] => { +export const getOptionsInftwtAnlsInfo = (anlsArr: TInftwtAnls[]): { text: string; val: number, hidden?: boolean }[] => { const options = anlsArr.map(anls => ({ text: anls.anlsInfo, val: anls.inftwtAnlsSn, })); - options.unshift({ val: 0, text: '선택' }); + options.unshift({ val: 0, text: '선택', hidden: true }); return options; }; diff --git a/apps/sn/app/(map-layout)/map/components/popupComponent/chart/infilChart.tsx b/apps/sn/app/(map-layout)/map/components/popupComponent/chart/infilChart.tsx index ac89a9403..cbaa5dafb 100644 --- a/apps/sn/app/(map-layout)/map/components/popupComponent/chart/infilChart.tsx +++ b/apps/sn/app/(map-layout)/map/components/popupComponent/chart/infilChart.tsx @@ -11,25 +11,25 @@ import { useInfilChartModalStore } from '../chartModalStore'; // 모달 상태 type ModalOpenProp = { pplMngNo?: string, // 관로관리번호 } -type DataType = {label: string; val: number}; +type DataType = { label: string; val: number }; const InfilChart = ({ pplMngNo }: ModalOpenProp) => { const { isOpen: isModalOpen, setIsOpen: setIsModalOpen } = useInfilChartModalStore(); // 모달 상태 관리 스토어 const [popupOpen, setPopupOpen] = React.useState(false); // 조회조건팝업 const buttonRef = React.useRef<HTMLButtonElement>(null); // 버튼 참조 const [popupPosition, setPopupPosition] = useState<{ top: number; left: number }>({ top: 0, left: 0 }); // 팝업 위치 - const [aplcnYm, setAplcnYm] = useState(""); + const [selectedAplcnYm, setSelectedAplcnYm] = useState(""); const [inftwtAnlss, setInftwtAnlss] = useState<TInftwtAnls[]>([]); - const [inftwtAnlsInfos, setInftwtAnlsInfos] = useState<any[]>([{ val: 0, text: '선택' }]); // 침입수분석정보 options + const [inftwtAnlsInfos, setInftwtAnlsInfos] = useState<any[]>([{ val: 0, text: '선택', hidden: true }]); // 침입수분석정보 options const [inftwtAnls, setInftwtAnls] = useState<TInftwtAnls | null>(null); // 침입수분석정보 - const [inftwtAnlsSn, setInftwtAnlsSn] = useState<number>(0); // 침입수분석 정보 선택시 사용 + const [selectedInftwtAnls, setSelectedInftwtAnls] = useState<number>(0); // 침입수분석 정보 선택시 사용 const [chartData, setChartData] = useState<{ chartData1: DataType[]; chartData2: DataType[] }>({ chartData1: [], chartData2: [], }); const [inftwtQty, setInftwtQty] = useState<number>(0); // 침입수량 const aplcnYms = fn.useAplcnYms('inftwtAnlsRslt'); // 상수사용월 데이터 가져오기 - - pplMngNo = "L"+pplMngNo; // inp파일에 하수관로와 맨홀의 번호가 같으면 분석 오류가 발행하여 + + pplMngNo = "L" + pplMngNo; // inp파일에 하수관로와 맨홀의 번호가 같으면 분석 오류가 발행하여 const openPopup = () => { if (buttonRef.current) { const rect = buttonRef.current.getBoundingClientRect(); // 버튼 위치 계산 @@ -50,60 +50,73 @@ const InfilChart = ({ pplMngNo }: ModalOpenProp) => { useEffect(() => { if (isModalOpen === false) { setPopupOpen(true); // 모달이 닫힐 때 조회조건팝업은 열어두기 - setAplcnYm(""); // 상수사용월 초기화 + setSelectedAplcnYm(""); // 상수사용월 초기화 setInftwtAnlss([]); // 침입수분석 초기화 - setInftwtAnlsInfos([{ val: 0, text: '선택' }]); // 침입수분석정보 초기화 + setInftwtAnlsInfos([{ val: 0, text: '선택', hidden: true }]); // 침입수분석정보 초기화 setInftwtAnls(null); // 침입수분석정보 초기화 - setInftwtAnlsSn(0); // 침입수분석 일련번호 초기화 - setChartData({chartData1: [], chartData2: [],}); // 차트 데이터 초기화 + setSelectedInftwtAnls(0); // 침입수분석 일련번호 초기화 + setChartData({ chartData1: [], chartData2: [], }); // 차트 데이터 초기화 setInftwtQty(0); // 침입수량 초기화 } }, [isModalOpen]); - // 상수사용월 변경시 분석 정보 리스트 가져오기 - useEffect(() => { - // aplcnYm이 비어있거나 "0"이면 API 호출하지 않음 - if (!aplcnYm || aplcnYm === "0" || aplcnYm === "") { - setInftwtAnlsSn(0); // 초기화 - setInftwtAnlsInfos([{ val: 0, text: '선택' }]); + // 상수사용월 변경 핸들러 + const handleAplcnYmChange = async (val: string | number) => { + const selectedValue = String(val); + console.log("선택된 상수사용월:", selectedValue); + + setSelectedAplcnYm(selectedValue); + + // 즉시 관련 상태 초기화 + setInftwtAnls(null); + + // 빈 값이면 초기화만 하고 종료 + if (!selectedValue || selectedValue === "0" || selectedValue === "") { + setInftwtAnlsInfos([{ val: 0, text: '선택', hidden: true }]); return; } - const dcData = fn.getDcTree(); // 자료관 데이터 가져오기 - fn.selectInftwtAnls(aplcnYm) - .then((data) => { - setInftwtAnlss(data ?? []); - setInftwtAnlsInfos(fn.getOptionsInftwtAnlsInfo(data)); - setInftwtAnls(null); // 오수전환율 이후 초기화 - setInftwtAnlsSn(0); // 초기화 - }) - .catch((error) => { - console.error("selectInftwtAnls 에러:", error); - setInftwtAnlss([]); - setInftwtAnlsInfos([{ val: 0, text: '선택' }]); - }); - - }, [aplcnYm]); - // 값 표시 - useEffect(() => { + // API 호출 + try { + const dcData = fn.getDcTree(); + const data = await fn.selectInftwtAnls(selectedValue); + setInftwtAnlss(data ?? []); + setInftwtAnlsInfos(fn.getOptionsInftwtAnlsInfo(data)); + } catch (error) { + console.error("selectInftwtAnls 에러:", error); + setInftwtAnlss([]); + setInftwtAnlsInfos([{ val: 0, text: '선택', hidden: true }]); + } + setSelectedInftwtAnls(0); + }; + + // 분석정보 변경 핸들러 + const handleInftwtAnlsSnChange = (val: string | number) => { + const selectedValue = Number(val); + setSelectedInftwtAnls(selectedValue); + // 선택된 침입수분석 정보에 대한 처리 로직 - if (inftwtAnlsSn === 0) return; - const selectedAnls = inftwtAnlss.find(anls => anls.inftwtAnlsSn === inftwtAnlsSn); + if (selectedValue === 0) { + setInftwtAnls(null); + return; + } + + const selectedAnls = inftwtAnlss.find(anls => anls.inftwtAnlsSn === selectedValue); if (selectedAnls) { setInftwtAnls(selectedAnls); } - }, [inftwtAnlsSn]); + }; const closePopup = () => { // 팝업 닫기 - - fn.selectInftwtAnlsRslt(inftwtAnlsSn, pplMngNo).then((data) => {// 침입수분석 결과 조회 + + fn.selectInftwtAnlsRslt(selectedInftwtAnls, pplMngNo).then((data) => {// 침입수분석 결과 조회 const rsltData = fn.getChartDataInftwtAnlsRslt(data ?? []); setChartData(prev => ({ ...prev, chartData1: rsltData // 침입수분석결과 })); }); - fn.selectOilq(inftwtAnlsSn, pplMngNo).then((data) => {// 유량 조회 + fn.selectOilq(selectedInftwtAnls, pplMngNo).then((data) => {// 유량 조회 const rsltData = fn.getChartDataOilq(data ?? []); setChartData(prev => ({ ...prev, @@ -116,7 +129,7 @@ const InfilChart = ({ pplMngNo }: ModalOpenProp) => { chartData2: [] // 유량 데이터 초기화 })); }); - fn.selectPplInftwtQty(inftwtAnlsSn, pplMngNo).then((data) => {// 침입수량 조회 + fn.selectPplInftwtQty(selectedInftwtAnls, pplMngNo).then((data) => {// 침입수량 조회 if (data > 0) { setInftwtQty(data); } @@ -138,7 +151,7 @@ const InfilChart = ({ pplMngNo }: ModalOpenProp) => { <span className="text-2xl text-red-600 font-bold">상수사용량 적용연월</span> <div className="flex items-center mt-2"> <span className="text-black text-xl font-bold"> - {(aplcnYm)? moment(aplcnYm, "YYYYMM").format("YYYY-MM"):""} + {(selectedAplcnYm) ? moment(selectedAplcnYm, "YYYYMM").format("YYYY-MM") : ""} </span> </div> </div> @@ -153,8 +166,8 @@ const InfilChart = ({ pplMngNo }: ModalOpenProp) => { </div> {/* 차트 */} <div className="bg-white text-3xl"> - <MainChart chartConfig={chartConfig} chartForm={chartForm} chartData={chartData} chartTitle={'관로유량'} - yFieldText2="시간" yFieldText="유량(m³/h)"/> + <MainChart chartConfig={chartConfig} chartForm={chartForm} chartData={chartData} chartTitle={'관로유량'} + yFieldText2="시간" yFieldText="유량(m³/h)" /> </div> </div> {popupOpen && ( @@ -175,10 +188,8 @@ const InfilChart = ({ pplMngNo }: ModalOpenProp) => { selectClass="rounded-lg bg-white" label='' selectArray={aplcnYms} - value={aplcnYm} - onSelectValue={(val) => {console.log("선택된 상수사용월:", val); - setAplcnYm(String(val)); - }} + value={selectedAplcnYm} + onSelectValue={handleAplcnYmChange} /> </div> <div className='flex items-center justify-between'> @@ -188,10 +199,8 @@ const InfilChart = ({ pplMngNo }: ModalOpenProp) => { selectClass="rounded-lg bg-white" label='' selectArray={inftwtAnlsInfos} - value={inftwtAnlsSn} - onSelectValue={(val) => { - setInftwtAnlsSn(Number(val)); - }} + value={selectedInftwtAnls} + onSelectValue={handleInftwtAnlsSnChange} /> </div> <div className='flex items-center justify-between'> @@ -264,7 +273,7 @@ const InfilChart = ({ pplMngNo }: ModalOpenProp) => { z-[50] `} body={renderBody()} - noFooterYn = 'Y' + noFooterYn='Y' /> ) } diff --git a/apps/sn/app/components/lib/utils/date.ts b/apps/sn/app/components/lib/utils/date.ts index fe717301f..f26944ce7 100644 --- a/apps/sn/app/components/lib/utils/date.ts +++ b/apps/sn/app/components/lib/utils/date.ts @@ -12,6 +12,26 @@ export const yearOptions = Array.from({ length: 10 }, (_, i) => { return { text: `${year}년`, val: year.toString() }; }); +// 현재 날짜를 기준으로 현재 분기까지의 분기 옵션 생성 +export const getQuarterOptions = (): { text: string; val: string }[] => { + const currentDate = new Date(); + const currentMonth = currentDate.getMonth() + 1; // 0부터 시작하므로 +1 + + // 현재 월을 기준으로 현재 분기 계산 + const currentQuarter = Math.ceil(currentMonth / 3); + + // 현재 분기까지의 분기 옵션 생성 + const quarterOptions = []; + for (let i = 1; i <= currentQuarter; i++) { + quarterOptions.push({ + text: `${i}분기`, + val: i.toString() + }); + } + + return quarterOptions; +}; + // ["202506", "202505", "202502", "202405"] 을 SelectBox의 옵션으로 변환 export const convertToTextValArray = (yyyymms: string[]): { text: string; val: string }[] => { return yyyymms.map(dateStr => { diff --git a/apps/sn/app/components/ui/operFileUploadForm.tsx b/apps/sn/app/components/ui/operFileUploadForm.tsx index 42d824e1b..1e7ed727a 100644 --- a/apps/sn/app/components/ui/operFileUploadForm.tsx +++ b/apps/sn/app/components/ui/operFileUploadForm.tsx @@ -2,16 +2,16 @@ import { SelectBox } from '@common/business_components/ui'; import React, { useState } from 'react' import FileUploadForm from './fileUploadForm'; -import { yearOptions } from '@/components/lib/utils'; +import { yearOptions, getQuarterOptions } from '@/components/lib/utils'; const operFileUploadForm = ({ title, onSubmit }: { title: string, onSubmit: (files: string, year: string, quarter: string, code: string, id: string) => void }) => { const [atflCd, setAtflCd] = useState<string | null>(null); const [atflId, setAtflId] = useState<string | null>(null); + const quaterOptions = getQuarterOptions(); - const quater = [{ text: '1분기', val: '1' }, { text: '2분기', val: '2' }, { text: '3분기', val: '3' }, { text: '4분기', val: '4' }] const [selectedYear, setSelectedYear] = useState<string>(yearOptions[0]?.val || ''); - const [selectedQuarter, setSelectedQuarter] = useState<string>(quater[0]?.val || ''); + const [selectedQuarter, setSelectedQuarter] = useState<string>(quaterOptions[0]?.val || ''); return ( <div> @@ -31,7 +31,7 @@ const operFileUploadForm = ({ title, onSubmit }: { title: string, onSubmit: (fil className="w-50" selectClass="rounded-lg bg-white" label="분기" - selectArray={quater} + selectArray={quaterOptions} onSelectValue={(value: string | number) => setSelectedQuarter(String(value))} /> <FileUploadForm From 3c73ba71258eac9c83e92e2be35a9425f08dc12d Mon Sep 17 00:00:00 2001 From: swcho <swcho@mind-one.co.kr> Date: Mon, 7 Jul 2025 21:12:57 +0900 Subject: [PATCH 104/108] =?UTF-8?q?feat:=20=EB=8F=84=EC=8B=9C=EC=B9=A8?= =?UTF-8?q?=EC=88=98=EC=98=88=EB=B0=A9=EC=82=AC=EC=97=85,=20=EB=85=B8?= =?UTF-8?q?=ED=9B=84=ED=95=98=EC=88=98=EA=B4=80=EB=A1=9C=20=EC=A0=95?= =?UTF-8?q?=EB=B9=84=EC=82=AC=EC=97=85=20=ED=8E=98=EC=9D=B4=EC=A7=80=20?= =?UTF-8?q?=EB=B6=84=EA=B8=B0=20=EC=A1=B0=EA=B1=B4=20=EC=B2=B4=ED=81=AC=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/sn/app/components/lib/utils/date.ts | 38 ++++++++++++------- .../app/components/ui/operFileUploadForm.tsx | 26 ++++++++++--- 2 files changed, 46 insertions(+), 18 deletions(-) diff --git a/apps/sn/app/components/lib/utils/date.ts b/apps/sn/app/components/lib/utils/date.ts index f26944ce7..9c9106ec2 100644 --- a/apps/sn/app/components/lib/utils/date.ts +++ b/apps/sn/app/components/lib/utils/date.ts @@ -13,23 +13,35 @@ export const yearOptions = Array.from({ length: 10 }, (_, i) => { }); // 현재 날짜를 기준으로 현재 분기까지의 분기 옵션 생성 -export const getQuarterOptions = (): { text: string; val: string }[] => { +export const getQuarterOptions = (selectedYear?: string): { text: string; val: string }[] => { const currentDate = new Date(); + const currentYear = currentDate.getFullYear().toString(); const currentMonth = currentDate.getMonth() + 1; // 0부터 시작하므로 +1 - // 현재 월을 기준으로 현재 분기 계산 - const currentQuarter = Math.ceil(currentMonth / 3); - - // 현재 분기까지의 분기 옵션 생성 - const quarterOptions = []; - for (let i = 1; i <= currentQuarter; i++) { - quarterOptions.push({ - text: `${i}분기`, - val: i.toString() - }); + // 현재 년도인 경우에만 현재 날짜까지의 분기 표시 + if (selectedYear === currentYear) { + // 현재 월을 기준으로 현재 분기 계산 + const currentQuarter = Math.ceil(currentMonth / 3); + + // 현재 분기까지의 분기 옵션 생성 + const quarterOptions = []; + for (let i = 1; i <= currentQuarter; i++) { + quarterOptions.push({ + text: `${i}분기`, + val: i.toString() + }); + } + + return quarterOptions; + } else { + // 이전 년도인 경우 모든 분기 표시 + return [ + { text: "1분기", val: "1" }, + { text: "2분기", val: "2" }, + { text: "3분기", val: "3" }, + { text: "4분기", val: "4" } + ]; } - - return quarterOptions; }; // ["202506", "202505", "202502", "202405"] 을 SelectBox의 옵션으로 변환 diff --git a/apps/sn/app/components/ui/operFileUploadForm.tsx b/apps/sn/app/components/ui/operFileUploadForm.tsx index 1e7ed727a..3c37673d2 100644 --- a/apps/sn/app/components/ui/operFileUploadForm.tsx +++ b/apps/sn/app/components/ui/operFileUploadForm.tsx @@ -1,6 +1,6 @@ "use client" import { SelectBox } from '@common/business_components/ui'; -import React, { useState } from 'react' +import React, { useState, useEffect } from 'react' import FileUploadForm from './fileUploadForm'; import { yearOptions, getQuarterOptions } from '@/components/lib/utils'; @@ -8,11 +8,24 @@ const operFileUploadForm = ({ title, onSubmit }: { title: string, onSubmit: (fil const [atflCd, setAtflCd] = useState<string | null>(null); const [atflId, setAtflId] = useState<string | null>(null); - const quaterOptions = getQuarterOptions(); - const [selectedYear, setSelectedYear] = useState<string>(yearOptions[0]?.val || ''); + const [quaterOptions, setQuaterOptions] = useState(() => getQuarterOptions(selectedYear)); const [selectedQuarter, setSelectedQuarter] = useState<string>(quaterOptions[0]?.val || ''); + // 년도가 변경될 때 분기 옵션 업데이트 + useEffect(() => { + const updatedQuarterOptions = getQuarterOptions(selectedYear); + setQuaterOptions(updatedQuarterOptions); + // 분기 옵션이 변경되면 첫 번째 옵션으로 자동 선택 + setSelectedQuarter(updatedQuarterOptions[0]?.val || ''); + }, [selectedYear]); + + // 년도 변경 핸들러 + const handleYearChange = (value: string | number) => { + const yearValue = String(value); + setSelectedYear(yearValue); + }; + return ( <div> <div className='card-header'> @@ -26,13 +39,16 @@ const operFileUploadForm = ({ title, onSubmit }: { title: string, onSubmit: (fil selectClass="rounded-lg bg-white" label="년도" selectArray={yearOptions} - onSelectValue={(value: string | number) => setSelectedYear(String(value))} - /><SelectBox + onSelectValue={handleYearChange} + value={selectedYear} + /> + <SelectBox className="w-50" selectClass="rounded-lg bg-white" label="분기" selectArray={quaterOptions} onSelectValue={(value: string | number) => setSelectedQuarter(String(value))} + value={selectedQuarter} /> <FileUploadForm onSubmit={(files, year, quarter, code, id) => { From 2592a52c103a6b6bc465aa1d807dcb2171b874c0 Mon Sep 17 00:00:00 2001 From: swcho <swcho@mind-one.co.kr> Date: Tue, 8 Jul 2025 09:31:20 +0900 Subject: [PATCH 105/108] Merge remote-tracking branch 'origin/iipbs/sn/dev' into iipbs/sn/ii --- pnpm-lock.yaml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 142279c5e..555292098 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -580,6 +580,9 @@ importers: tailwind-merge: specifier: ^3.0.1 version: 3.0.2 + tailwindcss-animate: + specifier: ^1.0.7 + version: 1.0.7(tailwindcss@3.4.17) xlsx: specifier: ^0.18.5 version: 0.18.5 @@ -2533,7 +2536,7 @@ packages: dnd-multi-backend: 8.1.2(dnd-core@16.0.1) framer-motion: 11.18.2(react-dom@18.3.1)(react@18.3.1) react: 18.3.1 - react-dnd: 16.0.1(@types/node@22.13.5)(@types/react@18.3.18)(react@18.3.1) + react-dnd: 16.0.1(@types/node@22.13.9)(@types/react@18.3.18)(react@18.3.1) react-dnd-html5-backend: 16.0.1 react-dnd-touch-backend: 16.0.1 react-dom: 18.3.1(react@18.3.1) From c2ee97901ae584d7c181eb58aada3b9f9e4e03ad Mon Sep 17 00:00:00 2001 From: ljpark <mind-one005@mind-one.co.kr> Date: Tue, 8 Jul 2025 11:26:30 +0900 Subject: [PATCH 106/108] =?UTF-8?q?=EA=B0=95=EC=9A=B0=EB=9F=89=EB=8B=B9=20?= =?UTF-8?q?rdii?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../map/components/popupComponent/chart/rdiiChart.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/sn/app/(map-layout)/map/components/popupComponent/chart/rdiiChart.tsx b/apps/sn/app/(map-layout)/map/components/popupComponent/chart/rdiiChart.tsx index 22f0d8876..840281988 100644 --- a/apps/sn/app/(map-layout)/map/components/popupComponent/chart/rdiiChart.tsx +++ b/apps/sn/app/(map-layout)/map/components/popupComponent/chart/rdiiChart.tsx @@ -106,7 +106,7 @@ const RdiiChart = ({ pplMngNo }: ModalOpenProp) => { }); fn.selectPplRdiiQty(rainInfwtAnlsSn, pplMngNo).then((data) => {// 강우유입수량 조회 setRdiiQty(Math.round(data.rdiih*24)); // cmd: m³/d 단위로 변환 - setTotalVolume(Math.round(data.rdiih * data.sustaintime)); // 총체적 m³/mm + setTotalVolume(Math.round(data.rdiih * data.sustaintime / data.raintotal)); // 총체적 m³/mm }); setPopupOpen(false); }; From 208c2fd32173030ccd6d95f491318c5c1d15aacf Mon Sep 17 00:00:00 2001 From: ljpark <mind-one005@mind-one.co.kr> Date: Tue, 8 Jul 2025 12:41:42 +0900 Subject: [PATCH 107/108] =?UTF-8?q?=EA=B0=95=EC=9A=B0=EC=9C=A0=EC=9E=85?= =?UTF-8?q?=EC=88=98=20=EB=B6=84=EC=84=9D=EC=A0=95=EB=B3=B4=20=EC=84=A0?= =?UTF-8?q?=ED=83=9D=20=EB=AA=A9=EB=A1=9D=EC=97=90=EC=84=9C=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../map/components/popupComponent/chart/aplcnYms.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/sn/app/(map-layout)/map/components/popupComponent/chart/aplcnYms.ts b/apps/sn/app/(map-layout)/map/components/popupComponent/chart/aplcnYms.ts index 5ac5ee0a8..9e7f3fe50 100644 --- a/apps/sn/app/(map-layout)/map/components/popupComponent/chart/aplcnYms.ts +++ b/apps/sn/app/(map-layout)/map/components/popupComponent/chart/aplcnYms.ts @@ -72,7 +72,7 @@ export const getDcTree = async () => { }; const getOptionsAplcnYm = (yyyymms: string[]): { text: string; val: string, hidden?: boolean }[] => { - let yyyymms1 = yyyymms.map(dateStr => { + const yyyymms1 = yyyymms.map(dateStr => { const year = dateStr.slice(0, 4); const month = dateStr.slice(4, 6); return { @@ -208,12 +208,12 @@ export const selectPplRdiiQty = async(rainInfwtAnlsSn:number, pplMngNo:string) = } }; -export const getOptionsRdiiAnlsInfo = (anlsArr: TRdiiAnls[]): { text: string; val: number }[] => { +export const getOptionsRdiiAnlsInfo = (anlsArr: TRdiiAnls[]): { text: string; val: number, hidden?: boolean }[] => { const options = anlsArr.map(anls => ({ text: anls.anlsInfo, val: anls.rainInfwtAnlsSn, })); - options.unshift({ val: 0, text: '선택' }); + options.unshift({ val: 0, text: '선택', hidden: true }); return options; }; From a6fb951aa6d1d8203fab6f8dabe67aef89171763 Mon Sep 17 00:00:00 2001 From: ljpark <mind-one005@mind-one.co.kr> Date: Tue, 8 Jul 2025 12:58:20 +0900 Subject: [PATCH 108/108] chart height --- .../map/components/popupComponent/chart/infilChart.tsx | 2 +- .../map/components/popupComponent/chart/rdiiChart.tsx | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/sn/app/(map-layout)/map/components/popupComponent/chart/infilChart.tsx b/apps/sn/app/(map-layout)/map/components/popupComponent/chart/infilChart.tsx index df9ac29a0..fe08f34f0 100644 --- a/apps/sn/app/(map-layout)/map/components/popupComponent/chart/infilChart.tsx +++ b/apps/sn/app/(map-layout)/map/components/popupComponent/chart/infilChart.tsx @@ -265,7 +265,7 @@ const InfilChart = ({ pplMngNo }: ModalOpenProp) => { onOpenChange={setIsModalOpen} headerTitle="" bodyClass={` - h-[50vh] + min-h-[50vh] w-[50vw] max-w-none max-h-none diff --git a/apps/sn/app/(map-layout)/map/components/popupComponent/chart/rdiiChart.tsx b/apps/sn/app/(map-layout)/map/components/popupComponent/chart/rdiiChart.tsx index 840281988..33b65ace0 100644 --- a/apps/sn/app/(map-layout)/map/components/popupComponent/chart/rdiiChart.tsx +++ b/apps/sn/app/(map-layout)/map/components/popupComponent/chart/rdiiChart.tsx @@ -106,7 +106,9 @@ const RdiiChart = ({ pplMngNo }: ModalOpenProp) => { }); fn.selectPplRdiiQty(rainInfwtAnlsSn, pplMngNo).then((data) => {// 강우유입수량 조회 setRdiiQty(Math.round(data.rdiih*24)); // cmd: m³/d 단위로 변환 - setTotalVolume(Math.round(data.rdiih * data.sustaintime / data.raintotal)); // 총체적 m³/mm + if (data.raintotal && data.rainTotal !== 0) { + setTotalVolume(Math.round(data.rdiih * data.sustaintime / data.raintotal)); // 총체적 m³/mm + } }); setPopupOpen(false); }; @@ -248,7 +250,7 @@ const RdiiChart = ({ pplMngNo }: ModalOpenProp) => { onOpenChange={setIsModalOpen} headerTitle="" bodyClass={` - h-[50vh] + min-h-[50vh] w-[50vw] max-w-none max-h-none