Merge commit '9cf281db7e9c139a21a9383caa99f31621e99c1e' into iipbs/wr/dev
iipbs_wr / build (push) Waiting to run
iipbs_wr / build (push) Waiting to run
This commit is contained in:
+10
-1
@@ -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*"],
|
||||
};
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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';
|
||||
@@ -8,7 +7,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,27 +14,11 @@ 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,
|
||||
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);
|
||||
@@ -46,21 +28,13 @@ 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)
|
||||
};
|
||||
|
||||
// Feature Click Event
|
||||
// SearchModal, GisContent 등에서 호출
|
||||
const handleFeatureSelect = useCallback((data) => {
|
||||
gisMap = mapInstanceRef.current;
|
||||
|
||||
@@ -115,10 +89,8 @@ function Gis({
|
||||
setFeatureData(data);
|
||||
setIsOpen(true);
|
||||
setIsSearchModalOpen(false);
|
||||
onFeatureSelect?.(data); // 선택된 피쳐 데이터 콜백 호출
|
||||
}, [mapInstanceRef]);
|
||||
|
||||
// 지도 생성 및 초기화
|
||||
useEffect(() => {
|
||||
if (!mapRef.current) return;
|
||||
if (mapInstanceRef.current) return; // 이미 생성된 맵이 있다면 중단
|
||||
@@ -132,7 +104,7 @@ function Gis({
|
||||
]);
|
||||
register(proj4);
|
||||
|
||||
// OpenLayers 지도 객체 생성
|
||||
//맵 생성
|
||||
const map = new OlMap({
|
||||
layers: [gisCore.baseLayers['base'], gisCore.baseLayers['satellite']],
|
||||
renderer: 'webgl',
|
||||
@@ -149,7 +121,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
|
||||
),
|
||||
@@ -160,7 +132,7 @@ function Gis({
|
||||
|
||||
gisMaps.current = map
|
||||
|
||||
// 미니맵 추가
|
||||
//미니맵
|
||||
if (isMiniMap) {
|
||||
const overviewMap = new OverviewMap({
|
||||
layers: [GisApp.Module.core.baseMiniMapLayers['base']],
|
||||
@@ -175,7 +147,6 @@ function Gis({
|
||||
map.addControl(overviewMap);
|
||||
}
|
||||
|
||||
// 스케일 컨트롤 및 베이스 레이어 설정
|
||||
gisCore.__proto__.map = map;
|
||||
gisCore.scaleControl();
|
||||
gisCore.changeBaseLayer('base');
|
||||
@@ -183,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]);
|
||||
@@ -226,159 +193,31 @@ 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}
|
||||
onCheckedChange={handleCheckedLayersChange}
|
||||
/>
|
||||
<GisTree visible={tabVisible} setVisible={setTabVisible} />
|
||||
|
||||
{/* <GisContent onFeatureSelect={handleFeatureSelect} isSearch={isSearch} isSearchModalOpen={isSearchModalOpen} setIsSearchModalOpen={setIsSearchModalOpen} /> */}
|
||||
|
||||
{/* DetailView가 존재할 때만 렌더링 isSearchModalOpen이 true면 visible=false로 팝업 숨김 */}
|
||||
{/* isSearchModalOpen이 true면 visible=false로 팝업 숨김 */}
|
||||
{isOpen && featureData && !isSearchModalOpen && (
|
||||
<DetailView
|
||||
featureData={featureData}
|
||||
onClose={closePopup} // 닫기 콜백 전달
|
||||
/>
|
||||
)}
|
||||
{/* SearchView가 존재할 때만 렌더링 */}
|
||||
{SearchView && (
|
||||
<SearchView
|
||||
open={isSearchModalOpen}
|
||||
onOpenChange={setIsSearchModalOpen}
|
||||
onFeatureSelect={handleFeatureSelect}
|
||||
/>
|
||||
)}
|
||||
<SearchView
|
||||
open={isSearchModalOpen}
|
||||
onOpenChange={setIsSearchModalOpen}
|
||||
onFeatureSelect={onFeatureSelect}
|
||||
/>
|
||||
<GisLonLat />
|
||||
<GisToolBar />
|
||||
<GisMapToggle />
|
||||
<GisSelect onFeatureSelect={handleFeatureSelect} />
|
||||
<GisSelect onFeatureSelect={onFeatureSelect} />
|
||||
</div>
|
||||
|
||||
{isSearch && (
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -92,7 +94,7 @@ GisApp.core.prototype = {
|
||||
target: '',
|
||||
centerX: 37.2806094,
|
||||
centerY: 127.0032502,
|
||||
zoom: 12,
|
||||
zoom: 15,
|
||||
minZoom: 6,
|
||||
maxZoom: 20,
|
||||
},
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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<string >('')
|
||||
|
||||
|
||||
|
||||
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 (
|
||||
<Input
|
||||
placeholder="전화번호를 입력하세요"
|
||||
type="text"
|
||||
className="!text-xl text-gray-600"
|
||||
{...field}
|
||||
value={formattedValue}
|
||||
onChange={(e) => {
|
||||
const value = e.target.value.replace(/\D/g, ''); // 숫자만 저장
|
||||
field.onChange(value);
|
||||
}}
|
||||
/>
|
||||
)
|
||||
)}
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user