diff --git a/apps/sn/AGENTS.md b/apps/sn/AGENTS.md index e7b30dc3f..a4923b8c4 100644 --- a/apps/sn/AGENTS.md +++ b/apps/sn/AGENTS.md @@ -228,7 +228,7 @@ VSCode나 별도 터미널에서 `sn` Next 개발 서버가 실행 중이면 `ne ### 프론트엔드 실행 프로세스 관리 -- 모든 세션·역할·문서/검증 섹션에 공통 D:/workspace/AGENTS.md의 테스트 임시 파일·결과 파일 정리 기준을 적용한다. 일회성 검증 스크립트·캡처·Network/서버 로그·원시 결과 파일은 최종 보고서에 요약 후 삭제한다. 정식 회귀 테스트·최종 산출물·사용자 원본/복구 백업은 유지한다. 사용자 dev server가 쓰는 .next·공유 캐시·node_modules는 임시 검증물이라는 이유로 포괄 삭제하지 않는다. +- 모든 세션·역할·문서/검증 섹션에 공통 D:/workspace/AGENTS.md의 테스트 파일 정리 기준을 적용한다. 에이전트가 테스트를 위해 만든 파일은 정식 회귀 테스트·재사용 가능한 소스·fixture·mock·임시 라우트를 포함해 위치와 관계없이 결과를 요약한 뒤 작업 종료 전에 삭제한다(2026-09-23 사용자 지시). 전용 빈 폴더와 끊어진 실행 명령도 정리한다. 명시 보존 요청 파일·최종 산출물·사용자 원본/복구 백업은 유지한다. 기존 타 작업자 테스트 자산은 삭제 권한을 먼저 확인한다. 사용자 dev server의 .next·공유 캐시·node_modules는 포괄 삭제하지 않는다. - Codex 에이전트가 작업 또는 검증을 위해 직접 기동한 `next dev`, `dev:sn`, Turborepo dev와 기타 프론트엔드 장시간 실행 프로세스는 작업 종료 전에 반드시 종료한다. - 셸, 백그라운드, 숨김 창 등 VSCode 밖에서 에이전트가 기동한 프로세스도 종료 대상이다. diff --git a/apps/sn/app/(map-layout)/map/components/agedPipe/AgedPipePopupPage.tsx b/apps/sn/app/(map-layout)/map/components/agedPipe/AgedPipePopupPage.tsx index 7fe989125..abeec2245 100644 --- a/apps/sn/app/(map-layout)/map/components/agedPipe/AgedPipePopupPage.tsx +++ b/apps/sn/app/(map-layout)/map/components/agedPipe/AgedPipePopupPage.tsx @@ -18,11 +18,13 @@ import { AGED_PIPE_LIST_HEADLIST, AGED_PIPE_PAGE_SIZE, buildAgedPipeCqlFilters, + getAgedPipeManagementName, normalizeAgedPipeFilters, toDisplayPipeRow, validateElapsedYears, type AgedPipeFilters, type AgedPipeRow, + type AgedPipeManagementAgency, } from "./agedPipeUtils"; import { downloadAgedPipeExcel } from "./agedPipeExport"; import MapPopupDataTable from "@/features/windowPopup/ui/MapPopupDataTable"; @@ -53,6 +55,15 @@ export default function AgedPipePopupPage() { const [appliedFilters, setAppliedFilters] = useState(initialFilters); const [rows, setRows] = useState([]); + // 관리기관명 조회가 늦게 완료되어도 기존 목록을 재조회하지 않고 표시만 갱신한다. + const [managementAgencies, setManagementAgencies] = useState([]); + const displayRows = useMemo( + () => rows.map((row) => ({ + ...row, + mngInstNm: getAgedPipeManagementName(row, managementAgencies), + })), + [rows, managementAgencies], + ); const [total, setTotal] = useState(0); const [currentPage, setCurrentPage] = useState(1); const [isLoading, setIsLoading] = useState(false); @@ -73,6 +84,19 @@ export default function AgedPipePopupPage() { if (sgg.length === 0) fetchSgg(); }, [ctpv.length, fetchCtpv, fetchSgg, sgg.length]); + useEffect(() => { + let active = true; + // 기존 GIS 관리 화면과 같은 기관 목록을 사용하고, 실패 시 관로 조회는 유지한다. + getData("v1/fcltPpl/mngInsts") + .then((response) => { + if (active) setManagementAgencies(Array.isArray(response) ? response : []); + }) + .catch(() => { + if (active) alert("관리기관명을 불러오지 못해 관리기관 코드로 표시합니다."); + }); + return () => { active = false; }; + }, [alert]); + const fetchRows = useCallback( async (page: number, nextFilters: AgedPipeFilters, size: number) => { const normalized = normalizeAgedPipeFilters(nextFilters); @@ -247,31 +271,6 @@ export default function AgedPipePopupPage() { ) : null} - - + +
+ )}
)}
node.data?.exprsYn === "Y") + : []; + const checkedIds = selectedNodes.map((node) => node.id); + const selected = new Set(checkedIds); + return { + checkedIds, + selectedNodes, + uncheckedIds: nodes.filter((node) => !selected.has(node.id)).map((node) => node.id), + }; +} + +/** 개별 체크 변경도 전체 토글에 반영하며 숨김 노드는 집계에서 제외한다. */ +export function getBulkLayerState(nodes, checkedIds) { + const visibleIds = nodes.filter((node) => node.data?.exprsYn === "Y").map((node) => node.id); + const checked = new Set(checkedIds); + const count = visibleIds.filter((id) => checked.has(id)).length; + return { + disabled: visibleIds.length === 0, + allChecked: visibleIds.length > 0 && count === visibleIds.length, + partiallyChecked: count > 0 && count < visibleIds.length, + }; +}