var WEEKDAYS = ["Пн", "Вт", "Ср", "Чт", "Пт", "Сб", "Вс"]; var MONTHS = [ "Январь", "Февраль", "Март", "Апрель", "Май", "Июнь", "Июль", "Август", "Сентябрь", "Октябрь", "Ноябрь", "Декабрь" ]; var now = new Date(); var view = { year: now.getFullYear(), month: now.getMonth() }; var openDate = null; var allByDate = {}; var gridEl = document.getElementById("grid"); var monthNameEl = document.getElementById("monthName"); var panelBody = document.getElementById("panelBody"); var panelEmpty = document.getElementById("panelEmpty"); var panelDateEl = document.getElementById("panelDate"); var notesEl = document.getElementById("notes"); var countEl = document.getElementById("count"); function pad(n) { return (n < 10 ? "0" : "") + n; } function fmt(d) { return pad(d.getDate()) + "." + pad(d.getMonth() + 1) + "." + d.getFullYear(); } function iso(date) { return date.getFullYear() + "-" + pad(date.getMonth() + 1) + "-" + pad(date.getDate()); } function escapeHtml(s) { return String(s).replace(/[&<>"']/g, function (c) { return { "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }[c]; }); } function renderWeekdays() { document.getElementById("weekdays").innerHTML = WEEKDAYS.map(function (w) { return "" + w + ""; }).join(""); } function render() { monthNameEl.textContent = MONTHS[view.month] + " " + view.year; var first = new Date(view.year, view.month, 1); var startPad = (first.getDay() + 6) % 7; var daysInMonth = new Date(view.year, view.month + 1, 0).getDate(); var prevDays = new Date(view.year, view.month, 0).getDate(); var html = ""; for (var i = startPad - 1; i >= 0; i--) { html += '
' + (prevDays - i) + "
"; } for (var d = 1; d <= daysInMonth; d++) { var date = new Date(view.year, view.month, d); var key = iso(date); var cls = "day"; var isToday = date.getFullYear() === now.getFullYear() && date.getMonth() === now.getMonth() && date.getDate() === now.getDate(); if (isToday) cls += " today"; if (openDate === key) cls += " selected"; var has = (allByDate[key] || []).length > 0; var dot = has ? '' : ''; html += '
' + d + dot + "
"; } var endPad = 7 - ((startPad + daysInMonth) % 7); if (endPad < 7) { for (var j = 1; j <= endPad; j++) { html += '
' + j + "
"; } } gridEl.innerHTML = html; } function changeMonth(delta) { view.month += delta; if (view.month < 0) { view.month = 11; view.year--; } if (view.month > 11) { view.month = 0; view.year++; } render(); } function goToday() { now = new Date(); view = { year: now.getFullYear(), month: now.getMonth() }; render(); } function api(url, opts) { return fetch(url, opts).then(function (r) { if (!r.ok) throw new Error("Ошибка сервера " + r.status); return r.json(); }); } function loadAll() { return api("/api/notes").then(function (list) { allByDate = {}; list.forEach(function (n) { (allByDate[n.date] = allByDate[n.date] || []).push(n); }); render(); if (openDate) renderPanel(); }); } function renderPanel() { var list = allByDate[openDate] || []; notesEl.innerHTML = list.map(function (n) { return ( '
  • ' + '" + '' + escapeHtml(n.text) + "" + '' + "
  • " ); }).join(""); countEl.textContent = list.length ? "Дела: " + list.length : ""; } function openDay(key) { openDate = key; panelEmpty.classList.add("hidden"); panelBody.classList.remove("hidden"); var parts = key.split("-"); var date = new Date(parts[0], +parts[1] - 1, +parts[2]); panelDateEl.textContent = fmt(date); render(); renderPanel(); } function closePanel() { openDate = null; panelBody.classList.add("hidden"); panelEmpty.classList.remove("hidden"); render(); } function addNote(text) { return api("/api/notes", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ date: openDate, text: text }) }).then(loadAll); } function toggleNote(id, done) { return api("/api/notes", { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ id: id, done: done }) }).then(loadAll); } function deleteNote(id) { return api("/api/notes/" + id, { method: "DELETE" }).then(loadAll); } gridEl.addEventListener("click", function (e) { var day = e.target.closest(".day"); if (!day || day.classList.contains("empty")) return; openDay(day.dataset.key); }); notesEl.addEventListener("click", function (e) { var li = e.target.closest(".note"); if (!li) return; var id = +li.dataset.id; var item = (allByDate[openDate] || []).find(function (n) { return n.id === id; }); if (!item) return; if (e.target.classList.contains("del")) { if (confirm("Удалить это дело?")) deleteNote(id); return; } if (e.target.classList.contains("chk")) { toggleNote(id, !item.done); } }); document.getElementById("addForm").addEventListener("submit", function (e) { e.preventDefault(); var input = document.getElementById("noteText"); var text = input.value.trim(); if (!text || !openDate) return; addNote(text); input.value = ""; }); document.getElementById("prev").addEventListener("click", function () { changeMonth(-1); }); document.getElementById("next").addEventListener("click", function () { changeMonth(1); }); document.getElementById("today").addEventListener("click", goToday); document.getElementById("closePanel").addEventListener("click", closePanel); renderWeekdays(); render(); loadAll();