/* Трекер привычек — демо. Данные: демо-набор по умолчанию, правки — в localStorage. */ (function () { "use strict"; var STORAGE_KEY = "kt_habit_tracker:v1"; var DAY_COUNT = 7; // показываем последние 7 дней var WEEKDAYS = ["вс", "пн", "вт", "ср", "чт", "пт", "сб"]; // Словарь иконок из спрайта ДС. var ICONS = { book: "book", dumbbell: "tool", droplet: "layers", moon: "moon", lightbulb: "lightbulb", check: "check", checklist: "checklist", sparkles: "sparkles", star: "trophy", trophy: "trophy", "check-circle": "check-circle", run: "rocket", rocket: "rocket", walk: "arrowRight" }; var ICON_BY_NAME_KEY = [ ["чит", "book"], ["чт", "book"], ["читан", "book"], ["спорт", "tool"], ["трени", "tool"], ["бег", "rocket"], ["зал", "tool"], ["вода", "layers"], ["пить", "layers"], ["hydration", "layers"], ["сон", "moon"], ["спать", "moon"], ["sleep", "moon"], ["медит", "sparkles"], ["relax", "sparkles"], ["идея", "lightbulb"], ["мысль", "lightbulb"], ["звезд", "trophy"], ["цель", "trophy"], ["trophy", "trophy"], ["heart", "check-circle"], ["люб", "check-circle"] ]; var DEFAULT_ICON = "checklist"; var DEMO = [ { id: "read", name: "Чтение", icon: "book" }, { id: "sport", name: "Спорт", icon: "tool" }, { id: "water", name: "Вода", icon: "layers" }, { id: "sleep", name: "Ранний подъём", icon: "moon" }, { id: "ideas", name: "Идеи на день", icon: "lightbulb"} ]; // Детерминированные демо-статусы за последние 7 дней (0 = не сделано, 1 = сделано). var DEMO_CHECKS = { read: [1, 1, 1, 1, 1, 1, 0], sport: [1, 0, 1, 1, 0, 1, 0], water: [1, 1, 1, 0, 1, 1, 1], sleep: [1, 1, 0, 1, 1, 1, 1], ideas: [0, 1, 1, 1, 1, 0, 1] }; // ---- Утилиты дат (локальные, без часовых поясов) ---- function toDateStr(d) { var y = d.getFullYear(); var m = String(d.getMonth() + 1).padStart(2, "0"); var day = String(d.getDate()).padStart(2, "0"); return y + "-" + m + "-" + day; } function todayStr() { return toDateStr(new Date()); } function lastDays(n) { var out = []; var now = new Date(); for (var i = n - 1; i >= 0; i--) { var d = new Date(now.getFullYear(), now.getMonth(), now.getDate() - i); out.push(toDateStr(d)); } return out; } // ---- Состояние ---- function blankChecks() { var out = {}; lastDays(DAY_COUNT).forEach(function (s) { out[s] = 0; }); return out; } function makeDemo() { var days = lastDays(DAY_COUNT); return DEMO.map(function (p) { var checks = {}; (DEMO_CHECKS[p.id] || [0,0,0,0,0,0,0]).forEach(function (v, idx) { checks[days[idx]] = v; }); return { id: p.id, name: p.name, icon: p.icon, checks: checks, addedAt: days[0] }; }); } function load() { try { var raw = localStorage.getItem(STORAGE_KEY); if (raw) { var p = JSON.parse(raw); if (p && typeof p === "object" && Array.isArray(p.habits) && p.habits.length) return p; } } catch (e) { /* повреждённое хранилище — начнём с демо */ } var demo = { habits: makeDemo(), demo: true }; save(demo); return demo; } function save(state) { try { localStorage.setItem(STORAGE_KEY, JSON.stringify(state)); } catch (e) {} } var state = load(); // ---- Расчёты ---- // Серия дней подряд: считаем от самого недавнего дня до первого пропуска. function visibleStreak(habit, days7) { var reverse = days7.slice().reverse(); // от старого к новому var streak = 0; for (var i = 0; i < reverse.length; i++) { if (habit.checks[reverse[i]]) streak++; else break; } return streak; } // ---- DOM ---- var $ = function (id) { return document.getElementById(id); }; var habitsEl = $("habits"); var emptyEl = $("emptyState"); var kpiStrip = $("kpiStrip"); var demoReset = $("demoReset"); var toastStack = $("toastStack"); function fmtDate(str) { var p = str.split("-"); return p[2] + "." + p[1]; } function iconUse(icon) { return ''; } function render() { var days7 = lastDays(DAY_COUNT); var today = todayStr(); // подпись даты в шапке var now = new Date(); $("todayLabel").textContent = WEEKDAYS[now.getDay()] + ", " + fmtDate(today) + "." + String(now.getFullYear()); var habits = state.habits; if (!habits.length) { habitsEl.hidden = true; kpiStrip.style.display = "none"; emptyEl.hidden = false; demoReset.hidden = true; return; } emptyEl.hidden = true; habitsEl.hidden = false; kpiStrip.style.display = ""; // KPI — тихая метрик-полоса var doneToday = habits.filter(function (h) { return h.checks[today]; }).length; var best = 0, bestName = ""; habits.forEach(function (h) { var s = visibleStreak(h, days7); if (s > best) { best = s; bestName = h.name; } }); $("kpiHabits").textContent = String(habits.length); $("kpiDone").textContent = doneToday > 0 ? (doneToday + " / " + habits.length) : "—"; document.querySelector("#kpiDoneWrap .label").textContent = "сегодня выполнено"; $("kpiBest").textContent = best > 0 ? (best + " дн.") : "—"; var bestNameEl = $("kpiBestName"); bestNameEl.textContent = best > 0 ? bestName : ""; bestNameEl.style.display = best > 0 ? "" : "none"; // Список habitsEl.innerHTML = habits.map(function (h) { var st = visibleStreak(h, days7); var daysHtml = days7.map(function (s) { var on = h.checks[s] ? "on" : ""; var isToday = s === today ? " today" : ""; var dte = new Date(s); var dow = WEEKDAYS[dte.getDay()]; return '