Anpassungen Dashboard, Halbe Urlaubstage Berechnung
This commit is contained in:
@@ -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 ? ' <span style="color: #6f42c1;">(Feiertag)</span>' : '';
|
||||
|
||||
// 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 `
|
||||
<tr>
|
||||
<td><strong>${getWeekday(dateStr)}</strong></td>
|
||||
@@ -490,7 +514,7 @@ function renderWeek() {
|
||||
data-date="${dateStr}" data-field="break_minutes"
|
||||
${timeFieldsDisabled} ${disabled} oninput="saveEntry(this)" onchange="saveEntry(this)">
|
||||
</td>
|
||||
<td><strong id="hours_${dateStr}">${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'}</strong></td>
|
||||
<td><strong id="hours_${dateStr}">${hoursDisplay}</strong></td>
|
||||
</tr>
|
||||
<tr class="activities-row">
|
||||
<td colspan="6" class="activities-cell">
|
||||
@@ -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 = '<span style="color: #28a745;">(Urlaub - ganzer Tag)</span>';
|
||||
|
||||
if (isFullDayVacation) {
|
||||
// Entferne zuerst alle Urlaub-Spans falls vorhanden (mit verschiedenen möglichen Formaten)
|
||||
dateText = dateText.replace(/\s*<span[^>]*>\(Urlaub - ganzer Tag\)<\/span>/gi, '');
|
||||
// Entferne auch "(Krank)" falls vorhanden
|
||||
dateText = dateText.replace(/\s*<span[^>]*>\(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*<span[^>]*>\(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 = '<span style="color: #e74c3c;">(Krank)</span>';
|
||||
|
||||
if (newStatus) {
|
||||
// Prüfe ob bereits vorhanden
|
||||
if (!dateText.includes('(Krank)')) {
|
||||
dateText += ' ' + sickSpan;
|
||||
dateCell.innerHTML = dateText;
|
||||
}
|
||||
} else {
|
||||
// Entferne "(Krank)" Span
|
||||
dateText = dateText.replace(/ <span style="color: #e74c3c;">\(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;
|
||||
|
||||
Reference in New Issue
Block a user