/* ============================================================================= * group_workflow.ts * D 예정노선 · E 안정성평가 단계 묶음(요구 345 · PLAN 529-1) — B05~B09 틀(`createWorkflowLayout`)을 * 단계 목록 · 아이콘 · 주소 인자로 그대로 씀. 빈 단계 = 틀 · 좌측 패널([?] [<] [>]) · 단계 막대만. * 페이지 길 = 각 폴더 `draft/<폴더>_Route.ts` · 본문이 생기면 그 폴더 페이지로 갈아 끼움. * ========================================================================== */ import { API_BASE_URL, CURRENT_PROJECT_ID_KEY, PLAN_STEP_ROUTES, STAB_STEP_ROUTES, type RoutePath, } from "@config/config_frontend"; import { el, hideLoadingOverlay, showLoadingOverlay, showToast } from "@ui/ui_template_elements"; import { t, type LocaleKey } from "@ui/ui_template_locale"; import { createWorkflowLayout } from "@ui/ui_template_workflow_layout"; import { navigateTo, type PageRenderer } from "./router"; interface GroupFlow { routes: readonly RoutePath[]; labels: readonly LocaleKey[]; icons: readonly string[]; } const FLOWS: readonly GroupFlow[] = [ { routes: PLAN_STEP_ROUTES, labels: ["DE_Step_PlanBase", "DE_Step_PlanAnalysis", "DE_Step_PlanConfirm"], icons: ["📍", "🧭", "✅"], }, { routes: STAB_STEP_ROUTES, labels: [ "DE_Step_StabGround", "DE_Step_StabSection", "DE_Step_Stab3D", "DE_Step_StabReport", "DE_Step_StabRock", ], icons: ["🪨", "📐", "🧊", "📄", "⛰️"], }, ]; /** 종류별 단계 묶음(대시보드 표 단계 막대 · 528-2) — ROAD · 모르는 종류 = 없음(B 막대 그대로) */ export function groupFlowOf( kind: string | null | undefined, ): { routes: readonly RoutePath[]; steps: string[]; icons: readonly string[] } | null { const flow = { PLAN: FLOWS[0], STABILITY: FLOWS[1] }[kind ?? ""]; return flow ? { ...flow, steps: flow.labels.map((key) => t(key)) } : null; } /** 단계 틀을 세움 — mainContent · leftPanel 이 없으면 빈 단계 안내 · [?] 줄만 */ export function mountGroupStep( root: HTMLElement, route: RoutePath, parts: { mainContent?: HTMLElement; leftPanel?: HTMLElement } = {}, ): void { const flow = FLOWS.find((f) => f.routes.includes(route)); if (!flow) throw new Error(`단계 묶음 밖 주소: ${route}`); const active = flow.routes.indexOf(route); const layout = createWorkflowLayout({ title: t(flow.labels[active]), steps: flow.labels.map((key) => t(key)), icons: flow.icons, activeStep: active, routes: flow.routes, leftPanel: parts.leftPanel, mainContent: parts.mainContent ?? el("p", { className: "app-placeholder", text: t("DE_Step_Empty") }), onStepClick: (_index, next) => navigateTo(next as RoutePath), }); root.append(layout.root); } /** 빈 단계 페이지(길 선언의 load 가 부름) */ export const groupStepPage = (route: RoutePath): PageRenderer => (root) => mountGroupStep(root, route); /** 등록 화면 [등록](D01 · E01) — POST → 현재 프로젝트로 기억 → 첫 단계로 · 실패 = 서버 까닭 알림 */ export async function createGroupProject( path: string, body: object, firstStep: RoutePath, ): Promise { showLoadingOverlay(); try { const response = await fetch(`${API_BASE_URL}${path}`, { method: "POST", headers: { "Content-Type": "application/json" }, credentials: "include", body: JSON.stringify(body), }); const data = (await response.json().catch(() => ({}))) as { project_id?: string; detail?: unknown; }; if (!response.ok || !data.project_id) { const detail = typeof data.detail === "string" ? data.detail : `HTTP ${response.status}`; throw new Error(detail); } localStorage.setItem(CURRENT_PROJECT_ID_KEY, data.project_id); showToast(t("DE_Reg_Success"), "success"); navigateTo(firstStep); } catch (error) { showToast(error instanceof Error ? error.message : t("Common_Status_Error"), "error"); } finally { hideLoadingOverlay(); } }