/* ============================================================================= * B03_FileInput_UI_TempPicker.ts * 임시 보관함 불러오기 — 대시보드에 미리 올려 둔 자료를 이 프로젝트로 가져온다. * * 파일 업로드 컨테이너 안에 버튼을 두고, 누르면 **완료된 보관 묶음만** 목록으로 * 보여 준다. 선택 후 [확인]을 누르면 지정 상태가 되고, 실제 이동은 화면의 * [업로드] 버튼을 눌렀을 때 일어난다(2026-08-08 사용자 지시). * ========================================================================== */ import { createButton, showToast } from "@ui/ui_template_elements"; import { currentLanguageIndex, ui_locales } from "@ui/ui_template_locale"; import { fetchTempBatches, type TempBatchItem } from "../B01_Dashboard/B01_Dashboard_Api_Temp"; import "./B03_FileInput_UI_Style_Temp.css"; function L(key: keyof typeof ui_locales): string { return ui_locales[key][currentLanguageIndex]; } function formatBytes(bytes: number): string { const gb = bytes / 1024 / 1024 / 1024; if (gb >= 1) return `${gb.toFixed(2)} GB`; return `${(bytes / 1024 / 1024).toFixed(1)} MB`; } export interface TempPickerHandle { /** 업로드 컨테이너에 붙일 요소(버튼 + 선택 안내). */ root: HTMLElement; /** 지금 선택된 보관 묶음(없으면 null). */ selected(): TempBatchItem | null; /** 연결 완료 후 선택 표시를 지운다. */ clear(): void; } /** * 불러오기 버튼과 선택 모달을 만든다. * `onSelected`는 선택이 바뀔 때마다 호출된다 — 호출측이 업로드 버튼 활성화를 조정한다. */ export function createTempPicker( onSelected: (batch: TempBatchItem | null) => void, ): TempPickerHandle { const root = document.createElement("div"); root.className = "b03-file__temp-picker"; const openButton = createButton({ label: L("B03_Temp_Btn_Open"), variant: "ghost", onClick: () => void openModal(), }); // 선택 요약(「선택된 보관 자료 없음」·묶음 이름)은 두지 않는다 — 불러온 자료는 카드가 // 곧바로 보여 주고, 고르기 전 상태는 [파일 업로드]가 잠긴 것으로 이미 드러난다 // (2026-09-03 사용자 지시). root.append(openButton); let selectedBatch: TempBatchItem | null = null; function applySelection(batch: TempBatchItem | null): void { selectedBatch = batch; onSelected(batch); } async function openModal(): Promise { let batches: TempBatchItem[] = []; try { const response = await fetchTempBatches(); // 프로젝트에 넣을 수 있는 것은 필수 파일이 다 찬 미연결 묶음뿐이다. batches = response.batches.filter( (item) => item.required_complete && item.status !== "linked", ); } catch (error) { showToast(error instanceof Error ? error.message : L("B03_Temp_Load_Failed"), "error"); return; } const backdrop = document.createElement("div"); backdrop.className = "b03-file__modal-backdrop"; const modal = document.createElement("div"); modal.className = "b03-file__modal b03-file__temp-modal"; modal.setAttribute("role", "dialog"); modal.setAttribute("aria-modal", "true"); const title = document.createElement("h3"); title.textContent = L("B03_Temp_Modal_Title"); const list = document.createElement("div"); list.className = "b03-file__temp-list"; let pending: TempBatchItem | null = selectedBatch; if (batches.length === 0) { const empty = document.createElement("p"); empty.className = "b03-file__temp-empty"; empty.textContent = L("B03_Temp_Modal_Empty"); list.append(empty); } for (const batch of batches) { const option = document.createElement("button"); option.type = "button"; option.className = "b03-file__temp-option"; option.classList.toggle("is-selected", pending?.batch_id === batch.batch_id); const name = document.createElement("strong"); name.textContent = batch.name; const meta = document.createElement("span"); meta.textContent = `${batch.files.length}${L("B03_Temp_FileCount")} · ${formatBytes( batch.total_size_bytes, )}`; const files = document.createElement("span"); files.className = "b03-file__temp-option-files"; files.textContent = batch.files.map((file) => file.file_type.toUpperCase()).join(", "); option.append(name, meta, files); option.addEventListener("click", () => { pending = batch; list .querySelectorAll(".b03-file__temp-option") .forEach((element) => element.classList.remove("is-selected")); option.classList.add("is-selected"); }); list.append(option); } const actions = document.createElement("div"); actions.className = "b03-file__temp-modal-actions"; const close = (): void => backdrop.remove(); actions.append( createButton({ label: L("Common_Btn_Cancel"), variant: "ghost", onClick: close }), createButton({ label: L("Common_Btn_Confirm"), variant: "filled", onClick: () => { if (!pending) { showToast(L("B03_Temp_Select_Required"), "warning"); return; } applySelection(pending); close(); }, }), ); modal.append(title, list, actions); backdrop.append(modal); backdrop.addEventListener("click", (event) => { if (event.target === backdrop) close(); }); document.body.append(backdrop); } return { root, selected: () => selectedBatch, clear: () => applySelection(null), }; }