(() => { "use strict"; const tg = window.Telegram?.WebApp; const tgUser = tg?.initDataUnsafe?.user; const query = new URLSearchParams(window.location.search); const previewUserId = Number(query.get("user_id")) || 0; const user = tgUser || (previewUserId ? { id: previewUserId, first_name: "Предпросмотр" } : null); const userId = Number(user?.id) || 0; const state = { kind: "income", board: "all", summary: null, config: null }; const categories = { income: ["Зарплата", "Фриланс", "Подарок", "Продажа", "Инвестиции", "Другое"], expense: ["Продукты", "Жильё", "Транспорт", "Здоровье", "Развлечения", "Покупки", "Другое"], }; const $ = (selector) => document.querySelector(selector); const money = new Intl.NumberFormat("ru-RU", { style: "currency", currency: "RUB", maximumFractionDigits: 2 }); function initTelegram() { if (!tg) return; tg.ready(); tg.expand(); tg.enableClosingConfirmation?.(); if (tg.isVersionAtLeast?.("6.1")) tg.setHeaderColor("secondary_bg_color"); if (tg.isVersionAtLeast?.("7.10")) tg.setBottomBarColor("secondary_bg_color"); } function showToast(message) { const toast = $("#toast"); toast.textContent = message; toast.classList.add("show"); window.setTimeout(() => toast.classList.remove("show"), 2600); } function setIdentity() { const name = [user?.first_name, user?.last_name].filter(Boolean).join(" ") || "Гость"; $("#userName").textContent = name; $("#userInitial").textContent = name.slice(0, 1).toUpperCase(); } function bindNavigation() { document.querySelectorAll(".bottom-nav button").forEach((button) => { button.addEventListener("click", () => { document.querySelectorAll(".bottom-nav button, .view").forEach((item) => item.classList.remove("active")); button.classList.add("active"); $(`#${button.dataset.view}`).classList.add("active"); if (button.dataset.view === "leaderboardView") loadLeaderboard(); }); }); } function fillCategories() { const select = $("#category"); select.innerHTML = categories[state.kind].map((category) => ``).join(""); $("#customCategoryWrap").classList.add("hidden"); $("#customCategory").required = false; } function bindForm() { document.querySelectorAll(".segment").forEach((button) => { button.addEventListener("click", () => { document.querySelectorAll(".segment").forEach((item) => item.classList.remove("active")); button.classList.add("active"); state.kind = button.dataset.kind; fillCategories(); }); }); $("#category").addEventListener("change", (event) => { const custom = event.target.value === "Другое"; $("#customCategoryWrap").classList.toggle("hidden", !custom); $("#customCategory").required = custom; if (custom) $("#customCategory").focus(); }); $("#transactionForm").addEventListener("submit", submitTransaction); } function submitTransaction(event) { event.preventDefault(); const amount = Number($("#amount").value); const selected = $("#category").value; const category = selected === "Другое" ? $("#customCategory").value.trim() : selected; if (!Number.isFinite(amount) || amount <= 0 || !category) { showToast("Проверьте сумму и категорию"); return; } const payload = { v: 1, type: "transaction", kind: state.kind, amount: amount.toFixed(2), category: category.slice(0, 40), note: $("#note").value.trim().slice(0, 160), occurred_at: new Date().toISOString(), }; const encoded = JSON.stringify(payload); if (encoded.length > 4096) { showToast("Запись получилась слишком длинной"); return; } if (!tg?.sendData || !tg.initData) { showToast("Сохранение доступно при открытии из Telegram"); return; } tg.HapticFeedback?.notificationOccurred("success"); tg.sendData(encoded); } async function fetchJson(url) { const response = await fetch(url, { headers: { Accept: "application/json" } }); if (!response.ok) throw new Error(`HTTP ${response.status}`); return response.json(); } async function loadSummary() { if (!userId) return; try { state.summary = await fetchJson(`../api/users/${userId}/summary`); renderSummary(state.summary); } catch (error) { console.warn("Summary is not available", error); } } function renderSummary(summary) { $("#balanceValue").textContent = money.format(summary.balance_cents / 100); $("#incomeValue").textContent = money.format(summary.income_cents / 100); $("#expenseValue").textContent = money.format(summary.expense_cents / 100); const percent = Number(summary.saved_percent); $("#savedPercent").textContent = `${percent.toLocaleString("ru-RU", { maximumFractionDigits: 1 })}%`; $("#savingProgress").style.width = `${Math.max(0, Math.min(100, percent))}%`; renderCategories(summary.categories || []); } function renderCategories(items) { const list = $("#categoryList"); if (!items.length) return; const max = Math.max(...items.map((item) => item.amount_cents)); $("#categoryCaption").textContent = `${items.length} категорий`; list.classList.remove("empty-state"); list.innerHTML = items.slice(0, 8).map((item) => `