Umstellung Auf h:mm in verwaltung
This commit is contained in:
@@ -15,5 +15,65 @@
|
|||||||
var prefix = sign < 0 ? '-' : '';
|
var prefix = sign < 0 ? '-' : '';
|
||||||
return prefix + h + 'h ' + min + 'min';
|
return prefix + h + 'h ' + min + 'min';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Dezimalstunden in h:mm (z. B. 1.5 -> "1:30", -0.75 -> "-0:45") */
|
||||||
|
function decimalHoursToHhMm(decimalHours) {
|
||||||
|
if (decimalHours == null || !Number.isFinite(Number(decimalHours))) return '0:00';
|
||||||
|
var n = Number(decimalHours);
|
||||||
|
var sign = n < 0 ? -1 : 1;
|
||||||
|
var absVal = Math.abs(n);
|
||||||
|
var h = Math.floor(absVal);
|
||||||
|
var min = Math.round((absVal - h) * 60);
|
||||||
|
if (min >= 60) {
|
||||||
|
h += 1;
|
||||||
|
min = 0;
|
||||||
|
}
|
||||||
|
var prefix = sign < 0 ? '-' : '';
|
||||||
|
return prefix + h + ':' + String(min).padStart(2, '0');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parst h:mm oder Xh Ymin zu Dezimalstunden.
|
||||||
|
* Beispiele: "1:30" -> 1.5, "-0:45" -> -0.75, "1h 30min" -> 1.5.
|
||||||
|
* @returns {number|null} Dezimalstunden oder null bei ungültiger Eingabe
|
||||||
|
*/
|
||||||
|
function parseHoursMin(str) {
|
||||||
|
if (str == null) return null;
|
||||||
|
var s = String(str).trim();
|
||||||
|
if (s === '') return 0;
|
||||||
|
var sign = 1;
|
||||||
|
if (s.charAt(0) === '-') {
|
||||||
|
sign = -1;
|
||||||
|
s = s.slice(1).trim();
|
||||||
|
} else if (s.charAt(0) === '+') {
|
||||||
|
s = s.slice(1).trim();
|
||||||
|
}
|
||||||
|
// h:mm oder h:mm:ss
|
||||||
|
var colonMatch = s.match(/^(\d+):(\d{1,2})(?::(\d{1,2}))?$/);
|
||||||
|
if (colonMatch) {
|
||||||
|
var hours = parseInt(colonMatch[1], 10);
|
||||||
|
var minutes = parseInt(colonMatch[2], 10);
|
||||||
|
if (minutes >= 60) return null;
|
||||||
|
var sec = colonMatch[3] != null ? parseInt(colonMatch[3], 10) : 0;
|
||||||
|
if (sec >= 60) return null;
|
||||||
|
var decimal = hours + minutes / 60 + sec / 3600;
|
||||||
|
return sign * decimal;
|
||||||
|
}
|
||||||
|
// Xh Ymin (optional Leerzeichen)
|
||||||
|
var hmMatch = s.match(/^(\d+)\s*h\s*(\d{1,2})?\s*min$/i);
|
||||||
|
if (hmMatch) {
|
||||||
|
var h = parseInt(hmMatch[1], 10);
|
||||||
|
var m = (hmMatch[2] != null) ? parseInt(hmMatch[2], 10) : 0;
|
||||||
|
if (m >= 60) return null;
|
||||||
|
return sign * (h + m / 60);
|
||||||
|
}
|
||||||
|
// Nur Zahl (Dezimalstunden) zulassen als Fallback
|
||||||
|
var num = parseFloat(s.replace(',', '.'));
|
||||||
|
if (!Number.isFinite(num)) return null;
|
||||||
|
return sign * num;
|
||||||
|
}
|
||||||
|
|
||||||
window.formatHoursMin = formatHoursMin;
|
window.formatHoursMin = formatHoursMin;
|
||||||
|
window.decimalHoursToHhMm = decimalHoursToHhMm;
|
||||||
|
window.parseHoursMin = parseHoursMin;
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -99,13 +99,13 @@
|
|||||||
<div style="display: inline-flex; gap: 8px; align-items: center; margin-right: 20px;">
|
<div style="display: inline-flex; gap: 8px; align-items: center; margin-right: 20px;">
|
||||||
<strong>Überstunden-Korrektur:</strong>
|
<strong>Überstunden-Korrektur:</strong>
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="text"
|
||||||
step="0.25"
|
|
||||||
class="overtime-offset-input"
|
class="overtime-offset-input"
|
||||||
data-user-id="<%= employee.user.id %>"
|
data-user-id="<%= employee.user.id %>"
|
||||||
value="0"
|
value="0:00"
|
||||||
style="width: 90px; padding: 4px 6px; border: 1px solid #ddd; border-radius: 4px;"
|
placeholder="z. B. 1:30, 10:30 oder -0:45"
|
||||||
title="Korrektur eingeben (z. B. +10 oder -20). Nach dem Speichern wird das Feld auf 0 gesetzt." />
|
style="width: 110px; padding: 4px 6px; border: 1px solid #ddd; border-radius: 4px;"
|
||||||
|
title="Korrektur in h:mm (z. B. 1:30, 10:30 oder -0:45). Nach dem Speichern wird das Feld auf 0:00 gesetzt." />
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="btn btn-success btn-sm save-overtime-offset-btn"
|
class="btn btn-success btn-sm save-overtime-offset-btn"
|
||||||
@@ -697,11 +697,16 @@
|
|||||||
|
|
||||||
// leere Eingabe => 0 (Backend macht das auch, aber UI soll sauber sein)
|
// leere Eingabe => 0 (Backend macht das auch, aber UI soll sauber sein)
|
||||||
const raw = (input.value || '').trim();
|
const raw = (input.value || '').trim();
|
||||||
const value = raw === '' ? '' : Number(raw);
|
const value = (typeof parseHoursMin === 'function' ? parseHoursMin(raw) : null);
|
||||||
|
if (value === null && raw !== '') {
|
||||||
|
alert('Ungültiges Format. Bitte h:mm oder hh:mm eingeben (z. B. 1:30, 10:30 oder -0:45).');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const decimalValue = (value === null ? 0 : value);
|
||||||
|
|
||||||
// Wenn keine Korrektur (0), nichts tun außer UI auf 0 zu normalisieren
|
// Wenn keine Korrektur (0), nichts tun außer UI auf 0:00 zu normalisieren
|
||||||
if (value === 0) {
|
if (decimalValue === 0) {
|
||||||
input.value = 0;
|
input.value = (typeof decimalHoursToHhMm === 'function' ? decimalHoursToHhMm(0) : '0:00');
|
||||||
this.textContent = originalText;
|
this.textContent = originalText;
|
||||||
this.disabled = false;
|
this.disabled = false;
|
||||||
return;
|
return;
|
||||||
@@ -713,7 +718,7 @@
|
|||||||
// Modal: Grund ist Pflicht
|
// Modal: Grund ist Pflicht
|
||||||
showOvertimeCorrectionReasonModal({
|
showOvertimeCorrectionReasonModal({
|
||||||
title: 'Grund für die Überstunden-Korrektur',
|
title: 'Grund für die Überstunden-Korrektur',
|
||||||
prompt: `Korrektur: ${value >= 0 ? '+' : ''}${formatHoursMin(value)}`,
|
prompt: `Korrektur: ${decimalValue >= 0 ? '+' : ''}${formatHoursMin(decimalValue)}`,
|
||||||
onCancel: () => {
|
onCancel: () => {
|
||||||
delete this.dataset.modalOpen;
|
delete this.dataset.modalOpen;
|
||||||
this.disabled = false;
|
this.disabled = false;
|
||||||
@@ -724,7 +729,7 @@
|
|||||||
const resp = await fetch(`/api/verwaltung/user/${userId}/overtime-offset`, {
|
const resp = await fetch(`/api/verwaltung/user/${userId}/overtime-offset`, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({ overtime_offset_hours: value, reason })
|
body: JSON.stringify({ overtime_offset_hours: decimalValue, reason })
|
||||||
});
|
});
|
||||||
const data = await resp.json();
|
const data = await resp.json();
|
||||||
if (!resp.ok) {
|
if (!resp.ok) {
|
||||||
@@ -735,10 +740,10 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Normalisiere Input auf Zahl (Backend gibt number zurück)
|
// Normalisiere Input auf h:mm (Backend gibt 0 zurück nach Speichern)
|
||||||
input.value = (data.overtime_offset_hours !== undefined && data.overtime_offset_hours !== null)
|
input.value = (typeof decimalHoursToHhMm === 'function')
|
||||||
? Number(data.overtime_offset_hours)
|
? decimalHoursToHhMm((data.overtime_offset_hours !== undefined && data.overtime_offset_hours !== null) ? Number(data.overtime_offset_hours) : 0)
|
||||||
: 0;
|
: '0:00';
|
||||||
|
|
||||||
// Stats für diesen User neu laden
|
// Stats für diesen User neu laden
|
||||||
const statDivs = document.querySelectorAll(`.group-stats[data-user-id="${userId}"]`);
|
const statDivs = document.querySelectorAll(`.group-stats[data-user-id="${userId}"]`);
|
||||||
|
|||||||
Reference in New Issue
Block a user