diff --git a/README.md b/README.md index bfa397e..9a42eb2 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,14 @@ Eine Full-Stack-Zeiterfassungsanwendung, entwickelt mit Node.js, Express, SQLite - Timer persistiert über Seiten-Reloads - Manuelle Startzeit-Eingabe möglich - Visueller Indikator (blinkendes Uhr-Icon) bei laufendem Timer + - **Timer-Metriken** bei laufendem Timer: + - Läuft seit: Startzeit mit Icon-Styling + - Soll erreicht: Uhrzeit wann Tagesziel erreicht wird (inkl. Pausen) + - Zeit bis Soll: Live-Countdown zur Zielzeit + - Saldo bei Soll: Prognostizierter Gesamtsaldo nach Erreichen der geplanten Zeit + - **Anpassbare Arbeitszeit**: Dropdown 4h-10h für flexible Arbeitstage + - Einstellung bleibt über Reloads erhalten (nur während Timer läuft) + - Wird bei Timer-Stop auf 8h zurückgesetzt - **Flexible Eingabemodi**: - Manuelle Eingabe (Datum, Start, Ende, Pause) - Inline-Bearbeitung direkt in der Tabelle @@ -38,10 +46,16 @@ Eine Full-Stack-Zeiterfassungsanwendung, entwickelt mit Node.js, Express, SQLite - **Automatische Pausenberechnung** (deutsches Arbeitszeitgesetz) - **10-Stunden-Cap** für maximale Nettoarbeitszeit pro Tag - **Live-Statistiken** mit laufendem Timer: - - Soll-Stunden (basierend auf Arbeitstagen) + - Soll-Stunden (basierend auf Arbeitstagen mit Einträgen) - Ist-Stunden (inkl. aktuell laufender Timer) - Monatssaldo + Gesamtsaldo mit Vormonatsübertrag - Arbeitstage-Zählung + - **Intelligente Soll-Berechnung**: Berücksichtigt nur Tage mit Einträgen oder laufendem Timer +- **Laufendes Saldo** in Monatsansicht: + - Spalte "Saldo" zeigt kumulatives Saldo bis zu jedem Tag + - Live-Updates während Timer läuft + - Farbcodierung: Grün (positiv) / Rot (negativ) + - Berücksichtigt Flextime-Tage korrekt (-8h) - **Urlaubsverwaltung**: - Konfigurierbares Jahres-Kontingent - Tracking: Genommen, Geplant, Verfügbar @@ -50,6 +64,10 @@ Eine Full-Stack-Zeiterfassungsanwendung, entwickelt mit Node.js, Express, SQLite ### 🗓️ Bundesland-spezifische Feiertage - **16 Bundesländer** mit korrekten regionalen Feiertagen - **Persistente Einstellung** (gespeichert in Datenbank) +- **Betriebsfreie Tage**: Wählbar zwischen Heiligabend (24.12.) oder Silvester (31.12.) + - Toggle in Einstellungen + - Wird als "Betriebsfrei" markiert + - Verhindert doppelte Einträge an diesen Tagen - **Kollisionserkennung**: Warnung bei Feiertagen mit bestehenden Einträgen - **Alle Feiertage**: Bundeseinheitlich + regional (z.B. Fronleichnam, Reformationstag) @@ -62,6 +80,7 @@ Eine Full-Stack-Zeiterfassungsanwendung, entwickelt mit Node.js, Express, SQLite - 🔴 Rot: Fehlende Arbeitstage - ⚫ Grau: Wochenenden - 🔵 Blau: Feiertage (mit Namen) + - 💙 Blauer Rand: Heutiger Tag - **Navigation**: Vor/Zurück-Buttons zum Monatswechsel - **Auto-Fill**: Automatisches Befüllen des Monats mit Standard-Arbeitszeiten (9:00-17:30) - **Quick-Actions**: Plus-Buttons für schnelles Hinzufügen von Einträgen diff --git a/public/js/main.js b/public/js/main.js index ccfb8b2..396f30a 100644 --- a/public/js/main.js +++ b/public/js/main.js @@ -1162,6 +1162,15 @@ async function updateStatistics(entries) { let futureFlextimeDays = 0; // Count future flextime days in current month + // Create a map to check if a day has an entry + const entriesMap = {}; + entries.forEach(entry => { + entriesMap[entry.date] = entry; + }); + + const todayISO = getTodayISO(); + const isCurrentMonth = currentYear === today.getFullYear() && currentMonth === today.getMonth(); + for (let day = 1; day <= lastDay; day++) { const dateObj = new Date(currentYear, currentMonth, day); const year = dateObj.getFullYear(); @@ -1172,31 +1181,35 @@ async function updateStatistics(entries) { const isVacation = vacationDays.has(dateISO); const isFlextime = flextimeDays.has(dateISO); const isWeekendHoliday = isWeekendOrHoliday(dateObj); + const hasEntry = entriesMap[dateISO]; + const isToday = dateISO === todayISO; + + // For today: only count as workday if there's an entry OR timer is running + const shouldCountToday = !isToday || hasEntry || (timerStartTime && isCurrentMonth); if (!isWeekendHoliday && !isVacation && !isFlextime) { // Normal workday (excluding vacation and flextime days) totalWorkdaysInMonth++; workdaysCount++; - if (dateObj <= today) { + if (dateObj <= today && shouldCountToday) { workdaysPassed++; } } else if (!isWeekendHoliday && !isVacation && isFlextime) { // Flextime on a workday - still counts as workday in calendar totalWorkdaysInMonth++; workdaysCount++; - if (dateObj <= today) { + if (dateObj <= today && shouldCountToday) { workdaysPassed++; } else { // Future flextime in current month - const isCurrentMonth = currentYear === today.getFullYear() && currentMonth === today.getMonth(); - if (isCurrentMonth) { + if (isCurrentMonth && dateObj > today) { futureFlextimeDays++; } } } else if (isFlextime && isWeekendHoliday) { // Flextime on weekend/holiday counts as additional workday totalWorkdaysInMonth++; - if (new Date(dateISO) <= today) { + if (new Date(dateISO) <= today && shouldCountToday) { workdaysPassed++; } } @@ -1212,7 +1225,6 @@ async function updateStatistics(entries) { .reduce((sum, entry) => sum + entry.netHours, 0); // Add currently running timer hours to actual hours (only for current month) - const isCurrentMonth = currentYear === today.getFullYear() && currentMonth === today.getMonth(); if (timerStartTime && isCurrentMonth) { const now = Date.now(); let elapsedSeconds;