From 9d355a926eded4f8f4ee450768b80263387317df Mon Sep 17 00:00:00 2001 From: Beesquit Date: Thu, 16 Oct 2025 13:05:29 +0300 Subject: [PATCH] simple v1 version --- README.md | 27 ++++++++++- compose.yml | 0 src/index.css | 98 ++++++++++++++++++++++++++++++++++++++++ src/index.html | 43 ++++++++++++++++++ src/index.js | 118 +++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 285 insertions(+), 1 deletion(-) create mode 100644 compose.yml create mode 100644 src/index.css create mode 100644 src/index.html create mode 100644 src/index.js diff --git a/README.md b/README.md index ae7ea53..184c00b 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,28 @@ # work-timerer -Simple web app that shows time left to work and some more things \ No newline at end of file +Simple web app that shows time left to work and some more things + +### Installation + +You can run the app running just `index.html` or you using docker compose. + +Just copy this compose.yml file: +```yml +services: + work-timerer: + image: git.frik.su/Beesquit/work-timerer:latest + container_name: work-timerer + ports: + 4000:80 + restart: unless stopped +``` + +The application should be available at the `localhost:4000` + +### Todo: +-[ ] Make travel time customizable with env + +### Limitations: +- The app is linked to the system time +- If you live in a different timezone than your office the app will not work correctly + diff --git a/compose.yml b/compose.yml new file mode 100644 index 0000000..e69de29 diff --git a/src/index.css b/src/index.css new file mode 100644 index 0000000..8be49b9 --- /dev/null +++ b/src/index.css @@ -0,0 +1,98 @@ +body { + width: 100%; + height: 100%; + margin: 0; + padding: 0; + overflow: hidden; + background-color: #1f1f1f; + color: #ffffff; +} + +H1 { + text-align: center +} + + +.jetbrains-mono { + font-family: 'JetBrains Mono Nerd Font', monospace; + font-optical-sizing: auto; + font-weight: 300; +} + + +.parent { + width: 100%; + height: 100%; + position: absolute; + top: 0; + left: 0; + overflow: auto; + justify-content: space-between; + flex-direction: column; +} + +.vblock { + display: flex; + margin: auto; + justify-content: space-between; + flex-direction: row; + padding: 10px; +} + +.hblock { + margin: auto; +} + +.time-blocks { + display: flex; + gap: 20px; + flex-wrap: wrap; + justify-content: center; + align-items: flex-start; +} + +.time-block { + padding: 10px 15px; + background-color: #2a2a2a; + border-radius: 5px; + min-width: 60px; + text-align: center; + flex-shrink: 0; +} + +.label-block { + background-color: #2a2a2a; + min-width: 16px; + flex-shrink: 0; + font-weight: 450; +} + +.multi-rows-container { + display: flex; + flex-direction: column; + gap: 10px; + align-items: center; +} + +.time-row { + display: flex; + gap: 20px; + justify-content: center; +} + +#currentTime { + font-size: 3em; + margin: 20px 0; +} + + +@media (max-width: 768px) { + .time-blocks { + gap: 10px; + } + + .time-block { + padding: 8px 12px; + min-width: 50px; + } +} diff --git a/src/index.html b/src/index.html new file mode 100644 index 0000000..771158c --- /dev/null +++ b/src/index.html @@ -0,0 +1,43 @@ + + + + + + + + + + w-timerer + + + + +
+
+
+

--:--

+
+
+ +
+
+
+
+
+ +
+
+
+
+
+ +
+
+
+
+
+
+ + + + diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..2d80301 --- /dev/null +++ b/src/index.js @@ -0,0 +1,118 @@ +function updateTimes() { + const travelTime = 1; // in hours + + const now = new Date(); + const currentHours = now.getHours(); + const currentMinutes = now.getMinutes(); + const currentSeconds = now.getSeconds(); + + const currentTimeElement = document.getElementById('currentTime'); + currentTimeElement.textContent = `${currentHours.toString().padStart(2, '0')}:${currentMinutes.toString().padStart(2, '0')}:${currentSeconds.toString().padStart(2, '0')}`; + + const targetTimesElement = document.getElementById('targetTimes'); + const leaveTimesElement = document.getElementById('leaveTimes'); + const timeDifferencesElement = document.getElementById('timeDifferences'); + + targetTimesElement.innerHTML = ''; + leaveTimesElement.innerHTML = ''; + timeDifferencesElement.innerHTML = ''; + + const ttBlock = document.createElement('span'); + ttBlock.className = 'time-block label-block'; + 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) { + const diffHours = -1 * Math.ceil(diffMs / (1000 * 60 * 60)); + const diffMinutes = -1 * Math.ceil((diffMs % (1000 * 60 * 60)) / (1000 * 60)); + diffBlock.textContent = `-${diffHours.toString().padStart(2, '0')}:${diffMinutes.toString().padStart(2, '0')}`; + } else { + const diffHours = Math.floor(diffMs / (1000 * 60 * 60)); + const diffMinutes = Math.ceil((diffMs % (1000 * 60 * 60)) / (1000 * 60)); + diffBlock.textContent = `${diffHours.toString().padStart(2, '0')}:${diffMinutes.toString().padStart(2, '0')}`; + } + + diffTimeBlocks.push(diffBlock); + } + + distributeBlocks(targetTimesElement, targetTimeBlocks); + distributeBlocks(leaveTimesElement, leaveTimeBlocks); + distributeBlocks(timeDifferencesElement, diffTimeBlocks); +} + + +function distributeBlocks(container, blocks) { + const screenWidth = window.innerWidth; + const blockWidth = 120; + const labelWidth = 40; + + const availableWidth = screenWidth - labelWidth; + const blocksPerRow = Math.floor(availableWidth / blockWidth); + + if (blocksPerRow > blocks.length) { + blocks.forEach(block => container.appendChild(block)); + return; + } + + const totalBlocks = blocks.length; + const halfBlocks = Math.ceil(totalBlocks / 2); + + const rowsContainer = document.createElement('div'); + rowsContainer.className = 'multi-rows-container'; + + const topRow = document.createElement('div'); + topRow.className = 'time-row'; + for (let i = 0; i < halfBlocks; i++) { + topRow.appendChild(blocks[i]); + } + rowsContainer.appendChild(topRow); + + const bottomRow = document.createElement('div'); + bottomRow.className = 'time-row'; + for (let i = halfBlocks; i < totalBlocks; i++) { + bottomRow.appendChild(blocks[i]); + } + rowsContainer.appendChild(bottomRow); + + const label = container.firstChild; + container.appendChild(rowsContainer); +} + + +updateTimes(); +setInterval(updateTimes, 200); +window.addEventListener('resize', updateTimes);