From e23d2247e08646ef006b18ce0f7282db9e32bd69 Mon Sep 17 00:00:00 2001 From: Nikolay Tatarinov Date: Tue, 17 Feb 2026 23:26:41 +0300 Subject: [PATCH] Refactor calendar rendering and duty list display - Updated the calendar cell rendering to conditionally show duty and event markers based on the month view. - Introduced a new HTML structure for day markers to improve layout and styling. - Enhanced the duty list rendering to filter duties based on the current month, ensuring only relevant duties are displayed. - Added CSS styles for day markers to improve visual presentation and alignment. --- webapp/app.js | 51 ++++++++++++++++++++++++++++++++---------------- webapp/style.css | 31 ++++++++++++++++++++++++----- 2 files changed, 60 insertions(+), 22 deletions(-) diff --git a/webapp/app.js b/webapp/app.js index 544536b..36ede1e 100644 --- a/webapp/app.js +++ b/webapp/app.js @@ -207,25 +207,29 @@ const hasEvent = eventSummaries.length > 0; const cell = document.createElement("div"); - cell.className = "day" + (isOther ? " other-month" : "") + (isToday ? " today" : "") + (hasAny ? " has-duty" : "") + (hasEvent ? " holiday" : ""); + const showMarkers = !isOther; + cell.className = "day" + (isOther ? " other-month" : "") + (isToday ? " today" : "") + (showMarkers && hasAny ? " has-duty" : "") + (showMarkers && hasEvent ? " holiday" : ""); function namesAttr(list) { return list.length ? escapeHtml(list.map(function (x) { return x.full_name; }).join("\n")) : ""; } function titleAttr(list) { return list.length ? escapeHtml(list.map(function (x) { return x.full_name; }).join(", ")) : ""; } - let markers = "" + d.getDate() + ""; - if (dutyList.length) { - markers += "Д"; + let html = "" + d.getDate() + "
"; + if (showMarkers) { + if (dutyList.length) { + html += "Д"; + } + if (unavailableList.length) { + html += "Н"; + } + if (vacationList.length) { + html += "О"; + } + if (hasEvent) { + html += ""; + } } - if (unavailableList.length) { - markers += "Н"; - } - if (vacationList.length) { - markers += "О"; - } - if (hasEvent) { - markers += ""; - } - cell.innerHTML = markers; + html += "
"; + cell.innerHTML = html; calendarEl.appendChild(cell); d.setDate(d.getDate() + 1); } @@ -470,8 +474,12 @@ setNavEnabled(false); loadingEl.classList.remove("hidden"); errorEl.hidden = true; - const from = localDateString(firstDayOfMonth(current)); - const to = localDateString(lastDayOfMonth(current)); + const first = firstDayOfMonth(current); + const start = getMonday(first); + const gridEnd = new Date(start); + gridEnd.setDate(gridEnd.getDate() + 41); + const from = localDateString(start); + const to = localDateString(gridEnd); try { const dutiesPromise = fetchDuties(from, to); const eventsPromise = fetchCalendarEvents(from, to); @@ -480,7 +488,16 @@ const byDate = dutiesByDate(duties); const calendarByDate = calendarEventsByDate(events); renderCalendar(current.getFullYear(), current.getMonth(), byDate, calendarByDate); - renderDutyList(duties); + const last = lastDayOfMonth(current); + const firstKey = localDateString(first); + const lastKey = localDateString(last); + const dutiesInMonth = duties.filter(function (d) { + const byDateLocal = dutiesByDate([d]); + return Object.keys(byDateLocal).some(function (key) { + return key >= firstKey && key <= lastKey; + }); + }); + renderDutyList(dutiesInMonth); } catch (e) { if (e.message === "ACCESS_DENIED") { showAccessDenied(e.serverDetail); diff --git a/webapp/style.css b/webapp/style.css index 8a1a8a4..73eccc7 100644 --- a/webapp/style.css +++ b/webapp/style.css @@ -143,6 +143,17 @@ body { opacity: 0.9; } +.day-markers { + display: flex; + flex-direction: row; + flex-wrap: wrap; + justify-content: center; + gap: 2px; + align-items: center; + margin-top: 2px; + min-width: 0; +} + .calendar-event-hint { position: fixed; z-index: 1000; @@ -170,10 +181,9 @@ body { display: inline-flex; align-items: center; justify-content: center; - width: 16px; - height: 16px; - margin-top: 2px; - font-size: 0.65rem; + width: 14px; + height: 14px; + font-size: 0.6rem; font-weight: 700; border-radius: 50%; flex-shrink: 0; @@ -205,6 +215,10 @@ body { } .duty-item { + display: grid; + grid-template-columns: 5.5em 1fr; + gap: 0 8px; + align-items: baseline; padding: 8px 10px; margin-bottom: 6px; border-radius: 8px; @@ -221,16 +235,23 @@ body { } .duty-item .duty-item-type { + grid-column: 1; + grid-row: 1; font-size: 0.75rem; color: var(--muted); - margin-right: 4px; } .duty-item .name { + grid-column: 2; + grid-row: 1 / -1; + min-width: 0; font-weight: 600; } .duty-item .time { + grid-column: 1; + grid-row: 2; + align-self: start; font-size: 0.8rem; color: var(--muted); }