import { createButton, createInputField, showToast } from "@ui/ui_template_elements"; import { searchAddress, type AddressCandidate } from "./B01_Dashboard_Api_Fetch"; import { buildAddressMap } from "./B01_Dashboard_UI_MapPreview"; import { L } from "./B01_Dashboard_UI_Common"; /** * 회사 주소 입력칸 — 검색해서 고르는 방식 (2026-09-13 사용자 지시). * * 손으로 정확히 치지 않아도 되게, 일부만 넣어 후보를 받고 고른다. * 고른 즉시 좌표를 알므로 지도를 다시 찾지 않고 바로 그린다. * 저장은 지금대로 한 줄 — 「기본주소 + 상세주소」. 우편번호는 화면 확인용. * * 줄 배치는 라벨·입력·버튼을 한 줄에 세운다(B06 횡단 설정과 같은 꼴) — ui-field 가 * 기본이 세로라 그대로 두면 오류 슬롯 높이만큼 버튼이 밀려 수평이 어긋난다. * 그래서 오류 슬롯은 접고, 잘못 넣은 것은 토스트로 알린다. */ export interface AddressFieldHandle { root: HTMLElement; /** 저장할 한 줄 주소. 비면 null. */ value: () => string | null; } /** 지번으로 찾은 줄은 `parcel` 쪽이 시·도까지 갖춘 온전한 주소다. */ function candidateLine(item: AddressCandidate): string { const main = item.category === "parcel" ? item.parcel : item.road; return main || item.road || item.parcel; } function candidateHint(item: AddressCandidate): string { const other = item.category === "parcel" ? item.road : item.parcel; return [item.building, other, item.zipcode].filter(Boolean).join(" · "); } /** createButton 은 글자를 span 에 담는다 — textContent 로 덮으면 그 껍질이 사라진다. */ function setButtonLabel(button: HTMLButtonElement, label: string): void { const slot = button.querySelector(".ui-btn__label"); if (slot) slot.textContent = label; else button.textContent = label; } function row(...parts: HTMLElement[]): HTMLElement { const line = document.createElement("div"); line.className = "b01-dashboard__address-row"; line.append(...parts); return line; } export function buildAddressField(initial: string | null): AddressFieldHandle { const root = document.createElement("div"); root.className = "b01-dashboard__address"; // 1) 검색 줄 — 일부만 넣어도 후보가 나온다. const query = createInputField({ label: L("B01_Dashboard_Field_Address"), placeholder: "도로명·지번·건물명 일부 (예: 판교역로 235)", }); const findBtn = createButton({ label: "주소 찾기", variant: "ghost", onClick: () => void runSearch(), }); const manualBtn = createButton({ label: "직접 입력", variant: "ghost", onClick: function onB01_Address_Manual_Click() { manual = !manual; setButtonLabel(manualBtn, manual ? "검색으로" : "직접 입력"); base.input.readOnly = !manual; query.root.hidden = manual; findBtn.hidden = manual; mapBtn.hidden = !manual; if (manual) base.input.focus(); }, }); const searchRow = row(query.root, findBtn, manualBtn); // 2) 후보 목록 — 고르면 사라진다. const list = document.createElement("div"); list.className = "b01-dashboard__address-list"; list.hidden = true; // 3) 고른 주소 + 상세주소. const base = createInputField({ label: "기본주소", value: initial ?? "" }); base.input.readOnly = true; const zip = document.createElement("span"); zip.className = "b01-dashboard__address-zip"; zip.hidden = true; const detail = createInputField({ label: "상세주소", placeholder: "동·층·호" }); // 4) 지도 — 고른 즉시 그린다. 직접 입력일 때만 버튼으로 확인한다. const map = buildAddressMap(); const mapBtn = createButton({ label: "지도 확인", variant: "ghost", onClick: async function onB01_Address_Map_Click() { await map.show(base.input.value.trim()); }, }); mapBtn.hidden = true; root.append(searchRow, list, row(base.root, zip), row(detail.root), row(mapBtn), map.root); let manual = false; function pick(item: AddressCandidate): void { base.input.value = candidateLine(item); zip.textContent = item.zipcode ? `우편번호 ${item.zipcode}` : ""; zip.hidden = !item.zipcode; list.hidden = true; list.innerHTML = ""; map.showPoint(item.lat, item.lon); detail.input.focus(); } async function runSearch(): Promise { const text = query.input.value.trim(); if (text.length < 2) { showToast("주소를 두 글자 이상 넣으십시오.", "error"); return; } findBtn.disabled = true; setButtonLabel(findBtn, "찾는 중…"); try { const items = await searchAddress(text); list.innerHTML = ""; list.hidden = false; if (items.length === 0) { const empty = document.createElement("p"); empty.className = "b01-dashboard__modal-text"; empty.textContent = "찾은 주소가 없습니다. 다른 낱말로 넣거나 「직접 입력」을 쓰십시오."; list.append(empty); return; } for (const item of items) { const option = document.createElement("button"); option.type = "button"; option.className = "b01-dashboard__address-item"; const line = document.createElement("strong"); line.textContent = candidateLine(item); const hint = document.createElement("small"); hint.textContent = candidateHint(item); option.append(line, hint); option.addEventListener("click", () => pick(item)); list.append(option); } } catch { showToast("주소를 찾지 못했습니다. 잠시 뒤 다시 하십시오.", "error"); } finally { findBtn.disabled = false; setButtonLabel(findBtn, "주소 찾기"); } } // 검색칸에서 Enter — 모달이 닫히지 않게 막고 찾기만 한다. query.input.addEventListener("keydown", (event) => { if (event.key !== "Enter") return; event.preventDefault(); void runSearch(); }); // 이미 있는 주소는 열자마자 지도로 보여 준다. if (initial && initial.trim()) void map.show(initial.trim()); return { root, value: () => { const merged = `${base.input.value.trim()} ${detail.input.value.trim()}`.trim(); return merged || null; }, }; }