Compare commits
5
Commits
deploy
...
iipbs/sn/dev
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
45c8d021cc | ||
|
|
0336226562 | ||
|
|
e9fb1e3a88 | ||
|
|
a4412001b3 | ||
|
|
2c901bd32d |
@@ -1,3 +1,14 @@
|
|||||||
|
/**
|
||||||
|
* 📄 Proxy Route
|
||||||
|
* API 프록시 라우트
|
||||||
|
*
|
||||||
|
* - 서버 API로의 요청 프록시 처리
|
||||||
|
* - 쿠키 기반 인증 토큰 및 리프레시 토큰 관리
|
||||||
|
* - POST 및 PUT 요청 핸들링
|
||||||
|
* - 사용자 인증 정보 검증 및 응답 처리
|
||||||
|
* - 에러 핸들링 및 상태 코드 반환
|
||||||
|
* - 사이트 ID 및 헤더 설정
|
||||||
|
*/
|
||||||
import {
|
import {
|
||||||
ACCESS_TOKEN,
|
ACCESS_TOKEN,
|
||||||
AUTH_USER_ID,
|
AUTH_USER_ID,
|
||||||
@@ -113,12 +124,8 @@ export async function PUT(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getCookieDomain(): string | undefined {
|
function getCookieDomain(): string | undefined {
|
||||||
if (typeof window === "undefined") return undefined;
|
// 개발에서는 현재 사이트 전용, 운영에서는 환경변수의 공용 도메인을 사용한다.
|
||||||
|
return process.env.NODE_ENV === "production"
|
||||||
if (COOKIE_DOMAIN) return COOKIE_DOMAIN;
|
? COOKIE_DOMAIN || undefined
|
||||||
|
: undefined;
|
||||||
const { hostname } = window.location;
|
|
||||||
if (hostname === "localhost" || hostname === "127.0.0.1") return undefined;
|
|
||||||
|
|
||||||
return hostname;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import type { ExmnMapFilters, ExmnMapJobType } from "./exmn-map-types";
|
import type { ExmnMapFilters, ExmnMapJobType } from "./exmn-map-types";
|
||||||
|
import { EXMN_RESULT_PPL_LEGEND_COLOR } from "./exmn-map-types";
|
||||||
import LayerSettingCard from "../layer-setting/layer-setting-card";
|
import LayerSettingCard from "../layer-setting/layer-setting-card";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
@@ -52,7 +53,8 @@ export default function ExmnLayerSettingPanel({
|
|||||||
<label className="flex min-h-[24px] flex-col items-stretch gap-1">
|
<label className="flex min-h-[24px] flex-col items-stretch gap-1">
|
||||||
<span className={`flex items-center gap-1 ${labelClass}`}>
|
<span className={`flex items-center gap-1 ${labelClass}`}>
|
||||||
<span
|
<span
|
||||||
className={`h-2 w-2 rounded-full ${jobType === "PPL" ? "bg-[#facc15]" : "bg-[#8b5cf6]"}`}
|
className="h-2 w-2 rounded-full"
|
||||||
|
style={{ backgroundColor: jobType === "PPL" ? EXMN_RESULT_PPL_LEGEND_COLOR : "#8b5cf6" }}
|
||||||
/>
|
/>
|
||||||
사업차수
|
사업차수
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ export const EXMN_RESULT_ROOT_LAYER_TYPE = "sn_exmn_result_group";
|
|||||||
export const EXMN_RESULT_PPL_LAYER_TYPE = "sn_exmn_result_ppl";
|
export const EXMN_RESULT_PPL_LAYER_TYPE = "sn_exmn_result_ppl";
|
||||||
export const EXMN_RESULT_MNHL_LAYER_TYPE = "sn_exmn_result_mnhl";
|
export const EXMN_RESULT_MNHL_LAYER_TYPE = "sn_exmn_result_mnhl";
|
||||||
// 레이어 트리에서 조사 관로와 기존 맨홀 분류 심볼을 즉시 구분하기 위한 전용 범례 스타일이다.
|
// 레이어 트리에서 조사 관로와 기존 맨홀 분류 심볼을 즉시 구분하기 위한 전용 범례 스타일이다.
|
||||||
export const EXMN_RESULT_PPL_LEGEND_COLOR = "#facc15";
|
export const EXMN_RESULT_PPL_LEGEND_COLOR = "#000080";
|
||||||
export const EXMN_RESULT_MNHL_LEGEND_ICON = "SN_EXMN_MANHOLE_PURPLE.png?v=3";
|
export const EXMN_RESULT_MNHL_LEGEND_ICON = "SN_EXMN_MANHOLE_PURPLE.png?v=3";
|
||||||
|
|
||||||
export type ExmnMapJobType = "PPL" | "MNHL";
|
export type ExmnMapJobType = "PPL" | "MNHL";
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import Icon from "ol/style/Icon.js";
|
|||||||
import Stroke from "ol/style/Stroke.js";
|
import Stroke from "ol/style/Stroke.js";
|
||||||
import Style from "ol/style/Style.js";
|
import Style from "ol/style/Style.js";
|
||||||
import Text from "ol/style/Text.js";
|
import Text from "ol/style/Text.js";
|
||||||
|
import { EXMN_RESULT_PPL_LEGEND_COLOR } from "../components/exmn/exmn-map-types";
|
||||||
import {
|
import {
|
||||||
EXMN_GRADE_COLORS,
|
EXMN_GRADE_COLORS,
|
||||||
EXMN_RESULT_MANHOLE_ICON_SRC,
|
EXMN_RESULT_MANHOLE_ICON_SRC,
|
||||||
@@ -14,10 +15,11 @@ export const getExmnGradeColor = (feature: any) => {
|
|||||||
return EXMN_GRADE_COLORS[grade] ?? "#2563eb";
|
return EXMN_GRADE_COLORS[grade] ?? "#2563eb";
|
||||||
};
|
};
|
||||||
|
|
||||||
export const createExmnPipeStyle = (feature: any) =>
|
// 조사 관로는 상태등급과 관계없이 범례와 같은 짙은 남색으로 표시한다.
|
||||||
|
export const createExmnPipeStyle = () =>
|
||||||
new Style({
|
new Style({
|
||||||
stroke: new Stroke({
|
stroke: new Stroke({
|
||||||
color: getExmnGradeColor(feature),
|
color: EXMN_RESULT_PPL_LEGEND_COLOR,
|
||||||
width: 4,
|
width: 4,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -122,12 +122,8 @@ export async function PUT(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getCookieDomain(): string | undefined {
|
function getCookieDomain(): string | undefined {
|
||||||
if (typeof window === "undefined") return undefined;
|
// 개발에서는 현재 사이트 전용, 운영에서는 환경변수의 공용 도메인을 사용한다.
|
||||||
|
return process.env.NODE_ENV === "production"
|
||||||
if (COOKIE_DOMAIN) return COOKIE_DOMAIN;
|
? COOKIE_DOMAIN || undefined
|
||||||
|
: undefined;
|
||||||
const { hostname } = window.location;
|
|
||||||
if (hostname === "localhost" || hostname === "127.0.0.1") return undefined;
|
|
||||||
|
|
||||||
return hostname;
|
|
||||||
}
|
}
|
||||||
+2
-34
@@ -4,47 +4,15 @@
|
|||||||
* 📄 Home Page
|
* 📄 Home Page
|
||||||
* 홈 페이지 컴포넌트
|
* 홈 페이지 컴포넌트
|
||||||
*
|
*
|
||||||
* - 쿠키 기반 인증 토큰 검증 및 자동 로그인 처리
|
* - 루트 진입 시 지도 페이지(/map)로 리다이렉트
|
||||||
* - 로그인 성공 시 지도 페이지로 리다이렉트
|
* - 인증 토큰 검증 및 로그인 화면 이동은 /map 및 공통 인증 흐름에서 처리
|
||||||
* - 개발 환경에서는 루트 진입 시 자동 로그인 후 지도 페이지 이동
|
|
||||||
* - 로그인 실패 시 에러 로깅 및 처리
|
|
||||||
* - 사용자 세션 관리 및 초기 라우팅 제어
|
* - 사용자 세션 관리 및 초기 라우팅 제어
|
||||||
* - Next.js useRouter를 통한 클라이언트 사이드 라우팅
|
|
||||||
*
|
|
||||||
* 🔗 사용 API
|
|
||||||
* - 로그인: POST /api/v1/auth/login (user-service)
|
|
||||||
*/
|
*/
|
||||||
"use client";
|
"use client";
|
||||||
import { authService } from "@/entities/auth";
|
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
|
|
||||||
const DEV_HOSTNAMES = new Set(["localhost", "127.0.0.1", "con.mind-one.co.kr"]);
|
|
||||||
|
|
||||||
const DEV_LOGIN_PAYLOAD = {
|
|
||||||
userId: process.env.NEXT_PUBLIC_DEV_LOGIN_USER_ID || "CLEANWATER",
|
|
||||||
password: process.env.NEXT_PUBLIC_DEV_LOGIN_PASSWORD || "gktneh12!@",
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const hostname = window.location.hostname;
|
|
||||||
const isDevHost = DEV_HOSTNAMES.has(hostname);
|
|
||||||
|
|
||||||
if (!isDevHost) {
|
|
||||||
window.location.replace("/map");
|
window.location.replace("/map");
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function doLogin() {
|
|
||||||
try {
|
|
||||||
// 개발 환경에서는 남아 있는 만료/부분 쿠키로 인한 /map <-> / 루프를 막기 위해 항상 토큰을 재발급한다.
|
|
||||||
await authService.login(DEV_LOGIN_PAYLOAD);
|
|
||||||
window.location.replace("/map");
|
|
||||||
} catch (err) {
|
|
||||||
console.error("로그인 실패", err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void doLogin();
|
|
||||||
}, []);
|
}, []);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -719,3 +719,19 @@
|
|||||||
- 실브라우저: 테스트 사업→초이처리분구→지도3개 표시→실제188001 클릭→사업명 테스트/GIS 맨홀번호188001 확인→조사보고서 보기→1쪽 PDF와188001 표시 성공. 사용자도 최종 정상동작 확인했다.
|
- 실브라우저: 테스트 사업→초이처리분구→지도3개 표시→실제188001 클릭→사업명 테스트/GIS 맨홀번호188001 확인→조사보고서 보기→1쪽 PDF와188001 표시 성공. 사용자도 최종 정상동작 확인했다.
|
||||||
- 실행 서버: 기존 재시작 승인 범위에서 코드 반영 후8883 PID27788로 유지한다(이전PID24204 종료). classpath/실행로그는 기존 .codex-tmp/manhole-backend-* 경로 유지. 종료명령 Stop-Process -Id 27788.
|
- 실행 서버: 기존 재시작 승인 범위에서 코드 반영 후8883 PID27788로 유지한다(이전PID24204 종료). classpath/실행로그는 기존 .codex-tmp/manhole-backend-* 경로 유지. 종료명령 Stop-Process -Id 27788.
|
||||||
- 롤백: 위 FE3개 소스와 ExmnManholeReportService의 이번 변경을 함께 복원한다. DB 롤백 없음. 잔여 리스크는 GIS 미연결 자료 열람 불가 및 대형 사업의 지도 매칭·PDF 판독 비용이며, 임의 위치/다른 사업 fallback을 하지 않는다.
|
- 롤백: 위 FE3개 소스와 ExmnManholeReportService의 이번 변경을 함께 복원한다. DB 롤백 없음. 잔여 리스크는 GIS 미연결 자료 열람 불가 및 대형 사업의 지도 매칭·PDF 판독 비용이며, 임의 위치/다른 사업 fallback을 하지 않는다.
|
||||||
|
|
||||||
|
|
||||||
|
## 2026-10-28 관로조사 남색 표시
|
||||||
|
- 정적 확인: 관로 선·범례·설정 표식은 `EXMN_RESULT_PPL_LEGEND_COLOR(#000080)`을 공유하고 관로 선 두께는 4를 유지한다. 맨홀·선택 이벤트·API·DB 변경 없음.
|
||||||
|
- `git -C frontend diff --check`: 통과.
|
||||||
|
- 3002 리스너 부재로 HMR·실브라우저 검증은 미수행. 실제 지도에서 관로조사 켜기, 등급 필터 변경 후 남색 유지, 선택 강조·맨홀 동시 표시 구분을 후속 확인한다.
|
||||||
|
|
||||||
|
### 2026-10-28 개발 서버 기동 후 확인
|
||||||
|
- 사용자 요청으로 `pnpm --filter sn dev` 실행, Next.js Ready 및 localhost:3002 접속 확인. 서버는 유지한다.
|
||||||
|
- Chrome에서 /map 진입 시 /auth/login으로 이동하고 로그인 화면이 표시됨. 로컬 로그인 세션이 없어 실제 지도 선·범례 가시성 검증은 로그인 후 계속해야 한다. 인증 우회·환경설정 변경 없음.
|
||||||
|
|
||||||
|
### 2026-10-28 백엔드 복구 및 실지도 검증 완료
|
||||||
|
- 원인: 프론트만 실행되어 localhost:8883 지도 API가 ECONNREFUSED로 실패했다. 기존 local 설정으로 Config Server와 sewerage-service를 기동했다. 코드·환경 파일·DB 변경 없음.
|
||||||
|
- 지도 레이어 API HTTP 200. 실제 레이어 목록·조사사업 조회 정상.
|
||||||
|
- 테스트 사업 → 관로조사 → 초이처리분구 → 지도보기: 3개 표시, 줌18.0에서 짙은 남색 관로가 일반지도 배경과 주황색 기존 관로에서 선명하게 구분됨. 이전 실지도 미검증 상태 해소. 위성지도·모든 등급·선택 강조 조합은 미검증.
|
||||||
|
- 사용자 계속 실행 지시에 따라 FE3002 PID28176, BE8883 PID31232, Config8888 PID27092 유지. 종료 시 해당 PID가 같은 프로세스인지 재확인 후 Stop-Process -Id 명령으로 종료한다.
|
||||||
@@ -38,7 +38,7 @@
|
|||||||
| 파일 | 역할 | 선언/주석 보조 근거 | 행 | 직접 참조 | 상태 |
|
| 파일 | 역할 | 선언/주석 보조 근거 | 행 | 직접 참조 | 상태 |
|
||||||
|---|---|---|---:|---:|---|
|
|---|---|---|---:|---:|---|
|
||||||
| [layout.tsx](<D:/workspace/IIPBS-FRONTEND/frontend/apps/sn/app/layout.tsx>) | Next 루트 HTML·테마·메타데이터·Provider 진입점 | metadata, RootLayout / ➡️ 경로 : 모든 페이지 | 44 | 0 | 등록/도구 진입 규칙 대상 |
|
| [layout.tsx](<D:/workspace/IIPBS-FRONTEND/frontend/apps/sn/app/layout.tsx>) | Next 루트 HTML·테마·메타데이터·Provider 진입점 | metadata, RootLayout / ➡️ 경로 : 모든 페이지 | 44 | 0 | 등록/도구 진입 규칙 대상 |
|
||||||
| [page.tsx](<D:/workspace/IIPBS-FRONTEND/frontend/apps/sn/app/page.tsx>) | 루트 진입/개발 자동로그인·지도 이동 | DEV_HOSTNAMES, DEV_LOGIN_PAYLOAD, Home / 홈 페이지 컴포넌트 | 51 | 0 | 등록/도구 진입 규칙 대상 |
|
| [page.tsx](<D:/workspace/IIPBS-FRONTEND/frontend/apps/sn/app/page.tsx>) | 루트 진입/지도 이동 | Home / 홈 페이지 컴포넌트 | 18 | 0 | 등록/도구 진입 규칙 대상 |
|
||||||
| [providers.tsx](<D:/workspace/IIPBS-FRONTEND/frontend/apps/sn/app/providers.tsx>) | 실제 src/app/providers의 AppProvider 재수출. 현재 layout에서 사용 | | 2 | 1 | 정적 참조 있음 |
|
| [providers.tsx](<D:/workspace/IIPBS-FRONTEND/frontend/apps/sn/app/providers.tsx>) | 실제 src/app/providers의 AppProvider 재수출. 현재 layout에서 사용 | | 2 | 1 | 정적 참조 있음 |
|
||||||
|
|
||||||
## app/(auth)/auth
|
## app/(auth)/auth
|
||||||
|
|||||||
@@ -1045,3 +1045,12 @@
|
|||||||
- 실브라우저: 테스트 사업→초이처리분구→지도3개 표시→실제188001 클릭→사업명 테스트/GIS 맨홀번호188001 확인→조사보고서 보기→1쪽 PDF와188001 표시 성공. 사용자도 최종 정상동작 확인했다.
|
- 실브라우저: 테스트 사업→초이처리분구→지도3개 표시→실제188001 클릭→사업명 테스트/GIS 맨홀번호188001 확인→조사보고서 보기→1쪽 PDF와188001 표시 성공. 사용자도 최종 정상동작 확인했다.
|
||||||
- 실행 서버: 기존 재시작 승인 범위에서 코드 반영 후8883 PID27788로 유지한다(이전PID24204 종료). classpath/실행로그는 기존 .codex-tmp/manhole-backend-* 경로 유지. 종료명령 Stop-Process -Id 27788.
|
- 실행 서버: 기존 재시작 승인 범위에서 코드 반영 후8883 PID27788로 유지한다(이전PID24204 종료). classpath/실행로그는 기존 .codex-tmp/manhole-backend-* 경로 유지. 종료명령 Stop-Process -Id 27788.
|
||||||
- 롤백: 위 FE3개 소스와 ExmnManholeReportService의 이번 변경을 함께 복원한다. DB 롤백 없음. 잔여 리스크는 GIS 미연결 자료 열람 불가 및 대형 사업의 지도 매칭·PDF 판독 비용이며, 임의 위치/다른 사업 fallback을 하지 않는다.
|
- 롤백: 위 FE3개 소스와 ExmnManholeReportService의 이번 변경을 함께 복원한다. DB 롤백 없음. 잔여 리스크는 GIS 미연결 자료 열람 불가 및 대형 사업의 지도 매칭·PDF 판독 비용이며, 임의 위치/다른 사업 fallback을 하지 않는다.
|
||||||
|
|
||||||
|
|
||||||
|
## 2026-10-28 관로조사 지도 색상 가시성 개선
|
||||||
|
- 사용자 요청에 따라 조사결과 > 관로조사의 지도 선을 상태등급별 색상에서 짙은 남색 `#000080`으로 통일했다. 선 두께 4와 등급 조회·필터는 유지한다.
|
||||||
|
- `app/(map-layout)/map/lib/analysis-layer-styles.ts`, `components/exmn/exmn-map-types.ts`, `components/exmn/exmn-layer-setting-panel.tsx`에서 지도 선·레이어 범례·설정 표식이 같은 색상 상수를 사용하도록 했다(뒤 두 경로도 같은 map 하위).
|
||||||
|
- 원본 근거: 요구사항정의서 `3. (기능)요구사항정의 및 추적표`의 SFR-010-004, 화면설계서 45쪽 SN_MN040103 및 48쪽 SN_MN040107의 조사관로 지도 표출. 색상 변경은 이번 사용자 요청을 적용했다.
|
||||||
|
- 영향: SN 지도 표시 전용. 맨홀 심볼·선택 처리·상세 탭·API·DB 변경 없음.
|
||||||
|
- 검증: git diff --check 통과, 지도 스타일과 범례·설정 표식의 공통 상수 참조 확인. 3002 개발 서버 리스너가 없어 HMR·실제 배경지도 가시성은 미검증. 빌드·서버 기동/재시작은 수행하지 않았다.
|
||||||
|
- 롤백: 위 3개 파일의 이번 색상 변경만 되돌린다. DB 조치 없음.
|
||||||
|
|||||||
@@ -20,3 +20,30 @@
|
|||||||
|
|
||||||
## 수동 확인 필요
|
## 수동 확인 필요
|
||||||
- 실제 로그인 화면에서 팝업 크기·가로 스크롤·강조 색상 및 지도 이동 회귀 확인
|
- 실제 로그인 화면에서 팝업 크기·가로 스크롤·강조 색상 및 지도 이동 회귀 확인
|
||||||
|
|
||||||
|
## 추가 작업: 확률강우량 팝업 엑셀다운로드 (2026-10-30)
|
||||||
|
|
||||||
|
### 변경 내용
|
||||||
|
- 팝업 헤더 우측(닫기 X 왼쪽)에 `엑셀다운로드` 버튼을 추가했다. 닫기 버튼과 겹치지 않도록 헤더에 `pr-14` 여백을 둔다.
|
||||||
|
- 공통 유틸 `src/shared/util/download-excel-workbook.ts`의 `downloadExcelWorkbook`을 재사용한다.
|
||||||
|
- 파일명은 `{시군구명}_확률강우량.xlsx`이며 시트는 두 개다.
|
||||||
|
- `확률강우량`: 재현기간(년) 행 × `N시간` 열. 셀은 소수 4자리로 자르지 않은 원본 숫자 값이다.
|
||||||
|
- `확률관측소 면적비율`: 확률관측소코드·관측소명·면적비율(%)과 합계(API `areaRtTotal`).
|
||||||
|
- 로딩 중·오류·표 데이터 없음 상태에서는 버튼을 비활성화한다.
|
||||||
|
- API 호출, 응답 구조, 화면 문구와 기존 표 렌더링은 변경하지 않았다.
|
||||||
|
|
||||||
|
### 이슈: 팝업 본체가 투명하게 렌더링됨
|
||||||
|
- 증상: 엑셀다운로드 버튼 추가 후 팝업 본체 배경이 투명해져 뒤쪽 목록 화면이 비쳐 보였다. 새로고침과 애니메이션 완료 후에도 재현됐다.
|
||||||
|
- 추정 원인: `DialogContent`의 배경이 `bg-[var(--dialog-bg)]`(`--dialog-bg: var(--background)`, `packages/common/assets/src/styles/base.css`)인데, 이 모달에서 해당 변수가 값을 가져오지 못한 것으로 추정한다. 정확한 근본 원인은 확인하지 못했다. 공통 패키지(`@common/components`, `@common/assets`)는 수정 범위 밖이라 변경하지 않았다.
|
||||||
|
- 조치: `PrbbtRnflDialog.tsx`의 `DialogContent` className에 `bg-background`를 추가해 배경을 직접 지정했다. 사용자가 해결을 확인했다(2026-10-30).
|
||||||
|
- 참고: 같은 증상이 다른 `DialogContent` 사용 팝업에서 보이면 공통 변수 문제일 수 있으므로 별도 보고 대상이다.
|
||||||
|
|
||||||
|
### 수정 파일
|
||||||
|
- `apps/sn/src/features/fldng-predc-prst/ui/PrbbtRnflDialog.tsx`
|
||||||
|
|
||||||
|
### 검증
|
||||||
|
- 사용자가 실제 화면에서 팝업 배경 정상 표시를 확인했다.
|
||||||
|
- 타입 체크·빌드·엑셀 파일 내용 열람은 수행하지 않았다.
|
||||||
|
|
||||||
|
### 롤백
|
||||||
|
- 위 파일의 변경(헤더 버튼, `handleExcelDownload`, 두 import, `bg-background`)을 되돌린다.
|
||||||
|
|||||||
@@ -2,7 +2,9 @@
|
|||||||
|
|
||||||
import { useLayoutEffect, useMemo, useRef } from "react";
|
import { useLayoutEffect, useMemo, useRef } from "react";
|
||||||
|
|
||||||
import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@common/components";
|
import { Button, Dialog, DialogContent, DialogHeader, DialogTitle } from "@common/components";
|
||||||
|
|
||||||
|
import { downloadExcelWorkbook } from "@/shared/util/download-excel-workbook";
|
||||||
|
|
||||||
/** 시군구별 면적가중 확률강우량 한 셀의 API 응답이다. */
|
/** 시군구별 면적가중 확률강우량 한 셀의 API 응답이다. */
|
||||||
export type PrbbtRnflCell = {
|
export type PrbbtRnflCell = {
|
||||||
@@ -77,6 +79,43 @@ export default function PrbbtRnflDialog({
|
|||||||
};
|
};
|
||||||
}, [data]);
|
}, [data]);
|
||||||
|
|
||||||
|
/** 화면에 표시 중인 확률강우량 표와 관측소 면적비율 표를 두 시트로 내려받는다. */
|
||||||
|
const handleExcelDownload = async () => {
|
||||||
|
const stations = data?.stations ?? [];
|
||||||
|
await downloadExcelWorkbook({
|
||||||
|
fileName: `${sggName}_확률강우량`,
|
||||||
|
sheets: [
|
||||||
|
{
|
||||||
|
name: "확률강우량",
|
||||||
|
rows: [
|
||||||
|
["재현기간(년)", ...durations.map((hour) => `${hour}시간`)],
|
||||||
|
...frequencies.map((frequencyYear) => [
|
||||||
|
frequencyYear,
|
||||||
|
...durations.map(
|
||||||
|
(durationHour) =>
|
||||||
|
rainfallByKey.get(`${frequencyYear}:${durationHour}`) ?? null,
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
],
|
||||||
|
columnWidths: [14, ...durations.map(() => 12)],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "확률관측소 면적비율",
|
||||||
|
rows: [
|
||||||
|
["확률관측소코드", "관측소명", "면적비율(%)"],
|
||||||
|
...stations.map((station) => [
|
||||||
|
station.prbbtObsvtrCd,
|
||||||
|
station.prbbtObsvtrNm || "-",
|
||||||
|
station.areaRt,
|
||||||
|
]),
|
||||||
|
["합계", "", data?.areaRtTotal ?? null],
|
||||||
|
],
|
||||||
|
columnWidths: [18, 18, 14],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
/** 표가 표시되면 선택 지속시간 열을 가로 스크롤 영역의 가운데로 맞춘다. */
|
/** 표가 표시되면 선택 지속시간 열을 가로 스크롤 영역의 가운데로 맞춘다. */
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
if (!open || loading || error || selectedDurationHour == null) return;
|
if (!open || loading || error || selectedDurationHour == null) return;
|
||||||
@@ -96,11 +135,19 @@ export default function PrbbtRnflDialog({
|
|||||||
return (
|
return (
|
||||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||||
<DialogContent
|
<DialogContent
|
||||||
className="flex max-h-[90vh] w-[94vw] !max-w-[150rem] flex-col gap-4 overflow-hidden"
|
className="flex max-h-[90vh] w-[94vw] !max-w-[150rem] flex-col gap-4 overflow-hidden bg-background"
|
||||||
aria-describedby={undefined}
|
aria-describedby={undefined}
|
||||||
>
|
>
|
||||||
<DialogHeader>
|
{/* 우측 상단 닫기(X) 버튼과 겹치지 않도록 pr-14 여백을 둔다. */}
|
||||||
|
<DialogHeader className="flex-row items-center justify-between pr-14">
|
||||||
<DialogTitle>{sggName} 확률강우량</DialogTitle>
|
<DialogTitle>{sggName} 확률강우량</DialogTitle>
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
disabled={loading || !!error || !frequencies.length || !durations.length}
|
||||||
|
onClick={() => void handleExcelDownload()}
|
||||||
|
>
|
||||||
|
엑셀다운로드
|
||||||
|
</Button>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
||||||
{loading ? (
|
{loading ? (
|
||||||
|
|||||||
@@ -113,12 +113,8 @@ export async function PUT(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getCookieDomain(): string | undefined {
|
function getCookieDomain(): string | undefined {
|
||||||
if (typeof window === "undefined") return undefined;
|
// 개발에서는 현재 사이트 전용, 운영에서는 환경변수의 공용 도메인을 사용한다.
|
||||||
|
return process.env.NODE_ENV === "production"
|
||||||
if (COOKIE_DOMAIN) return COOKIE_DOMAIN;
|
? COOKIE_DOMAIN || undefined
|
||||||
|
: undefined;
|
||||||
const { hostname } = window.location;
|
|
||||||
if (hostname === "localhost" || hostname === "127.0.0.1") return undefined;
|
|
||||||
|
|
||||||
return hostname;
|
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user