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/map/Gis.js b/packages/common/business_components/src/ui/map/Gis.js index 5ac98f27f..8929a3124 100644 --- a/packages/common/business_components/src/ui/map/Gis.js +++ b/packages/common/business_components/src/ui/map/Gis.js @@ -8,7 +8,6 @@ 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'; @@ -16,7 +15,6 @@ 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); @@ -46,10 +44,6 @@ 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([]); @@ -149,7 +143,7 @@ function Gis({ 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 ), @@ -226,120 +220,6 @@ 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(); - // }); - - // // 초기 가시성 업데이트 - // 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); 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 72a584970..7488e4b72 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 @@ -92,7 +92,7 @@ GisApp.core.prototype = { target: '', centerX: 37.2806094, centerY: 127.0032502, - zoom: 12, + zoom: 15, minZoom: 6, maxZoom: 20, }, 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()