Verwaltung Überstunden anzeige, Buttons am unteren ende der Seite

This commit is contained in:
2026-02-09 17:37:06 +01:00
parent c396fe7d0e
commit cf1ca107f5
4 changed files with 326 additions and 5 deletions

View File

@@ -61,9 +61,17 @@
</div>
<div class="actions">
<div style="display: flex; gap: 10px; align-items: center; justify-content: center;">
<button id="submitWeek" class="btn btn-success" onclick="window.submitWeekHandler(event)" disabled>Woche abschicken</button>
<button id="viewPdfBtn" class="btn btn-info" disabled>PDF anzeigen</button>
<div style="display: flex; justify-content: space-between; align-items: center; width: 100%; flex-wrap: wrap; gap: 15px;">
<div style="flex: 1; display: flex; justify-content: flex-start; min-width: 0;">
<button type="button" class="btn btn-secondary" onclick="document.getElementById('prevWeek').click()">◀ Vorherige Woche</button>
</div>
<div style="display: flex; gap: 10px; align-items: center; flex-shrink: 0;">
<button id="submitWeek" class="btn btn-success" onclick="window.submitWeekHandler(event)" disabled>Woche abschicken</button>
<button id="viewPdfBtn" class="btn btn-info" disabled>PDF anzeigen</button>
</div>
<div style="flex: 1; display: flex; justify-content: flex-end; min-width: 0;">
<button type="button" class="btn btn-secondary" onclick="document.getElementById('nextWeek').click()">Nächste Woche ▶</button>
</div>
</div>
<p class="help-text">Stunden werden automatisch gespeichert. Am Ende der Woche können Sie die Stunden abschicken.</p>
</div>

View File

@@ -88,10 +88,10 @@
</div>
<div class="employee-details" style="margin-top: 10px;">
<div style="display: inline-block; margin-right: 20px;">
<strong>Wochenstunden:</strong> <span><%= employee.user.wochenstunden || '-' %></span>
<strong>Aktuelle Überstunden:</strong> <span class="current-overtime-value" data-user-id="<%= employee.user.id %>">-</span> Stunden
</div>
<div style="display: inline-block; margin-right: 20px;">
<strong>Urlaubstage:</strong> <span><%= employee.user.urlaubstage || '-' %></span>
<strong>Verbleibender Urlaub:</strong> <span class="remaining-vacation-value" data-user-id="<%= employee.user.id %>">-</span> Tage
</div>
<div style="display: inline-flex; gap: 8px; align-items: center; margin-right: 20px;">
<strong>Überstunden-Offset:</strong>
@@ -399,8 +399,95 @@
}
}
// Aktuelle Überstunden für alle Mitarbeiter-Header laden (wie im Dashboard)
async function loadCurrentOvertime() {
const elements = document.querySelectorAll('.current-overtime-value');
const userIds = [...new Set(Array.from(elements).map(el => el.dataset.userId).filter(Boolean))];
if (userIds.length === 0) return;
try {
const response = await fetch('/api/verwaltung/employees/current-overtime?userIds=' + userIds.join(','));
if (!response.ok) throw new Error('Fehler beim Laden der Überstunden');
const data = await response.json();
elements.forEach(el => {
const userId = el.dataset.userId;
const value = data[userId] != null ? Number(data[userId]) : null;
if (value === null) {
el.textContent = '-';
el.style.color = '';
} else {
el.textContent = value >= 0 ? '+' + value.toFixed(2) : value.toFixed(2);
el.style.color = value >= 0 ? '#27ae60' : '#e74c3c';
}
});
} catch (error) {
console.error('Fehler beim Laden der aktuellen Überstunden:', error);
elements.forEach(el => {
el.textContent = '-';
el.style.color = '';
});
}
}
// Verbleibenden Urlaub (Tage) für alle Mitarbeiter-Header laden
async function loadRemainingVacation() {
const elements = document.querySelectorAll('.remaining-vacation-value');
const byUser = {};
elements.forEach(el => {
const userId = el.dataset.userId;
if (!userId) return;
if (!byUser[userId]) byUser[userId] = [];
byUser[userId].push(el);
});
const userIds = Object.keys(byUser);
if (userIds.length === 0) return;
for (const userId of userIds) {
try {
// Nimm die erste (neueste) Woche für diesen Mitarbeiter
const statsDiv = document.querySelector(`.group-stats[data-user-id="${userId}"]`);
if (!statsDiv) {
byUser[userId].forEach(el => { el.textContent = '-'; });
continue;
}
const weekStart = statsDiv.dataset.weekStart;
const weekEnd = statsDiv.dataset.weekEnd;
if (!weekStart || !weekEnd) {
byUser[userId].forEach(el => { el.textContent = '-'; });
continue;
}
const resp = await fetch(`/api/verwaltung/user/${userId}/stats?week_start=${weekStart}&week_end=${weekEnd}`);
if (!resp.ok) {
byUser[userId].forEach(el => { el.textContent = '-'; });
continue;
}
const data = await resp.json();
const value = data && typeof data.remainingVacation === 'number'
? data.remainingVacation
: null;
byUser[userId].forEach(el => {
if (value === null) {
el.textContent = '-';
} else {
el.textContent = value.toFixed(1);
}
});
} catch (err) {
console.error('Fehler beim Laden des verbleibenden Urlaubs für User', userId, err);
byUser[userId].forEach(el => { el.textContent = '-'; });
}
}
}
// Krankheitstage beim Laden der Seite abrufen
loadSickDays();
loadCurrentOvertime();
loadRemainingVacation();
// Überstunden-Offset speichern
document.querySelectorAll('.save-overtime-offset-btn').forEach(btn => {
@@ -446,6 +533,7 @@
}
loadStatsForDiv(div);
});
loadCurrentOvertime();
this.textContent = '✓';
setTimeout(() => {