/* ============================================================================= * B01_Dashboard_UI_PlanLink.ts * 받은 예정노선 [연결] 모달(요구서 345 3-5 · PLAN 531-4) — 본보기 `B03_FileInput_UI_PlanImport.ts`. * ① 새 임도 프로젝트로 ② 기존 프로젝트에 연결(우리 회사 ROAD 프로젝트 고르기) → * POST /api/dashboard/plan-routes/{assignee}/link · 지울 결과 409 confirm → 확인 창 → 다시 · * 끝나면 그 임도 프로젝트 B03 으로 이동. * ========================================================================== */ import { API_BASE_URL, ROUTES } from "@config/config_frontend"; import { createButton, createSelectField, el, hideLoadingOverlay, showConfirmDialog, showLoadingOverlay, showToast, } from "@ui/ui_template_elements"; import { openModal } from "@ui/ui_template_modal"; import { goToWorkflowStage } from "../A00_Common/b_workflow_nav"; import { fetchUserProjects } from "./B01_Dashboard_Api_Fetch"; import { L } from "./B01_Dashboard_UI_Common"; type LinkTarget = { mode: "new" } | { mode: "existing"; project_id: string }; /** 연결 — 지울 결과가 있으면 한 번 묻고 다시 · 성공 = 임도 프로젝트 id */ async function postLink(assigneeId: number, target: LinkTarget): Promise { // 「LAS 없이 설계」는 B03 화면 기억값(localStorage) — 기존 프로젝트만 const lasFree = target.mode === "existing" && localStorage.getItem(`b03_las_free_${target.project_id}`) === "1"; const send = (confirmReplace: boolean): Promise => fetch(`${API_BASE_URL}/dashboard/plan-routes/${assigneeId}/link`, { method: "POST", credentials: "include", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ ...target, confirm_replace: confirmReplace, las_free: lasFree }), }); showLoadingOverlay(); try { let response = await send(false); let data = (await response.json().catch(() => ({}))) as { confirm?: string; message?: string; detail?: unknown; project_id?: string; }; if (response.status === 409 && data.confirm === "replace_outputs") { hideLoadingOverlay(); const agreed = await showConfirmDialog(data.message ?? "", L("DE_Assigned_Link")); showLoadingOverlay(); if (!agreed) return null; response = await send(true); data = (await response.json().catch(() => ({}))) as typeof data; } if (!response.ok || !data.project_id) { throw new Error(typeof data.detail === "string" ? data.detail : `HTTP ${response.status}`); } showToast(L("D04_Import_Done"), "success"); return data.project_id; } catch (error) { showToast(error instanceof Error ? error.message : String(error), "error"); return null; } finally { hideLoadingOverlay(); } } export async function openPlanLinkModal(assigneeId: number, title: string): Promise { // 사용자 프로젝트 목록 = 회사 프로젝트(서버 범위) — 그중 임도만 const roads = (await fetchUserProjects().catch(() => [])).filter( (project) => (project.project_kind ?? "ROAD") === "ROAD", ); const run = async (target: LinkTarget, close: () => void): Promise => { close(); const projectId = await postLink(assigneeId, target); if (projectId) await goToWorkflowStage(projectId, ROUTES.B03_FILE_INPUT); }; openModal({ title: `${L("DE_Link_Title")} — ${title}`, closeLabel: "×", mount: (body, close) => { const pick = createSelectField({ label: L("DE_Link_Existing"), options: roads.map((project) => ({ value: project.id, text: project.name })), }); const existing = createButton({ label: L("DE_Link_Existing"), variant: "ghost", onClick: () => void run({ mode: "existing", project_id: pick.select.value }, close), }); existing.disabled = !roads.length; body.append( createButton({ label: L("DE_Link_New"), variant: "filled", onClick: () => void run({ mode: "new" }, close), }), roads.length ? pick.root : el("p", { text: L("DE_Link_NoRoad") }), existing, ); }, }); }