Styling
This commit is contained in:
@@ -840,6 +840,17 @@ table input[type="text"] {
|
||||
color: #7f8c8d;
|
||||
}
|
||||
|
||||
/* Überstunden-Farbklassen (global genutzt, z. B. Verwaltung & Auswertung) */
|
||||
.overtime-positive {
|
||||
color: #27ae60;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.overtime-negative {
|
||||
color: #e74c3c;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Activities/Tätigkeiten */
|
||||
.activities-row {
|
||||
background-color: #f8f9fa;
|
||||
@@ -864,7 +875,7 @@ table input[type="text"] {
|
||||
|
||||
.activity-row {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 150px 120px;
|
||||
grid-template-columns: 1fr 150px 150px;
|
||||
gap: 15px;
|
||||
align-items: center;
|
||||
}
|
||||
@@ -893,7 +904,7 @@ table input[type="text"] {
|
||||
}
|
||||
|
||||
.activity-hours-input {
|
||||
width: 80px;
|
||||
width: 64px;
|
||||
}
|
||||
|
||||
.activity-hours-label {
|
||||
@@ -901,6 +912,11 @@ table input[type="text"] {
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.activity-hours-hh-input,
|
||||
.activity-hours-mm-input {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.activity-project {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
@@ -374,6 +374,52 @@ function getFullDayHours() {
|
||||
return userWochenstunden && userArbeitstage ? (userWochenstunden / userArbeitstage) : 8;
|
||||
}
|
||||
|
||||
function decimalHoursToParts(decimalHours) {
|
||||
const parsed = Number(decimalHours);
|
||||
if (!Number.isFinite(parsed) || parsed <= 0) {
|
||||
return { hh: '', mm: '' };
|
||||
}
|
||||
|
||||
const totalMinutes = Math.round(parsed * 60);
|
||||
const hh = Math.floor(totalMinutes / 60);
|
||||
const mm = totalMinutes % 60;
|
||||
return {
|
||||
hh: String(hh),
|
||||
mm: String(mm).padStart(2, '0')
|
||||
};
|
||||
}
|
||||
|
||||
function parseActivityHoursFromInputs(hoursInput, minutesInput, fallbackValue) {
|
||||
const hoursRaw = hoursInput ? hoursInput.value.trim() : '';
|
||||
const minutesRaw = minutesInput ? minutesInput.value.trim() : '';
|
||||
|
||||
if (hoursRaw === '' && minutesRaw === '') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const parsedHours = parseInt(hoursRaw, 10);
|
||||
const parsedMinutes = parseInt(minutesRaw, 10);
|
||||
|
||||
const safeHours = Number.isFinite(parsedHours) && parsedHours >= 0 ? parsedHours : 0;
|
||||
const safeMinutes = Number.isFinite(parsedMinutes) && parsedMinutes >= 0 ? parsedMinutes : 0;
|
||||
const totalMinutes = (safeHours * 60) + safeMinutes;
|
||||
|
||||
if (!Number.isFinite(totalMinutes) || totalMinutes < 0) {
|
||||
return Number.isFinite(fallbackValue) ? fallbackValue : 0;
|
||||
}
|
||||
|
||||
const normalizedHours = Math.floor(totalMinutes / 60);
|
||||
const normalizedMinutes = totalMinutes % 60;
|
||||
if (hoursInput) {
|
||||
hoursInput.value = totalMinutes > 0 ? String(normalizedHours) : '';
|
||||
}
|
||||
if (minutesInput) {
|
||||
minutesInput.value = totalMinutes > 0 ? String(normalizedMinutes).padStart(2, '0') : '';
|
||||
}
|
||||
|
||||
return totalMinutes / 60;
|
||||
}
|
||||
|
||||
// Woche laden
|
||||
async function loadWeek() {
|
||||
try {
|
||||
@@ -603,7 +649,9 @@ function renderWeek() {
|
||||
<td colspan="6" class="activities-cell">
|
||||
<div class="activities-form">
|
||||
<div class="activities-header"><strong>Tätigkeiten:</strong></div>
|
||||
${activities.map((activity, idx) => `
|
||||
${activities.map((activity, idx) => {
|
||||
const timeParts = decimalHoursToParts(activity.hours);
|
||||
return `
|
||||
<div class="activity-row">
|
||||
<div class="activity-desc">
|
||||
<input type="text"
|
||||
@@ -626,22 +674,36 @@ function renderWeek() {
|
||||
class="activity-project-input">
|
||||
</div>
|
||||
<div class="activity-hours">
|
||||
<input type="number"
|
||||
<input type="text"
|
||||
data-date="${dateStr}"
|
||||
data-field="activity${idx + 1}_hours"
|
||||
value="${activity.hours > 0 ? activity.hours.toFixed(2) : ''}"
|
||||
min="0"
|
||||
step="0.25"
|
||||
placeholder="0.00"
|
||||
data-field="activity${idx + 1}_hours_hh"
|
||||
value="${timeParts.hh}"
|
||||
inputmode="numeric"
|
||||
pattern="[0-9]*"
|
||||
placeholder="hh"
|
||||
${timeFieldsDisabled} ${disabled}
|
||||
onblur="saveEntry(this)"
|
||||
oninput="updateOvertimeDisplay();"
|
||||
onchange="updateOvertimeDisplay();"
|
||||
class="activity-hours-input">
|
||||
class="activity-hours-input activity-hours-hh-input">
|
||||
<span class="activity-hours-label">h</span>
|
||||
<input type="text"
|
||||
data-date="${dateStr}"
|
||||
data-field="activity${idx + 1}_hours_mm"
|
||||
value="${timeParts.mm}"
|
||||
inputmode="numeric"
|
||||
pattern="[0-9]*"
|
||||
placeholder="mm"
|
||||
${timeFieldsDisabled} ${disabled}
|
||||
onblur="saveEntry(this)"
|
||||
oninput="updateOvertimeDisplay();"
|
||||
onchange="updateOvertimeDisplay();"
|
||||
class="activity-hours-input activity-hours-mm-input">
|
||||
<span class="activity-hours-label">min</span>
|
||||
</div>
|
||||
</div>
|
||||
`).join('')}
|
||||
`;
|
||||
}).join('')}
|
||||
</div>
|
||||
<div class="overtime-vacation-controls" style="margin-top: 15px; display: flex; gap: 15px; align-items: center;">
|
||||
<div class="overtime-control">
|
||||
@@ -1038,7 +1100,8 @@ function handleOvertimeChange(dateStr, overtimeHours) {
|
||||
// (Überstunden werden nur im PDF angezeigt, nicht als Tätigkeit)
|
||||
for (let i = 1; i <= 5; i++) {
|
||||
const descInput = document.querySelector(`input[data-date="${dateStr}"][data-field="activity${i}_desc"]`);
|
||||
const hoursInput = document.querySelector(`input[data-date="${dateStr}"][data-field="activity${i}_hours"]`);
|
||||
const hoursInput = document.querySelector(`input[data-date="${dateStr}"][data-field="activity${i}_hours_hh"]`);
|
||||
const minutesInput = document.querySelector(`input[data-date="${dateStr}"][data-field="activity${i}_hours_mm"]`);
|
||||
|
||||
if (descInput && descInput.value && descInput.value.trim().toLowerCase() === 'überstunden') {
|
||||
descInput.value = '';
|
||||
@@ -1047,6 +1110,10 @@ function handleOvertimeChange(dateStr, overtimeHours) {
|
||||
hoursInput.value = '';
|
||||
saveEntry(hoursInput);
|
||||
}
|
||||
if (minutesInput) {
|
||||
minutesInput.value = '';
|
||||
saveEntry(minutesInput);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1155,7 +1222,18 @@ async function saveEntry(input) {
|
||||
// Wichtig: Leere Strings werden zu null konvertiert, aber ein Wert sollte vorhanden sein
|
||||
const start_time = actualStartTime;
|
||||
const end_time = actualEndTime;
|
||||
let break_minutes = breakInput && breakInput.value ? (parseInt(breakInput.value) || 0) : (parseInt(currentEntries[date].break_minutes) || 0);
|
||||
let break_minutes = defaultBreakMinutes;
|
||||
if (breakInput && breakInput.value !== '') {
|
||||
const parsedBreak = parseInt(breakInput.value, 10);
|
||||
break_minutes = Number.isFinite(parsedBreak) && parsedBreak >= 0 ? parsedBreak : defaultBreakMinutes;
|
||||
} else if (
|
||||
currentEntries[date].break_minutes !== null &&
|
||||
currentEntries[date].break_minutes !== undefined &&
|
||||
currentEntries[date].break_minutes !== ''
|
||||
) {
|
||||
const parsedStoredBreak = parseInt(currentEntries[date].break_minutes, 10);
|
||||
break_minutes = Number.isFinite(parsedStoredBreak) && parsedStoredBreak >= 0 ? parsedStoredBreak : defaultBreakMinutes;
|
||||
}
|
||||
|
||||
const notes = notesInput ? (notesInput.value || '') : (currentEntries[date].notes || '');
|
||||
const vacation_type = vacationSelect && vacationSelect.value ? vacationSelect.value : (currentEntries[date].vacation_type || null);
|
||||
@@ -1168,12 +1246,16 @@ async function saveEntry(input) {
|
||||
const activities = [];
|
||||
for (let i = 1; i <= 5; i++) {
|
||||
const descInput = document.querySelector(`input[data-date="${date}"][data-field="activity${i}_desc"]`);
|
||||
const hoursInput = document.querySelector(`input[data-date="${date}"][data-field="activity${i}_hours"]`);
|
||||
const hoursInput = document.querySelector(`input[data-date="${date}"][data-field="activity${i}_hours_hh"]`);
|
||||
const minutesInput = document.querySelector(`input[data-date="${date}"][data-field="activity${i}_hours_mm"]`);
|
||||
const projectInput = document.querySelector(`input[data-date="${date}"][data-field="activity${i}_project_number"]`);
|
||||
const fallbackHours = parseFloat(currentEntries[date][`activity${i}_hours`]) || 0;
|
||||
|
||||
activities.push({
|
||||
desc: descInput ? (descInput.value || null) : (currentEntries[date][`activity${i}_desc`] || null),
|
||||
hours: hoursInput ? (parseFloat(hoursInput.value) || 0) : (parseFloat(currentEntries[date][`activity${i}_hours`]) || 0),
|
||||
hours: (hoursInput || minutesInput)
|
||||
? parseActivityHoursFromInputs(hoursInput, minutesInput, fallbackHours)
|
||||
: fallbackHours,
|
||||
projectNumber: projectInput ? (projectInput.value || null) : (currentEntries[date][`activity${i}_project_number`] || null)
|
||||
});
|
||||
}
|
||||
@@ -1337,21 +1419,27 @@ async function saveEntry(input) {
|
||||
// 5. Bei ganztägigem Urlaub: Setze "Urlaub" als erste Tätigkeit und leere andere
|
||||
if (isFullDayVacation) {
|
||||
const descInput = document.querySelector(`input[data-date="${date}"][data-field="activity1_desc"]`);
|
||||
const hoursInput = document.querySelector(`input[data-date="${date}"][data-field="activity1_hours"]`);
|
||||
const hoursInput = document.querySelector(`input[data-date="${date}"][data-field="activity1_hours_hh"]`);
|
||||
const minutesInput = document.querySelector(`input[data-date="${date}"][data-field="activity1_hours_mm"]`);
|
||||
|
||||
if (descInput) {
|
||||
descInput.value = 'Urlaub';
|
||||
currentEntries[date].activity1_desc = 'Urlaub';
|
||||
}
|
||||
if (hoursInput) {
|
||||
hoursInput.value = fullDayHours.toFixed(2);
|
||||
const fullDayParts = decimalHoursToParts(fullDayHours);
|
||||
hoursInput.value = fullDayParts.hh;
|
||||
if (minutesInput) {
|
||||
minutesInput.value = fullDayParts.mm;
|
||||
}
|
||||
currentEntries[date].activity1_hours = fullDayHours;
|
||||
}
|
||||
|
||||
// Leere andere Tätigkeiten
|
||||
for (let i = 2; i <= 5; i++) {
|
||||
const descInput = document.querySelector(`input[data-date="${date}"][data-field="activity${i}_desc"]`);
|
||||
const hoursInput = document.querySelector(`input[data-date="${date}"][data-field="activity${i}_hours"]`);
|
||||
const hoursInput = document.querySelector(`input[data-date="${date}"][data-field="activity${i}_hours_hh"]`);
|
||||
const minutesInput = document.querySelector(`input[data-date="${date}"][data-field="activity${i}_hours_mm"]`);
|
||||
const projectInput = document.querySelector(`input[data-date="${date}"][data-field="activity${i}_project_number"]`);
|
||||
|
||||
if (descInput) {
|
||||
@@ -1360,6 +1448,9 @@ async function saveEntry(input) {
|
||||
}
|
||||
if (hoursInput) {
|
||||
hoursInput.value = '';
|
||||
if (minutesInput) {
|
||||
minutesInput.value = '';
|
||||
}
|
||||
currentEntries[date][`activity${i}_hours`] = 0;
|
||||
}
|
||||
if (projectInput) {
|
||||
@@ -1371,7 +1462,8 @@ async function saveEntry(input) {
|
||||
// Bei Abwahl von Urlaub (nicht full): Alle Tätigkeitsfelder leeren
|
||||
for (let i = 1; i <= 5; i++) {
|
||||
const descInput = document.querySelector(`input[data-date="${date}"][data-field="activity${i}_desc"]`);
|
||||
const hoursInput = document.querySelector(`input[data-date="${date}"][data-field="activity${i}_hours"]`);
|
||||
const hoursInput = document.querySelector(`input[data-date="${date}"][data-field="activity${i}_hours_hh"]`);
|
||||
const minutesInput = document.querySelector(`input[data-date="${date}"][data-field="activity${i}_hours_mm"]`);
|
||||
const projectInput = document.querySelector(`input[data-date="${date}"][data-field="activity${i}_project_number"]`);
|
||||
|
||||
if (descInput) {
|
||||
@@ -1380,6 +1472,9 @@ async function saveEntry(input) {
|
||||
}
|
||||
if (hoursInput) {
|
||||
hoursInput.value = '';
|
||||
if (minutesInput) {
|
||||
minutesInput.value = '';
|
||||
}
|
||||
currentEntries[date][`activity${i}_hours`] = 0;
|
||||
}
|
||||
if (projectInput) {
|
||||
@@ -2118,7 +2213,8 @@ function toggleSickStatus(dateStr) {
|
||||
// Leere alle Tätigkeitsfelder
|
||||
for (let i = 1; i <= 5; i++) {
|
||||
const descInput = document.querySelector(`input[data-date="${dateStr}"][data-field="activity${i}_desc"]`);
|
||||
const hoursInput = document.querySelector(`input[data-date="${dateStr}"][data-field="activity${i}_hours"]`);
|
||||
const hoursInput = document.querySelector(`input[data-date="${dateStr}"][data-field="activity${i}_hours_hh"]`);
|
||||
const minutesInput = document.querySelector(`input[data-date="${dateStr}"][data-field="activity${i}_hours_mm"]`);
|
||||
const projectInput = document.querySelector(`input[data-date="${dateStr}"][data-field="activity${i}_project_number"]`);
|
||||
|
||||
if (descInput) {
|
||||
@@ -2127,6 +2223,9 @@ function toggleSickStatus(dateStr) {
|
||||
}
|
||||
if (hoursInput) {
|
||||
hoursInput.value = '';
|
||||
if (minutesInput) {
|
||||
minutesInput.value = '';
|
||||
}
|
||||
currentEntries[dateStr][`activity${i}_hours`] = 0;
|
||||
}
|
||||
if (projectInput) {
|
||||
@@ -2137,7 +2236,8 @@ function toggleSickStatus(dateStr) {
|
||||
} else {
|
||||
// Bei Aktivierung: Setze "Krank" als erste Tätigkeit und leere andere
|
||||
const descInput = document.querySelector(`input[data-date="${dateStr}"][data-field="activity1_desc"]`);
|
||||
const hoursInput = document.querySelector(`input[data-date="${dateStr}"][data-field="activity1_hours"]`);
|
||||
const hoursInput = document.querySelector(`input[data-date="${dateStr}"][data-field="activity1_hours_hh"]`);
|
||||
const minutesInput = document.querySelector(`input[data-date="${dateStr}"][data-field="activity1_hours_mm"]`);
|
||||
const fullDayHours = getFullDayHours();
|
||||
|
||||
if (descInput) {
|
||||
@@ -2145,14 +2245,19 @@ function toggleSickStatus(dateStr) {
|
||||
currentEntries[dateStr].activity1_desc = 'Krank';
|
||||
}
|
||||
if (hoursInput) {
|
||||
hoursInput.value = fullDayHours.toFixed(2);
|
||||
const fullDayParts = decimalHoursToParts(fullDayHours);
|
||||
hoursInput.value = fullDayParts.hh;
|
||||
if (minutesInput) {
|
||||
minutesInput.value = fullDayParts.mm;
|
||||
}
|
||||
currentEntries[dateStr].activity1_hours = fullDayHours;
|
||||
}
|
||||
|
||||
// Leere andere Tätigkeiten
|
||||
for (let i = 2; i <= 5; i++) {
|
||||
const descInput = document.querySelector(`input[data-date="${dateStr}"][data-field="activity${i}_desc"]`);
|
||||
const hoursInput = document.querySelector(`input[data-date="${dateStr}"][data-field="activity${i}_hours"]`);
|
||||
const hoursInput = document.querySelector(`input[data-date="${dateStr}"][data-field="activity${i}_hours_hh"]`);
|
||||
const minutesInput = document.querySelector(`input[data-date="${dateStr}"][data-field="activity${i}_hours_mm"]`);
|
||||
const projectInput = document.querySelector(`input[data-date="${dateStr}"][data-field="activity${i}_project_number"]`);
|
||||
|
||||
if (descInput) {
|
||||
@@ -2161,6 +2266,9 @@ function toggleSickStatus(dateStr) {
|
||||
}
|
||||
if (hoursInput) {
|
||||
hoursInput.value = '';
|
||||
if (minutesInput) {
|
||||
minutesInput.value = '';
|
||||
}
|
||||
currentEntries[dateStr][`activity${i}_hours`] = 0;
|
||||
}
|
||||
if (projectInput) {
|
||||
|
||||
Reference in New Issue
Block a user