"use strict"; const $ = (sel) => document.querySelector(sel); const $$ = (sel) => Array.from(document.querySelectorAll(sel)); async function api(path, opts = {}) { const res = await fetch(path, { headers: { "Content-Type": "application/json" }, ...opts, }); let data = null; try { data = await res.json(); } catch (_) { /* no body */ } if (!res.ok) { const msg = (data && (data.error || data.message)) || res.statusText; throw new Error(msg); } return data; } function toast(message, kind = "") { const el = $("#toast"); el.textContent = message; el.className = "toast " + kind; setTimeout(() => el.classList.add("hidden"), 3000); } function fmtTime(iso) { if (!iso) return "—"; try { return new Date(iso).toLocaleString(locale()); } catch (_) { return iso; } } function fmtTs(ts) { if (!ts) return "—"; return new Date(ts * 1000).toLocaleString(locale()); } function escapeHtml(s) { return String(s).replace(/[&<>"]/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """ }[c])); } /* ---------------- Language ---------------- */ const langSel = $("#lang-select"); langSel.value = LANG; langSel.addEventListener("change", async () => { setLang(langSel.value); refreshStatus(); refreshLogs(); if ($("#tab-feeds").classList.contains("active")) loadFeeds(); try { await api("/api/settings", { method: "PUT", body: JSON.stringify({ language: LANG }) }); } catch (_) { /* ignore */ } }); /* ---------------- Tabs ---------------- */ $$(".tab").forEach((tab) => { tab.addEventListener("click", () => { $$(".tab").forEach((t) => t.classList.remove("active")); $$(".tab-panel").forEach((p) => p.classList.remove("active")); tab.classList.add("active"); $("#tab-" + tab.dataset.tab).classList.add("active"); if (tab.dataset.tab === "feeds") loadFeeds(); if (tab.dataset.tab === "settings") loadSettings(); }); }); /* ---------------- Status / dashboard ---------------- */ async function refreshStatus() { try { const s = await api("/api/status"); $("#stat-feeds").textContent = `${s.feed_active}/${s.feed_total}`; $("#stat-topics").textContent = s.topics.length; $("#stat-sent").textContent = s.sent_total; $("#stat-history").textContent = s.history_count; const enabled = s.enabled; const pill = $("#engine-pill"); if (s.syncing) { pill.textContent = t("pill_syncing"); pill.className = "pill pill-on"; } else if (enabled) { pill.textContent = t("pill_running"); pill.className = "pill pill-on"; } else { pill.textContent = t("pill_paused"); pill.className = "pill pill-off"; } $("#btn-toggle").textContent = enabled ? t("pause") : t("resume"); $("#dash-engine").textContent = s.syncing ? t("st_syncing") : (enabled ? t("st_running") : t("st_paused")); $("#dash-last").textContent = fmtTime(s.last_sync) + (s.last_sync_ok === false ? t("err_suffix_sync_failed") : ""); $("#dash-next").textContent = enabled ? fmtTs(s.next_sync) : "—"; $("#dash-interval").textContent = s.sync_interval + " " + t("sec"); $("#dash-error").textContent = s.last_error || "—"; } catch (e) { $("#engine-pill").textContent = t("no_connection"); } } async function refreshLogs() { try { const { logs } = await api("/api/logs"); const box = $("#log"); box.innerHTML = logs.map((l) => { const time = new Date(l.time).toLocaleTimeString(locale()); return `