window.pageBoot = async (me) => {
const state = { page:1, pageSize:50, total:0, data:[] };
async function load(){
const d = await GET(`/subjects/list?q=${encodeURIComponent($("#search").value)}&page=${state.page}&limit=${state.pageSize}`);
state.total = d.total; state.data = d.subjects || [];
$("#count").textContent = String(state.total);
$("#empty").style.display = state.total ? "none" : "";
$("#tbl").style.display = state.total ? "" : "none";
$("#body").innerHTML = state.data.map(s => `
| ${s.id} |
${esc(s.subject)} |
${s.use_count} |
|
`).join("");
const pages = Math.max(1, Math.ceil(state.total / state.pageSize));
$("#pager").innerHTML = [
``,
`Page ${state.page} / ${pages}`,
``
].join("");
}
$("#btnAdd").addEventListener("click", () => {
const inp = el("input", {class:"inp", required:"required", placeholder:"Invoice for %CompanyName% — due %DueDate%"});
openModal("Add Subject Line", el("div",{class:"form"}, el("label",{},"Subject text (max 500 chars)"), inp), {
buttons: [{ label:"Add", kind:"btn", onClick: async (close) => {
if (!inp.value.trim()) { toast("Subject is required","err"); return; }
await POST("/subjects", { subject: inp.value });
toast("Subject added", "ok"); close(); await load();
}}]
});
});
$("#btnBulk").addEventListener("click", () => {
const ta = el("textarea", {class:"inp", rows:8, placeholder:"One subject per line…\nInvoice %InvoiceNumber% for %CompanyName%\nPayment reminder — %FirstName%\nBalance due: %TotalAmount% — %DueDate%"});
openModal("Bulk Add Subjects", el("div",{class:"form"}, el("label",{},"One subject per line"), ta), {
wide: true,
buttons: [{ label:"Add All", kind:"btn", onClick: async (close) => {
const list = ta.value.split("\n").map(s=>s.trim()).filter(Boolean);
if (!list.length) { toast("No subjects entered","err"); return; }
const d = await POST("/subjects/bulk", { subjects: list });
toast(`Added ${d.added} subjects`, "ok"); close(); await load();
}}]
});
});
$("#btnImport").addEventListener("click", () => {
const f = el("input", {type:"file", accept:".csv,.txt", required:"required"});
openModal("Import Subjects", el("div",{class:"form"},
el("p",{class:"mut",style:"margin-top:0;font-size:.85rem"},"CSV: one column (header optional). TXT: one subject per line. JSON is rejected."),
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("/subjects/import", {method:"POST", body: fd});
toast(`Seen ${d.seen}, added ${d.added} (duplicates skipped)`, "ok");
close(); await load();
} catch (err) { toast("Import failed: " + err.message, "err"); }
}}]
});
});
$("#btnExport").addEventListener("click", () => { window.location.href = "/subjects/export"; });
$("#btnClear").addEventListener("click", () => confirmedAction(
"Delete ALL subject lines? This cannot be undone.", async () => {
const d = await POST("/subjects/clear-all");
toast(`Deleted ${d.deleted} subjects`, "ok"); await load();
}));
$("#body").addEventListener("click", (e) => {
const b = e.target.closest("button"); if (!b) return;
if (b.dataset.edit) {
const inp = el("input", {class:"inp", value: b.dataset.v, required:"required"});
openModal("Edit Subject", el("div",{class:"form"}, el("label",{},"Subject"), inp), {
buttons: [{ label:"Save", kind:"btn", onClick: async (close) => {
await api(`/subjects/${b.dataset.edit}`, { method:"PUT", body:{ subject: inp.value }});
toast("Saved", "ok"); close(); await load();
}}]
});
}
if (b.dataset.del) confirmedAction("Delete this subject line?", async () => {
await api(`/subjects/${b.dataset.del}`, { method:"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();
});
$("#search").addEventListener("input", debounce(() => { state.page=1; load(); }, 400));
await load();
};