1230 lines
43 KiB
TypeScript
1230 lines
43 KiB
TypeScript
/**
|
||
* ➡️ 경로 : 하수관망도 메인 페이지 (시설물 클릭) - 오른쪽 팝업창
|
||
*
|
||
* 📄 FeatureClickEventPopup
|
||
* 시설물(피처) 클릭시 오른쪽 팝업창 컴포넌트
|
||
*
|
||
* - 지도 시설물(피처) 클릭 시 팝업 표시 및 데이터 로딩
|
||
* - 레이어 유형에 따른 동적 테이블 및 헤드리스트 설정
|
||
* - 자체입력정보 조회 및 저장 기능 (하수관로, 우수토실, 하수펌프장, 하수맨홀)
|
||
* - 코드 매핑 및 데이터 포맷팅 처리
|
||
* - 상류관로 검색, 침입수/RDII 분석 차트 표시
|
||
* - 모달을 통한 저장 확인 및 API 연동
|
||
*
|
||
* 🔗 사용 API
|
||
* - 하수관로 자체입력정보 조회: GET /api/v1/fcltppl/{fcltMngNo}/{mngInstCd} (sewerage-service)
|
||
* - 우수토실 자체입력정보 조회: GET /api/v1/fcltstofcbr/{fcltMngNo}/{mngInstCd} (sewerage-service)
|
||
* - 하수펌프장 자체입력정보 조회: GET /api/v1/fcltpump/{fcltMngNo}/{mngInstCd} (sewerage-service)
|
||
* - 하수맨홀 자체입력정보 조회: GET /api/v1/fcltmhl/{fcltMngNo}/{mngInstCd} (sewerage-service)
|
||
* - 하수관로 자체입력정보 저장: POST /api/v1/fcltppl (sewerage-service)
|
||
* - 우수토실 자체입력정보 저장: POST /api/v1/fcltstofcbr (sewerage-service)
|
||
* - 하수펌프장 자체입력정보 저장: POST /api/v1/fcltpump (sewerage-service)
|
||
* - 하수맨홀 자체입력정보 저장: POST /api/v1/fcltmhl (sewerage-service)
|
||
*/
|
||
"use client";
|
||
import { getData, postData } from "@/components/lib/api";
|
||
import { VerticalTable } from "@/components/ui/verticalTable";
|
||
import { useGisContext } from "@common/business_components/ui/map/Gis";
|
||
import GisApp from "@common/business_components/ui/map/lib/gis.core";
|
||
import { Button } from "@common/components/ui";
|
||
import { useEffect, useRef, useState, useMemo } from "react";
|
||
import Style from "ol/style/Style.js";
|
||
import Fill from "ol/style/Fill.js";
|
||
import Stroke from "ol/style/Stroke.js";
|
||
import CircleStyle from "ol/style/Circle.js";
|
||
import { getLayerContentComponent } from "../common";
|
||
import {
|
||
FldngWtlvMapCol,
|
||
FldngWtlvMapCol2,
|
||
FldngDpthMapCol,
|
||
SwtrmtFcltCol,
|
||
SwtrmtFcltCol2,
|
||
SwtrmtPrcsZoneCol,
|
||
SwtrmtPrcsZoneCol2,
|
||
untrtSwgRainCol1,
|
||
untrtSwgRainCol2,
|
||
MsSwagePplCol,
|
||
MsSwagePplCol2,
|
||
ExclnSewagePumpCol,
|
||
ExclnSewagePumpCol2,
|
||
DrtwSewagePumpCol,
|
||
DrtwSewagePumpCol2,
|
||
MsSwageMhlCol,
|
||
MsSwageMhlCol2,
|
||
stofcbrCol,
|
||
stofcbrCol2,
|
||
MS_SWAGE_MHL_CODES,
|
||
DRTW_SEWAGE_PUMP_USE_CODES,
|
||
} from "../common/table";
|
||
import { formatDisplayData } from "@/components/lib/utils/formatDisplayValue";
|
||
import { codeMapper } from "../common/codeMapper";
|
||
import LoadingOverlay from "@/components/ui/LoadingBar";
|
||
import { searchUpperPpls } from "./smh";
|
||
import {
|
||
getElapsedYearsDisplayText,
|
||
isInvalidInstallYmd,
|
||
} from "../agedPipe/agedPipeUtils";
|
||
import { openWindowPopup } from "@/features/windowPopup/lib/openWindowPopup";
|
||
import { useAlertStore } from "@/shared/lib";
|
||
import { useConfirm } from "@frontend-opensource/use-react-hooks";
|
||
import useStore from "@/store/useStore";
|
||
import ExmnSurveyResultSection from "../exmn/ExmnSurveyResultSection";
|
||
|
||
const linkedFacilityHighlightStyle = new Style({
|
||
image: new CircleStyle({
|
||
radius: 18,
|
||
fill: new Fill({
|
||
color: "rgba(255, 235, 59, 0.35)",
|
||
}),
|
||
stroke: new Stroke({
|
||
color: "rgba(255, 193, 7, 0.45)",
|
||
width: 2,
|
||
}),
|
||
}),
|
||
fill: new Fill({
|
||
color: "rgba(255, 235, 59, 0.2)",
|
||
}),
|
||
stroke: new Stroke({
|
||
color: "rgba(255, 193, 7, 0.35)",
|
||
width: 2.5,
|
||
}),
|
||
zIndex: 9999,
|
||
});
|
||
|
||
const normalizeFldngWtlvMapDisplayId = (value: unknown) => {
|
||
const normalized = String(value ?? "").trim();
|
||
const prefix = "tb_ms_fldng_wtlv_map.";
|
||
return normalized.startsWith(prefix)
|
||
? normalized.slice(prefix.length)
|
||
: normalized;
|
||
};
|
||
|
||
const normalizeFldngWtlvMapApiFid = (value: unknown) => {
|
||
const normalized = String(value ?? "").trim();
|
||
if (!normalized) return "";
|
||
return normalizeFldngWtlvMapDisplayId(normalized);
|
||
};
|
||
|
||
const normalizeRainAnalysisPayload = (data: any) => {
|
||
if (!data || typeof data !== "object") return {};
|
||
return {
|
||
rain_bgng_tm: data.rainBgngTm ?? data.rain_bgng_tm ?? null,
|
||
rainBgngTm: data.rainBgngTm ?? data.rain_bgng_tm ?? null,
|
||
rain_end_tm: data.rainEndTm ?? data.rain_end_tm ?? null,
|
||
rainEndTm: data.rainEndTm ?? data.rain_end_tm ?? null,
|
||
rain_tot: data.rainTot ?? data.rain_tot ?? null,
|
||
rainTot: data.rainTot ?? data.rain_tot ?? null,
|
||
rain_str: data.rainStr ?? data.rain_str ?? null,
|
||
rainStr: data.rainStr ?? data.rain_str ?? null,
|
||
};
|
||
};
|
||
|
||
// 침수흔적도 침수시작일시, 침수종료일시 한줄로 보이도록
|
||
const formatFldngWtlvDateTime = (dateValue: unknown, timeValue: unknown) => {
|
||
const rawDate = String(dateValue ?? "").trim();
|
||
const rawTime = String(timeValue ?? "").trim();
|
||
|
||
const normalizedDate = /^\d{8}$/.test(rawDate)
|
||
? `${rawDate.slice(0, 4)}-${rawDate.slice(4, 6)}-${rawDate.slice(6, 8)}`
|
||
: rawDate;
|
||
|
||
const timeDigits = rawTime.replace(/[^0-9]/g, "");
|
||
const normalizedTime = !timeDigits
|
||
? ""
|
||
: timeDigits.length <= 2
|
||
? `${timeDigits.padStart(2, "0")}:00`
|
||
: `${timeDigits.padStart(4, "0").slice(0, 2)}:${timeDigits.padStart(4, "0").slice(2, 4)}`;
|
||
|
||
return [normalizedDate, normalizedTime].filter(Boolean).join(" ");
|
||
};
|
||
|
||
type MngInstOption = {
|
||
mngInstCd: string;
|
||
lwrInstNm: string;
|
||
};
|
||
|
||
const FeatureClickEventPopup = ({
|
||
onClose,
|
||
featureData,
|
||
surveyMode = false,
|
||
}: {
|
||
onClose: () => void;
|
||
featureData: any;
|
||
surveyMode?: boolean;
|
||
}) => {
|
||
const alert = useAlertStore((state) => state.setMessage);
|
||
const { confirm } = useConfirm();
|
||
const { gisMap } = useGisContext() as unknown as {
|
||
checkedLayerIds: React.MutableRefObject<number[]>;
|
||
treeData: { lyrSn: number }[] | null;
|
||
gisCore: any;
|
||
gisMap: any;
|
||
};
|
||
const [layerNm, setLayerNm] = useState<string>(""); // 레이어 이름 상태
|
||
const [layerSpecificNm, setLayerSpecificNm] = useState<string>(""); // 레이어 이름 상태
|
||
const [tableData, setTableData] = useState<any>(null); // 피처 데이터
|
||
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<any>(); // api 호출여부
|
||
const contentRef = useRef<HTMLDivElement>(null);
|
||
const [isMainSsc, setIsMainSsc] = useState(false); // 중점우수토실 여부
|
||
const [isDrtwSewagePump, setIsDrtwSewagePump] = useState(false); // 오수펌프장 여부
|
||
// 🎯 InfilChart 및 RdiiChart 관련 상태 추가
|
||
const [isClicked, setIsClicked] = useState(true); // 클릭 여부 상태
|
||
const [isLoading, setIsLoading] = useState(false); // 로딩 상태
|
||
const [chmbrXcnts, setChmbrXcnts] = useState<string>(""); // 로딩 상태
|
||
const [chmbrYdnts, setChmbrYdnts] = useState<string>(""); // 로딩 상태
|
||
const linkedFacilityFeatureRef = useRef<any>(null);
|
||
const linkedFacilityOriginalStyleRef = useRef<any>(null);
|
||
const flowDirectionBasisRequestCacheRef = useRef(
|
||
new Map<string, Promise<any | null>>(),
|
||
);
|
||
const [mngInstOptions, setMngInstOptions] = useState<MngInstOption[]>([]);
|
||
const [floodDepthRows, setFloodDepthRows] = useState<
|
||
Record<string, unknown>[]
|
||
>([]);
|
||
const [selectedFloodDepthYear, setSelectedFloodDepthYear] = useState<
|
||
string | null
|
||
>(null);
|
||
const floodDepthYearListRef = useRef<HTMLDivElement>(null);
|
||
const { ctpv, sgg, fetchCtpv, fetchSgg } = useStore();
|
||
|
||
// 🎯 관로관리번호 추출
|
||
const pplMngNo =
|
||
featureData?.values_?.fclt_mng_no || featureData?.fcltMngNo || "";
|
||
|
||
const featureId =
|
||
typeof featureData.getId === "function"
|
||
? featureData.getId()
|
||
: featureData.fid;
|
||
const selectedLayerTypeName = featureData?.values_
|
||
? featureData?.id_?.split(".")?.[0]
|
||
: featureData?.lyrTypeNm;
|
||
const isPipeFeature = selectedLayerTypeName === "tb_ms_swage_ppl";
|
||
|
||
// 🎯 데이터 전처리 메모이제이션
|
||
const processedData = useMemo(() => {
|
||
if (!featureData?.values_) return null;
|
||
|
||
return {
|
||
...featureData.values_,
|
||
discharge_pipe_spec: `직경: ${featureData.values_.pipe_diameter || "-"} / 높이: ${featureData.values_.pipe_hg || "-"} / 길이: ${featureData.values_.pipe_lt || "-"}`,
|
||
};
|
||
}, [featureData?.values_]);
|
||
|
||
useEffect(() => {
|
||
let isMounted = true;
|
||
|
||
if (isPipeFeature) {
|
||
setMngInstOptions([]);
|
||
return () => {
|
||
isMounted = false;
|
||
};
|
||
}
|
||
|
||
const fetchMngInstOptions = async () => {
|
||
try {
|
||
const result = await getData("v1/fcltppl/mngInsts");
|
||
if (!isMounted) return;
|
||
const sortedOptions = Array.isArray(result)
|
||
? [...result].sort((a, b) =>
|
||
String(a?.lwrInstNm ?? a?.mngInstCd ?? "").localeCompare(
|
||
String(b?.lwrInstNm ?? b?.mngInstCd ?? ""),
|
||
"ko",
|
||
),
|
||
)
|
||
: [];
|
||
setMngInstOptions(sortedOptions);
|
||
} catch (error) {
|
||
console.error("관리기관 목록 조회 실패:", error);
|
||
if (isMounted) setMngInstOptions([]);
|
||
}
|
||
};
|
||
|
||
fetchMngInstOptions();
|
||
return () => {
|
||
isMounted = false;
|
||
};
|
||
}, [isPipeFeature]);
|
||
|
||
useEffect(() => {
|
||
if (ctpv.length === 0) void fetchCtpv();
|
||
if (sgg.length === 0) void fetchSgg();
|
||
}, [ctpv.length, fetchCtpv, fetchSgg, sgg.length]);
|
||
|
||
const mngInstNameMap = useMemo(() => {
|
||
const map = new Map<string, string>();
|
||
mngInstOptions.forEach((option) => {
|
||
const code = String(option.mngInstCd ?? "").trim();
|
||
if (!code) return;
|
||
map.set(code, option.lwrInstNm || code);
|
||
});
|
||
return map;
|
||
}, [mngInstOptions]);
|
||
|
||
const withMngInstName = (data: any) => {
|
||
if (!data || typeof data !== "object") return data;
|
||
const mngInstCd = String(data.mng_inst_cd ?? data.mngInstCd ?? "").trim();
|
||
if (!mngInstCd) return data;
|
||
return {
|
||
...data,
|
||
mngInstNm:
|
||
mngInstNameMap.get(mngInstCd) ??
|
||
data.mngInstNm ??
|
||
data.mng_inst_nm ??
|
||
mngInstCd,
|
||
};
|
||
};
|
||
|
||
const withResolvedRegionNames = (data: any) => {
|
||
if (!data || typeof data !== "object") return data;
|
||
|
||
const stdgCd = String(data.stdg_cd ?? data.stdgCd ?? "").trim();
|
||
const ctpvCd = String(
|
||
data.ctpv_cd ?? data.ctpvCd ?? stdgCd.slice(0, 2),
|
||
).trim();
|
||
const sggCd = String(
|
||
data.sgg_cd ?? data.sggCd ?? stdgCd.slice(0, 5),
|
||
).trim();
|
||
const resolvedCtpvNm =
|
||
ctpv.find((item) => String(item.val).trim() === ctpvCd)?.text ?? "";
|
||
const resolvedSggNm =
|
||
sgg.find((item) => String(item.val).trim() === sggCd)?.text ?? "";
|
||
|
||
return {
|
||
...data,
|
||
ctpv_nm: data.ctpv_nm ?? data.ctpvNm ?? resolvedCtpvNm,
|
||
ctpvNm: data.ctpvNm ?? data.ctpv_nm ?? resolvedCtpvNm,
|
||
sgg_nm: data.sgg_nm ?? data.sggNm ?? resolvedSggNm,
|
||
sggNm: data.sggNm ?? data.sgg_nm ?? resolvedSggNm,
|
||
};
|
||
};
|
||
|
||
const hasDetailValue = (value: unknown) => {
|
||
if (value === null || value === undefined) return false;
|
||
if (typeof value === "string") return value.trim() !== "";
|
||
return true;
|
||
};
|
||
|
||
const isValidDetailIdentifier = (value: unknown) => {
|
||
if (typeof value !== "string") return false;
|
||
const normalizedValue = value.trim();
|
||
if (!normalizedValue) return false;
|
||
if (normalizedValue.toLowerCase() === "null") return false;
|
||
if (normalizedValue.toLowerCase() === "undefined") return false;
|
||
return true;
|
||
};
|
||
|
||
const mergeDetailData = (baseData: any, detailData: any) => {
|
||
if (!detailData || typeof detailData !== "object") return baseData;
|
||
|
||
const meaningfulDetailData = Object.entries(detailData).reduce<
|
||
Record<string, unknown>
|
||
>((acc, [key, value]) => {
|
||
if (hasDetailValue(value)) acc[key] = value;
|
||
return acc;
|
||
}, {});
|
||
|
||
return {
|
||
...(baseData ?? {}),
|
||
...meaningfulDetailData,
|
||
};
|
||
};
|
||
|
||
const clearLinkedFacilityHighlight = () => {
|
||
if (linkedFacilityFeatureRef.current) {
|
||
linkedFacilityFeatureRef.current.setStyle(
|
||
linkedFacilityOriginalStyleRef.current ?? undefined,
|
||
);
|
||
linkedFacilityFeatureRef.current.changed?.();
|
||
}
|
||
|
||
linkedFacilityFeatureRef.current = null;
|
||
linkedFacilityOriginalStyleRef.current = null;
|
||
};
|
||
|
||
// 하수처리구역에 연결된 하수처리시설 피처를 찾아 기존 아이콘 위에 노랑 하이라이트를 덧씌운다.
|
||
const highlightLinkedFacility = (facilityCode: string | null | undefined) => {
|
||
clearLinkedFacilityHighlight();
|
||
|
||
if (!gisMap || !facilityCode) return;
|
||
|
||
const normalizedFacilityCode = String(facilityCode).trim();
|
||
const layers = gisMap.getLayers().getArray();
|
||
|
||
for (const layer of layers) {
|
||
const source = layer?.getSource?.();
|
||
if (!source?.getFeatures) continue;
|
||
|
||
const features = source.getFeatures();
|
||
const targetFeature = features.find((feature: any) => {
|
||
const featureId = feature?.getId?.();
|
||
const isFacilityLayer =
|
||
typeof featureId === "string" &&
|
||
featureId.startsWith("tb_il_swage_prcs_fclt.");
|
||
const featureFacilityCode = feature?.get?.("swtrmt_fclt_cd");
|
||
|
||
return (
|
||
isFacilityLayer &&
|
||
String(featureFacilityCode ?? "").trim() === normalizedFacilityCode
|
||
);
|
||
});
|
||
|
||
if (targetFeature) {
|
||
const originalFeatureStyle = targetFeature.getStyle?.();
|
||
const featureLayer = targetFeature.getLayer?.();
|
||
const layerStyleFn = featureLayer?.getStyleFunction?.() ?? null;
|
||
|
||
linkedFacilityOriginalStyleRef.current = originalFeatureStyle;
|
||
linkedFacilityFeatureRef.current = targetFeature;
|
||
targetFeature.setStyle((feature: any, resolution: number) => {
|
||
let styles: any[] = [];
|
||
|
||
if (typeof originalFeatureStyle === "function") {
|
||
const originalStyles = originalFeatureStyle(feature, resolution);
|
||
if (Array.isArray(originalStyles)) styles.push(...originalStyles);
|
||
else if (originalStyles) styles.push(originalStyles);
|
||
} else if (originalFeatureStyle) {
|
||
if (Array.isArray(originalFeatureStyle))
|
||
styles.push(...originalFeatureStyle);
|
||
else styles.push(originalFeatureStyle);
|
||
} else if (layerStyleFn) {
|
||
const layerStyles = layerStyleFn(feature, resolution);
|
||
if (Array.isArray(layerStyles)) styles.push(...layerStyles);
|
||
else if (layerStyles) styles.push(layerStyles);
|
||
}
|
||
|
||
styles.push(linkedFacilityHighlightStyle);
|
||
return styles;
|
||
});
|
||
targetFeature.changed?.();
|
||
return;
|
||
}
|
||
}
|
||
};
|
||
|
||
// 피쳐데이터의 현재 레이어 이름 가져오는 함수
|
||
const getSelectedLayers = () => {
|
||
let result = null;
|
||
const layers = gisMap.getLayers().getArray(); // gisMap에서 모든 레이어 가져오기
|
||
let featureLayer: any = null; // 피처가 속한 레이어를 찾기 위한 변수
|
||
layers.some((layer: any) => {
|
||
const source = layer.getSource();
|
||
// if (source && featureData && featureData.fid) {
|
||
// featureLayer = layer; // 피처가 속한 레이어 찾기
|
||
// }
|
||
if (
|
||
source &&
|
||
source.hasFeature &&
|
||
featureData &&
|
||
typeof featureData.getId === "function" &&
|
||
source.hasFeature(featureData)
|
||
) {
|
||
featureLayer = layer; // 피처가 속한 레이어 찾기
|
||
return true; // 반복 종료
|
||
}
|
||
return false;
|
||
});
|
||
|
||
if (featureData["ol_uid"]) {
|
||
// Gis 레이어 클릭 이벤트
|
||
setIsClicked(true);
|
||
const lyrTyNm = featureData.id_.split(".")[0];
|
||
|
||
switch (lyrTyNm) {
|
||
case "tb_ms_swage_ppl": {
|
||
result = "하수관로";
|
||
setHeadlist(MsSwagePplCol);
|
||
setLayerSpecificNm(featureData.values_.fclt_mng_no);
|
||
break;
|
||
}
|
||
case "tb_ms_swage_mhl": {
|
||
result = "하수맨홀";
|
||
setHeadlist(MsSwageMhlCol);
|
||
setLayerSpecificNm(featureData.values_.fclt_mng_no);
|
||
break;
|
||
}
|
||
case "tb_ms_swage_pmstn": {
|
||
const drtwPumpCode = DRTW_SEWAGE_PUMP_USE_CODES.find(
|
||
(code) => code.text === "오수펌프장",
|
||
);
|
||
if (
|
||
drtwPumpCode &&
|
||
featureData.values_.usg_cd.trim() === drtwPumpCode.val
|
||
) {
|
||
setIsDrtwSewagePump(true);
|
||
setHeadlist(DrtwSewagePumpCol);
|
||
} else {
|
||
setIsDrtwSewagePump(false);
|
||
setHeadlist(ExclnSewagePumpCol);
|
||
}
|
||
setLayerSpecificNm(featureData.values_.fclt_mng_no);
|
||
result = "하수펌프장";
|
||
break;
|
||
}
|
||
case "tb_il_swage_prcs_fclt": {
|
||
result = "하수처리시설";
|
||
setHeadlist(SwtrmtFcltCol);
|
||
setLayerSpecificNm(featureData.values_.swtrmt_fclt_cd);
|
||
break;
|
||
}
|
||
case "tb_ms_swage_prcs_zone": {
|
||
result = "하수처리구역";
|
||
setHeadlist(SwtrmtPrcsZoneCol);
|
||
setLayerSpecificNm(featureData.values_.swtrmt_fclt_cd);
|
||
break;
|
||
}
|
||
case "tb_ms_fldng_wtlv_map": {
|
||
result = "침수흔적위선";
|
||
setHeadlist(FldngWtlvMapCol);
|
||
setLayerSpecificNm(
|
||
normalizeFldngWtlvMapDisplayId(
|
||
featureData.values_.fid ?? featureId,
|
||
),
|
||
);
|
||
break;
|
||
}
|
||
case "tb_ms_fldng_dpth_map": {
|
||
result = "침수흔적심선";
|
||
setHeadlist(FldngDpthMapCol);
|
||
setLayerSpecificNm(
|
||
String(featureId ?? "").replace(/^tb_ms_fldng_dpth_map\./, ""),
|
||
);
|
||
break;
|
||
}
|
||
case "tb_ms_stofcbr": {
|
||
result = "우수토실";
|
||
setHeadlist(stofcbrCol);
|
||
setLayerSpecificNm(featureData.values_.fclt_mng_no);
|
||
break;
|
||
}
|
||
case "om_dt_untrt_swg_rain": {
|
||
result = "중점우수토실";
|
||
setHeadlist(untrtSwgRainCol1);
|
||
setHeadlist2(untrtSwgRainCol2);
|
||
setLayerSpecificNm(featureData.values_.manage_no);
|
||
break;
|
||
}
|
||
default: {
|
||
result = featureLayer ? featureLayer.get("lyrNm") : null;
|
||
break;
|
||
}
|
||
}
|
||
} else {
|
||
// SearchModal 클릭 이벤트
|
||
setIsClicked(false);
|
||
const lyrTyNm = featureData.lyrTypeNm;
|
||
|
||
switch (lyrTyNm) {
|
||
case "tb_ms_swage_ppl": {
|
||
result = "하수관로";
|
||
setHeadlist(MsSwagePplCol2);
|
||
setLayerSpecificNm(featureData.fcltMngNo ?? featureData.swtrmtFcltCd);
|
||
break;
|
||
}
|
||
case "tb_ms_swage_mhl": {
|
||
result = "하수맨홀";
|
||
setHeadlist(MsSwageMhlCol2);
|
||
setLayerSpecificNm(featureData.fcltMngNo);
|
||
break;
|
||
}
|
||
case "tb_ms_swage_pmstn": {
|
||
const drtwPumpCode = DRTW_SEWAGE_PUMP_USE_CODES.find(
|
||
(code) => code.text === "오수펌프장",
|
||
);
|
||
if (drtwPumpCode && featureData.usgCd.trim() === drtwPumpCode.val) {
|
||
setIsDrtwSewagePump(false);
|
||
setHeadlist(ExclnSewagePumpCol2);
|
||
} else {
|
||
setIsDrtwSewagePump(true);
|
||
setHeadlist(DrtwSewagePumpCol2);
|
||
}
|
||
setLayerSpecificNm(featureData.fcltMngNo);
|
||
result = "하수펌프장";
|
||
break;
|
||
}
|
||
case "tb_il_swage_prcs_fclt": {
|
||
result = "하수처리시설";
|
||
setHeadlist(SwtrmtFcltCol2);
|
||
setLayerSpecificNm(featureData.swtrmtFcltCd);
|
||
break;
|
||
}
|
||
case "tb_ms_swage_prcs_zone": {
|
||
result = "하수처리구역";
|
||
setHeadlist(SwtrmtPrcsZoneCol2);
|
||
setLayerSpecificNm(featureData.swtrmtFcltCd);
|
||
break;
|
||
}
|
||
case "tb_ms_fldng_wtlv_map": {
|
||
result = "침수흔적위선";
|
||
setHeadlist(FldngWtlvMapCol2);
|
||
setLayerSpecificNm(normalizeFldngWtlvMapDisplayId(featureData.fid));
|
||
break;
|
||
}
|
||
case "tb_ms_fldng_dpth_map": {
|
||
result = "침수흔적심선";
|
||
setHeadlist(FldngDpthMapCol);
|
||
setLayerSpecificNm(
|
||
String(featureData.fid ?? featureId ?? "").replace(
|
||
/^tb_ms_fldng_dpth_map\./,
|
||
"",
|
||
),
|
||
);
|
||
break;
|
||
}
|
||
case "tb_ms_stofcbr": {
|
||
result = "우수토실";
|
||
setHeadlist(stofcbrCol2);
|
||
setLayerSpecificNm(featureData.fcltMngNo);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
|
||
return result;
|
||
};
|
||
|
||
useEffect(() => {
|
||
const fetchData = async () => {
|
||
if (!featureId) return;
|
||
|
||
try {
|
||
const selectedLayerName = getSelectedLayers(); // 레이어 이름 가져오기
|
||
setLayerNm(selectedLayerName); // 상태 업데이트
|
||
|
||
let data;
|
||
if (featureData["ol_uid"])
|
||
data = featureData.values_; // Gis 레이어 클릭 이벤트일 때
|
||
else data = featureData; // SearchModal 클릭 이벤트일 때
|
||
|
||
// 하수맨홀인 경우 용도코드를 텍스트로 변환
|
||
if (selectedLayerName === "하수맨홀" && data) {
|
||
// mqlt_cd 값 변환 (재질코드)
|
||
if (data.mqlt_cd) {
|
||
const materialCodeItem = MS_SWAGE_MHL_CODES.find(
|
||
(code) => code.val === data.mqlt_cd.trim(),
|
||
);
|
||
if (materialCodeItem) data.mqlt_cd = `${materialCodeItem.text}`;
|
||
}
|
||
} else {
|
||
// 중점우수토실인 경우 구분 값
|
||
if (selectedLayerName === "중점우수토실" && data) {
|
||
if (data.mesure_spot_se) {
|
||
const mesureSpotSe = [
|
||
{ id: "0", text: "합류식" },
|
||
{ id: "1", text: "분류식" },
|
||
];
|
||
const mesureSpotSeItem = mesureSpotSe.find(
|
||
(code) => code.id === data.mesure_spot_se.trim(),
|
||
);
|
||
if (mesureSpotSeItem) data.mesure_spot_se = mesureSpotSeItem.text;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 🎯 기본 데이터 포맷팅
|
||
const normalizedRegionData =
|
||
selectedLayerName === "침수흔적위선" ||
|
||
selectedLayerName === "침수흔적심선"
|
||
? withResolvedRegionNames(data)
|
||
: data;
|
||
const formattedData = withMngInstName(
|
||
formatDisplayData(normalizedRegionData),
|
||
);
|
||
if (selectedLayerName === "침수흔적위선") {
|
||
const fldngBgngDtm = formatFldngWtlvDateTime(
|
||
formattedData.bgngYmd ??
|
||
formattedData.bgng_ymd ??
|
||
data.bgngYmd ??
|
||
data.bgng_ymd,
|
||
formattedData.fldngBgngHm ??
|
||
formattedData.fldng_bgng_hm ??
|
||
data.fldngBgngHm ??
|
||
data.fldng_bgng_hm,
|
||
);
|
||
const fldngEndDtm = formatFldngWtlvDateTime(
|
||
formattedData.endYmd ??
|
||
formattedData.end_ymd ??
|
||
data.endYmd ??
|
||
data.end_ymd,
|
||
formattedData.fldngEndHm ??
|
||
formattedData.fldng_end_hm ??
|
||
data.fldngEndHm ??
|
||
data.fldng_end_hm,
|
||
);
|
||
|
||
formattedData.fldng_bgng_dtm = fldngBgngDtm;
|
||
formattedData.fldngBgngDtm = fldngBgngDtm;
|
||
formattedData.fldng_end_dtm = fldngEndDtm;
|
||
formattedData.fldngEndDtm = fldngEndDtm;
|
||
}
|
||
if (selectedLayerName === "하수관로") {
|
||
const installYmd = data.instl_ymd ?? data.instlYmd;
|
||
formattedData.elapsedYears = getElapsedYearsDisplayText(installYmd);
|
||
if (isInvalidInstallYmd(installYmd)) {
|
||
formattedData.instl_ymd = "확인불가";
|
||
formattedData.instlYmd = "확인불가";
|
||
}
|
||
}
|
||
let mergedDetailData = formattedData;
|
||
|
||
setFetchData(formattedData);
|
||
setTableData(formattedData);
|
||
|
||
switch (selectedLayerName) {
|
||
case "하수관로":
|
||
setButtonChk(true);
|
||
|
||
// ✅ codeMapper 사용하여 API 결과도 변환
|
||
{
|
||
const initialPipeData = withMngInstName(
|
||
codeMapper({
|
||
layerName: selectedLayerName,
|
||
data: { ...formattedData },
|
||
}),
|
||
);
|
||
setFetchData(initialPipeData);
|
||
setTableData(initialPipeData);
|
||
}
|
||
break;
|
||
case "하수맨홀":
|
||
setButtonChk(true);
|
||
|
||
// ✅ codeMapper 사용하여 API 결과도 변환
|
||
setTableData(
|
||
codeMapper({
|
||
layerName: selectedLayerName,
|
||
data: { ...formattedData },
|
||
}),
|
||
);
|
||
break;
|
||
case "하수펌프장":
|
||
setButtonChk(true);
|
||
|
||
setTableData(
|
||
codeMapper({
|
||
layerName: selectedLayerName,
|
||
data: { ...formattedData },
|
||
}),
|
||
);
|
||
break;
|
||
case "하수처리시설":
|
||
setButtonChk(false);
|
||
break;
|
||
case "하수처리구역":
|
||
setButtonChk(false);
|
||
break;
|
||
case "침수흔적위선":
|
||
setButtonChk(false);
|
||
break;
|
||
case "우수토실":
|
||
setButtonChk(true);
|
||
setIsMainSsc(false);
|
||
break;
|
||
case "중점우수토실":
|
||
setButtonChk(false);
|
||
setIsMainSsc(true);
|
||
// 🎯 processedData도 포맷팅하여 설정
|
||
const processedFormattedData = formatDisplayData(processedData);
|
||
|
||
setFetchData([processedFormattedData]);
|
||
setTableData(processedFormattedData);
|
||
// setIsLoading(false); // 기본 렌더링 완료
|
||
|
||
setTableData(
|
||
codeMapper({
|
||
layerName: selectedLayerName,
|
||
data: { ...processedFormattedData },
|
||
}),
|
||
);
|
||
|
||
setChmbrXcnts(data.chmbr_xcnts);
|
||
setChmbrYdnts(data.chmbr_ydnts);
|
||
|
||
break;
|
||
default:
|
||
break;
|
||
}
|
||
|
||
// 자체입력정보 API 엔드포인트 선택
|
||
let apiEndpoint = "";
|
||
const fcltMngNo = String(
|
||
featureData?.values_?.fclt_mng_no || featureData?.fcltMngNo || "",
|
||
).trim();
|
||
const mngInstCd = String(
|
||
featureData?.values_?.mng_inst_cd || featureData?.mngInstCd || "",
|
||
).trim();
|
||
const canRequestDetail =
|
||
isValidDetailIdentifier(fcltMngNo) &&
|
||
isValidDetailIdentifier(mngInstCd);
|
||
if (selectedLayerName === "하수관로")
|
||
apiEndpoint = `v1/fcltppl/${fcltMngNo}/${mngInstCd}`;
|
||
else if (selectedLayerName === "우수토실")
|
||
apiEndpoint = `v1/fcltstofcbr/${fcltMngNo}/${mngInstCd}`;
|
||
else if (selectedLayerName === "하수펌프장")
|
||
apiEndpoint = `v1/fcltpump/${fcltMngNo}/${mngInstCd}`;
|
||
else if (selectedLayerName === "하수맨홀")
|
||
apiEndpoint = `v1/fcltmhl/${fcltMngNo}/${mngInstCd}`;
|
||
|
||
// API에서 상세 데이터 가져오기
|
||
if (apiEndpoint && canRequestDetail) {
|
||
try {
|
||
const result = await getData(apiEndpoint);
|
||
|
||
if (result) {
|
||
mergedDetailData = mergeDetailData(mergedDetailData, result);
|
||
const processedApiData = withMngInstName(
|
||
codeMapper({
|
||
layerName: selectedLayerName,
|
||
data: mergedDetailData,
|
||
}),
|
||
);
|
||
|
||
setFetchData(processedApiData);
|
||
setTableData(processedApiData);
|
||
}
|
||
} catch (apiError) {
|
||
void apiError;
|
||
console.warn("Pipe detail fallback kept initial feature data.");
|
||
}
|
||
}
|
||
|
||
if (selectedLayerName === "하수관로" && canRequestDetail) {
|
||
try {
|
||
const escapeCqlLiteral = (value: string) =>
|
||
value.replace(/'/g, "''");
|
||
const requestCacheKey = `${fcltMngNo}::${mngInstCd}`;
|
||
const params = new URLSearchParams({
|
||
lyrTypeNm: "tb_ms_swage_ppl",
|
||
includeFlowDirectionBasis: "true",
|
||
cqlFiter:
|
||
`fclt_mng_no='${escapeCqlLiteral(fcltMngNo)}' and ` +
|
||
`mng_inst_cd='${escapeCqlLiteral(mngInstCd)}'`,
|
||
page: "0",
|
||
size: "1",
|
||
});
|
||
let flowDirectionRequest =
|
||
flowDirectionBasisRequestCacheRef.current.get(requestCacheKey);
|
||
if (!flowDirectionRequest) {
|
||
flowDirectionRequest = getData(`v1/fclts?${params.toString()}`)
|
||
.then((response) =>
|
||
Array.isArray(response?.data) ? response.data[0] : null,
|
||
)
|
||
.catch((error) => {
|
||
flowDirectionBasisRequestCacheRef.current.delete(
|
||
requestCacheKey,
|
||
);
|
||
throw error;
|
||
});
|
||
flowDirectionBasisRequestCacheRef.current.set(
|
||
requestCacheKey,
|
||
flowDirectionRequest,
|
||
);
|
||
}
|
||
const enrichedPipe = await flowDirectionRequest;
|
||
if (enrichedPipe) {
|
||
mergedDetailData = mergeDetailData(
|
||
mergedDetailData,
|
||
enrichedPipe,
|
||
);
|
||
const processedPipeData = withMngInstName(
|
||
codeMapper({
|
||
layerName: selectedLayerName,
|
||
data: mergedDetailData,
|
||
}),
|
||
);
|
||
setFetchData(processedPipeData);
|
||
setTableData(processedPipeData);
|
||
}
|
||
} catch (flowDirectionError) {
|
||
console.warn(
|
||
"관로 유하방향 근거 조회 실패, 기존 상세정보를 유지합니다.",
|
||
flowDirectionError,
|
||
);
|
||
}
|
||
}
|
||
|
||
if (selectedLayerName === "침수흔적위선" && featureId) {
|
||
const depthLookupKey = normalizeFldngWtlvMapApiFid(featureId);
|
||
setFloodDepthRows([]);
|
||
setSelectedFloodDepthYear(null);
|
||
try {
|
||
const normalizedFloodMapFid =
|
||
normalizeFldngWtlvMapApiFid(featureId);
|
||
if (!normalizedFloodMapFid) {
|
||
throw new Error("침수흔적도 fid가 비어 있습니다.");
|
||
}
|
||
const rainAnalysis = await getData(
|
||
`v1/fldng/hstry/rain-anls/${encodeURIComponent(normalizedFloodMapFid)}`,
|
||
);
|
||
if (rainAnalysis) {
|
||
const mergedData = {
|
||
...formattedData,
|
||
...normalizeRainAnalysisPayload(rainAnalysis),
|
||
};
|
||
setFetchData(mergedData);
|
||
setTableData(mergedData);
|
||
}
|
||
} catch (rainError) {
|
||
console.warn("침수강우분석 조회 실패:", rainError);
|
||
}
|
||
try {
|
||
if (depthLookupKey) {
|
||
const depthRows = await getData(
|
||
`v1/fldng/hstry/depth-map/${encodeURIComponent(depthLookupKey)}`,
|
||
);
|
||
const depthPayload = depthRows?.data ?? depthRows;
|
||
const normalizedDepthRows = Array.isArray(depthPayload)
|
||
? depthPayload.filter((row) => row && typeof row === "object")
|
||
: [];
|
||
setFloodDepthRows(normalizedDepthRows);
|
||
const years = normalizedDepthRows
|
||
.map((row) =>
|
||
String(row.fldng_yr ?? row.fldngYr ?? row.yr ?? "").trim(),
|
||
)
|
||
.filter(Boolean);
|
||
console.log("depth years:", years);
|
||
// 최초에는 연도를 선택하지 않고 목록만 표시한다.
|
||
setSelectedFloodDepthYear(null);
|
||
}
|
||
} catch (depthError) {
|
||
console.warn("침수깊이지도 조회 실패:", depthError);
|
||
}
|
||
}
|
||
} catch (error) {
|
||
console.error("데이터 로딩 중 오류:", error);
|
||
}
|
||
};
|
||
|
||
fetchData();
|
||
}, [featureId, processedData, mngInstNameMap]);
|
||
|
||
useEffect(() => {
|
||
const layerTypeNm = featureData?.values_
|
||
? featureData?.id_?.split(".")?.[0]
|
||
: featureData?.lyrTypeNm;
|
||
|
||
if (layerTypeNm === "tb_ms_swage_prcs_zone") {
|
||
const facilityCode =
|
||
featureData?.values_?.swtrmt_fclt_cd ?? featureData?.swtrmtFcltCd;
|
||
highlightLinkedFacility(facilityCode);
|
||
return () => {
|
||
clearLinkedFacilityHighlight();
|
||
};
|
||
}
|
||
|
||
clearLinkedFacilityHighlight();
|
||
|
||
return () => {
|
||
clearLinkedFacilityHighlight();
|
||
};
|
||
}, [featureData, gisMap]);
|
||
|
||
// 저장 시 모달 띄우기
|
||
const handleSaveClick = async () => {
|
||
const shouldSave = await confirm("정보를 저장하시겠습니까?");
|
||
if (!shouldSave) return;
|
||
btnClickEvent();
|
||
};
|
||
|
||
// 저장 버튼 이벤트
|
||
const btnClickEvent = async () => {
|
||
const bodyData: Record<string, any> = {};
|
||
|
||
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;
|
||
});
|
||
|
||
// 레이어명에 따라 API 엔드포인트 분기처리
|
||
let apiEndpoint = "";
|
||
let result = null;
|
||
|
||
if (layerNm === "하수관로") apiEndpoint = "v1/fcltppl";
|
||
else if (layerNm === "우수토실") apiEndpoint = "v1/fcltstofcbr";
|
||
else if (layerNm === "하수펌프장") apiEndpoint = "v1/fcltpump";
|
||
else if (layerNm === "하수맨홀") apiEndpoint = "v1/fcltmhl";
|
||
|
||
bodyData["fcltMngNo"] =
|
||
featureData.values_.fclt_mng_no.toString() || null;
|
||
bodyData["mngInstCd"] =
|
||
featureData.values_.mng_inst_cd.toString() || null;
|
||
// 빈 키 제거
|
||
if (bodyData[""] !== undefined) delete bodyData[""];
|
||
|
||
// 🎯 우수토실일 경우 prrtymngYn 값 변환
|
||
if (layerNm === "우수토실" && bodyData.prrtymngYn) {
|
||
if (bodyData.prrtymngYn === "중점우수토실") {
|
||
bodyData.prrtymngYn = "Y";
|
||
} else if (bodyData.prrtymngYn === "우수토실") {
|
||
bodyData.prrtymngYn = "N";
|
||
}
|
||
}
|
||
|
||
try {
|
||
result = await postData(apiEndpoint, bodyData);
|
||
|
||
if (result) {
|
||
alert("저장되었습니다");
|
||
setFetchData(result.data || result);
|
||
} else {
|
||
alert("저장에 실패했습니다");
|
||
console.error("API 응답 오류:", result);
|
||
}
|
||
} catch (error) {
|
||
alert("저장 중 오류가 발생했습니다");
|
||
console.error("API 요청 오류:", error);
|
||
}
|
||
}
|
||
};
|
||
|
||
// 레이어명에 따라 상세페이지 렌더링
|
||
const ContentComponent = layerNm ? getLayerContentComponent(layerNm) : null;
|
||
|
||
return (
|
||
<div
|
||
id="popup"
|
||
className="absolute right-0 top-0 z-[50] flex h-[calc(100vh-5rem)] w-[350px] flex-col overflow-hidden border-r border-border bg-background transition-all duration-300"
|
||
>
|
||
<LoadingOverlay isLoading={isLoading} />
|
||
<div className="card-header relative flex items-center justify-between bg-primary p-4 pr-12">
|
||
<div className="flex flex-1 items-center space-x-4">
|
||
{surveyMode && (
|
||
<h3 className="whitespace-nowrap text-2xl font-medium text-primary-foreground">
|
||
조사관리 <br />
|
||
관로번호 :{" "}
|
||
{featureData?.get?.("gisManagementNo") ??
|
||
featureData?.values_?.gisManagementNo ??
|
||
featureData?.gisManagementNo ??
|
||
layerSpecificNm}
|
||
</h3>
|
||
)}
|
||
<h3
|
||
className={`${surveyMode ? "hidden" : ""} whitespace-nowrap text-2xl font-medium text-primary-foreground`}
|
||
>
|
||
{layerNm}
|
||
{tableData?.usg_cd && <>({tableData.usg_cd})</>} 정보 <br />
|
||
관리번호 : {layerSpecificNm}
|
||
</h3>
|
||
</div>
|
||
<div
|
||
className={`flex shrink-0 items-center space-x-3 ${layerNm === "하수관로" ? "mr-8" : ""}`}
|
||
>
|
||
{/* 하수관로일 때만 Infil/RDII 버튼 표시 */}
|
||
{layerNm === "하수관로" && (
|
||
<>
|
||
<Button
|
||
className="px-2"
|
||
onClick={() => {
|
||
const popupWindow = openWindowPopup("infilChart", {
|
||
pplMngNo,
|
||
});
|
||
if (!popupWindow) {
|
||
alert(
|
||
"팝업이 차단되었습니다. 브라우저 팝업 허용 설정을 확인해주세요.",
|
||
);
|
||
}
|
||
}}
|
||
color="primary"
|
||
>
|
||
침입수분석
|
||
</Button>
|
||
<Button
|
||
className="px-2"
|
||
onClick={() => {
|
||
const popupWindow = openWindowPopup("rdiiChart", {
|
||
pplMngNo,
|
||
});
|
||
if (!popupWindow) {
|
||
alert(
|
||
"팝업이 차단되었습니다. 브라우저 팝업 허용 설정을 확인해주세요.",
|
||
);
|
||
}
|
||
}}
|
||
color="primary"
|
||
>
|
||
RDII분석
|
||
</Button>
|
||
</>
|
||
)}
|
||
{layerNm === "하수맨홀" && (
|
||
<>
|
||
<Button
|
||
color="white"
|
||
onClick={() => {
|
||
setIsLoading(true);
|
||
searchUpperPpls(gisMap, layerSpecificNm, alert).finally(() =>
|
||
setIsLoading(false),
|
||
);
|
||
}}
|
||
>
|
||
상류관로 보기
|
||
</Button>
|
||
</>
|
||
)}
|
||
</div>
|
||
<Button
|
||
onClick={onClose}
|
||
className="text-primary-foreground/80 absolute right-2 top-1/2 z-10 h-8 w-8 -translate-y-1/2 p-0 hover:text-primary-foreground focus:outline-none"
|
||
size="xs"
|
||
aria-label="시설 상세정보 닫기"
|
||
title="닫기"
|
||
>
|
||
<span aria-hidden="true">✕</span>
|
||
</Button>
|
||
</div>
|
||
<div
|
||
className="flex flex-1 flex-col overflow-y-auto overflow-x-hidden"
|
||
ref={contentRef}
|
||
>
|
||
<VerticalTable
|
||
headName={
|
||
layerNm === "침수흔적위선" && floodDepthRows.length > 0 ? (
|
||
<div className="flex flex-col items-start gap-2">
|
||
<span>기본정보</span>
|
||
<div className="flex w-full items-center gap-1">
|
||
<button
|
||
type="button"
|
||
aria-label="이전 연도"
|
||
className="shrink-0 px-1 text-lg leading-none text-foreground"
|
||
onClick={() =>
|
||
floodDepthYearListRef.current?.scrollBy({
|
||
left: -120,
|
||
behavior: "smooth",
|
||
})
|
||
}
|
||
>
|
||
‹
|
||
</button>
|
||
<div
|
||
ref={floodDepthYearListRef}
|
||
className="flex min-w-0 flex-1 gap-2 overflow-x-auto whitespace-nowrap"
|
||
>
|
||
{Array.from(
|
||
new Set(
|
||
floodDepthRows
|
||
.map((row) =>
|
||
String(
|
||
row.fldng_yr ?? row.fldngYr ?? row.yr ?? "",
|
||
).trim(),
|
||
)
|
||
.filter(Boolean),
|
||
),
|
||
).map((year) => (
|
||
<button
|
||
key={year}
|
||
type="button"
|
||
onClick={async () => {
|
||
setSelectedFloodDepthYear(year);
|
||
const selectedDepthRow = floodDepthRows.find(
|
||
(row) =>
|
||
String(
|
||
row.fldng_yr ?? row.fldngYr ?? row.yr ?? "",
|
||
).trim() === year,
|
||
);
|
||
const depthFid = String(
|
||
selectedDepthRow?.fldng_dpth_map_sn ??
|
||
selectedDepthRow?.fid ??
|
||
"",
|
||
).trim();
|
||
if (depthFid) {
|
||
const core = GisApp.Module.layer as any;
|
||
await core.showSearchResultsFromGeoJSON?.(
|
||
"tb_ms_fldng_dpth_map",
|
||
`tb_ms_fldng_dpth_map.${depthFid.replace(/^tb_ms_fldng_dpth_map\./, "")}`,
|
||
);
|
||
}
|
||
}}
|
||
className={`rounded border px-3 py-1 text-[1.2rem] font-medium leading-5 ${selectedFloodDepthYear === year ? "border-blue-600 bg-white text-blue-700" : "border-border bg-muted text-muted-foreground"}`}
|
||
>
|
||
{year}년
|
||
</button>
|
||
))}
|
||
</div>
|
||
<button
|
||
type="button"
|
||
aria-label="다음 연도"
|
||
className="shrink-0 px-1 text-lg leading-none text-foreground"
|
||
onClick={() =>
|
||
floodDepthYearListRef.current?.scrollBy({
|
||
left: 120,
|
||
behavior: "smooth",
|
||
})
|
||
}
|
||
>
|
||
›
|
||
</button>
|
||
</div>
|
||
</div>
|
||
) : layerNm === "하수관로" && tableData?.elapsedYears ? (
|
||
<>
|
||
기본정보{" "}
|
||
<span className="ml-2 rounded-sm bg-red-50 px-2 py-0.5 text-[1.1rem] font-semibold text-red-700">
|
||
경과년도 {tableData.elapsedYears}
|
||
</span>
|
||
</>
|
||
) : (
|
||
"기본정보"
|
||
)
|
||
}
|
||
tableName1={layerNm === "중점우수토실" ? "측정지점" : undefined}
|
||
tableData={[tableData]}
|
||
headlist={headlist}
|
||
pageSize={20}
|
||
total={0}
|
||
cellClick={undefined}
|
||
onPageChange={function (): void {
|
||
throw new Error("Function not implemented.");
|
||
}}
|
||
// 두 번째 테이블 조건부 props
|
||
showSecondTable={layerNm === "중점우수토실" ? true : false}
|
||
tableName2={layerNm === "중점우수토실" ? "시설현황" : undefined}
|
||
tableData2={
|
||
layerNm === "중점우수토실" && tableData ? [tableData] : []
|
||
}
|
||
headlist2={layerNm === "중점우수토실" ? headlist2 : undefined}
|
||
pageSize2={20}
|
||
total2={0}
|
||
isEditable2={buttonChk}
|
||
/>
|
||
{ContentComponent && (
|
||
<ContentComponent
|
||
featureData={fetchData}
|
||
isMainSsc={isMainSsc}
|
||
isDrtwSewagePump={isDrtwSewagePump}
|
||
onSaveClick={handleSaveClick}
|
||
chmbrXcnts={chmbrXcnts}
|
||
chmbrYdnts={chmbrYdnts}
|
||
gisObject={featureData?.values_ ?? featureData}
|
||
rawFeatureData={featureData}
|
||
surveyResultContent={
|
||
surveyMode ? (
|
||
<ExmnSurveyResultSection featureData={featureData} />
|
||
) : undefined
|
||
}
|
||
/>
|
||
)}
|
||
</div>
|
||
</div>
|
||
);
|
||
};
|
||
export default FeatureClickEventPopup;
|