window.pageBoot = async (me) => { const fmtDT = window.fmtDT || (s => s || "—"); const state = { page:1, pageSize:100, total:0, data:[] }; async function load(){ const d = await GET(`/phones/list?active_only=${$("#activeOnly").checked}&search=${encodeURIComponent($("#search").value)}&page=${state.page}&page_size=${state.pageSize}`); state.total = d.total; state.data = d.phones || []; $("#count").textContent = String(state.total); $("#empty").style.display = state.total ? "none" : ""; $("#body").innerHTML = state.data.map(p => ` ${esc(p.phone)} ${esc(p.label || "—")} ${p.is_active ? 'active' : 'inactive'} ${p.use_count} ${esc(fmtDT(p.created_at))}
`).join("") || `No numbers found`; const pages = Math.max(1, Math.ceil(state.total / state.pageSize)); $("#pager").innerHTML = [ ``, `Page ${state.page} / ${pages}`, `` ].join(""); } $("#btnAdd").addEventListener("click", () => { const ph = el("input", {class:"inp", required:"required", placeholder:"+1 555 010 1234"}); const lb = el("input", {class:"inp", placeholder:"Support line (optional)"}); openModal("Add Phone Number", el("div",{class:"form"}, el("label",{},"Phone (digits, spaces, + - allowed)"), ph, el("label",{},"Label"), lb), { buttons: [{ label:"Add", kind:"btn", onClick: async (close) => { try { await POST("/phones/add", { phone: ph.value, label: lb.value }); toast("Phone added", "ok"); close(); await load(); } catch (err) { toast("Add failed: " + err.message, "err"); } }}] }); }); $("#btnImport").addEventListener("click", () => { const f = el("input", {type:"file", accept:".csv,.txt", required:"required"}); openModal("Import Phone Numbers", el("div",{class:"form"}, el("p",{class:"mut",style:"margin-top:0;font-size:.85rem"},"CSV: phone,label per row (header optional). TXT: one phone per line. JSON is rejected. Great for bulk-adding 10+ numbers."), el("label",{},"File"), f), { buttons: [{ label:"Import", kind:"btn", onClick: async (close) => { if (!f.files[0]) return; const fd = new FormData(); fd.append("file", f.files[0]); try { const d = await api("/phones/import", { method:"POST", body: fd }); toast(`Added ${d.added} (${d.duplicates} duplicates, ${d.invalid.length} invalid)`, "ok"); close(); await load(); } catch (err) { toast("Import failed: " + err.message, "err"); } }}] }); }); $("#btnClear").addEventListener("click", () => confirmedAction( "Delete ALL phone numbers? This cannot be undone.", async () => { const d = await POST("/phones/clear-all"); toast(`Deleted ${d.deleted} numbers`, "ok"); await load(); })); $("#body").addEventListener("click", (e) => { const b = e.target.closest("button"); if (!b) return; if (b.dataset.tog) { POST(`/phones/${b.dataset.tog}/patch`, { is_active: b.dataset.act !== "1" }) .then(() => { toast("Updated", "ok"); load(); }); } if (b.dataset.lbl) { const p = state.data.find(x => x.id == b.dataset.lbl); const lb = el("input", {class:"inp", value: p.label || ""}); openModal("Edit Label — " + p.phone, el("div",{class:"form"}, el("label",{},"Label"), lb), { buttons: [{ label:"Save", kind:"btn", onClick: async (close) => { await POST(`/phones/${b.dataset.lbl}/patch`, { label: lb.value }); toast("Saved", "ok"); close(); await load(); }}] }); } if (b.dataset.del) confirmedAction("Delete this phone number?", async () => { await POST(`/phones/${b.dataset.del}/delete`); toast("Deleted","ok"); await load(); }); }); $("#pager").addEventListener("click", (e) => { const b = e.target.closest("button"); if (!b || b.disabled) return; state.page = +b.dataset.pg; load(); }); $("#activeOnly").addEventListener("change", () => { state.page=1; load(); }); $("#search").addEventListener("input", debounce(() => { state.page=1; load(); }, 400)); await load(); };