202 lines
6.0 KiB
JavaScript
202 lines
6.0 KiB
JavaScript
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 "<span>" + w + "</span>"; }).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 += '<div class="day empty">' + (prevDays - i) + "</div>";
|
||
}
|
||
|
||
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 ? '<span class="dot"></span>' : '<span class="dot" style="visibility:hidden"></span>';
|
||
|
||
html += '<div class="' + cls + '" data-key="' + key + '">' + d + dot + "</div>";
|
||
}
|
||
|
||
var endPad = 7 - ((startPad + daysInMonth) % 7);
|
||
if (endPad < 7) {
|
||
for (var j = 1; j <= endPad; j++) {
|
||
html += '<div class="day empty">' + j + "</div>";
|
||
}
|
||
}
|
||
|
||
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 (
|
||
'<li class="note' + (n.done ? " done" : "") + '" data-id="' + n.id + '">' +
|
||
'<button class="chk' + (n.done ? " on" : "") + '" title="Отметить">' + (n.done ? "✓" : "") + "</button>" +
|
||
'<span class="note-text">' + escapeHtml(n.text) + "</span>" +
|
||
'<button class="del" title="Удалить">×</button>' +
|
||
"</li>"
|
||
);
|
||
}).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();
|