Ä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

@@ -26,6 +26,13 @@ function initDatabase() {
// Fehler ignorieren wenn Spalte bereits existiert // Fehler ignorieren wenn Spalte bereits existiert
}); });
// Migration: default_break_minutes (Standard-Pausenzeit pro Mitarbeiter)
db.run(`ALTER TABLE users ADD COLUMN default_break_minutes INTEGER DEFAULT 30`, (err) => {
if (err && !err.message.includes('duplicate column')) {
console.warn('Warnung beim Hinzufügen der Spalte default_break_minutes:', err.message);
}
});
// Stundenerfassung-Tabelle // Stundenerfassung-Tabelle
db.run(`CREATE TABLE IF NOT EXISTS timesheet_entries ( db.run(`CREATE TABLE IF NOT EXISTS timesheet_entries (
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,

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 */
.app-footer { .app-footer {
text-align: center; text-align: center;

View File

@@ -17,6 +17,10 @@ document.addEventListener('DOMContentLoaded', function() {
return; 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 = { const formData = {
username: document.getElementById('username').value, username: document.getElementById('username').value,
password: document.getElementById('password').value, password: document.getElementById('password').value,
@@ -26,7 +30,8 @@ document.addEventListener('DOMContentLoaded', function() {
personalnummer: document.getElementById('personalnummer').value, personalnummer: document.getElementById('personalnummer').value,
wochenstunden: document.getElementById('wochenstunden').value, wochenstunden: document.getElementById('wochenstunden').value,
arbeitstage: document.getElementById('arbeitstage').value, arbeitstage: document.getElementById('arbeitstage').value,
urlaubstage: document.getElementById('urlaubstage').value urlaubstage: document.getElementById('urlaubstage').value,
default_break_minutes: default_break_minutes
}; };
try { try {
@@ -318,6 +323,9 @@ async function saveUser(userId) {
const wochenstunden = row.querySelector('input[data-field="wochenstunden"]').value; const wochenstunden = row.querySelector('input[data-field="wochenstunden"]').value;
const arbeitstage = row.querySelector('input[data-field="arbeitstage"]').value; const arbeitstage = row.querySelector('input[data-field="arbeitstage"]').value;
const urlaubstage = row.querySelector('input[data-field="urlaubstage"]').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 // Rollen aus Checkboxen sammeln
const roleCheckboxes = row.querySelectorAll('.role-checkbox:checked'); const roleCheckboxes = row.querySelectorAll('.role-checkbox:checked');
@@ -340,6 +348,7 @@ async function saveUser(userId) {
wochenstunden: wochenstunden || null, wochenstunden: wochenstunden || null,
arbeitstage: arbeitstage || 5, arbeitstage: arbeitstage || 5,
urlaubstage: urlaubstage || null, urlaubstage: urlaubstage || null,
default_break_minutes: normalizedDefaultBreak,
roles: roles roles: roles
}) })
}); });
@@ -351,6 +360,8 @@ async function saveUser(userId) {
row.querySelector('span[data-field="personalnummer"]').textContent = personalnummer || '-'; row.querySelector('span[data-field="personalnummer"]').textContent = personalnummer || '-';
row.querySelector('span[data-field="wochenstunden"]').textContent = wochenstunden || '-'; row.querySelector('span[data-field="wochenstunden"]').textContent = wochenstunden || '-';
row.querySelector('span[data-field="urlaubstage"]').textContent = urlaubstage || '-'; 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 // Rollen-Display aktualisieren
const rolesDisplay = row.querySelector('div[data-field="roles"]'); 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 userWochenstunden = 0; // Wochenstunden des Users
let userArbeitstage = 5; // Arbeitstage pro Woche des Users (Standard: 5) let userArbeitstage = 5; // Arbeitstage pro Woche des Users (Standard: 5)
let weekendPercentages = { saturday: 100, sunday: 100 }; // Wochenend-Prozentsätze (100% = normal) 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 let latestSubmittedTimesheetId = null; // ID der neuesten eingereichten Version
// Wochenend-Prozentsätze laden // Wochenend-Prozentsätze laden
@@ -376,16 +377,18 @@ function getFullDayHours() {
// Woche laden // Woche laden
async function loadWeek() { async function loadWeek() {
try { try {
// User-Daten laden (Wochenstunden, Arbeitstage) // User-Daten laden (Wochenstunden, Arbeitstage, Standard-Pausenzeit)
try { try {
const userResponse = await fetch('/api/user/data'); const userResponse = await fetch('/api/user/data');
const userData = await userResponse.json(); const userData = await userResponse.json();
userWochenstunden = userData.wochenstunden || 0; userWochenstunden = userData.wochenstunden || 0;
userArbeitstage = userData.arbeitstage || 5; userArbeitstage = userData.arbeitstage || 5;
defaultBreakMinutes = userData.default_break_minutes ?? 30;
} catch (error) { } catch (error) {
console.warn('Konnte User-Daten nicht laden:', error); console.warn('Konnte User-Daten nicht laden:', error);
userWochenstunden = 0; userWochenstunden = 0;
userArbeitstage = 5; userArbeitstage = 5;
defaultBreakMinutes = 30;
} }
const parts = currentWeekStart.split('-'); const parts = currentWeekStart.split('-');
@@ -472,7 +475,7 @@ function renderWeek() {
const startTime = entry.start_time || ''; const startTime = entry.start_time || '';
const endTime = entry.end_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 hours = entry.total_hours || 0;
const overtimeTaken = entry.overtime_taken_hours || ''; const overtimeTaken = entry.overtime_taken_hours || '';
const vacationType = entry.vacation_type || ''; const vacationType = entry.vacation_type || '';
@@ -568,24 +571,29 @@ function renderWeek() {
hoursDisplay = hours.toFixed(2) + ' h'; 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 ` return `
<tr> <tr>
<td><strong>${getWeekday(dateStr)}</strong></td> <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>${formatDateDE(dateStr)}${isFullDayVacation ? ' <span style="color: #28a745;">(Urlaub - ganzer Tag)</span>' : ''}${isSick ? ' <span style="color: #e74c3c;">(Krank)</span>' : ''}${holidayLabel}</td>
<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" data-date="${dateStr}" data-field="start_time"
step="60" step="60"
${timeFieldsDisabled} ${disabled} oninput="saveEntry(this)" onchange="saveEntry(this)" onblur="saveEntry(this); checkWeekComplete();"> ${timeFieldsDisabled} ${disabled} oninput="saveEntry(this)" onchange="saveEntry(this)" onblur="saveEntry(this); checkWeekComplete();">
</td> </td>
<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" data-date="${dateStr}" data-field="end_time"
step="60" step="60"
${timeFieldsDisabled} ${disabled} oninput="saveEntry(this)" onchange="saveEntry(this)" onblur="saveEntry(this); checkWeekComplete();"> ${timeFieldsDisabled} ${disabled} oninput="saveEntry(this)" onchange="saveEntry(this)" onblur="saveEntry(this); checkWeekComplete();">
</td> </td>
<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" data-date="${dateStr}" data-field="break_minutes"
${timeFieldsDisabled} ${disabled} oninput="saveEntry(this)" onchange="saveEntry(this)"> ${timeFieldsDisabled} ${disabled} oninput="saveEntry(this)" onchange="saveEntry(this)">
</td> </td>
@@ -1087,6 +1095,26 @@ function calculateRequiredBreakMinutes(startTime, endTime) {
return 0; // Weniger als 6 Stunden: keine gesetzliche Pause erforderlich 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 // Eintrag speichern
async function saveEntry(input) { async function saveEntry(input) {
const date = input.dataset.date; const date = input.dataset.date;
@@ -1133,19 +1161,6 @@ async function saveEntry(input) {
const end_time = actualEndTime; const end_time = actualEndTime;
let break_minutes = breakInput && breakInput.value ? (parseInt(breakInput.value) || 0) : (parseInt(currentEntries[date].break_minutes) || 0); 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 notes = notesInput ? (notesInput.value || '') : (currentEntries[date].notes || '');
const vacation_type = vacationSelect && vacationSelect.value ? vacationSelect.value : (currentEntries[date].vacation_type || null); 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); 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) // Submit-Button Status prüfen (nach jedem Speichern)
checkWeekComplete(); checkWeekComplete();
if (field === 'start_time' || field === 'end_time' || field === 'break_minutes') {
updateBreakCompliance(date);
}
// Visuelles Feedback // Visuelles Feedback
input.style.backgroundColor = '#d4edda'; input.style.backgroundColor = '#d4edda';
setTimeout(() => { setTimeout(() => {

View File

@@ -8,7 +8,7 @@ const { requireAdmin } = require('../middleware/auth');
function registerAdminRoutes(app) { function registerAdminRoutes(app) {
// Admin-Bereich // Admin-Bereich
app.get('/admin', requireAdmin, (req, res) => { app.get('/admin', requireAdmin, (req, res) => {
db.all('SELECT id, username, firstname, lastname, role, personalnummer, wochenstunden, urlaubstage, arbeitstage, created_at FROM users ORDER BY created_at DESC', db.all('SELECT id, username, firstname, lastname, role, personalnummer, wochenstunden, urlaubstage, arbeitstage, default_break_minutes, created_at FROM users ORDER BY created_at DESC',
(err, users) => { (err, users) => {
// LDAP-Konfiguration, Sync-Log und Optionen abrufen // LDAP-Konfiguration, Sync-Log und Optionen abrufen
db.get('SELECT * FROM ldap_config WHERE id = 1', (err, ldapConfig) => { db.get('SELECT * FROM ldap_config WHERE id = 1', (err, ldapConfig) => {
@@ -48,7 +48,7 @@ function registerAdminRoutes(app) {
// Benutzer erstellen // Benutzer erstellen
app.post('/admin/users', requireAdmin, (req, res) => { app.post('/admin/users', requireAdmin, (req, res) => {
const { username, password, firstname, lastname, roles, personalnummer, wochenstunden, urlaubstage, arbeitstage } = req.body; const { username, password, firstname, lastname, roles, personalnummer, wochenstunden, urlaubstage, arbeitstage, default_break_minutes } = req.body;
const hashedPassword = bcrypt.hashSync(password, 10); const hashedPassword = bcrypt.hashSync(password, 10);
// Normalisiere die optionalen Felder // Normalisiere die optionalen Felder
@@ -56,6 +56,8 @@ function registerAdminRoutes(app) {
const normalizedWochenstunden = wochenstunden && wochenstunden !== '' ? parseFloat(wochenstunden) : null; const normalizedWochenstunden = wochenstunden && wochenstunden !== '' ? parseFloat(wochenstunden) : null;
const normalizedUrlaubstage = urlaubstage && urlaubstage !== '' ? parseFloat(urlaubstage) : null; const normalizedUrlaubstage = urlaubstage && urlaubstage !== '' ? parseFloat(urlaubstage) : null;
const normalizedArbeitstage = arbeitstage && arbeitstage !== '' ? parseInt(arbeitstage) : 5; const normalizedArbeitstage = arbeitstage && arbeitstage !== '' ? parseInt(arbeitstage) : 5;
const parsedBreak = default_break_minutes !== undefined && default_break_minutes !== '' ? parseInt(default_break_minutes, 10) : 30;
const normalizedDefaultBreak = (!isNaN(parsedBreak) && parsedBreak >= 0) ? parsedBreak : 30;
// Rollen verarbeiten: Erwarte Array, konvertiere zu JSON-String // Rollen verarbeiten: Erwarte Array, konvertiere zu JSON-String
let rolesArray = []; let rolesArray = [];
@@ -73,8 +75,8 @@ function registerAdminRoutes(app) {
const rolesJson = JSON.stringify(rolesArray); const rolesJson = JSON.stringify(rolesArray);
db.run('INSERT INTO users (username, password, firstname, lastname, role, personalnummer, wochenstunden, urlaubstage, arbeitstage) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)', db.run('INSERT INTO users (username, password, firstname, lastname, role, personalnummer, wochenstunden, urlaubstage, arbeitstage, default_break_minutes) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
[username, hashedPassword, firstname, lastname, rolesJson, normalizedPersonalnummer, normalizedWochenstunden, normalizedUrlaubstage, normalizedArbeitstage], [username, hashedPassword, firstname, lastname, rolesJson, normalizedPersonalnummer, normalizedWochenstunden, normalizedUrlaubstage, normalizedArbeitstage, normalizedDefaultBreak],
(err) => { (err) => {
if (err) { if (err) {
return res.status(400).json({ error: 'Benutzername existiert bereits' }); return res.status(400).json({ error: 'Benutzername existiert bereits' });
@@ -100,10 +102,13 @@ function registerAdminRoutes(app) {
}); });
}); });
// Benutzer aktualisieren (Personalnummer, Wochenstunden, Urlaubstage, Rollen) // Benutzer aktualisieren (Personalnummer, Wochenstunden, Urlaubstage, Rollen, Standard-Pause)
app.put('/admin/users/:id', requireAdmin, (req, res) => { app.put('/admin/users/:id', requireAdmin, (req, res) => {
const userId = req.params.id; const userId = req.params.id;
const { personalnummer, wochenstunden, urlaubstage, arbeitstage, roles } = req.body; const { personalnummer, wochenstunden, urlaubstage, arbeitstage, roles, default_break_minutes } = req.body;
const parsedBreak = default_break_minutes !== undefined && default_break_minutes !== '' ? parseInt(default_break_minutes, 10) : 30;
const normalizedDefaultBreak = (!isNaN(parsedBreak) && parsedBreak >= 0) ? parsedBreak : 30;
// Rollen verarbeiten falls vorhanden // Rollen verarbeiten falls vorhanden
let rolesJson = null; let rolesJson = null;
@@ -122,12 +127,13 @@ function registerAdminRoutes(app) {
// SQL-Query dynamisch zusammenstellen // SQL-Query dynamisch zusammenstellen
if (rolesJson !== null) { if (rolesJson !== null) {
// Aktualisiere auch Rollen // Aktualisiere auch Rollen
db.run('UPDATE users SET personalnummer = ?, wochenstunden = ?, urlaubstage = ?, arbeitstage = ?, role = ? WHERE id = ?', db.run('UPDATE users SET personalnummer = ?, wochenstunden = ?, urlaubstage = ?, arbeitstage = ?, default_break_minutes = ?, role = ? WHERE id = ?',
[ [
personalnummer || null, personalnummer || null,
wochenstunden ? parseFloat(wochenstunden) : null, wochenstunden ? parseFloat(wochenstunden) : null,
urlaubstage ? parseFloat(urlaubstage) : null, urlaubstage ? parseFloat(urlaubstage) : null,
arbeitstage ? parseInt(arbeitstage) : 5, arbeitstage ? parseInt(arbeitstage) : 5,
normalizedDefaultBreak,
rolesJson, rolesJson,
userId userId
], ],
@@ -139,12 +145,13 @@ function registerAdminRoutes(app) {
}); });
} else { } else {
// Nur andere Felder aktualisieren // Nur andere Felder aktualisieren
db.run('UPDATE users SET personalnummer = ?, wochenstunden = ?, urlaubstage = ?, arbeitstage = ? WHERE id = ?', db.run('UPDATE users SET personalnummer = ?, wochenstunden = ?, urlaubstage = ?, arbeitstage = ?, default_break_minutes = ? WHERE id = ?',
[ [
personalnummer || null, personalnummer || null,
wochenstunden ? parseFloat(wochenstunden) : null, wochenstunden ? parseFloat(wochenstunden) : null,
urlaubstage ? parseFloat(urlaubstage) : null, urlaubstage ? parseFloat(urlaubstage) : null,
arbeitstage ? parseInt(arbeitstage) : 5, arbeitstage ? parseInt(arbeitstage) : 5,
normalizedDefaultBreak,
userId userId
], ],
(err) => { (err) => {

View File

@@ -57,14 +57,15 @@ function registerUserRoutes(app) {
app.get('/api/user/data', requireAuth, (req, res) => { app.get('/api/user/data', requireAuth, (req, res) => {
const userId = req.session.userId; const userId = req.session.userId;
db.get('SELECT wochenstunden, arbeitstage FROM users WHERE id = ?', [userId], (err, user) => { db.get('SELECT wochenstunden, arbeitstage, default_break_minutes FROM users WHERE id = ?', [userId], (err, user) => {
if (err) { if (err) {
return res.status(500).json({ error: 'Fehler beim Abrufen der User-Daten' }); return res.status(500).json({ error: 'Fehler beim Abrufen der User-Daten' });
} }
res.json({ res.json({
wochenstunden: user?.wochenstunden || 0, wochenstunden: user?.wochenstunden || 0,
arbeitstage: user?.arbeitstage || 5 arbeitstage: user?.arbeitstage || 5,
default_break_minutes: user?.default_break_minutes ?? 30
}); });
}); });
}); });

View File

@@ -107,6 +107,11 @@
<label for="urlaubstage">Urlaubstage</label> <label for="urlaubstage">Urlaubstage</label>
<input type="number" id="urlaubstage" name="urlaubstage" step="0.5" min="0" placeholder="z.B. 25"> <input type="number" id="urlaubstage" name="urlaubstage" step="0.5" min="0" placeholder="z.B. 25">
</div> </div>
<div class="form-group">
<label for="defaultBreakMinutes">Standard-Pausenzeit (Min)</label>
<input type="number" id="defaultBreakMinutes" name="default_break_minutes" min="0" step="15" placeholder="30">
<small style="color: #666; display: block; margin-top: 5px;">Vorbelegung Pausenzeit pro Tag (Min., mind. 0).</small>
</div>
</div> </div>
<button type="submit" class="btn btn-primary">Benutzer anlegen</button> <button type="submit" class="btn btn-primary">Benutzer anlegen</button>
@@ -136,6 +141,7 @@
<th>Wochenstunden</th> <th>Wochenstunden</th>
<th>Arbeitstage pro Woche</th> <th>Arbeitstage pro Woche</th>
<th>Urlaubstage</th> <th>Urlaubstage</th>
<th>Standard-Pause (Min)</th>
<th>Erstellt am</th> <th>Erstellt am</th>
<th>Aktionen</th> <th>Aktionen</th>
</tr> </tr>
@@ -194,6 +200,10 @@
<span class="user-field-display" data-field="urlaubstage"><%= u.urlaubstage || '-' %></span> <span class="user-field-display" data-field="urlaubstage"><%= u.urlaubstage || '-' %></span>
<input type="number" step="0.5" class="user-field-edit" data-field="urlaubstage" data-user-id="<%= u.id %>" value="<%= u.urlaubstage || '' %>" style="display: none; width: 80px;"> <input type="number" step="0.5" class="user-field-edit" data-field="urlaubstage" data-user-id="<%= u.id %>" value="<%= u.urlaubstage || '' %>" style="display: none; width: 80px;">
</td> </td>
<td>
<span class="user-field-display" data-field="default_break_minutes"><%= (u.default_break_minutes != null && u.default_break_minutes !== '') ? u.default_break_minutes : '-' %></span>
<input type="number" min="0" step="15" class="user-field-edit" data-field="default_break_minutes" data-user-id="<%= u.id %>" value="<%= (u.default_break_minutes != null && u.default_break_minutes !== '') ? u.default_break_minutes : '' %>" style="display: none; width: 80px;">
</td>
<td><%= new Date(u.created_at).toLocaleDateString('de-DE') %></td> <td><%= new Date(u.created_at).toLocaleDateString('de-DE') %></td>
<td> <td>
<button onclick="editUser(<%= u.id %>)" class="btn btn-primary btn-sm edit-user-btn" data-user-id="<%= u.id %>">Bearbeiten</button> <button onclick="editUser(<%= u.id %>)" class="btn btn-primary btn-sm edit-user-btn" data-user-id="<%= u.id %>">Bearbeiten</button>