feat: add target hours selector and update timer calculations based on user input
All checks were successful
Build and Push Docker Image / build (push) Successful in 25s
All checks were successful
Build and Push Docker Image / build (push) Successful in 25s
This commit is contained in:
@@ -382,6 +382,23 @@
|
|||||||
Startzeit manuell eingeben
|
Startzeit manuell eingeben
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
<!-- Target hours selector -->
|
||||||
|
<div class="mt-3 flex items-center gap-2">
|
||||||
|
<label for="targetHoursSelect" class="text-sm text-gray-400">Soll-Stunden:</label>
|
||||||
|
<select id="targetHoursSelect" class="px-3 py-1 bg-gray-700 text-gray-100 border border-gray-600 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 text-sm">
|
||||||
|
<option value="1">1h</option>
|
||||||
|
<option value="2">2h</option>
|
||||||
|
<option value="3">3h</option>
|
||||||
|
<option value="4">4h</option>
|
||||||
|
<option value="5">5h</option>
|
||||||
|
<option value="6">6h</option>
|
||||||
|
<option value="7">7h</option>
|
||||||
|
<option value="8" selected>8h</option>
|
||||||
|
<option value="9">9h</option>
|
||||||
|
<option value="10">10h</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
<input type="text" id="manualStartTimeInput" class="hidden">
|
<input type="text" id="manualStartTimeInput" class="hidden">
|
||||||
|
|
||||||
<!-- Additional Timer Metrics -->
|
<!-- Additional Timer Metrics -->
|
||||||
@@ -392,7 +409,7 @@
|
|||||||
</button>
|
</button>
|
||||||
<div class="flex items-center gap-2 text-gray-300">
|
<div class="flex items-center gap-2 text-gray-300">
|
||||||
<i data-lucide="target" class="w-4 h-4 text-green-400"></i>
|
<i data-lucide="target" class="w-4 h-4 text-green-400"></i>
|
||||||
<span>Soll erreicht: <span id="targetReachedTime" class="font-semibold text-white">--:--</span> (8h)</span>
|
<span>Soll erreicht: <span id="targetReachedTime" class="font-semibold text-white">--:--</span></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-2 text-gray-300">
|
<div class="flex items-center gap-2 text-gray-300">
|
||||||
<i data-lucide="timer" class="w-4 h-4 text-blue-400"></i>
|
<i data-lucide="timer" class="w-4 h-4 text-blue-400"></i>
|
||||||
|
|||||||
@@ -339,7 +339,7 @@ function updateTimer() {
|
|||||||
const balanceCell = document.getElementById('current-day-balance');
|
const balanceCell = document.getElementById('current-day-balance');
|
||||||
if (balanceCell) {
|
if (balanceCell) {
|
||||||
const netHours = elapsed / 3600; // Convert seconds to hours
|
const netHours = elapsed / 3600; // Convert seconds to hours
|
||||||
const deviation = netHours - 8.0;
|
const deviation = netHours - targetHours;
|
||||||
const baseBalance = parseFloat(balanceCell.dataset.baseBalance || 0);
|
const baseBalance = parseFloat(balanceCell.dataset.baseBalance || 0);
|
||||||
const currentBalance = baseBalance + deviation;
|
const currentBalance = baseBalance + deviation;
|
||||||
|
|
||||||
@@ -388,8 +388,8 @@ function updateTimerMetrics(netElapsedSeconds) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Target: 8 hours (28800 seconds)
|
// Target hours from user selection (default 8)
|
||||||
const targetSeconds = 8 * 60 * 60;
|
const targetSeconds = targetHours * 60 * 60;
|
||||||
|
|
||||||
// Calculate total pause time: 30 min after 6h + 15 min after 9h
|
// Calculate total pause time: 30 min after 6h + 15 min after 9h
|
||||||
const pauseDuration30Min = 30 * 60; // 30 minutes in seconds
|
const pauseDuration30Min = 30 * 60; // 30 minutes in seconds
|
||||||
@@ -398,14 +398,14 @@ function updateTimerMetrics(netElapsedSeconds) {
|
|||||||
// Time needed including pauses
|
// Time needed including pauses
|
||||||
// After 6h work -> 30 min pause
|
// After 6h work -> 30 min pause
|
||||||
// After 9h work -> 15 min pause
|
// After 9h work -> 15 min pause
|
||||||
// Total gross time = 8h work + 30min pause + 15min pause = 8h 45min
|
// Total gross time = target work hours + 30min pause + 15min pause
|
||||||
const totalGrossTimeNeeded = targetSeconds + pauseDuration30Min + pauseDuration15Min;
|
const totalGrossTimeNeeded = targetSeconds + pauseDuration30Min + pauseDuration15Min;
|
||||||
|
|
||||||
// Calculate when target will be reached (clock time)
|
// Calculate when target will be reached (clock time)
|
||||||
const targetReachedTimestamp = new Date(timerStartTime + totalGrossTimeNeeded * 1000);
|
const targetReachedTimestamp = new Date(timerStartTime + totalGrossTimeNeeded * 1000);
|
||||||
const targetHours = String(targetReachedTimestamp.getHours()).padStart(2, '0');
|
const targetHoursTime = String(targetReachedTimestamp.getHours()).padStart(2, '0');
|
||||||
const targetMinutes = String(targetReachedTimestamp.getMinutes()).padStart(2, '0');
|
const targetMinutesDisplay = String(targetReachedTimestamp.getMinutes()).padStart(2, '0');
|
||||||
targetReachedTimeSpan.textContent = `${targetHours}:${targetMinutes}`;
|
targetReachedTimeSpan.textContent = `${targetHoursTime}:${targetMinutesDisplay}`;
|
||||||
|
|
||||||
// Calculate countdown to target (remaining net work time)
|
// Calculate countdown to target (remaining net work time)
|
||||||
const remainingNetSeconds = Math.max(0, targetSeconds - netElapsedSeconds);
|
const remainingNetSeconds = Math.max(0, targetSeconds - netElapsedSeconds);
|
||||||
@@ -823,16 +823,21 @@ function renderMonthlyView(entries) {
|
|||||||
let balanceCell = '';
|
let balanceCell = '';
|
||||||
|
|
||||||
if (isPastDay && entryType !== 'vacation') {
|
if (isPastDay && entryType !== 'vacation') {
|
||||||
// For all workdays (including flextime): Actual - Target (8h)
|
// For all workdays (including flextime): Actual - Target (8h default)
|
||||||
// Flextime has netHours = 0, so deviation will be -8h
|
// Flextime has netHours = 0, so deviation will be -8h
|
||||||
|
|
||||||
|
// For current day: store balance before today, then use custom target hours in live update
|
||||||
|
const balanceBeforeToday = runningBalance;
|
||||||
const deviation = entry.netHours - 8.0;
|
const deviation = entry.netHours - 8.0;
|
||||||
runningBalance += deviation;
|
runningBalance += deviation;
|
||||||
|
|
||||||
const balanceColor = runningBalance >= 0 ? 'text-green-400' : 'text-red-400';
|
const balanceColor = runningBalance >= 0 ? 'text-green-400' : 'text-red-400';
|
||||||
const balanceSign = runningBalance >= 0 ? '+' : '';
|
const balanceSign = runningBalance >= 0 ? '+' : '';
|
||||||
|
|
||||||
// Add ID for current day to enable live updates
|
// Add ID for current day to enable live updates
|
||||||
|
// Store balance before today, so live update can add current day's deviation with custom target
|
||||||
const balanceId = isToday && entryType === 'work' ? 'id="current-day-balance"' : '';
|
const balanceId = isToday && entryType === 'work' ? 'id="current-day-balance"' : '';
|
||||||
const balanceDataAttr = isToday && entryType === 'work' ? `data-base-balance="${runningBalance - deviation}"` : '';
|
const balanceDataAttr = isToday && entryType === 'work' ? `data-base-balance="${balanceBeforeToday}"` : '';
|
||||||
|
|
||||||
balanceCell = `<td class="px-4 py-3 whitespace-nowrap text-sm font-semibold ${balanceColor}" ${balanceId} ${balanceDataAttr}>${balanceSign}${runningBalance.toFixed(2)}h</td>`;
|
balanceCell = `<td class="px-4 py-3 whitespace-nowrap text-sm font-semibold ${balanceColor}" ${balanceId} ${balanceDataAttr}>${balanceSign}${runningBalance.toFixed(2)}h</td>`;
|
||||||
} else {
|
} else {
|
||||||
@@ -3840,6 +3845,16 @@ function initializeEventListeners() {
|
|||||||
document.getElementById('btnStartWork').addEventListener('click', startWork);
|
document.getElementById('btnStartWork').addEventListener('click', startWork);
|
||||||
document.getElementById('btnStopWork').addEventListener('click', stopWork);
|
document.getElementById('btnStopWork').addEventListener('click', stopWork);
|
||||||
|
|
||||||
|
// Target hours selector
|
||||||
|
document.getElementById('targetHoursSelect').addEventListener('change', (e) => {
|
||||||
|
targetHours = parseInt(e.target.value);
|
||||||
|
// Update metrics if timer is running
|
||||||
|
if (timerStartTime) {
|
||||||
|
const elapsed = Math.floor((Date.now() - timerStartTime) / 1000) - timerPausedDuration;
|
||||||
|
updateTimerMetrics(elapsed);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Timer status - manual start time entry
|
// Timer status - manual start time entry
|
||||||
document.getElementById('timerStatus').addEventListener('click', () => {
|
document.getElementById('timerStatus').addEventListener('click', () => {
|
||||||
if (!timerStartTime) {
|
if (!timerStartTime) {
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ let timerPausedDuration = 0; // Total paused time in seconds
|
|||||||
let isPaused = false;
|
let isPaused = false;
|
||||||
let pauseTimeout = null;
|
let pauseTimeout = null;
|
||||||
let currentEntryId = null; // ID of today's entry being timed
|
let currentEntryId = null; // ID of today's entry being timed
|
||||||
|
let targetHours = 8; // Target work hours per day (1-10)
|
||||||
|
|
||||||
// Current month display state
|
// Current month display state
|
||||||
let displayYear = new Date().getFullYear();
|
let displayYear = new Date().getFullYear();
|
||||||
|
|||||||
Reference in New Issue
Block a user