/* ============================================================ MeasurerDashboard — личная статистика замерщика #/master/measurer-stats ============================================================ */ const MeasurerDashboard = (function () { "use strict"; function escHtml(s) { return String(s == null ? "" : s) .replace(/&/g, "&").replace(//g, ">").replace(/"/g, """); } function el(html) { const t = document.createElement("template"); t.innerHTML = html.trim(); return t.content.firstChild; } function fmtMoney(n) { return Math.round(n || 0).toLocaleString("ru-RU") + " ₽"; } function fmtMonth(ym) { try { const d = new Date(ym + "-01"); return d.toLocaleDateString("ru-RU", { month: "long", year: "numeric" }); } catch { return ym; } } async function _api(path, body = {}) { const ctrl = new AbortController(); const t = setTimeout(() => ctrl.abort(), 30000); try { const res = await fetch(`${BACKEND_URL}/api/${path}`, { method: "POST", signal: ctrl.signal, headers: { "Content-Type": "application/json" }, body: JSON.stringify({ initData: (typeof Platform !== "undefined" ? Platform.initData : (window.tg?.initData || "")), initDataUnsafe: (typeof Platform !== "undefined" ? Platform.initDataUnsafe : null), ...body, }), }); if (!res.ok) throw new Error(`HTTP ${res.status}`); return await res.json(); } catch (e) { if (e.name === "AbortError") throw new Error("Таймаут"); throw e; } finally { clearTimeout(t); } } async function mount(container) { container.innerHTML = ""; document.body.classList.remove("has-bottom-nav"); document.getElementById("bottom-nav")?.remove(); const h = el(`
Мои замеры
`); h.querySelector(".podbor-back").addEventListener("click", () => { haptic && haptic("impact"); history.back(); }); const yearEl = el(`
`); const screen = el(`
`); container.appendChild(h); container.appendChild(yearEl); container.appendChild(screen); const load = async (year) => { screen.innerHTML = `
`; try { const data = await _api("measurer_earnings", { year }); if (data.error) { screen.innerHTML = `
${escHtml(data.error)}
`; return; } _render(screen, data); } catch (e) { screen.innerHTML = `
Ошибка: ${escHtml(e.message)}
`; } }; yearEl.querySelector("#yearSelect").addEventListener("change", function () { load(this.value); }); h.querySelector("#reloadBtn").addEventListener("click", () => { haptic && haptic("impact"); load(yearEl.querySelector("#yearSelect").value); }); load("2026"); } function _render(screen, data) { screen.innerHTML = ""; const months = data.months || {}; const monthKeys = Object.keys(months).sort().reverse(); const now = new Date(); const curYM = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}`; const prevD = new Date(now.getFullYear(), now.getMonth() - 1, 1); const prevYM = `${prevD.getFullYear()}-${String(prevD.getMonth() + 1).padStart(2, "0")}`; const curMonth = months[curYM] || null; const prevMonth = months[prevYM] || null; // Hero screen.appendChild(el(`
Всего за период
${escHtml(fmtMoney(data.total_amount))}
${escHtml(String(data.total_measurements))} замеров ${data.total_amount > 0 ? ` · ${escHtml(fmtMoney(data.total_amount / data.total_measurements))} в среднем` : ""}
`)); // Мини-карточки if (curMonth || prevMonth) { const row = el(`
`); const mini = (label, m) => !m ? el(`
${escHtml(label)}
`) : el(`
${escHtml(label)}
${escHtml(fmtMoney(m.total_amount))}
${m.measurements} замеров · ${m.paid} оплачено
`); row.appendChild(mini("Текущий месяц", curMonth)); row.appendChild(mini("Прошлый месяц", prevMonth)); screen.appendChild(row); } if (!monthKeys.length) { screen.appendChild(el(`
📐
Замеров за этот период нет
Данные появятся после выставления счёта за замер
`)); return; } // Таблица по месяцам screen.appendChild(el(`
📅 По месяцам
`)); const maxAmt = Math.max(...monthKeys.map(k => months[k].total_amount), 1); monthKeys.forEach(ym => { const m = months[ym]; const pct = Math.round((m.total_amount / maxAmt) * 100); const isCur = ym === curYM; screen.appendChild(el(`
${escHtml(fmtMonth(ym))} ${isCur ? `сейчас` : ""}
${m.total_amount > 0 ? escHtml(fmtMoney(m.total_amount)) : "—"}
${m.measurements} замеров · ${m.paid} со счётом
${m.total_amount > 0 ? `
` : ""}
`)); }); screen.appendChild(el(`
💡 Сумма учитывается когда вы выставляете счёт за замер через кнопку «💳 Выставить счёт» в карточке клиента
`)); screen.appendChild(el(`
`)); } return { mount }; })();