diff --git a/Stunderfassung todo.txt b/Stunderfassung todo.txt
index eb39ffd..f6fe8e2 100644
--- a/Stunderfassung todo.txt
+++ b/Stunderfassung todo.txt
@@ -8,4 +8,4 @@
- Feiertage müssen als ausgefüllt zählen -> DONE
- Mitarbeiter sollen PDF ansehen können. -> DONE
- Wenn bereits heruntergeladen wurde und neue version da ist Meldung an Verwaltung. -> DONE Muss getestet werden
--
\ No newline at end of file
+- Wenn ganzer Tag Urlaub gesetzt wird steht erst 8h (Urlaub) und dann nur noch 8h
\ No newline at end of file
diff --git a/public/js/dashboard.js b/public/js/dashboard.js
index 56d1010..06d6f2e 100644
--- a/public/js/dashboard.js
+++ b/public/js/dashboard.js
@@ -464,11 +464,35 @@ function renderWeek() {
// Bearbeitung ist immer möglich, auch nach Abschicken
// Bei ganztägigem Urlaub oder Krank werden Zeitfelder deaktiviert; Feiertag: Anzeige, Zeitfelder optional (Überstunden)
const isFullDayVacation = vacationType === 'full';
+ const isHalfDayVacation = vacationType === 'half';
const isSick = sickStatus === true || sickStatus === 1;
const timeFieldsDisabled = (isFullDayVacation || isSick) ? 'disabled' : '';
const disabled = '';
const holidayLabel = isHoliday ? ' (Feiertag)' : '';
+ // Stunden-Anzeige für halben Tag Urlaub berechnen
+ let hoursDisplay = '';
+ if (isFullDayVacation) {
+ hoursDisplay = fullDayHours.toFixed(2) + ' h (Urlaub)';
+ } else if (isHalfDayVacation) {
+ const halfHours = fullDayHours / 2;
+ const workHours = hours || 0; // Das sind die gearbeiteten Stunden (ohne Urlaub)
+ const totalHours = halfHours + workHours;
+ if (workHours > 0.01) {
+ hoursDisplay = totalHours.toFixed(2) + ' h (' + halfHours.toFixed(2) + ' h Urlaub + ' + workHours.toFixed(2) + ' h)';
+ } else {
+ hoursDisplay = halfHours.toFixed(2) + ' h (Urlaub)';
+ }
+ } else if (isSick) {
+ hoursDisplay = fullDayHours.toFixed(2) + ' h (Krank)';
+ } else if (isHoliday && !hours) {
+ hoursDisplay = fullDayHours.toFixed(2) + ' h (Feiertag)';
+ } else if (isHoliday && hours) {
+ hoursDisplay = fullDayHours.toFixed(2) + ' + ' + hours.toFixed(2) + ' h (Überst.)';
+ } else {
+ hoursDisplay = hours.toFixed(2) + ' h';
+ }
+
return `
| ${getWeekday(dateStr)} |
@@ -490,7 +514,7 @@ function renderWeek() {
data-date="${dateStr}" data-field="break_minutes"
${timeFieldsDisabled} ${disabled} oninput="saveEntry(this)" onchange="saveEntry(this)">
- ${isFullDayVacation ? fullDayHours.toFixed(2) + ' h (Urlaub)' : isSick ? fullDayHours.toFixed(2) + ' h (Krank)' : isHoliday && !hours ? fullDayHours.toFixed(2) + ' h (Feiertag)' : isHoliday && hours ? fullDayHours.toFixed(2) + ' + ' + hours.toFixed(2) + ' h (Überst.)' : hours.toFixed(2) + ' h'} |
+ ${hoursDisplay} |
|
@@ -988,6 +1012,7 @@ async function saveEntry(input) {
if (!currentEntries[date]) {
currentEntries[date] = { date };
}
+
currentEntries[date][field] = value;
// Lese alle aktuellen Werte direkt aus dem DOM, nicht nur aus currentEntries
@@ -1065,6 +1090,199 @@ async function saveEntry(input) {
currentEntries[date][`activity${i}_project_number`] = activities[i-1].projectNumber;
}
+ // SOFORTIGE DOM-UPDATES wenn vacation_type geändert wurde
+ if (input.dataset.field === 'vacation_type') {
+ const isFullDayVacation = vacation_type === 'full';
+ const isHalfDayVacation = vacation_type === 'half';
+ const fullDayHours = getFullDayHours();
+ const entry = currentEntries[date];
+ const isHoliday = currentHolidayDates.has(date);
+ const isSick = entry.sick_status || false;
+ const hours = entry.total_hours || 0;
+
+ // 1. Datum-Zelle: "(Urlaub - ganzer Tag)" in grün hinzufügen/entfernen
+ // Suche die Datum-Zelle über verschiedene Wege
+ let dateCell = null;
+
+ // Versuche zuerst über das Select-Element selbst
+ if (input.tagName === 'SELECT' && input.dataset.date === date) {
+ const selectRow = input.closest('tr');
+ if (selectRow) {
+ // Das Select ist in der activities-row, suche die vorherige Zeile
+ const prevRow = selectRow.previousElementSibling;
+ if (prevRow) {
+ dateCell = prevRow.querySelector('td:nth-child(2)');
+ }
+ }
+ }
+
+ // Fallback: Suche über start_time Input
+ if (!dateCell) {
+ const startInput = document.querySelector(`input[data-date="${date}"][data-field="start_time"]`);
+ if (startInput) {
+ const row = startInput.closest('tr');
+ if (row) {
+ dateCell = row.querySelector('td:nth-child(2)'); // Zweite Spalte ist das Datum
+ }
+ }
+ }
+
+ // Fallback: Suche direkt über alle Tabellenzeilen
+ if (!dateCell) {
+ const allRows = document.querySelectorAll('#timesheetTable tr, table tr');
+ for (let row of allRows) {
+ const testInput = row.querySelector(`input[data-date="${date}"][data-field="start_time"]`);
+ if (testInput) {
+ dateCell = row.querySelector('td:nth-child(2)');
+ break;
+ }
+ }
+ }
+
+ if (dateCell) {
+ let dateText = dateCell.innerHTML;
+ const vacationSpan = '(Urlaub - ganzer Tag)';
+
+ if (isFullDayVacation) {
+ // Entferne zuerst alle Urlaub-Spans falls vorhanden (mit verschiedenen möglichen Formaten)
+ dateText = dateText.replace(/\s*]*>\(Urlaub - ganzer Tag\)<\/span>/gi, '');
+ // Entferne auch "(Krank)" falls vorhanden
+ dateText = dateText.replace(/\s*]*>\(Krank\)<\/span>/gi, '');
+ // Füge "(Urlaub - ganzer Tag)" hinzu, wenn noch nicht vorhanden
+ if (!dateText.includes('(Urlaub - ganzer Tag)')) {
+ dateText += ' ' + vacationSpan;
+ }
+ dateCell.innerHTML = dateText;
+ } else {
+ // Entferne "(Urlaub - ganzer Tag)" Span (mit verschiedenen möglichen Formaten)
+ dateText = dateText.replace(/\s*]*>\(Urlaub - ganzer Tag\)<\/span>/gi, '');
+ dateCell.innerHTML = dateText;
+ }
+ }
+
+ // 2. Stunden-Anzeige sofort aktualisieren
+ const hoursElement = document.getElementById(`hours_${date}`);
+ if (hoursElement) {
+ if (isFullDayVacation) {
+ // Ganzer Tag Urlaub: Zeige fullDayHours mit "(Urlaub)" Label
+ hoursElement.textContent = fullDayHours.toFixed(2) + ' h (Urlaub)';
+ currentEntries[date].total_hours = fullDayHours;
+ } else if (isHalfDayVacation) {
+ // Halber Tag Urlaub: Berechne Stunden aus Start/Ende falls vorhanden
+ const startInput = document.querySelector(`input[data-date="${date}"][data-field="start_time"]`);
+ const endInput = document.querySelector(`input[data-date="${date}"][data-field="end_time"]`);
+ const breakInput = document.querySelector(`input[data-date="${date}"][data-field="break_minutes"]`);
+
+ const halfHours = fullDayHours / 2;
+ let workHours = 0;
+
+ if (startInput && endInput && startInput.value && endInput.value) {
+ const start = new Date(`2000-01-01T${startInput.value}`);
+ const end = new Date(`2000-01-01T${endInput.value}`);
+ const diffMs = end - start;
+ const breakMinutes = parseInt(breakInput ? breakInput.value : 0) || 0;
+ workHours = (diffMs / (1000 * 60 * 60)) - (breakMinutes / 60);
+ }
+
+ const totalHours = halfHours + workHours;
+ if (workHours > 0) {
+ hoursElement.textContent = totalHours.toFixed(2) + ' h (' + halfHours.toFixed(2) + ' h Urlaub + ' + workHours.toFixed(2) + ' h)';
+ } else {
+ hoursElement.textContent = halfHours.toFixed(2) + ' h (Urlaub)';
+ }
+ currentEntries[date].total_hours = totalHours;
+ } else {
+ // Zurück zu normaler Anzeige basierend auf anderen Status
+ if (isSick) {
+ hoursElement.textContent = fullDayHours.toFixed(2) + ' h (Krank)';
+ } else if (isHoliday && !hours) {
+ hoursElement.textContent = fullDayHours.toFixed(2) + ' h (Feiertag)';
+ } else if (isHoliday && hours) {
+ hoursElement.textContent = fullDayHours.toFixed(2) + ' + ' + hours.toFixed(2) + ' h (Überst.)';
+ } else {
+ hoursElement.textContent = hours.toFixed(2) + ' h';
+ }
+ }
+ }
+
+ // 3. Tätigkeiten-Felder sofort deaktivieren/aktivieren (nur bei ganztägigem Urlaub)
+ const activityInputs = document.querySelectorAll(`input[data-date="${date}"][data-field^="activity"]`);
+ activityInputs.forEach(input => {
+ if (isFullDayVacation) {
+ input.disabled = true;
+ } else {
+ // Nur aktivieren wenn nicht krank
+ input.disabled = isSick;
+ }
+ });
+
+ // 4. Zeitfelder sofort deaktivieren/aktivieren (nur bei ganztägigem Urlaub)
+ const timeInputs = document.querySelectorAll(`input[data-date="${date}"][data-field="start_time"], input[data-date="${date}"][data-field="end_time"], input[data-date="${date}"][data-field="break_minutes"]`);
+ timeInputs.forEach(input => {
+ if (isFullDayVacation) {
+ input.disabled = true;
+ } else {
+ // Nur aktivieren wenn nicht krank
+ input.disabled = isSick;
+ }
+ });
+
+ // 5. Bei ganztägigem Urlaub: Setze "Urlaub" als erste Tätigkeit und leere andere
+ if (isFullDayVacation) {
+ const descInput = document.querySelector(`input[data-date="${date}"][data-field="activity1_desc"]`);
+ const hoursInput = document.querySelector(`input[data-date="${date}"][data-field="activity1_hours"]`);
+
+ if (descInput) {
+ descInput.value = 'Urlaub';
+ currentEntries[date].activity1_desc = 'Urlaub';
+ }
+ if (hoursInput) {
+ hoursInput.value = fullDayHours.toFixed(2);
+ currentEntries[date].activity1_hours = fullDayHours;
+ }
+
+ // Leere andere Tätigkeiten
+ for (let i = 2; i <= 5; i++) {
+ const descInput = document.querySelector(`input[data-date="${date}"][data-field="activity${i}_desc"]`);
+ const hoursInput = document.querySelector(`input[data-date="${date}"][data-field="activity${i}_hours"]`);
+ const projectInput = document.querySelector(`input[data-date="${date}"][data-field="activity${i}_project_number"]`);
+
+ if (descInput) {
+ descInput.value = '';
+ currentEntries[date][`activity${i}_desc`] = null;
+ }
+ if (hoursInput) {
+ hoursInput.value = '';
+ currentEntries[date][`activity${i}_hours`] = 0;
+ }
+ if (projectInput) {
+ projectInput.value = '';
+ currentEntries[date][`activity${i}_project_number`] = null;
+ }
+ }
+ } else {
+ // Bei Abwahl von Urlaub (nicht full): Alle Tätigkeitsfelder leeren
+ for (let i = 1; i <= 5; i++) {
+ const descInput = document.querySelector(`input[data-date="${date}"][data-field="activity${i}_desc"]`);
+ const hoursInput = document.querySelector(`input[data-date="${date}"][data-field="activity${i}_hours"]`);
+ const projectInput = document.querySelector(`input[data-date="${date}"][data-field="activity${i}_project_number"]`);
+
+ if (descInput) {
+ descInput.value = '';
+ currentEntries[date][`activity${i}_desc`] = null;
+ }
+ if (hoursInput) {
+ hoursInput.value = '';
+ currentEntries[date][`activity${i}_hours`] = 0;
+ }
+ if (projectInput) {
+ projectInput.value = '';
+ currentEntries[date][`activity${i}_project_number`] = null;
+ }
+ }
+ }
+ }
+
const entry = currentEntries[date];
@@ -1108,8 +1326,48 @@ async function saveEntry(input) {
// Aktualisiere Stunden-Anzeige
const hoursElement = document.getElementById(`hours_${date}`);
if (hoursElement && result.total_hours !== undefined) {
- hoursElement.textContent = result.total_hours.toFixed(2) + ' h';
- currentEntries[date].total_hours = result.total_hours;
+ // Prüfe ob Urlaub oder Krank aktiv ist, um das richtige Label anzuzeigen
+ const entry = currentEntries[date] || {};
+ const vacationType = entry.vacation_type || '';
+ const isSick = entry.sick_status || false;
+ const isHoliday = currentHolidayDates.has(date);
+ const isFullDayVacation = vacationType === 'full';
+ const isHalfDayVacation = vacationType === 'half';
+ const fullDayHours = getFullDayHours();
+
+ let hoursText = result.total_hours.toFixed(2) + ' h';
+
+ if (isFullDayVacation) {
+ hoursText = fullDayHours.toFixed(2) + ' h (Urlaub)';
+ } else if (isHalfDayVacation) {
+ // Bei halbem Tag Urlaub: result.total_hours enthält nur die gearbeiteten Stunden
+ // Die Urlaubsstunden müssen addiert werden
+ const halfHours = fullDayHours / 2;
+ const workHours = result.total_hours || 0; // Das sind die gearbeiteten Stunden
+ const totalHours = halfHours + workHours; // Gesamt = Urlaub + gearbeitet
+
+ if (workHours > 0.01) {
+ hoursText = totalHours.toFixed(2) + ' h (' + halfHours.toFixed(2) + ' h Urlaub + ' + workHours.toFixed(2) + ' h)';
+ } else {
+ hoursText = halfHours.toFixed(2) + ' h (Urlaub)';
+ }
+
+ // Aktualisiere currentEntries mit den Gesamtstunden
+ currentEntries[date].total_hours = totalHours;
+ } else if (isSick) {
+ hoursText = fullDayHours.toFixed(2) + ' h (Krank)';
+ } else if (isHoliday && result.total_hours <= fullDayHours) {
+ hoursText = fullDayHours.toFixed(2) + ' h (Feiertag)';
+ } else if (isHoliday && result.total_hours > fullDayHours) {
+ const overtime = result.total_hours - fullDayHours;
+ hoursText = fullDayHours.toFixed(2) + ' + ' + overtime.toFixed(2) + ' h (Überst.)';
+ }
+
+ hoursElement.textContent = hoursText;
+ // total_hours wurde bereits für halben Tag Urlaub gesetzt, sonst verwende result.total_hours
+ if (!isHalfDayVacation) {
+ currentEntries[date].total_hours = result.total_hours;
+ }
}
// Gesamtstunden neu berechnen
@@ -1653,6 +1911,138 @@ function toggleSickStatus(dateStr) {
button.style.color = '';
}
+ // SOFORTIGE DOM-UPDATES
+
+ // 1. Datum-Zelle: "(Krank)" in rot hinzufügen/entfernen
+ const startInput = document.querySelector(`input[data-date="${dateStr}"][data-field="start_time"]`);
+ if (startInput) {
+ const row = startInput.closest('tr');
+ if (row) {
+ const dateCell = row.querySelector('td:nth-child(2)'); // Zweite Spalte ist das Datum
+ if (dateCell) {
+ let dateText = dateCell.innerHTML;
+ const sickSpan = '(Krank)';
+
+ if (newStatus) {
+ // Prüfe ob bereits vorhanden
+ if (!dateText.includes('(Krank)')) {
+ dateText += ' ' + sickSpan;
+ dateCell.innerHTML = dateText;
+ }
+ } else {
+ // Entferne "(Krank)" Span
+ dateText = dateText.replace(/ \(Krank\)<\/span>/g, '');
+ dateCell.innerHTML = dateText;
+ }
+ }
+ }
+ }
+
+ // 2. Stunden-Anzeige sofort aktualisieren
+ const hoursElement = document.getElementById(`hours_${dateStr}`);
+ if (hoursElement) {
+ const fullDayHours = getFullDayHours();
+ const entry = currentEntries[dateStr] || {};
+ const vacationType = entry.vacation_type || '';
+ const isHoliday = currentHolidayDates.has(dateStr);
+ const hours = entry.total_hours || 0;
+ const isFullDayVacation = vacationType === 'full';
+
+ if (newStatus) {
+ // Krank: Zeige fullDayHours mit "(Krank)" Label
+ hoursElement.textContent = fullDayHours.toFixed(2) + ' h (Krank)';
+ currentEntries[dateStr].total_hours = fullDayHours;
+ } else {
+ // Zurück zu normaler Anzeige basierend auf anderen Status
+ if (isFullDayVacation) {
+ hoursElement.textContent = fullDayHours.toFixed(2) + ' h (Urlaub)';
+ } else if (isHoliday && !hours) {
+ hoursElement.textContent = fullDayHours.toFixed(2) + ' h (Feiertag)';
+ } else if (isHoliday && hours) {
+ hoursElement.textContent = fullDayHours.toFixed(2) + ' + ' + hours.toFixed(2) + ' h (Überst.)';
+ } else {
+ hoursElement.textContent = hours.toFixed(2) + ' h';
+ }
+ }
+ }
+
+ // 3. Tätigkeiten-Felder sofort deaktivieren/aktivieren
+ const activityInputs = document.querySelectorAll(`input[data-date="${dateStr}"][data-field^="activity"]`);
+ activityInputs.forEach(input => {
+ if (newStatus) {
+ input.disabled = true;
+ } else {
+ input.disabled = false;
+ }
+ });
+
+ // 4. Zeitfelder sofort deaktivieren/aktivieren
+ const timeInputs = document.querySelectorAll(`input[data-date="${dateStr}"][data-field="start_time"], input[data-date="${dateStr}"][data-field="end_time"], input[data-date="${dateStr}"][data-field="break_minutes"]`);
+ timeInputs.forEach(input => {
+ if (newStatus) {
+ input.disabled = true;
+ } else {
+ input.disabled = false;
+ }
+ });
+
+ // 5. Bei Abwahl: Alle Tätigkeitsfelder leeren
+ if (!newStatus) {
+ // Leere alle Tätigkeitsfelder
+ for (let i = 1; i <= 5; i++) {
+ const descInput = document.querySelector(`input[data-date="${dateStr}"][data-field="activity${i}_desc"]`);
+ const hoursInput = document.querySelector(`input[data-date="${dateStr}"][data-field="activity${i}_hours"]`);
+ const projectInput = document.querySelector(`input[data-date="${dateStr}"][data-field="activity${i}_project_number"]`);
+
+ if (descInput) {
+ descInput.value = '';
+ currentEntries[dateStr][`activity${i}_desc`] = null;
+ }
+ if (hoursInput) {
+ hoursInput.value = '';
+ currentEntries[dateStr][`activity${i}_hours`] = 0;
+ }
+ if (projectInput) {
+ projectInput.value = '';
+ currentEntries[dateStr][`activity${i}_project_number`] = null;
+ }
+ }
+ } else {
+ // Bei Aktivierung: Setze "Krank" als erste Tätigkeit und leere andere
+ const descInput = document.querySelector(`input[data-date="${dateStr}"][data-field="activity1_desc"]`);
+ const hoursInput = document.querySelector(`input[data-date="${dateStr}"][data-field="activity1_hours"]`);
+ const fullDayHours = getFullDayHours();
+
+ if (descInput) {
+ descInput.value = 'Krank';
+ currentEntries[dateStr].activity1_desc = 'Krank';
+ }
+ if (hoursInput) {
+ hoursInput.value = fullDayHours.toFixed(2);
+ currentEntries[dateStr].activity1_hours = fullDayHours;
+ }
+
+ // Leere andere Tätigkeiten
+ for (let i = 2; i <= 5; i++) {
+ const descInput = document.querySelector(`input[data-date="${dateStr}"][data-field="activity${i}_desc"]`);
+ const hoursInput = document.querySelector(`input[data-date="${dateStr}"][data-field="activity${i}_hours"]`);
+ const projectInput = document.querySelector(`input[data-date="${dateStr}"][data-field="activity${i}_project_number"]`);
+
+ if (descInput) {
+ descInput.value = '';
+ currentEntries[dateStr][`activity${i}_desc`] = null;
+ }
+ if (hoursInput) {
+ hoursInput.value = '';
+ currentEntries[dateStr][`activity${i}_hours`] = 0;
+ }
+ if (projectInput) {
+ projectInput.value = '';
+ currentEntries[dateStr][`activity${i}_project_number`] = null;
+ }
+ }
+ }
+
// Speichere den Wert (erstellen ein temporäres Input-Element für saveEntry)
const tempInput = document.createElement('input');
tempInput.dataset.date = dateStr;
|