/* ============================================================================= * config_frontend.ts * 프론트엔드 제어 및 웹 환경 설정 (API 주소, 업로드 제한, 라우트 등) * * 규칙: * - 프론트엔드의 모든 제어 상수는 여기서 관리. 컴포넌트 내 하드코딩 금지. * - API 주소, 파일 제한, WebCAD 렌더 옵션 등을 중앙 집중. * ========================================================================== */ /* ----------------------------------------------------------------------------- * 1. API 통신 * -------------------------------------------------------------------------- */ /** 백엔드 API 기본 경로. vite proxy(/api → :8000)를 통해 FastAPI로 전달. */ export const API_BASE_URL = "/api"; /** API 요청 타임아웃 (ms) */ export const API_TIMEOUT_MS = 30_000; /** 격자 해석처럼 수십 초가 걸리는 분석 요청용 타임아웃 (ms). * 일반 요청에 이 값을 쓰면 장애 시 화면이 오래 멈추므로 분석 엔드포인트에만 쓴다. */ export const API_ANALYSIS_TIMEOUT_MS = 60_000; /** B03~B09 워크플로우에서 사용할 현재 프로젝트 UUID 저장 키 */ export const CURRENT_PROJECT_ID_KEY = "frd_current_project_id"; /* ----------------------------------------------------------------------------- * 2. 파일 업로드 제한 (B03_FileInput) * -------------------------------------------------------------------------- */ /** 단일 파일 최대 크기 (MB) */ export const UPLOAD_MAX_MB = 30 * 1024; /** 한 요청에서 선택 가능한 최대 파일 수 */ export const UPLOAD_MAX_FILES = 5; /** 청크 업로드 단위 (MB) */ export const UPLOAD_CHUNK_SIZE_MB = 1024; /** 업로드 진행 UI 갱신 주기 (ms) */ export const PROGRESS_UPDATE_INTERVAL_MS = 10_000; /** B03 업로드 Service Worker 번들 경로 */ export const SERVICE_WORKER_PATH = "/assets/B03_FileInput_ServiceWorker.js"; /** 허용 확장자 (계획노선/지형/포인트클라우드/도면) */ export const UPLOAD_ALLOWED_EXT = [".csv", ".las", ".laz", ".tif", ".tfw", ".prj", ".dxf"] as const; /* ----------------------------------------------------------------------------- * 3. WebCAD / 3D 렌더링 옵션 * -------------------------------------------------------------------------- */ export const RENDER_OPTIONS = { /** Three.js 배경색 (theme.css 변수와 별개로 캔버스 clear color) */ canvasClearColorLight: 0xf6f7fa, canvasClearColorDark: 0x16121f, pointColor: 0x3e0079, gridCenterColor: 0x9491a1, gridLineColor: 0xe6e2e3, /** 카메라 기본 시야각 */ cameraFov: 60, /** 카메라 near/far */ cameraNear: 0.1, cameraFar: 100_000, /** 안티앨리어싱 사용 여부 */ antialias: true, /** 픽셀 비율 상한 (고DPI 성능 보호) */ maxPixelRatio: 2, } as const; /* ----------------------------------------------------------------------------- * 4. 라우트 상수 (해시 기반 SPA 라우팅) * 각 페이지 폴더명과 1:1 매핑. * -------------------------------------------------------------------------- */ export const ROUTES = { // 로그인 전 A01_HOME: "a01-home", A02_PROG_DETAIL: "a02-prog-detail", A03_COMP_DETAIL: "a03-comp-detail", A04_NEWS_HISTORY: "a04-news-history", A05_EDU_DETAIL: "a05-edu-detail", A06_LOGIN: "a06-login", A07_REGISTER: "a07-register", A08_SUPPORT: "a08-support", A09_SECURITY: "a09-security", // 로그인 후 B01_ACCOUNT: "b01-account", B02_PROJ_REGISTER: "b02-proj-register", B03_FILE_INPUT: "b03-file-input", B04_PREPROCESS: "b04-preprocess", B05_PROFILE: "b05-profile", B06_SECTION: "b06-section", B07_WF4_DESIGN_DETAIL: "b07-wf4-design-detail", B08_WF5_QUANTITY: "b08-wf5-quantity", B09_WF6_ESTIMATION: "b09-wf6-estimation", B10_PAYMENT: "b10-payment", B11_STATUS: "b11-status", // 대시보드에서 B그룹으로 처음 들어갈 때 3D·등고선을 미리 받아 두는 준비 화면. B11_LOADING: "b11-loading", } as const; export type RouteKey = keyof typeof ROUTES; export type RoutePath = (typeof ROUTES)[RouteKey]; /** 기본 진입 라우트 */ export const DEFAULT_ROUTE: RoutePath = ROUTES.A01_HOME; /** 로그인이 필요한 라우트 (인증 가드용) */ export const PROTECTED_ROUTES: readonly RoutePath[] = [ ROUTES.B01_ACCOUNT, ROUTES.B02_PROJ_REGISTER, ROUTES.B03_FILE_INPUT, ROUTES.B04_PREPROCESS, ROUTES.B05_PROFILE, ROUTES.B06_SECTION, ROUTES.B07_WF4_DESIGN_DETAIL, ROUTES.B08_WF5_QUANTITY, ROUTES.B09_WF6_ESTIMATION, ROUTES.B10_PAYMENT, ROUTES.B11_STATUS, ROUTES.B11_LOADING, ]; /* ----------------------------------------------------------------------------- * 5. 테마 / 언어 저장 키 * -------------------------------------------------------------------------- */ export const THEME_STORAGE_KEY = "frd_theme"; export const LANG_STORAGE_KEY = "frd_lang"; /* ----------------------------------------------------------------------------- * 6. B05 구조물 옵션 (드롭다운 목록·기본값) * 정본은 config/config_system.py의 STRUCTURE_* / PIPE_* 상수 — 값을 바꿀 때는 * 양쪽을 함께 고친다(2026-08-05 사용자 확정. 계산은 백엔드, 표시는 프론트). * -------------------------------------------------------------------------- */ export const STRUCTURE_TYPES = ["배관", "기성막이", "대피로", "기타"] as const; export type StructureType = (typeof STRUCTURE_TYPES)[number]; export const STRUCTURE_DEFAULT_TYPE: StructureType = "배관"; /** 관종별 직경(mm) 목록. */ export const PIPE_DIAMETERS_MM: Record = { 이중벽관: [150, 200, 250, 300, 400, 450, 500, 600, 700, 800, 900, 1000, 1200, 1500], 삼중벽관: [200, 250, 300, 400, 450, 500, 600, 700, 800, 900, 1000, 1200, 1500], 파형강관: [ 150, 200, 250, 300, 350, 400, 450, 500, 600, 700, 800, 900, 1000, 1200, 1350, 1500, 1650, 1800, 2000, ], } as const; export const PIPE_TYPES = Object.keys(PIPE_DIAMETERS_MM); export const PIPE_DEFAULT_TYPE = "파형강관"; /** 자동 지정 기본 관경 — 별표2 (나) 예외 하한 800mm. 유효직경이 더 크면 바로 위 규격. */ export const PIPE_DEFAULT_DIAMETER_MM = 800; export const ESCAPE_ROUTE_WIDTHS_M = [1.5, 2.0, 2.5, 3.0] as const; export const ESCAPE_ROUTE_DEFAULT_WIDTH_M = 2.0; export const STRUCTURE_ETC_DEFAULT_NAME = "기타 구조물"; /** 유효직경(mm) 이상인 가장 작은 규격을 고른다. 목록을 넘으면 최대 규격. */ export function pickPipeDiameter(pipeType: string, effectiveDiameterMm: number | null): number { const sizes = PIPE_DIAMETERS_MM[pipeType] ?? PIPE_DIAMETERS_MM[PIPE_DEFAULT_TYPE]; const need = Math.max(PIPE_DEFAULT_DIAMETER_MM, effectiveDiameterMm ?? 0); return sizes.find((size) => size >= need) ?? sizes[sizes.length - 1]; }