Overtime Corrections mit Historie und Grund

This commit is contained in:
2026-02-12 11:24:45 +01:00
parent e020aa4e46
commit 3edc0fe60c
6 changed files with 648 additions and 225 deletions

View File

@@ -153,6 +153,15 @@
<span class="summary-label">Manuelle Korrektur (Verwaltung):</span>
<span class="summary-value" id="overtimeOffset">-</span>
</div>
<div id="correctionsSection" style="margin-top: 15px; display: none;">
<div id="correctionsHeader" class="collapsible-header" style="cursor: pointer; padding: 12px; background-color: #f5f5f5; border: 1px solid #ddd; border-radius: 4px; display: flex; justify-content: space-between; align-items: center;">
<span style="font-weight: 600; color: #2c3e50;">Korrekturen durch die Verwaltung</span>
<span id="correctionsToggleIcon" style="font-size: 16px; transition: transform 0.3s;">▼</span>
</div>
<div id="correctionsContent" style="display: none; border: 1px solid #ddd; border-top: none; border-radius: 0 0 4px 4px; background-color: #fff; padding: 10px 12px;">
<ul id="correctionsList" style="margin: 0; padding-left: 18px;"></ul>
</div>
</div>
</div>
<div id="loading" class="loading">Lade Daten...</div>
@@ -213,6 +222,35 @@
return date.toLocaleDateString('de-DE');
}
// SQLite-Datetime (YYYY-MM-DD HH:MM:SS) robust parsen
function parseSqliteDatetime(value) {
if (!value) return null;
const s = String(value);
if (s.includes('T')) return new Date(s);
// SQLite datetime('now') liefert UTC ohne "T" / "Z"
return new Date(s.replace(' ', 'T') + 'Z');
}
function formatHours(value) {
const n = Number(value);
if (!Number.isFinite(n)) return '';
const sign = n > 0 ? '+' : '';
let s = sign + n.toFixed(2);
s = s.replace(/\.00$/, '');
s = s.replace(/(\.\d)0$/, '$1');
return s;
}
let correctionsExpanded = false;
function toggleCorrectionsSection() {
const content = document.getElementById('correctionsContent');
const icon = document.getElementById('correctionsToggleIcon');
if (!content || !icon) return;
correctionsExpanded = !correctionsExpanded;
content.style.display = correctionsExpanded ? 'block' : 'none';
icon.style.transform = correctionsExpanded ? 'rotate(180deg)' : 'rotate(0deg)';
}
// Überstunden-Daten laden
async function loadOvertimeBreakdown() {
const loadingEl = document.getElementById('loading');
@@ -273,6 +311,41 @@
} else {
offsetItem.style.display = 'none';
}
// Korrekturen durch die Verwaltung anzeigen (Collapsible, nur wenn vorhanden)
const correctionsSectionEl = document.getElementById('correctionsSection');
const correctionsListEl = document.getElementById('correctionsList');
const correctionsHeaderEl = document.getElementById('correctionsHeader');
const correctionsContentEl = document.getElementById('correctionsContent');
const correctionsIconEl = document.getElementById('correctionsToggleIcon');
const corrections = Array.isArray(data.overtime_corrections) ? data.overtime_corrections : [];
if (correctionsSectionEl && correctionsListEl && correctionsHeaderEl && correctionsContentEl && correctionsIconEl && corrections.length > 0) {
correctionsSectionEl.style.display = 'block';
correctionsListEl.innerHTML = '';
corrections.forEach(c => {
const dt = parseSqliteDatetime(c.corrected_at);
const dateText = dt ? dt.toLocaleDateString('de-DE') : '';
const hoursText = formatHours(c.correction_hours);
const reason = (c && c.reason != null) ? String(c.reason).trim() : '';
const li = document.createElement('li');
li.textContent = reason
? `Korrektur am ${dateText} ${hoursText} h ${reason}`
: `Korrektur am ${dateText} ${hoursText} h`;
correctionsListEl.appendChild(li);
});
// Standard: zugeklappt
correctionsExpanded = false;
correctionsContentEl.style.display = 'none';
correctionsIconEl.style.transform = 'rotate(0deg)';
// Click-Handler setzen (idempotent)
correctionsHeaderEl.onclick = toggleCorrectionsSection;
} else if (correctionsSectionEl) {
correctionsSectionEl.style.display = 'none';
}
summaryBoxEl.style.display = 'block';