From 0dcbe8549caa13decc9721b0f51490ac273dea19 Mon Sep 17 00:00:00 2001 From: leedj312 Date: Tue, 1 Jul 2025 10:28:48 +0900 Subject: [PATCH 1/4] =?UTF-8?q?libphonenumber=20=EB=9D=BC=EC=9D=B4?= =?UTF-8?q?=EB=B8=8C=EB=9F=AC=EB=A6=AC=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20?= =?UTF-8?q?=EC=A0=84=ED=99=94=EB=B2=88=ED=98=B8=20=EC=9E=85=EB=A0=A5=20?= =?UTF-8?q?=ED=95=84=EB=93=9C=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/business_components/package.json | 1 + .../src/ui/sdmInsertForm/form.tsx | 30 ++++++++++++++++--- .../src/ui/sdmInsertForm/index.tsx | 4 +-- 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/packages/common/business_components/package.json b/packages/common/business_components/package.json index e7aae9fc5..da6f8c344 100644 --- a/packages/common/business_components/package.json +++ b/packages/common/business_components/package.json @@ -35,6 +35,7 @@ "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "date-fns": "^3.6.0", + "libphonenumber-js": "^1.12.9", "lucide-react": "^0.474.0", "path": "^0.12.7", "react-day-picker": "^8.10.1", diff --git a/packages/common/business_components/src/ui/sdmInsertForm/form.tsx b/packages/common/business_components/src/ui/sdmInsertForm/form.tsx index 6ccaac946..2d7d2bf90 100644 --- a/packages/common/business_components/src/ui/sdmInsertForm/form.tsx +++ b/packages/common/business_components/src/ui/sdmInsertForm/form.tsx @@ -3,9 +3,9 @@ import { SelectBox } from "../select"; import { Input } from "@common/components"; import { FormUi } from "../form_ui"; import { useCallback, useEffect, useState } from "react"; +import { AsYouType } from "libphonenumber-js"; import type { ControllerRenderProps, UseFormReturn } from "react-hook-form"; import { SdmSearchForm } from "./sdmNameBox"; -import { set } from "date-fns"; // 수요처 정보 export type DemandCompType = { @@ -79,6 +79,19 @@ export function SdmRegForm({ form, onCheckboxChange, selectSidoData, selectSigun const [updateCoNm, setUpdateNm] = useState('') + + const formatPhoneNumber = (value:string) => { + const input = value; + const formatter = new AsYouType("KR"); + const formatted = formatter.input(input); + + // 저장할 값 처리: 숫자만 추출 + const digitsOnly = input.replace(/\D/g, ""); + + return formatted + }; + + const initializeSigunData = useCallback((sidoCd: string) => { if (sidoCd === '0') { setSigunValue('0'); @@ -325,14 +338,23 @@ export function SdmRegForm({ form, onCheckboxChange, selectSidoData, selectSigun { name: "telno", label: "전화번호", - component: (field) => ( + component: (field) => + + { + const formattedValue = formatPhoneNumber(field.value??''); // 초기 렌더링 시에도 포맷 적용 + + return ( { + const value = e.target.value.replace(/\D/g, ''); // 숫자만 저장 + field.onChange(value); + }} /> - ) + )} }, { diff --git a/packages/common/business_components/src/ui/sdmInsertForm/index.tsx b/packages/common/business_components/src/ui/sdmInsertForm/index.tsx index 13e461910..4219602e6 100644 --- a/packages/common/business_components/src/ui/sdmInsertForm/index.tsx +++ b/packages/common/business_components/src/ui/sdmInsertForm/index.tsx @@ -41,8 +41,8 @@ const createFormSchema = (isBznmTotNoDisabled: boolean, isDuplicate: boolean ,up telno: z.string() .min(8, "전화번호는 8자 이상 입력해주세요") .max(11, "전화번호는 11자 이내로 입력해주세요") - .regex(/^[0-9-]+$/, "전화번호는 숫자와 하이픈(-)만 입력 가능합니다"), - rgtr: z.string().optional(), + .regex(/^[0-9]+$/, "전화번호는 숫자만 입력 가능합니다"), + rgtr: z.string().optional(), dmdAplyPswd : updateList?.dmdInfoPk // 업데이트일경우 ? z.string().optional() // 패스워드는 선택 사항이며 길이 제한 없음 : z.string().optional() From 557cb7c882221a41323874078eec85f6595574d7 Mon Sep 17 00:00:00 2001 From: swcho Date: Tue, 1 Jul 2025 17:18:59 +0900 Subject: [PATCH 2/4] =?UTF-8?q?feat:=20SN(=ED=95=98=EC=88=98=EA=B4=80?= =?UTF-8?q?=EB=A1=9C)=20GIS=20=EA=B3=B5=ED=86=B5=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EC=BB=A4=EB=B0=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../business_components/src/ui/map/Gis.js | 19 +++++++++---------- .../src/ui/map/lib/gis.core.js | 2 +- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/packages/common/business_components/src/ui/map/Gis.js b/packages/common/business_components/src/ui/map/Gis.js index 8b8acf1cb..fa1cf7ab4 100644 --- a/packages/common/business_components/src/ui/map/Gis.js +++ b/packages/common/business_components/src/ui/map/Gis.js @@ -1,20 +1,19 @@ -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'; export const GisContext = createContext({ gisCore: null, gisMap: null, treeData: null, checkedLayerIds: null }); export const useGisContext = () => useContext(GisContext); @@ -122,7 +121,7 @@ function Gis({ isOpen , gisMaps, isSearchModalOpen, treeData = [], isSearch = fa projection: GisApp.TO_PROJ, zoom: gisCore.defaults.zoom, center: olProj.transform( - [126.6757, 37.5585], + [127.4875, 37.4916], GisApp.FROM_PROJ, GisApp.TO_PROJ ), @@ -242,4 +241,4 @@ function Gis({ isOpen , gisMaps, isSearchModalOpen, treeData = [], isSearch = fa ); } -export default React.memo(Gis); \ No newline at end of file +export default React.memo(Gis); 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 db58c898f..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 @@ -94,7 +94,7 @@ GisApp.core.prototype = { target: '', centerX: 37.2806094, centerY: 127.0032502, - zoom: 12, + zoom: 15, minZoom: 6, maxZoom: 20, }, From 9b07952340e11f275d7aa12321c74b09bcb7613a Mon Sep 17 00:00:00 2001 From: yschoi Date: Tue, 1 Jul 2025 19:09:45 +0900 Subject: [PATCH 3/4] =?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 4/4] =?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; }