/* ============================================================ AssemblerDashboard — личная аналитика сборщика #/master/dashboard ============================================================ */ const AssemblerDashboard = (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("assembler_earnings", { year }); if (data.error) { screen.innerHTML = `
${escHtml(data.error === "no_name" ? "Имя не задано в профиле. Обратитесь к менеджеру." : 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 = ""; // Имя не нашли if (!data.matched_name) { screen.appendChild(el(`
🔍
Данные не найдены
${escHtml(data.message || "Ваше имя не найдено в таблице занятости")}
Имя в профиле: ${escHtml(data.full_name || "—")}
`)); return; } 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 prevDate = new Date(now.getFullYear(), now.getMonth() - 1, 1); const prevYM = `${prevDate.getFullYear()}-${String(prevDate.getMonth() + 1).padStart(2, "0")}`; const curMonth = months[curYM] || null; const prevMonth = months[prevYM] || null; // === Hero-карточка === const heroCard = el(`
Всего за период
${escHtml(fmtMoney(data.total_amount))}
${escHtml(String(data.total_orders))} заказов
${data.match_score < 2 ? `
⚠ Неточное совпадение: «${escHtml(data.matched_name)}»
` : ""}
`); screen.appendChild(heroCard); // === Мини-карточки текущий / прошлый месяц === if (curMonth || prevMonth) { const row = el(`
`); const _miniCard = (label, m) => { if (!m) return el(`
${escHtml(label)}
`); return el(`
${escHtml(label)}
${escHtml(fmtMoney(m.total_amount))}
${escHtml(String(m.orders))} заказов
`); }; row.appendChild(_miniCard("Текущий месяц", curMonth)); row.appendChild(_miniCard("Прошлый месяц", prevMonth)); screen.appendChild(row); } // === Таблица по месяцам === if (monthKeys.length) { 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 avgPer = m.orders ? Math.round(m.total_amount / m.orders) : 0; const isCurrentMonth = ym === curYM; const card = el(`
${escHtml(fmtMonth(ym))} ${isCurrentMonth ? `сейчас` : ""}
${escHtml(fmtMoney(m.total_amount))}
${escHtml(String(m.orders))} зак. · ср. ${escHtml(fmtMoney(avgPer))}
`); screen.appendChild(card); }); } else { screen.appendChild(el(`
Нет данных за выбранный период.
Попробуй выбрать другой год.
`)); } // Footer if (data.parsed_at) { const parsedAt = new Date(data.parsed_at).toLocaleString("ru-RU"); screen.appendChild(el(`
Данные обновлены: ${escHtml(parsedAt)}
`)); } screen.appendChild(el(`
`)); } return { mount }; })();