simple v1 version
This commit is contained in:
25
README.md
25
README.md
@ -1,3 +1,28 @@
|
|||||||
# work-timerer
|
# work-timerer
|
||||||
|
|
||||||
Simple web app that shows time left to work and some more things
|
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
|
||||||
|
|
||||||
|
|||||||
0
compose.yml
Normal file
0
compose.yml
Normal file
98
src/index.css
Normal file
98
src/index.css
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
43
src/index.html
Normal file
43
src/index.html
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200" />
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>w-timerer</title>
|
||||||
|
<link rel="stylesheet" type="text/css" href="index.css">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="parent">
|
||||||
|
<div class="vblock">
|
||||||
|
<div class="hblock">
|
||||||
|
<h1 class="jetbrains-mono" id="currentTime">--:--</h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="vblock">
|
||||||
|
<div class="hblock">
|
||||||
|
<div class="time-blocks jetbrains-mono" id="targetTimes"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="vblock">
|
||||||
|
<div class="hblock">
|
||||||
|
<div class="time-blocks jetbrains-mono" id="leaveTimes"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="vblock">
|
||||||
|
<div class="hblock">
|
||||||
|
<div class="time-blocks jetbrains-mono" id="timeDifferences"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="index.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
118
src/index.js
Normal file
118
src/index.js
Normal file
@ -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);
|
||||||
Reference in New Issue
Block a user