Merge branch 'iipbs/sn/dev_kic' into iipbs/sn/dev
This commit is contained in:
@@ -20,6 +20,7 @@ type Props = {
|
||||
onToggleGisEdit?: () => void;
|
||||
isGisEditActive?: boolean;
|
||||
isInfoSearchActive?: boolean;
|
||||
initialActiveKey?: WindowPopupKey | null;
|
||||
};
|
||||
|
||||
type MenuIconProps = {
|
||||
@@ -73,9 +74,10 @@ export default function AnalysisPopupMenu({
|
||||
onToggleGisEdit,
|
||||
isGisEditActive = false,
|
||||
isInfoSearchActive = false,
|
||||
initialActiveKey = null,
|
||||
}: Props) {
|
||||
const [isOpen, setIsOpen] = useState(true);
|
||||
const [activeKey, setActiveKey] = useState<WindowPopupKey | "infoSearch" | null>(null);
|
||||
const [activeKey, setActiveKey] = useState<WindowPopupKey | "infoSearch" | null>(initialActiveKey);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isGisEditActive && activeKey === "gisEditPopup") {
|
||||
@@ -89,6 +91,11 @@ export default function AnalysisPopupMenu({
|
||||
}
|
||||
}, [activeKey, isInfoSearchActive]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!initialActiveKey) return;
|
||||
setActiveKey((currentKey) => currentKey ?? initialActiveKey);
|
||||
}, [initialActiveKey]);
|
||||
|
||||
return (
|
||||
<div className="absolute right-[0.55rem] top-[20px] z-[45]">
|
||||
<div className="relative">
|
||||
|
||||
@@ -12,6 +12,7 @@ import useStore from "@/store/useStore";
|
||||
|
||||
import {
|
||||
INFTWT_RESULT_ROW_SELECT_MESSAGE,
|
||||
INFTWT_RESULT_ROW_SELECT_STORAGE_KEY,
|
||||
type InftwtResultRow,
|
||||
} from "./inftwtResultTypes";
|
||||
|
||||
@@ -56,6 +57,20 @@ const DEFAULT_FILTERS: InftwtResultFilters = {
|
||||
};
|
||||
|
||||
const postRowSelectMessage = (row: InftwtResultRow) => {
|
||||
if (typeof window !== "undefined") {
|
||||
try {
|
||||
window.localStorage.setItem(
|
||||
INFTWT_RESULT_ROW_SELECT_STORAGE_KEY,
|
||||
JSON.stringify({
|
||||
row,
|
||||
timestamp: Date.now(),
|
||||
}),
|
||||
);
|
||||
} catch (error) {
|
||||
console.warn("침입수 분석결과 선택 동기화 저장에 실패했습니다.", error);
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof window === "undefined" || !window.opener) return;
|
||||
window.opener.postMessage(
|
||||
{
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
export const INFTWT_RESULT_ROW_SELECT_MESSAGE = "SN_INFTWT_RESULT_ROW_SELECT";
|
||||
export const INFTWT_RESULT_OVERLAY_TYPE = "INFTWT_RESULT";
|
||||
export const INFTWT_RESULT_ROW_SELECT_STORAGE_KEY = "SN_INFTWT_RESULT_ROW_SELECT_STORAGE";
|
||||
|
||||
export type InftwtResultRow = {
|
||||
rowNumber: number;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"use client";
|
||||
"use client";
|
||||
|
||||
import { useCallback, useEffect, useRef, useState, type MutableRefObject } from "react";
|
||||
import GeoJSON from "ol/format/GeoJSON.js";
|
||||
@@ -18,6 +18,7 @@ import { getSigunguCenter, moveMapToCenter, sidoCenter } from "@common/business_
|
||||
import {
|
||||
INFTWT_RESULT_OVERLAY_TYPE,
|
||||
INFTWT_RESULT_ROW_SELECT_MESSAGE,
|
||||
INFTWT_RESULT_ROW_SELECT_STORAGE_KEY,
|
||||
type InftwtResultMapViewResponse,
|
||||
type InftwtResultRow,
|
||||
} from "./inftwtResultTypes";
|
||||
@@ -160,6 +161,33 @@ const getFirstFeatureId = (payload: any) => {
|
||||
return String(firstFeature?.id ?? "").trim();
|
||||
};
|
||||
|
||||
const getOilqPipeExtent = (features: any[]) => {
|
||||
const oilqFeatures = features.filter((feature) => Boolean(feature?.get?.("hasOilq")));
|
||||
if (oilqFeatures.length === 0) return null;
|
||||
|
||||
let nextExtent: [number, number, number, number] | null = null;
|
||||
oilqFeatures.forEach((feature) => {
|
||||
const geometryExtent = feature?.getGeometry?.()?.getExtent?.();
|
||||
if (!Array.isArray(geometryExtent) || geometryExtent.some((value: number) => !Number.isFinite(value))) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!nextExtent) {
|
||||
nextExtent = [...geometryExtent] as [number, number, number, number];
|
||||
return;
|
||||
}
|
||||
|
||||
nextExtent = [
|
||||
Math.min(nextExtent[0], geometryExtent[0]),
|
||||
Math.min(nextExtent[1], geometryExtent[1]),
|
||||
Math.max(nextExtent[2], geometryExtent[2]),
|
||||
Math.max(nextExtent[3], geometryExtent[3]),
|
||||
];
|
||||
});
|
||||
|
||||
return nextExtent;
|
||||
};
|
||||
|
||||
export const useInftwtResultMap = ({
|
||||
gisMaps,
|
||||
setFeatureData,
|
||||
@@ -197,7 +225,7 @@ export const useInftwtResultMap = ({
|
||||
});
|
||||
|
||||
layer.set("id", INFTWT_ZONE_LAYER_ID);
|
||||
layer.set("lyrNm", "침입수분석-처리구역");
|
||||
layer.set("lyrNm", "移⑥엯?섎텇??泥섎━援ъ뿭");
|
||||
layer.set("lyrTyNm", "tb_ms_swage_prcs_zone");
|
||||
layer.set("clickPsblYn", "N");
|
||||
layer.setVisible?.(true);
|
||||
@@ -223,7 +251,7 @@ export const useInftwtResultMap = ({
|
||||
});
|
||||
|
||||
layer.set("id", INFTWT_PIPE_LAYER_ID);
|
||||
layer.set("lyrNm", "침입수분석-결과관로");
|
||||
layer.set("lyrNm", "침입수분석 결과관로");
|
||||
layer.set("lyrTyNm", "tb_ms_swage_ppl");
|
||||
layer.set("clickPsblYn", "Y");
|
||||
layer.setVisible?.(true);
|
||||
@@ -240,6 +268,7 @@ export const useInftwtResultMap = ({
|
||||
const zoneExtent = zoneSourceRef.current?.getExtent?.();
|
||||
const pipeExtent = pipeSourceRef.current?.getExtent?.();
|
||||
const pipeFeatures = pipeSourceRef.current?.getFeatures?.() ?? [];
|
||||
const oilqPipeExtent = getOilqPipeExtent(pipeFeatures);
|
||||
const hasPipeFeatures = pipeFeatures.length > 0;
|
||||
const hasZoneFeatures = Boolean(zoneSourceRef.current?.getFeatures?.().length);
|
||||
const preferZone = mapView?.focusTargetType === "ZONE" && hasZoneFeatures;
|
||||
@@ -256,7 +285,9 @@ export const useInftwtResultMap = ({
|
||||
: false;
|
||||
|
||||
const shouldUseZoneExtent = preferZone && (hasPipeInsideZone || !hasPipeFeatures);
|
||||
const extent = shouldUseZoneExtent
|
||||
const extent = oilqPipeExtent
|
||||
? oilqPipeExtent
|
||||
: shouldUseZoneExtent
|
||||
? zoneExtent
|
||||
: hasPipeFeatures
|
||||
? pipeExtent
|
||||
@@ -271,7 +302,7 @@ export const useInftwtResultMap = ({
|
||||
map.getView?.().fit?.(extent, {
|
||||
duration: 350,
|
||||
padding: DEFAULT_RESULT_FIT_PADDING,
|
||||
maxZoom: shouldUseZoneExtent ? 15 : 16,
|
||||
maxZoom: oilqPipeExtent ? 17 : shouldUseZoneExtent ? 15 : 16,
|
||||
constrainResolution: true,
|
||||
nearest: true,
|
||||
});
|
||||
@@ -306,6 +337,11 @@ export const useInftwtResultMap = ({
|
||||
|
||||
const moveToStableFocus = useCallback(
|
||||
async (mapView: InftwtResultMapViewResponse) => {
|
||||
if ((mapView.oilqPipeCount ?? 0) > 0) {
|
||||
fitToRenderedFeatures(mapView);
|
||||
return;
|
||||
}
|
||||
|
||||
const activeRow = activeResultRowRef.current;
|
||||
const movedByRegion = moveByRegionCodes(
|
||||
activeRow?.ctpvCd ?? mapView.analysis?.ctpvCd,
|
||||
@@ -352,7 +388,7 @@ export const useInftwtResultMap = ({
|
||||
pipeLayerRef.current?.changed?.();
|
||||
gisMaps.current?.renderSync?.();
|
||||
|
||||
console.info("침입수 FE overlay 렌더 결과", {
|
||||
console.info("移⑥엯??FE overlay ?뚮뜑 寃곌낵", {
|
||||
focusTargetType: mapView.focusTargetType,
|
||||
zoneFeatureCount: zoneFeatures.length,
|
||||
pipeFeatureCount: pipeFeatures.length,
|
||||
@@ -371,6 +407,15 @@ export const useInftwtResultMap = ({
|
||||
setIsOpen(false);
|
||||
setIsSearchModalOpen(false);
|
||||
|
||||
const cachedMapView = mapViewCacheRef.current.get(row.inftwtAnlsSn);
|
||||
let mapView = cachedMapView;
|
||||
|
||||
if (!mapView) {
|
||||
const payload = await getRawJson(`/sewerage-service/api/v1/inftwtAnlsRslt/mapView/${row.inftwtAnlsSn}`);
|
||||
mapView = (payload?.data ?? payload) as InftwtResultMapViewResponse;
|
||||
mapViewCacheRef.current.set(row.inftwtAnlsSn, mapView);
|
||||
}
|
||||
|
||||
if (typeof window !== "undefined") {
|
||||
window.dispatchEvent(
|
||||
new CustomEvent(INFTWT_PROC_SEARCH_EVENT, {
|
||||
@@ -378,26 +423,17 @@ export const useInftwtResultMap = ({
|
||||
ctpvCd: row.ctpvCd ?? "0",
|
||||
sggCd: row.sggCd ?? "0",
|
||||
swtrmtFcltNm: row.swtrmtFcltNm ?? "",
|
||||
suppressMapMove: (mapView.oilqPipeCount ?? 0) > 0,
|
||||
},
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
const cachedMapView = mapViewCacheRef.current.get(row.inftwtAnlsSn);
|
||||
if (cachedMapView) {
|
||||
setActiveMapView(cachedMapView);
|
||||
void renderMapView(cachedMapView);
|
||||
return;
|
||||
}
|
||||
|
||||
const payload = await getRawJson(`/sewerage-service/api/v1/inftwtAnlsRslt/mapView/${row.inftwtAnlsSn}`);
|
||||
const mapView = (payload?.data ?? payload) as InftwtResultMapViewResponse;
|
||||
mapViewCacheRef.current.set(row.inftwtAnlsSn, mapView);
|
||||
setActiveMapView(mapView);
|
||||
void renderMapView(mapView);
|
||||
} catch (error) {
|
||||
console.error("침입수 분석결과 지도 연계 중 오류:", error);
|
||||
alert("침입수 분석결과를 지도에 연계하지 못했습니다.");
|
||||
console.error("移⑥엯??遺꾩꽍寃곌낵 吏???곌퀎 以??ㅻ쪟:", error);
|
||||
alert("移⑥엯??遺꾩꽍寃곌낵瑜?吏?꾩뿉 ?곌퀎?섏? 紐삵뻽?듬땲??");
|
||||
}
|
||||
},
|
||||
[renderMapView, setFeatureData, setIsOpen, setIsSearchModalOpen],
|
||||
@@ -410,8 +446,24 @@ export const useInftwtResultMap = ({
|
||||
void handleRowSelect(event.data.row as InftwtResultRow);
|
||||
};
|
||||
|
||||
const handleStorageEvent = (event: StorageEvent) => {
|
||||
if (event.key !== INFTWT_RESULT_ROW_SELECT_STORAGE_KEY || !event.newValue) return;
|
||||
|
||||
try {
|
||||
const payload = JSON.parse(event.newValue) as { row?: InftwtResultRow };
|
||||
if (!payload?.row) return;
|
||||
void handleRowSelect(payload.row);
|
||||
} catch (error) {
|
||||
console.warn("移⑥엯??遺꾩꽍寃곌낵 storage ?숆린??媛믪쓣 ?댁꽍?섏? 紐삵뻽?듬땲??", error);
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener("message", handlePopupMessage);
|
||||
return () => window.removeEventListener("message", handlePopupMessage);
|
||||
window.addEventListener("storage", handleStorageEvent);
|
||||
return () => {
|
||||
window.removeEventListener("message", handlePopupMessage);
|
||||
window.removeEventListener("storage", handleStorageEvent);
|
||||
};
|
||||
}, [handleRowSelect]);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -442,7 +494,7 @@ export const useInftwtResultMap = ({
|
||||
});
|
||||
|
||||
if (!popupWindow) {
|
||||
alert("팝업이 차단되었습니다. 브라우저 팝업 허용 설정을 확인해주세요.");
|
||||
alert("?앹뾽??李⑤떒?섏뿀?듬땲?? 釉뚮씪?곗? ?앹뾽 ?덉슜 ?ㅼ젙???뺤씤?댁<?몄슂.");
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -12,6 +12,7 @@ import useStore from "@/store/useStore";
|
||||
|
||||
import {
|
||||
RDII_RESULT_ROW_SELECT_MESSAGE,
|
||||
RDII_RESULT_ROW_SELECT_STORAGE_KEY,
|
||||
type RdiiResultRow,
|
||||
} from "./rdiiResultTypes";
|
||||
|
||||
@@ -58,6 +59,20 @@ const DEFAULT_FILTERS: RdiiResultFilters = {
|
||||
};
|
||||
|
||||
const postRowSelectMessage = (row: RdiiResultRow) => {
|
||||
if (typeof window !== "undefined") {
|
||||
try {
|
||||
window.localStorage.setItem(
|
||||
RDII_RESULT_ROW_SELECT_STORAGE_KEY,
|
||||
JSON.stringify({
|
||||
row,
|
||||
timestamp: Date.now(),
|
||||
}),
|
||||
);
|
||||
} catch (error) {
|
||||
console.warn("RDII 분석결과 선택 동기화 저장에 실패했습니다.", error);
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof window === "undefined" || !window.opener) return;
|
||||
window.opener.postMessage(
|
||||
{
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
export const RDII_RESULT_ROW_SELECT_MESSAGE = "SN_RDII_RESULT_ROW_SELECT";
|
||||
export const RDII_RESULT_OVERLAY_TYPE = "RDII_RESULT";
|
||||
export const RDII_RESULT_ROW_SELECT_STORAGE_KEY = "SN_RDII_RESULT_ROW_SELECT_STORAGE";
|
||||
|
||||
export type RdiiResultRow = {
|
||||
rowNumber: number;
|
||||
|
||||
@@ -16,6 +16,7 @@ import { getSigunguCenter, moveMapToCenter, sidoCenter } from "@common/business_
|
||||
import {
|
||||
RDII_RESULT_OVERLAY_TYPE,
|
||||
RDII_RESULT_ROW_SELECT_MESSAGE,
|
||||
RDII_RESULT_ROW_SELECT_STORAGE_KEY,
|
||||
type RdiiResultMapViewResponse,
|
||||
type RdiiResultRow,
|
||||
} from "./rdiiResultTypes";
|
||||
@@ -303,8 +304,24 @@ export const useRdiiResultMap = ({
|
||||
void handleRowSelect(event.data.row as RdiiResultRow);
|
||||
};
|
||||
|
||||
const handleStorageEvent = (event: StorageEvent) => {
|
||||
if (event.key !== RDII_RESULT_ROW_SELECT_STORAGE_KEY || !event.newValue) return;
|
||||
|
||||
try {
|
||||
const payload = JSON.parse(event.newValue) as { row?: RdiiResultRow };
|
||||
if (!payload?.row) return;
|
||||
void handleRowSelect(payload.row);
|
||||
} catch (error) {
|
||||
console.warn("RDII 분석결과 storage 동기화 값을 해석하지 못했습니다.", error);
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener("message", handlePopupMessage);
|
||||
return () => window.removeEventListener("message", handlePopupMessage);
|
||||
window.addEventListener("storage", handleStorageEvent);
|
||||
return () => {
|
||||
window.removeEventListener("message", handlePopupMessage);
|
||||
window.removeEventListener("storage", handleStorageEvent);
|
||||
};
|
||||
}, [handleRowSelect]);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
import dynamic from "next/dynamic";
|
||||
import { Fragment, useCallback, useEffect, useMemo, useRef, useState, type ReactNode } from "react";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { usePathname, useSearchParams } from "next/navigation";
|
||||
|
||||
import Collection from "ol/Collection.js";
|
||||
import Feature from "ol/Feature.js";
|
||||
@@ -41,7 +41,7 @@ import Style from "ol/style/Style.js";
|
||||
import Text from "ol/style/Text.js";
|
||||
|
||||
import { openWindowPopup } from "@/features/windowPopup/lib/openWindowPopup";
|
||||
import type { WindowPopupKey } from "@/features/windowPopup/model/popupDefinitions";
|
||||
import { isWindowPopupKey, type WindowPopupKey } from "@/features/windowPopup/model/popupDefinitions";
|
||||
import { WINDOW_POPUP_MESSAGE_TYPE } from "@/features/windowPopup/model/types";
|
||||
import Gis from "@common/business_components/ui/map/Gis";
|
||||
import GisApp from "@common/business_components/ui/map/lib/gis.core";
|
||||
@@ -2401,7 +2401,14 @@ const appendSinkholeControlNode = (data: any[]) => {
|
||||
};
|
||||
|
||||
const Map = () => {
|
||||
const searchParams = useSearchParams();
|
||||
const pathname = usePathname();
|
||||
const initialAnalysisPopupKey = useMemo(() => {
|
||||
const popupKey = searchParams.get("analysisPopupKey");
|
||||
if (!popupKey || !isWindowPopupKey(popupKey)) return null;
|
||||
if (popupKey !== "inftwtResultPopup" && popupKey !== "rdiiResultPopup") return null;
|
||||
return popupKey;
|
||||
}, [searchParams]);
|
||||
const flowDirectionMode = useMemo(() => getFlowDirectionModeByPathname(pathname), [pathname]);
|
||||
const { isSearchModalOpen, setIsSearchModalOpen } = useSearchModalStore();
|
||||
const userInfo = useUserStore((state) => state.userInfo);
|
||||
@@ -7080,6 +7087,7 @@ const Map = () => {
|
||||
/>
|
||||
) : null}
|
||||
<AnalysisPopupMenu
|
||||
initialActiveKey={initialAnalysisPopupKey}
|
||||
onOpenPopup={openAnalysisPopup}
|
||||
onOpenInfoSearch={() => setIsSearchModalOpen(true)}
|
||||
onToggleGisEdit={handleToggleGisEdit}
|
||||
|
||||
@@ -49,6 +49,7 @@ type BaseLayout = {
|
||||
onSubmitEvent: Function
|
||||
graphBtn?: boolean
|
||||
analysisBtn?: string
|
||||
resultViewBtn?: string
|
||||
firstDivBtn?: boolean
|
||||
selectBox?: SelectBox
|
||||
selectionComponent?: ReactNode
|
||||
@@ -67,10 +68,12 @@ type BaseLayout = {
|
||||
setSggCd: (sgg: string) => void;
|
||||
setSggName?: (sggName: string) => void;
|
||||
setSwtrmtFcltNm?: (swtrmtFcltNm: string) => void;
|
||||
defaultSwtrmtFcltNm?: string;
|
||||
setIsLoading?: (isLoading: boolean) => void;
|
||||
onExcelDownload?: () => void;
|
||||
onDeleteFile?: () => void;
|
||||
onAnalysis?: () => void;
|
||||
onResultView?: () => void;
|
||||
selectedRowData?: any;
|
||||
isIr?: boolean;
|
||||
isRdii?: boolean;
|
||||
@@ -83,6 +86,7 @@ const BaseLayout = ({
|
||||
onSubmitEvent,
|
||||
graphBtn = false,
|
||||
analysisBtn,
|
||||
resultViewBtn,
|
||||
selectBox,
|
||||
selectionComponent,
|
||||
showDatePicker = false,
|
||||
@@ -100,10 +104,12 @@ const BaseLayout = ({
|
||||
setSggCd,
|
||||
setSggName,
|
||||
setSwtrmtFcltNm,
|
||||
defaultSwtrmtFcltNm,
|
||||
setIsLoading,
|
||||
onExcelDownload,
|
||||
onDeleteFile,
|
||||
onAnalysis,
|
||||
onResultView,
|
||||
isIr = false,
|
||||
isRdii = false,
|
||||
}: BaseLayout) => {
|
||||
@@ -208,6 +214,7 @@ const BaseLayout = ({
|
||||
selectedSwagePrcsFclt={selectedSwagePrcsFclt}
|
||||
onSwagePrcsFcltChange={onSwagePrcsFcltChange}
|
||||
setSwtrmtFcltNm={setSwtrmtFcltNm}
|
||||
defaultSwtrmtFcltNm={defaultSwtrmtFcltNm}
|
||||
setIsLoading={setIsLoading}
|
||||
/>
|
||||
)}
|
||||
@@ -251,6 +258,15 @@ const BaseLayout = ({
|
||||
{analysisBtn && (
|
||||
<Button className="px-4 py-2 bg-blue-600 text-white rounded-sm text-sm font-medium hover:bg-blue-700 !mr-[1.2rem]" size="sm" onClick={handleConfirm}>{analysisBtn}</Button>
|
||||
)}
|
||||
{resultViewBtn && (
|
||||
<Button
|
||||
className="px-4 py-2 bg-slate-600 text-white rounded-sm text-sm font-medium hover:bg-slate-700 !mr-[1.2rem]"
|
||||
size="sm"
|
||||
onClick={onResultView}
|
||||
>
|
||||
{resultViewBtn}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<Form {...form}>
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
"use client";
|
||||
|
||||
import { openWindowPopup } from "@/features/windowPopup/lib/openWindowPopup";
|
||||
|
||||
type AnalysisResultPopupKey = "inftwtResultPopup" | "rdiiResultPopup";
|
||||
|
||||
const ANALYSIS_RESULT_MAP_WINDOW_NAME = "sn-analysis-result-map";
|
||||
|
||||
export const openAnalysisResultMapView = (popupKey: AnalysisResultPopupKey) => {
|
||||
if (typeof window === "undefined") return;
|
||||
|
||||
const mapUrl = `/map?analysisPopupKey=${encodeURIComponent(popupKey)}`;
|
||||
const mapWindow = window.open(mapUrl, ANALYSIS_RESULT_MAP_WINDOW_NAME);
|
||||
|
||||
if (!mapWindow) {
|
||||
alert("하수관망도 창이 차단되었습니다. 브라우저 팝업 허용 후 다시 시도해주세요.");
|
||||
return;
|
||||
}
|
||||
|
||||
// 분석 화면에서 직접 결과 popup을 열면 opener는 map이 아니라 현재 화면이 된다.
|
||||
// map 창은 별도 탭으로 띄우고, 결과 popup의 row 선택은 storage 이벤트로 다시 map에 전달한다.
|
||||
const popupWindow = openWindowPopup(popupKey);
|
||||
mapWindow.focus();
|
||||
|
||||
if (!popupWindow) {
|
||||
alert("분석결과 popup이 차단되었습니다. 브라우저 팝업 허용 후 다시 시도해주세요.");
|
||||
return;
|
||||
}
|
||||
|
||||
popupWindow.focus();
|
||||
};
|
||||
@@ -28,6 +28,7 @@ import { useForm } from 'react-hook-form'
|
||||
import { z } from 'zod'
|
||||
import { excelToFlowRateData, excelToMatchingResultData, excelToTimePatternData, excelToRcptrInfoData } from '@/components/lib/utils'
|
||||
import BaseLayout from '../BaseLayout'
|
||||
import { openAnalysisResultMapView } from '../analysisResultView'
|
||||
import { validateMultipleFiles, FILE_EXTENSIONS, type FileValidationConfig } from '../../fileValidation';
|
||||
import { validateBasicSelectionsWithAlert } from '../../selectionValidation';
|
||||
import { tokenManager } from '@/entities/auth/lib/token-manager';
|
||||
@@ -58,6 +59,10 @@ interface UploadedFileInfo {
|
||||
}
|
||||
// 초기 파일 정보 상태
|
||||
const initialFileInfo: UploadedFileInfo = { file: null, atflCd: null, atflId: null };
|
||||
const DEFAULT_CTPV_CD = '41';
|
||||
const DEFAULT_SGG_CD = '41830';
|
||||
const DEFAULT_SGG_NAME = '\uC591\uD3C9\uAD70';
|
||||
const DEFAULT_SWTRMT_FCLT_NM = '\uC591\uD3C9';
|
||||
|
||||
const IrSetting = () => {
|
||||
const [input1, setInput1] = useState('0.8'); // 오수전환율 초기값
|
||||
@@ -65,10 +70,10 @@ const IrSetting = () => {
|
||||
const [selectedSwagePrcsFclt, setSelectedSwagePrcsFclt] = useState<string>('0'); // ?????? ?? ??
|
||||
const [formResetKey, setFormResetKey] = useState(0); // FileUploadForm 리셋을 위한 key 상태
|
||||
const [isLoadingResult, setIsLoadingResult] = useState<boolean>(false); // 로딩 상태
|
||||
const [ctpvCd, setCtpvCd] = useState<string>('0'); // 시도 코드 상태
|
||||
const [sggCd, setSggCd] = useState<string>('0'); // 시군구 코드 상태
|
||||
const [sggName, setSggName] = useState<string>(''); // 시군구 이름 상태
|
||||
const [swtrmtFcltNm, setSwtrmtFcltNm] = useState<string>(''); // 하수처리시설명 상태
|
||||
const [ctpvCd, setCtpvCd] = useState<string>(DEFAULT_CTPV_CD); // 시도 코드 상태
|
||||
const [sggCd, setSggCd] = useState<string>(DEFAULT_SGG_CD); // 시군구 코드 상태
|
||||
const [sggName, setSggName] = useState<string>(DEFAULT_SGG_NAME); // 시군구 이름 상태
|
||||
const [swtrmtFcltNm, setSwtrmtFcltNm] = useState<string>(DEFAULT_SWTRMT_FCLT_NM); // 하수처리시설명 상태
|
||||
const [uploadedFilesData, setUploadedFilesData] = useState<UploadedFileInfo[]>([
|
||||
initialFileInfo, // 0: 오수관망도
|
||||
initialFileInfo, // 1: 수용가-맨홀 매칭결과
|
||||
@@ -127,7 +132,10 @@ const IrSetting = () => {
|
||||
setInput1(''); // 입력 필드 초기화
|
||||
setInput2(''); // 입력 필드 초기화
|
||||
setSelectedSwagePrcsFclt('0');
|
||||
setSwtrmtFcltNm('');
|
||||
setCtpvCd(DEFAULT_CTPV_CD);
|
||||
setSggCd(DEFAULT_SGG_CD);
|
||||
setSggName(DEFAULT_SGG_NAME);
|
||||
setSwtrmtFcltNm(DEFAULT_SWTRMT_FCLT_NM);
|
||||
setFormResetKey(prevKey => prevKey + 1); // FileUploadForm 재마운트를 위해 key 변경
|
||||
};
|
||||
|
||||
@@ -377,6 +385,7 @@ const IrSetting = () => {
|
||||
form={form}
|
||||
onSubmitEvent={() => { }}
|
||||
graphBtn={false}
|
||||
resultViewBtn={'결과보기'}
|
||||
analysisBtn={'침입수분석 실행'}
|
||||
selectBox={{ title: '분석 대상지역 선택' }}
|
||||
ctpvCd={ctpvCd}
|
||||
@@ -388,7 +397,9 @@ const IrSetting = () => {
|
||||
setSggCd={setSggCd}
|
||||
setSggName={setSggName}
|
||||
setSwtrmtFcltNm={setSwtrmtFcltNm}
|
||||
defaultSwtrmtFcltNm={swtrmtFcltNm}
|
||||
setIsLoading={setIsLoadingResult}
|
||||
onResultView={() => openAnalysisResultMapView('inftwtResultPopup')}
|
||||
onAnalysis={handleAnalysisClick}
|
||||
isIr={true}
|
||||
>
|
||||
|
||||
@@ -22,6 +22,7 @@ import { postMutiPart } from '@/components/lib/api'
|
||||
import FileUploadForm from "@/components/ui/fileUploadForm"
|
||||
import FormSection from "@/components/ui/formSection"
|
||||
import BaseLayout from "../BaseLayout"
|
||||
import { openAnalysisResultMapView } from "../analysisResultView"
|
||||
import { DateMonthPicker, filePicked } from "@common/business_components/ui"
|
||||
import { Input } from "@common/components/ui"
|
||||
import { zodResolver } from "@hookform/resolvers/zod"
|
||||
@@ -74,9 +75,9 @@ const formSchema = z.object({
|
||||
type FormSchemaType = z.infer<typeof formSchema>;
|
||||
// 기본 폼 값 정의 (테스트 편의를 위해 초기값 설정, 추후 삭제 필요)
|
||||
const defaultFormValues: FormSchemaType = {
|
||||
ctpvCd: "",
|
||||
sggCd: "",
|
||||
swtrmtFcltNm: "",
|
||||
ctpvCd: "41",
|
||||
sggCd: "41830",
|
||||
swtrmtFcltNm: "\uC591\uD3C9",
|
||||
rtkSource: "",
|
||||
rdiiArea: "", // 문자열로 기본값 설정
|
||||
unitFactors: [
|
||||
@@ -112,6 +113,10 @@ interface UploadedFileInfo {
|
||||
|
||||
// 초기 파일 정보 상태
|
||||
const initialFileInfo: UploadedFileInfo = { file: null, atflCd: null, atflId: null };
|
||||
const DEFAULT_CTPV_CD = '41';
|
||||
const DEFAULT_SGG_CD = '41830';
|
||||
const DEFAULT_SGG_NAME = '\uC591\uD3C9\uAD70';
|
||||
const DEFAULT_SWTRMT_FCLT_NM = '\uC591\uD3C9';
|
||||
|
||||
export default function RTKForm() {
|
||||
const [formResetKey, setFormResetKey] = useState(0); // FileUploadForm 리셋을 위한 key 상태
|
||||
@@ -126,10 +131,10 @@ export default function RTKForm() {
|
||||
]);
|
||||
|
||||
const [select1, setSelect1] = useState<string>('0'); // 하수처리장 (swtrmtFcltCd)
|
||||
const [ctpvCd, setCtpvCd] = useState<string>('0'); // 시도 코드 상태
|
||||
const [sggCd, setSggCd] = useState<string>('0'); // 시군구 코드 상태
|
||||
const [sggName, setSggName] = useState<string>(''); // 시군구 이름 상태
|
||||
const [swtrmtFcltNm, setSwtrmtFcltNm] = useState<string>(''); // 하수처리시설명 상태
|
||||
const [ctpvCd, setCtpvCd] = useState<string>(DEFAULT_CTPV_CD); // 시도 코드 상태
|
||||
const [sggCd, setSggCd] = useState<string>(DEFAULT_SGG_CD); // 시군구 코드 상태
|
||||
const [sggName, setSggName] = useState<string>(DEFAULT_SGG_NAME); // 시군구 이름 상태
|
||||
const [swtrmtFcltNm, setSwtrmtFcltNm] = useState<string>(DEFAULT_SWTRMT_FCLT_NM); // 하수처리시설명 상태
|
||||
|
||||
const { data: userInfo } = useQuery({
|
||||
queryKey: [ENDPOINT.USER_SERVICE.USERS],
|
||||
@@ -153,6 +158,10 @@ export default function RTKForm() {
|
||||
]);
|
||||
setFormResetKey(prevKey => prevKey + 1); // FileUploadForm 재마운트를 위해 key 변경
|
||||
setSelect1('0');
|
||||
setCtpvCd(DEFAULT_CTPV_CD);
|
||||
setSggCd(DEFAULT_SGG_CD);
|
||||
setSggName(DEFAULT_SGG_NAME);
|
||||
setSwtrmtFcltNm(DEFAULT_SWTRMT_FCLT_NM);
|
||||
};
|
||||
|
||||
// FileUploadForm의 onFileSelect prop으로 전달될 콜백 함수
|
||||
@@ -413,6 +422,7 @@ export default function RTKForm() {
|
||||
form={form} // react-hook-form 인스턴스 전달
|
||||
onSubmitEvent={() => { }} // BaseLayout의 form 태그에 연결될 onSubmit
|
||||
graphBtn={false}
|
||||
resultViewBtn={'결과보기'}
|
||||
analysisBtn={'RDII분석 실행'}
|
||||
ctpvCd={ctpvCd}
|
||||
sggCd={sggCd}
|
||||
@@ -423,7 +433,9 @@ export default function RTKForm() {
|
||||
setSggCd={setSggCd}
|
||||
setSggName={setSggName}
|
||||
setSwtrmtFcltNm={setSwtrmtFcltNm}
|
||||
defaultSwtrmtFcltNm={swtrmtFcltNm}
|
||||
setIsLoading={setIsLoadingResult}
|
||||
onResultView={() => openAnalysisResultMapView('rdiiResultPopup')}
|
||||
onAnalysis={handleAnalysisClick}
|
||||
isRdii={true}
|
||||
>
|
||||
|
||||
@@ -35,10 +35,15 @@ interface CtpvSggPrcsFcltSelectBoxProps {
|
||||
selectedSwagePrcsFclt?: string;
|
||||
onSwagePrcsFcltChange?: (value: string) => void;
|
||||
setSwtrmtFcltNm?: (name: string) => void;
|
||||
defaultSwtrmtFcltNm?: string;
|
||||
setIsLoading?: (isLoading: boolean) => void;
|
||||
}
|
||||
|
||||
const defaultOption: ApiSelectOption = { val: '0', text: '전체' };
|
||||
const buildFacilityDefaultOption = (facilityName: string): ApiSelectOption => ({
|
||||
val: '0',
|
||||
text: facilityName || defaultOption.text,
|
||||
});
|
||||
|
||||
export function CtpvSggPrcsFcltSelectBox({
|
||||
sido,
|
||||
@@ -52,6 +57,7 @@ export function CtpvSggPrcsFcltSelectBox({
|
||||
selectedSwagePrcsFclt,
|
||||
onSwagePrcsFcltChange,
|
||||
setSwtrmtFcltNm,
|
||||
defaultSwtrmtFcltNm,
|
||||
setIsLoading,
|
||||
}: CtpvSggPrcsFcltSelectBoxProps) {
|
||||
|
||||
@@ -59,18 +65,7 @@ export function CtpvSggPrcsFcltSelectBox({
|
||||
const { ctpv, sgg } = useStore();
|
||||
const sidoStr = String(sido ?? '');
|
||||
const sidoPrefix = sidoStr.slice(0, 2);
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
onSidoChange("0");
|
||||
onSigunguChange("0");
|
||||
if (setCtpvName && setSggName) {
|
||||
setCtpvName("");
|
||||
setSggName("");
|
||||
}
|
||||
if (setSwtrmtFcltNm) setSwtrmtFcltNm("");
|
||||
})();
|
||||
}, []);
|
||||
const normalizedDefaultFacilityName = String(defaultSwtrmtFcltNm ?? '').trim();
|
||||
|
||||
useEffect(() => {
|
||||
onSwagePrcsFcltChange?.('0');
|
||||
@@ -115,6 +110,25 @@ export function CtpvSggPrcsFcltSelectBox({
|
||||
fetchData();
|
||||
}, [sggName]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!normalizedDefaultFacilityName) return;
|
||||
if (!selectedSwagePrcsFclt || selectedSwagePrcsFclt !== "0") return;
|
||||
if (prcsFcltOptions.length === 0) return;
|
||||
|
||||
const matchedDefaultFacility = prcsFcltOptions.find((option) =>
|
||||
String(option.text ?? "").trim() === normalizedDefaultFacilityName,
|
||||
);
|
||||
if (!matchedDefaultFacility) return;
|
||||
|
||||
onSwagePrcsFcltChange?.(String(matchedDefaultFacility.val));
|
||||
setSwtrmtFcltNm?.(matchedDefaultFacility.text);
|
||||
}, [
|
||||
normalizedDefaultFacilityName,
|
||||
onSwagePrcsFcltChange,
|
||||
prcsFcltOptions,
|
||||
selectedSwagePrcsFclt,
|
||||
setSwtrmtFcltNm,
|
||||
]);
|
||||
|
||||
return (
|
||||
<div className={`flex items-center justify-center ${className} mr-8`}>
|
||||
@@ -153,7 +167,7 @@ export function CtpvSggPrcsFcltSelectBox({
|
||||
className="w-[160px]"
|
||||
selectClass="rounded-lg bg-white truncate"
|
||||
label=""
|
||||
selectArray={[defaultOption, ...prcsFcltOptions]}
|
||||
selectArray={[buildFacilityDefaultOption(normalizedDefaultFacilityName), ...prcsFcltOptions]}
|
||||
value={selectedSwagePrcsFclt}
|
||||
onSelectValue={(val) => {
|
||||
onSwagePrcsFcltChange?.(String(val))
|
||||
|
||||
@@ -136,3 +136,9 @@
|
||||
- 새 selector는 시도/시군구 선택 뒤 `GET /api/v1/swagePrcsZones/selectionOptions`를 호출해 `하수처리구역명` 목록을 받고, 선택 시 `하수처리시설명`을 읽기 전용으로 자동 표시한다.
|
||||
- `ii/rdii`, `ii/result`, 지도 검색 모달 등 기존 `CtpvSggPrcsFcltSelectBox` 사용처는 이번 범위에서 변경하지 않는다.
|
||||
- 저장 payload는 기존과 동일하게 `swtrmtFcltNm`만 유지해 회귀 범위를 최소화한다.
|
||||
## 14. 2026-07-09 분석 실행 화면 결과보기 유지보수
|
||||
|
||||
- `ii/irsetting` 우측 액션 영역에 `결과보기` 버튼을 추가한다.
|
||||
- 버튼 클릭 시 분석 화면을 닫지 않고, 별도 `/map` 창과 `침입수 분석결과` popup을 함께 열어 기존 지도형 결과 흐름으로 바로 진입시킨다.
|
||||
- popup row select는 기존 `window.opener.postMessage`를 유지하되, opener가 분석 화면일 수 있으므로 storage 이벤트 기반 보조 동기화를 함께 둔다.
|
||||
- `map` 화면은 `analysisPopupKey` query를 받아 `분석도구` 활성 메뉴 상태를 `침입수 분석결과` 기준으로 맞춘다.
|
||||
|
||||
@@ -44,3 +44,15 @@
|
||||
## 8. 최종 확인 메모
|
||||
- 사용자 실동작 기준으로 `분석도구 > RDII 분석` popup, row click 지도 이동, 관로 highlight, resultOnly popup 흐름이 정상 동작하는 것으로 마감한다.
|
||||
- FE 전체 타입 오류는 이번 RDII 변경과 무관한 기존 선행 이슈만 남아 별도 정리 대상으로 유지한다.
|
||||
## 9. 2026-07-09 분석 화면 결과보기 버튼 연계
|
||||
|
||||
- `ii/BaseLayout.tsx` 공통 액션 영역에 `결과보기` 버튼 슬롯을 추가하고, `ii/rdii/page.tsx`에서 `rdiiResultPopup` 전용 진입 함수를 연결했다.
|
||||
- `ii/analysisResultView.ts`는 `/map?analysisPopupKey=rdiiResultPopup` 창과 RDII 결과 popup을 같은 사용자 클릭에서 함께 연다.
|
||||
- `RdiiResultPopupPage` row click은 storage 이벤트용 payload를 함께 남기고, `useRdiiResultMap`은 storage listener를 추가해 opener가 map이 아닌 경우에도 highlight 흐름을 이어가게 했다.
|
||||
- `map/page.tsx`와 `AnalysisPopupMenu.tsx`는 query 기준으로 `RDII 분석결과` 메뉴 활성 상태를 맞춘다.
|
||||
- 같은 TypeScript 검증에서 이번 RDII 결과보기 버튼 연계 파일 기준 신규 오류는 확인되지 않았다.
|
||||
|
||||
## 10. 2026-07-09 버튼 순서 미세조정
|
||||
|
||||
- 사용자 요청에 따라 RDII 분석 화면 액션 버튼 순서를 `양식 받기 -> RDII분석 실행 -> 결과보기`로 조정했다.
|
||||
- 변경은 공통 `ii/BaseLayout.tsx` 렌더 순서만 수정해 침입수 분석 화면과 동일한 배치 규칙을 사용하도록 맞췄다.
|
||||
|
||||
@@ -100,3 +100,33 @@
|
||||
- 사용자 요청 기준으로 좌측 `I/I 분석` 메뉴의 `침입수 분석결과` 항목은 현재 화면에서 숨겼다.
|
||||
- 숨김 방식은 공통 LNB 데이터 구조를 다시 건드리지 않고, `FE_APP_ROOT/src/app/styles/components.css`에서 해당 서브메뉴 항목만 표시하지 않도록 제한했다.
|
||||
- 이 보정은 메뉴 노출만 감추는 수준이며, 기존 `ii/result` route와 화면 코드는 유지한다.
|
||||
- 따라서 추후 필요 시 스타일 규칙만 제거하면 기존 업무형 결과 화면 진입선을 다시 살릴 수 있다.
|
||||
## 15. 2026-07-09 분석 화면 결과보기 버튼 연계
|
||||
|
||||
- `ii/BaseLayout.tsx`에 `결과보기` 버튼 슬롯을 추가하고, `ii/irsetting/page.tsx`에서 `inftwtResultPopup` 전용 진입 함수를 연결했다.
|
||||
- 새 helper `ii/analysisResultView.ts`는 사용자 클릭 1회에서 `/map?analysisPopupKey=inftwtResultPopup` 창과 결과 popup을 함께 열어 popup 차단 가능성을 줄인다.
|
||||
- `InftwtResultPopupPage` row click은 기존 opener postMessage 외에 `localStorage`에도 선택 row를 기록하고, `useInftwtResultMap`은 storage 이벤트를 함께 구독해 map 창이 opener가 아니어도 지도 연계를 이어가게 했다.
|
||||
- `map/page.tsx`와 `AnalysisPopupMenu.tsx`는 `analysisPopupKey`가 있을 때 `침입수 분석결과` 메뉴를 활성 상태로 보여 주도록 보강했다.
|
||||
- `node node_modules\typescript\bin\tsc --noEmit --ignoreDeprecations 5.0 -p tsconfig.json` 재실행 결과, 이번 변경 파일 기준 신규 오류는 없고 기존 선행 오류만 남았다.
|
||||
|
||||
## 16. 2026-07-09 버튼 순서 미세조정
|
||||
|
||||
- 사용자 요청에 따라 침입수 분석 화면 액션 버튼 순서를 `양식 받기 -> 침입수분석 실행 -> 결과보기`로 조정했다.
|
||||
- 변경은 공통 `ii/BaseLayout.tsx` 렌더 순서만 수정해 RDII 분석 화면에도 같은 순서가 적용되도록 맞췄다.
|
||||
|
||||
## 17. 2026-07-09 실측하수량 관로 우선 줌인 보정
|
||||
|
||||
- 침입수 분석결과 popup에서 row click 시 `oilqPipeCount > 0`이면 시도/시군구 중심 이동보다 `hasOilq` 관로 extent fit을 우선 적용하도록 `useInftwtResultMap`을 보정했다.
|
||||
- 실측하수량 관로가 없는 row만 기존 지역 중심 이동과 일반 결과 관로 fallback을 유지한다.
|
||||
- 이 보정으로 주황색으로 표시되는 실측하수량 관로가 있는 케이스는 지도 진입 직후 해당 관로 주변으로 바로 확대된다.
|
||||
|
||||
## 18. 2026-07-09 지역 선이동 제거 보정
|
||||
|
||||
- `useInftwtResultMap`은 row click 직후 mapView를 먼저 확보한 뒤, `oilqPipeCount > 0`이면 검색바 동기화 이벤트에 `suppressMapMove`를 내려 지역 중심 이동을 건너뛰도록 보정했다.
|
||||
- `procSearchBar`는 `suppressMapMove`가 내려오면 시도/시군구/시설 상태만 갱신하고 `moveMapToCenter`는 호출하지 않는다.
|
||||
- 이 보정으로 실측하수량 관로가 있는 케이스는 처음부터 해당 관로 extent로 바로 확대된다.
|
||||
|
||||
## 19. 2026-07-09 분석 대상지역 기본값 보정
|
||||
|
||||
- `ii/irsetting`, `ii/rdii`의 기본 선택값을 `경기도 / 양평군 / 양평`으로 맞췄다.
|
||||
- 공통 `CtpvSggPrcsFcltSelectBox`는 mount 시 부모 값을 강제로 `0`으로 되돌리던 초기화 흐름을 제거하고, 시설 목록 로딩 후 `defaultSwtrmtFcltNm` 이름 기준으로 기본 시설을 자동 선택하게 보강했다.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"use client";
|
||||
"use client";
|
||||
import { SelectBox, type SelectDataType } from "../../../ui";
|
||||
import { Button } from "@common/components/ui";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
@@ -29,7 +29,7 @@ interface SearchBarProps {
|
||||
}
|
||||
|
||||
interface CenterSelectDataType extends SelectDataType {
|
||||
center?: [number, number]; // 좌표 중심 위치
|
||||
center?: [number, number]; // 醫뚰몴 以묒떖 ?꾩튂
|
||||
}
|
||||
|
||||
export const ProcSearchBar = ({
|
||||
@@ -74,7 +74,7 @@ export const ProcSearchBar = ({
|
||||
center: sidoCenter[String(f.properties.mctpv_cd ?? "").trim()] //getGeometryCenter(f.geometry) ?? undefined,
|
||||
}))
|
||||
.sort((a: any, b: any) => {
|
||||
// 커스텀 순서 정의
|
||||
// 而ㅼ뒪? ?쒖꽌 ?뺤쓽
|
||||
const customOrder = [
|
||||
"서울특별시",
|
||||
"부산광역시",
|
||||
@@ -98,19 +98,19 @@ export const ProcSearchBar = ({
|
||||
const indexA = customOrder.indexOf(a.text);
|
||||
const indexB = customOrder.indexOf(b.text);
|
||||
|
||||
// 둘 다 커스텀 순서에 있으면 해당 순서로
|
||||
// ????而ㅼ뒪? ?쒖꽌???덉쑝硫??대떦 ?쒖꽌濡?
|
||||
if (indexA !== -1 && indexB !== -1) {
|
||||
return indexA - indexB;
|
||||
}
|
||||
|
||||
// 하나만 커스텀 순서에 있으면 그것을 앞으로
|
||||
// ?섎굹留?而ㅼ뒪? ?쒖꽌???덉쑝硫?洹멸쾬???욎쑝濡?
|
||||
if (indexA !== -1) return -1;
|
||||
if (indexB !== -1) return 1;
|
||||
|
||||
// 둘 다 커스텀 순서에 없으면 기본 알파벳 순서
|
||||
// ????而ㅼ뒪? ?쒖꽌???놁쑝硫?湲곕낯 ?뚰뙆踰??쒖꽌
|
||||
return a.text.localeCompare(b.text, 'ko');
|
||||
});
|
||||
setSidoData([{ text: "시도", val: "0" }, ...list]);
|
||||
setSidoData([{ text: "?쒕룄", val: "0" }, ...list]);
|
||||
};
|
||||
|
||||
const fetchSigungu = async () => {
|
||||
@@ -174,7 +174,7 @@ export const ProcSearchBar = ({
|
||||
const json = await res.json();
|
||||
list = (json.features || [])
|
||||
.map((f: any) => ({
|
||||
text: f.properties.swtrmt_fclt_nm ?? "(이름없음)",
|
||||
text: f.properties.swtrmt_fclt_nm ?? "(?대쫫?놁쓬)",
|
||||
val: f.id,
|
||||
}))
|
||||
.sort((a: any, b: any) => a.text.localeCompare(b.text, "ko"));
|
||||
@@ -193,28 +193,28 @@ export const ProcSearchBar = ({
|
||||
});
|
||||
const json = await res.json();
|
||||
list = (json.data || []).map((item: any) => ({
|
||||
text: item.ruseUtztnFcltNm ?? "(이름없음)",
|
||||
text: item.ruseUtztnFcltNm ?? "(?대쫫?놁쓬)",
|
||||
val: item.pbnstRuseFcltId,
|
||||
}));
|
||||
} else if (type === "tb_wr_ruse_fclt_dmd_co") {
|
||||
const res = await fetch(url);
|
||||
const json = await res.json();
|
||||
list = (json.data || []).map((item: any) => ({
|
||||
text: item.dmdCoNm ?? "(이름없음)",
|
||||
text: item.dmdCoNm ?? "(?대쫫?놁쓬)",
|
||||
val: item.dmdInfoId,
|
||||
}));
|
||||
} else if (type === "tb_wr_tmpr_dmd_co") {
|
||||
const res = await fetch(url);
|
||||
const json = await res.json();
|
||||
list = (json.data || []).map((item: any) => ({
|
||||
text: item.dmdCoNm ?? "(이름없음)",
|
||||
text: item.dmdCoNm ?? "(?대쫫?놁쓬)",
|
||||
val: item.fctryMngSn,
|
||||
}));
|
||||
} else {
|
||||
const res = await fetch(url);
|
||||
const json = await res.json();
|
||||
list = (json.data || []).map((item: any) => ({
|
||||
text: item.bdstNm ?? "(이름없음)",
|
||||
text: item.bdstNm ?? "(?대쫫?놁쓬)",
|
||||
val: item.bdstInfoSn,
|
||||
}));
|
||||
}
|
||||
@@ -315,7 +315,7 @@ export const ProcSearchBar = ({
|
||||
|
||||
const handleSearch = useCallback(() => {
|
||||
if (sidoValue === "0") {
|
||||
alert("시도를 선택해 주세요");
|
||||
alert("시도를 선택해 주세요.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -344,10 +344,12 @@ export const ProcSearchBar = ({
|
||||
ctpvCd?: string;
|
||||
sggCd?: string;
|
||||
swtrmtFcltNm?: string;
|
||||
suppressMapMove?: boolean;
|
||||
}>;
|
||||
const nextCtpvCd = String(event.detail?.ctpvCd ?? "0").trim() || "0";
|
||||
const nextSggCd = String(event.detail?.sggCd ?? "0").trim() || "0";
|
||||
const nextSwtrmtFcltNm = String(event.detail?.swtrmtFcltNm ?? "").trim();
|
||||
const suppressMapMove = Boolean(event.detail?.suppressMapMove);
|
||||
const nextSidoCenter = sidoCenter[nextCtpvCd.slice(0, 2)];
|
||||
const nextSigunguCenter = getSigunguCenter(nextSggCd);
|
||||
|
||||
@@ -364,6 +366,10 @@ export const ProcSearchBar = ({
|
||||
setFacilityValue("0");
|
||||
setPendingExternalFacilityName(nextSwtrmtFcltNm);
|
||||
|
||||
if (suppressMapMove) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (nextSggCd !== "0" && nextSigunguCenter) {
|
||||
moveMapToCenter(gisMaps, nextSigunguCenter, 12);
|
||||
return;
|
||||
@@ -430,7 +436,7 @@ export const ProcSearchBar = ({
|
||||
/>
|
||||
<div className="flex flex-row space-x-2">
|
||||
<Button color="primary" className="mx-2 mt-1" size="sm" onClick={handleSearch}>
|
||||
조회
|
||||
議고쉶
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user