Änderung der pausen regelung

This commit is contained in:
2026-02-11 12:08:16 +01:00
parent b0abba5f0f
commit e020aa4e46
7 changed files with 89 additions and 29 deletions

View File

@@ -1100,6 +1100,11 @@ table input[type="text"] {
}
}
/* Pausenfeld: rot nur wenn unter gesetzlicher Mindestpause (Tooltip im HTML) */
input.break-below-legal {
color: #dc3545;
}
/* App Footer */
.app-footer {
text-align: center;

View File

@@ -17,6 +17,10 @@ document.addEventListener('DOMContentLoaded', function() {
return;
}
const defaultBreakInput = document.getElementById('defaultBreakMinutes');
const defaultBreakVal = defaultBreakInput && defaultBreakInput.value !== '' ? parseInt(defaultBreakInput.value, 10) : 30;
const default_break_minutes = (!isNaN(defaultBreakVal) && defaultBreakVal >= 0) ? defaultBreakVal : 30;
const formData = {
username: document.getElementById('username').value,
password: document.getElementById('password').value,
@@ -26,7 +30,8 @@ document.addEventListener('DOMContentLoaded', function() {
personalnummer: document.getElementById('personalnummer').value,
wochenstunden: document.getElementById('wochenstunden').value,
arbeitstage: document.getElementById('arbeitstage').value,
urlaubstage: document.getElementById('urlaubstage').value
urlaubstage: document.getElementById('urlaubstage').value,
default_break_minutes: default_break_minutes
};
try {
@@ -318,6 +323,9 @@ async function saveUser(userId) {
const wochenstunden = row.querySelector('input[data-field="wochenstunden"]').value;
const arbeitstage = row.querySelector('input[data-field="arbeitstage"]').value;
const urlaubstage = row.querySelector('input[data-field="urlaubstage"]').value;
const defaultBreakInput = row.querySelector('input[data-field="default_break_minutes"]');
const default_break_minutes = defaultBreakInput && defaultBreakInput.value !== '' ? parseInt(defaultBreakInput.value, 10) : 30;
const normalizedDefaultBreak = (!isNaN(default_break_minutes) && default_break_minutes >= 0) ? default_break_minutes : 30;
// Rollen aus Checkboxen sammeln
const roleCheckboxes = row.querySelectorAll('.role-checkbox:checked');
@@ -340,6 +348,7 @@ async function saveUser(userId) {
wochenstunden: wochenstunden || null,
arbeitstage: arbeitstage || 5,
urlaubstage: urlaubstage || null,
default_break_minutes: normalizedDefaultBreak,
roles: roles
})
});
@@ -351,6 +360,8 @@ async function saveUser(userId) {
row.querySelector('span[data-field="personalnummer"]').textContent = personalnummer || '-';
row.querySelector('span[data-field="wochenstunden"]').textContent = wochenstunden || '-';
row.querySelector('span[data-field="urlaubstage"]').textContent = urlaubstage || '-';
const defaultBreakDisplay = row.querySelector('span[data-field="default_break_minutes"]');
if (defaultBreakDisplay) defaultBreakDisplay.textContent = normalizedDefaultBreak;
// Rollen-Display aktualisieren
const rolesDisplay = row.querySelector('div[data-field="roles"]');

View File

@@ -6,6 +6,7 @@ let currentHolidayDates = new Set(); // Feiertage der aktuellen Woche (YYYY-MM-D
let userWochenstunden = 0; // Wochenstunden des Users
let userArbeitstage = 5; // Arbeitstage pro Woche des Users (Standard: 5)
let weekendPercentages = { saturday: 100, sunday: 100 }; // Wochenend-Prozentsätze (100% = normal)
let defaultBreakMinutes = 30; // Standard-Pausenzeit des Mitarbeiters (Vorbelegung)
let latestSubmittedTimesheetId = null; // ID der neuesten eingereichten Version
// Wochenend-Prozentsätze laden
@@ -376,16 +377,18 @@ function getFullDayHours() {
// Woche laden
async function loadWeek() {
try {
// User-Daten laden (Wochenstunden, Arbeitstage)
// User-Daten laden (Wochenstunden, Arbeitstage, Standard-Pausenzeit)
try {
const userResponse = await fetch('/api/user/data');
const userData = await userResponse.json();
userWochenstunden = userData.wochenstunden || 0;
userArbeitstage = userData.arbeitstage || 5;
defaultBreakMinutes = userData.default_break_minutes ?? 30;
} catch (error) {
console.warn('Konnte User-Daten nicht laden:', error);
userWochenstunden = 0;
userArbeitstage = 5;
defaultBreakMinutes = 30;
}
const parts = currentWeekStart.split('-');
@@ -472,7 +475,7 @@ function renderWeek() {
const startTime = entry.start_time || '';
const endTime = entry.end_time || '';
const breakMinutes = entry.break_minutes || 0;
const breakMinutes = (entry.break_minutes != null && entry.break_minutes !== '') ? entry.break_minutes : defaultBreakMinutes;
const hours = entry.total_hours || 0;
const overtimeTaken = entry.overtime_taken_hours || '';
const vacationType = entry.vacation_type || '';
@@ -568,24 +571,29 @@ function renderWeek() {
hoursDisplay = hours.toFixed(2) + ' h';
}
const requiredBreak = (startTime && endTime) ? calculateRequiredBreakMinutes(startTime, endTime) : null;
const isBreakBelowLegal = requiredBreak !== null && breakMinutes < requiredBreak;
const breakClass = isBreakBelowLegal ? 'break-below-legal' : '';
const breakTitle = isBreakBelowLegal ? ' title="Die Pausenzeit liegt unterhalb der gesetzlichen Vorgabe."' : '';
return `
<tr>
<td><strong>${getWeekday(dateStr)}</strong></td>
<td>${formatDateDE(dateStr)}${isFullDayVacation ? ' <span style="color: #28a745;">(Urlaub - ganzer Tag)</span>' : ''}${isSick ? ' <span style="color: #e74c3c;">(Krank)</span>' : ''}${holidayLabel}</td>
<td>
<input type="time" value="${startTime}"
<input type="time" id="start_time_${dateStr}" name="start_time_${dateStr}" value="${startTime}"
data-date="${dateStr}" data-field="start_time"
step="60"
${timeFieldsDisabled} ${disabled} oninput="saveEntry(this)" onchange="saveEntry(this)" onblur="saveEntry(this); checkWeekComplete();">
</td>
<td>
<input type="time" value="${endTime}"
<input type="time" id="end_time_${dateStr}" name="end_time_${dateStr}" value="${endTime}"
data-date="${dateStr}" data-field="end_time"
step="60"
${timeFieldsDisabled} ${disabled} oninput="saveEntry(this)" onchange="saveEntry(this)" onblur="saveEntry(this); checkWeekComplete();">
</td>
<td>
<input type="number" value="${breakMinutes}" min="0" step="15"
<input type="number" id="break_minutes_${dateStr}" name="break_minutes_${dateStr}" value="${breakMinutes}" min="0" step="15" class="${breakClass}"${breakTitle}
data-date="${dateStr}" data-field="break_minutes"
${timeFieldsDisabled} ${disabled} oninput="saveEntry(this)" onchange="saveEntry(this)">
</td>
@@ -1087,6 +1095,26 @@ function calculateRequiredBreakMinutes(startTime, endTime) {
return 0; // Weniger als 6 Stunden: keine gesetzliche Pause erforderlich
}
// Aktualisiert die visuelle Kennzeichnung (nur rot + Tooltip wenn unter gesetzlicher Mindestpause)
function updateBreakCompliance(dateStr) {
const startInput = document.querySelector(`input[data-date="${dateStr}"][data-field="start_time"]`);
const endInput = document.querySelector(`input[data-date="${dateStr}"][data-field="end_time"]`);
const breakInput = document.querySelector(`input[data-date="${dateStr}"][data-field="break_minutes"]`);
if (!breakInput) return;
breakInput.classList.remove('break-below-legal');
breakInput.removeAttribute('title');
const startTime = startInput && startInput.value ? startInput.value.trim() : '';
const endTime = endInput && endInput.value ? endInput.value.trim() : '';
if (!startTime || !endTime) return;
const required = calculateRequiredBreakMinutes(startTime, endTime);
if (required === null) return;
const breakVal = breakInput.value ? (parseInt(breakInput.value, 10) || 0) : 0;
if (breakVal < required) {
breakInput.classList.add('break-below-legal');
breakInput.setAttribute('title', 'Die Pausenzeit liegt unterhalb der gesetzlichen Vorgabe.');
}
}
// Eintrag speichern
async function saveEntry(input) {
const date = input.dataset.date;
@@ -1133,19 +1161,6 @@ async function saveEntry(input) {
const end_time = actualEndTime;
let break_minutes = breakInput && breakInput.value ? (parseInt(breakInput.value) || 0) : (parseInt(currentEntries[date].break_minutes) || 0);
// Automatische Vorbelegung der Pausenzeiten basierend auf gesetzlichen Vorgaben
// Wird ausgelöst, wenn start_time oder end_time geändert werden
if ((input.dataset.field === 'start_time' || input.dataset.field === 'end_time') && start_time && end_time) {
const requiredBreakMinutes = calculateRequiredBreakMinutes(start_time, end_time);
if (requiredBreakMinutes !== null && requiredBreakMinutes > break_minutes) {
// Setze den höheren Wert (gesetzliche Mindestpause)
break_minutes = requiredBreakMinutes;
// Aktualisiere das Input-Feld im DOM
if (breakInput) {
breakInput.value = break_minutes;
}
}
}
const notes = notesInput ? (notesInput.value || '') : (currentEntries[date].notes || '');
const vacation_type = vacationSelect && vacationSelect.value ? vacationSelect.value : (currentEntries[date].vacation_type || null);
const overtime_taken_hours = overtimeInput && overtimeInput.value ? overtimeInput.value : (currentEntries[date].overtime_taken_hours || null);
@@ -1490,6 +1505,10 @@ async function saveEntry(input) {
// Submit-Button Status prüfen (nach jedem Speichern)
checkWeekComplete();
if (field === 'start_time' || field === 'end_time' || field === 'break_minutes') {
updateBreakCompliance(date);
}
// Visuelles Feedback
input.style.backgroundColor = '#d4edda';
setTimeout(() => {