Enhance calendar layout and duty list rendering

- Introduced a sticky header for the calendar to improve navigation.
- Updated the duty list display to highlight today's date with a distinct style.
- Added new CSS styles for better visual presentation of duty days and improved layout consistency.
This commit is contained in:
2026-02-17 23:44:43 +03:00
parent 8a7057a23f
commit 1a8ee72b8e
3 changed files with 65 additions and 9 deletions

View File

@@ -368,6 +368,7 @@
grouped[date].push(d);
});
const dates = Object.keys(grouped).sort();
const todayKey = localDateString(new Date());
let html = "";
/** Format UTC date from ISO string as DD.MM for display. */
function formatDateKey(isoDateStr) {
@@ -376,9 +377,16 @@
const month = String(d.getUTCMonth() + 1).padStart(2, "0");
return day + "." + month;
}
/** Format YYYY-MM-DD as DD.MM for list header. */
function dateKeyToDDMM(key) {
return key.slice(8, 10) + "." + key.slice(5, 7);
}
dates.forEach(function (date) {
const list = grouped[date];
html += "<h2>" + date + "</h2>";
const isToday = date === todayKey;
const dayBlockClass = "duty-list-day" + (isToday ? " duty-list-day--today" : "");
const titleText = isToday ? "Сегодня, " + dateKeyToDDMM(date) : dateKeyToDDMM(date);
html += "<div class=\"" + dayBlockClass + "\"><h2 class=\"duty-list-day-title\">" + escapeHtml(titleText) + "</h2>";
list.forEach(function (d) {
const startDate = new Date(d.start_at);
const endDate = new Date(d.end_at);
@@ -396,6 +404,7 @@
}
html += "<div class=\"" + itemClass + "\"><span class=\"duty-item-type\">" + escapeHtml(typeLabel) + "</span> <span class=\"name\">" + escapeHtml(d.full_name) + "</span><div class=\"time\">" + timeOrRange + "</div></div>";
});
html += "</div>";
});
dutyListEl.innerHTML = html;
}
@@ -527,8 +536,20 @@
loadMonth();
});
function bindStickyScrollShadow() {
var stickyEl = document.getElementById("calendarSticky");
if (!stickyEl || document._stickyScrollBound) return;
document._stickyScrollBound = true;
function updateScrolled() {
stickyEl.classList.toggle("is-scrolled", window.scrollY > 0);
}
window.addEventListener("scroll", updateScrolled, { passive: true });
updateScrolled();
}
runWhenReady(function () {
requireTelegramOrLocalhost(function () {
bindStickyScrollShadow();
loadMonth();
});
});

View File

@@ -8,15 +8,17 @@
</head>
<body>
<div class="container">
<header class="header">
<button type="button" class="nav" id="prevMonth" aria-label="Предыдущий месяц"></button>
<h1 class="title" id="monthTitle"></h1>
<button type="button" class="nav" id="nextMonth" aria-label="Следующий месяц"></button>
</header>
<div class="weekdays">
<span>Пн</span><span>Вт</span><span>Ср</span><span>Чт</span><span>Пт</span><span>Сб</span><span>Вс</span>
<div class="calendar-sticky" id="calendarSticky">
<header class="header">
<button type="button" class="nav" id="prevMonth" aria-label="Предыдущий месяц"></button>
<h1 class="title" id="monthTitle"></h1>
<button type="button" class="nav" id="nextMonth" aria-label="Следующий месяц"></button>
</header>
<div class="weekdays">
<span>Пн</span><span>Вт</span><span>Ср</span><span>Чт</span><span>Пт</span><span>Сб</span><span>Вс</span>
</div>
<div class="calendar" id="calendar"></div>
</div>
<div class="calendar" id="calendar"></div>
<div class="duty-list" id="dutyList"></div>
<div class="loading" id="loading">Загрузка…</div>
<div class="error" id="error" hidden></div>

View File

@@ -73,6 +73,19 @@ body {
text-align: center;
}
.calendar-sticky {
position: sticky;
top: 0;
z-index: 10;
background: var(--bg);
padding-bottom: 12px;
margin-bottom: 4px;
}
.calendar-sticky.is-scrolled {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25);
}
.calendar {
display: grid;
grid-template-columns: repeat(7, 1fr);
@@ -215,6 +228,26 @@ body {
margin: 0 0 8px 0;
}
.duty-list-day {
margin-bottom: 16px;
}
.duty-list-day--today .duty-list-day-title {
color: var(--today);
font-weight: 700;
}
.duty-list-day--today .duty-list-day-title::before {
content: "";
display: inline-block;
width: 4px;
height: 1em;
background: var(--today);
border-radius: 2px;
margin-right: 8px;
vertical-align: middle;
}
.duty-item {
display: grid;
grid-template-columns: 5.5em 1fr;