Overtime Corrections mit Historie und Grund
This commit is contained in:
@@ -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';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user