fix: fixed calendar logic (February) and added some optimisations
Build and Push Docker Image / build-and-push (release) Failing after 1m25s

This commit is contained in:
2026-07-22 14:05:02 +03:00
parent 90069d9aa7
commit ac2696d387
2 changed files with 190 additions and 133 deletions
-2
View File
@@ -1,8 +1,6 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head> <head>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>w-timerer</title> <title>w-timerer</title>
+183 -124
View File
@@ -1,82 +1,50 @@
function updateTimes() { const TRAVEL_TIME_HOURS = 1;
const travelTime = 1; // in hours const FIRST_HOUR = 13;
const LAST_HOUR = 22;
const now = new Date(); let clockEl, dateEl;
const currentHours = now.getHours(); let diffNodes = [];
const currentMinutes = now.getMinutes(); let resizeTimer = null;
const currentSeconds = now.getSeconds(); let lastDayKey = "";
const currentTimeElement = document.getElementById('currentTime'); function pad2(n) {
currentTimeElement.textContent = `${currentHours.toString().padStart(2, '0')}:${currentMinutes.toString().padStart(2, '0')}:${currentSeconds.toString().padStart(2, '0')}`; return n < 10 ? "0" + n : "" + n;
}
const targetTimesElement = document.getElementById('targetTimes'); function formatClock(d) {
const leaveTimesElement = document.getElementById('leaveTimes'); return pad2(d.getHours()) + ":" + pad2(d.getMinutes()) + ":" + pad2(d.getSeconds());
const timeDifferencesElement = document.getElementById('timeDifferences'); }
targetTimesElement.innerHTML = ''; function formatDate(d) {
leaveTimesElement.innerHTML = ''; return pad2(d.getDate()) + "." + pad2(d.getMonth() + 1) + "." + d.getFullYear();
timeDifferencesElement.innerHTML = ''; }
const ttBlock = document.createElement('span'); function diffText(targetMs, nowMs) {
ttBlock.className = 'time-block label-block'; const diffMs = targetMs - nowMs;
ttBlock.textContent = 'TT';
targetTimesElement.appendChild(ttBlock);
const ltBlock = document.createElement('span');
ltBlock.className = 'time-block label-block';
ltBlock.textContent = 'LT';
leaveTimesElement.appendChild(ltBlock);
const tdBlock = document.createElement('span');
tdBlock.className = 'time-block label-block';
tdBlock.textContent = 'TD';
timeDifferencesElement.appendChild(tdBlock);
const targetTimeBlocks = [];
const leaveTimeBlocks = [];
const diffTimeBlocks = [];
for (let hour = 13; hour <= 22; hour++) {
let leaveHour = hour - travelTime;
const timeBlock = document.createElement('span');
timeBlock.className = 'time-block';
timeBlock.textContent = `${hour.toString().padStart(2, '0')}:00`;
targetTimeBlocks.push(timeBlock);
const leaveTimeBlock = document.createElement('span');
leaveTimeBlock.className = 'time-block';
leaveTimeBlock.textContent = `${(leaveHour).toString().padStart(2, '0')}:00`;
leaveTimeBlocks.push(leaveTimeBlock);
const targetTime = new Date();
targetTime.setHours(leaveHour, 0, 0, 0);
const diffBlock = document.createElement('span');
diffBlock.className = 'time-block';
const diffMs = targetTime - now;
if (diffMs < 0) { if (diffMs < 0) {
const diffHours = -1 * Math.ceil(diffMs / (1000 * 60 * 60)); const diffHours = -1 * Math.ceil(diffMs / 3600000);
const diffMinutes = -1 * Math.ceil((diffMs % (1000 * 60 * 60)) / (1000 * 60)); const diffMinutes = -1 * Math.ceil((diffMs % 3600000) / 60000);
diffBlock.textContent = `-${diffHours.toString().padStart(2, '0')}:${diffMinutes.toString().padStart(2, '0')}`; return "-" + pad2(diffHours) + ":" + pad2(diffMinutes);
} else { }
const diffHours = Math.floor(diffMs / (1000 * 60 * 60)); const diffHours = Math.floor(diffMs / 3600000);
const diffMinutes = Math.ceil((diffMs % (1000 * 60 * 60)) / (1000 * 60)); const diffMinutes = Math.ceil((diffMs % 3600000) / 60000);
diffBlock.textContent = `${diffHours.toString().padStart(2, '0')}:${diffMinutes.toString().padStart(2, '0')}`; return pad2(diffHours) + ":" + pad2(diffMinutes);
} }
diffTimeBlocks.push(diffBlock); function buildLabel(text) {
const span = document.createElement("span");
span.className = "time-block label-block";
span.textContent = text;
return span;
} }
distributeBlocks(targetTimesElement, targetTimeBlocks); function buildTimeBlock(text) {
distributeBlocks(leaveTimesElement, leaveTimeBlocks); const span = document.createElement("span");
distributeBlocks(timeDifferencesElement, diffTimeBlocks); span.className = "time-block";
span.textContent = text;
updateCalendar(); return span;
} }
function distributeBlocks(container, blocks) { function distributeBlocks(container, blocks) {
const screenWidth = window.innerWidth; const screenWidth = window.innerWidth;
const blockWidth = 120; const blockWidth = 120;
@@ -85,86 +53,105 @@ function distributeBlocks(container, blocks) {
const availableWidth = screenWidth - labelWidth; const availableWidth = screenWidth - labelWidth;
const blocksPerRow = Math.floor(availableWidth / blockWidth); const blocksPerRow = Math.floor(availableWidth / blockWidth);
while (container.childNodes.length > 1) {
container.removeChild(container.lastChild);
}
if (blocksPerRow > blocks.length) { if (blocksPerRow > blocks.length) {
blocks.forEach(block => container.appendChild(block)); blocks.forEach(function (b) { container.appendChild(b); });
return; return;
} }
const totalBlocks = blocks.length; const totalBlocks = blocks.length;
const halfBlocks = Math.ceil(totalBlocks / 2); const halfBlocks = Math.ceil(totalBlocks / 2);
const rowsContainer = document.createElement('div'); const rowsContainer = document.createElement("div");
rowsContainer.className = 'multi-rows-container'; rowsContainer.className = "multi-rows-container";
const topRow = document.createElement('div'); const topRow = document.createElement("div");
topRow.className = 'time-row'; topRow.className = "time-row";
for (let i = 0; i < halfBlocks; i++) { for (let i = 0; i < halfBlocks; i++) {
topRow.appendChild(blocks[i]); topRow.appendChild(blocks[i]);
} }
rowsContainer.appendChild(topRow); rowsContainer.appendChild(topRow);
const bottomRow = document.createElement('div'); const bottomRow = document.createElement("div");
bottomRow.className = 'time-row'; bottomRow.className = "time-row";
for (let i = halfBlocks; i < totalBlocks; i++) { for (let i = halfBlocks; i < totalBlocks; i++) {
bottomRow.appendChild(blocks[i]); bottomRow.appendChild(blocks[i]);
} }
rowsContainer.appendChild(bottomRow); rowsContainer.appendChild(bottomRow);
const label = container.firstChild;
container.appendChild(rowsContainer); container.appendChild(rowsContainer);
} }
function updateCalendar() { function buildTargetBlocks() {
const targetTimesElement = document.getElementById("targetTimes");
const leaveTimesElement = document.getElementById("leaveTimes");
const timeDifferencesElement = document.getElementById("timeDifferences");
targetTimesElement.appendChild(buildLabel("TT"));
leaveTimesElement.appendChild(buildLabel("LT"));
timeDifferencesElement.appendChild(buildLabel("TD"));
const targetBlocks = [];
const leaveBlocks = [];
const diffBlocks = [];
for (let hour = FIRST_HOUR; hour <= LAST_HOUR; hour++) {
const leaveHour = hour - TRAVEL_TIME_HOURS;
targetBlocks.push(buildTimeBlock(pad2(hour) + ":00"));
leaveBlocks.push(buildTimeBlock(pad2(leaveHour) + ":00"));
diffBlocks.push(buildTimeBlock(""));
}
distributeBlocks(targetTimesElement, targetBlocks);
distributeBlocks(leaveTimesElement, leaveBlocks);
distributeBlocks(timeDifferencesElement, diffBlocks);
diffNodes = diffBlocks;
}
function buildCalendar() {
const calendar = document.getElementById("calendar");
calendar.innerHTML = "";
const now = new Date(); const now = new Date();
const currentDateElement = document.getElementById('currentDate'); const daysOfWeek = ["Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"];
const day = now.getDate().toString().padStart(2, '0'); const thead = document.createElement("thead");
const month = (now.getMonth() + 1).toString().padStart(2, '0'); const headerRow = document.createElement("tr");
const year = now.getFullYear(); daysOfWeek.forEach(function (day, index) {
currentDateElement.textContent = `${day}.${month}.${year}`; const th = document.createElement("th");
const calendarElement = document.getElementById('calendar');
calendarElement.innerHTML = '';
const daysOfWeek = ['Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa', 'Su'];
const firstDayOfMonth = new Date(year, now.getMonth(), 1);
let firstDayWeek = firstDayOfMonth.getDay();
firstDayWeek = firstDayWeek === 0 ? 7 : firstDayWeek;
const lastDayOfMonth = new Date(year, now.getMonth() + 1, 0);
const daysInMonth = lastDayOfMonth.getDate();
const lastDayOfPrevMonth = new Date(year, now.getMonth(), 0);
const daysInPrevMonth = lastDayOfPrevMonth.getDate();
const thead = document.createElement('thead');
const headerRow = document.createElement('tr');
daysOfWeek.forEach((day, index) => {
const th = document.createElement('th');
th.textContent = day; th.textContent = day;
th.className = 'calendar-weekday';
if (index === 5 || index === 6) { if (index === 5 || index === 6) {
th.classList.add('weekend-text'); th.classList.add("weekend-text");
} }
headerRow.appendChild(th); headerRow.appendChild(th);
}); });
thead.appendChild(headerRow); thead.appendChild(headerRow);
calendarElement.appendChild(thead); calendar.appendChild(thead);
const tbody = document.createElement('tbody'); const tbody = document.createElement("tbody");
const year = now.getFullYear();
const month = now.getMonth();
const firstDayOfMonth = new Date(year, month, 1);
let firstDayWeek = firstDayOfMonth.getDay();
firstDayWeek = firstDayWeek === 0 ? 7 : firstDayWeek;
const daysInMonth = new Date(year, month + 1, 0).getDate();
const daysInPrevMonth = new Date(year, month, 0).getDate();
let dayCounter = 1; let dayCounter = 1;
let nextMonthDayCounter = 1; let nextMonthDayCounter = 1;
let startDay = daysInPrevMonth - firstDayWeek + 2; let startDay = daysInPrevMonth - firstDayWeek + 2;
for (let row = 0; row < 5; row++) { const totalRows = Math.ceil((firstDayWeek - 1 + daysInMonth) / 7);
const tr = document.createElement('tr'); for (let row = 0; row < totalRows; row++) {
const tr = document.createElement("tr");
for (let col = 0; col < 7; col++) { for (let col = 0; col < 7; col++) {
const td = document.createElement('td'); const td = document.createElement("td");
td.className = 'calendar-cell';
let cellDay; let cellDay;
let isCurrentMonth = true; let isCurrentMonth = true;
@@ -184,28 +171,100 @@ function updateCalendar() {
td.textContent = cellDay; td.textContent = cellDay;
if (isCurrentMonth && cellDay === now.getDate()) { if (isCurrentMonth && cellDay === now.getDate()) {
td.classList.add('today-text'); td.classList.add("today-text");
} }
if (col === 5 || col === 6) { if (col === 5 || col === 6) {
td.classList.add('weekend-text'); td.classList.add("weekend-text");
} }
if (!isCurrentMonth) { if (!isCurrentMonth) {
td.classList.add('other-month'); td.classList.add("other-month");
} }
tr.appendChild(td); tr.appendChild(td);
} }
tbody.appendChild(tr); tbody.appendChild(tr);
} }
calendar.appendChild(tbody);
calendarElement.appendChild(tbody);
} }
function updateClock() {
const now = new Date();
clockEl.textContent = formatClock(now);
scheduleNextClockTick();
}
// Init function scheduleNextClockTick() {
updateTimes(); const delay = 1000 - new Date().getMilliseconds();
setInterval(updateTimes, 200); setTimeout(updateClock, delay);
window.addEventListener('resize', updateTimes); }
function updateDiffs() {
const now = new Date();
for (let hour = FIRST_HOUR; hour <= LAST_HOUR; hour++) {
const target = new Date();
target.setHours(hour - TRAVEL_TIME_HOURS, 0, 0, 0);
diffNodes[hour - FIRST_HOUR].textContent = diffText(target.getTime(), now.getTime());
}
scheduleNextDiffTick();
}
function scheduleNextDiffTick() {
const now = new Date();
const msUntilNextMinute =
(60 - now.getSeconds()) * 1000 - now.getMilliseconds();
setTimeout(updateDiffs, msUntilNextMinute);
}
function checkDayChange() {
const now = new Date();
const key =
now.getFullYear() + "-" + (now.getMonth() + 1) + "-" + now.getDate();
if (key !== lastDayKey) {
lastDayKey = key;
dateEl.textContent = formatDate(now);
buildCalendar();
}
}
function init() {
clockEl = document.getElementById("currentTime");
dateEl = document.getElementById("currentDate");
buildTargetBlocks();
buildCalendar();
const now = new Date();
lastDayKey = now.getFullYear() + "-" + (now.getMonth() + 1) + "-" + now.getDate();
dateEl.textContent = formatDate(now);
updateClock();
updateDiffs();
setInterval(checkDayChange, 60000);
}
function onResize() {
if (resizeTimer !== null) clearTimeout(resizeTimer);
resizeTimer = setTimeout(function () {
resizeTimer = null;
const targetTimesElement = document.getElementById("targetTimes");
const leaveTimesElement = document.getElementById("leaveTimes");
const timeDifferencesElement = document.getElementById("timeDifferences");
const allBlocks = [].slice.call(
timeDifferencesElement.querySelectorAll(".time-block:not(.label-block)")
);
const targetBlocks = [].slice.call(
targetTimesElement.querySelectorAll(".time-block:not(.label-block)")
);
const leaveBlocks = [].slice.call(
leaveTimesElement.querySelectorAll(".time-block:not(.label-block)")
);
distributeBlocks(targetTimesElement, targetBlocks);
distributeBlocks(leaveTimesElement, leaveBlocks);
distributeBlocks(timeDifferencesElement, allBlocks);
}, 150);
}
init();
window.addEventListener("resize", onResize);