/* ============================================================================= * B03_FileInput_UI_TempPicker.ts * 임시 보관함 불러오기 — 대시보드에 미리 올려 둔 자료를 이 프로젝트로 가져온다. * * 파일 업로드 컨테이너 안에 버튼을 두고, 누르면 **완료된 보관 묶음만** 목록으로 * 보여 준다. 선택 후 [확인]을 누르면 지정 상태가 되고, 실제 이동은 화면의 * [업로드] 버튼을 눌렀을 때 일어난다(2026-08-08 사용자 지시). * ========================================================================== */ import { createCard, createButton, setCardSelected, showToast } from "@ui/ui_template_elements"; import { createEmpty } from "@ui/ui_template_empty"; import { currentLanguageIndex, ui_locales } from "@ui/ui_template_locale"; import { openModal as openDialog } from "@ui/ui_template_modal"; import { createText } from "@ui/ui_template_text"; 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; } let pending: TempBatchItem | null = selectedBatch; openDialog({ title: L("B03_Temp_Modal_Title"), closeLabel: L("Common_Btn_Cancel"), width: "40rem", dialogClass: "b03-file__temp-modal", mount: (body, close, actions) => { const list = document.createElement("div"); list.className = "b03-file__temp-list"; if (batches.length === 0) { list.append(createEmpty({ text: L("B03_Temp_Modal_Empty"), small: true })); } for (const batch of batches) { const files = batch.files.map((file) => file.file_type.toUpperCase()).join(", "); const option = createCard({ title: batch.name, pad: 12, selected: pending?.batch_id === batch.batch_id, body: [ createText( `${batch.files.length}${L("B03_Temp_FileCount")} · ${formatBytes( batch.total_size_bytes, )}`, ), createText(files), ], onSelect: () => { pending = batch; list .querySelectorAll(".ui-card--select") .forEach((card) => setCardSelected(card, card === option)); }, }); list.append(option); } body.append(list); actions.append( createButton({ label: L("Common_Btn_Confirm"), variant: "filled", onClick: () => { if (!pending) { showToast(L("B03_Temp_Select_Required"), "warning"); return; } applySelection(pending); close(); }, }), ); }, }); } return { root, selected: () => selectedBatch, clear: () => applySelection(null), }; }