V1.1 Verschiedene Anpassungen
This commit is contained in:
@@ -55,7 +55,10 @@
|
||||
</div>
|
||||
|
||||
<div class="actions">
|
||||
<button id="submitWeek" class="btn btn-success" onclick="window.submitWeekHandler(event)" disabled>Woche abschicken</button>
|
||||
<div style="display: flex; gap: 10px; align-items: 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>
|
||||
<p class="help-text">Stunden werden automatisch gespeichert. Am Ende der Woche können Sie die Stunden abschicken.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -111,6 +111,25 @@
|
||||
Speichern
|
||||
</button>
|
||||
</div>
|
||||
<div style="display: inline-flex; gap: 8px; align-items: center; margin-right: 20px;">
|
||||
<strong>Urlaubstage-Offset:</strong>
|
||||
<input
|
||||
type="number"
|
||||
step="0.5"
|
||||
class="vacation-offset-input"
|
||||
data-user-id="<%= employee.user.id %>"
|
||||
value="<%= (employee.user.vacation_offset_days !== undefined && employee.user.vacation_offset_days !== null) ? employee.user.vacation_offset_days : 0 %>"
|
||||
style="width: 90px; padding: 4px 6px; border: 1px solid #ddd; border-radius: 4px;"
|
||||
title="Manuelle Korrektur (positiv oder negativ) in Tagen" />
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-success btn-sm save-vacation-offset-btn"
|
||||
data-user-id="<%= employee.user.id %>"
|
||||
style="padding: 6px 10px; white-space: nowrap;"
|
||||
title="Urlaubstage-Offset speichern">
|
||||
Speichern
|
||||
</button>
|
||||
</div>
|
||||
<div style="display: inline-block; margin-right: 20px;">
|
||||
<strong>Kalenderwochen:</strong> <span><%= employee.weeks.length %></span>
|
||||
</div>
|
||||
@@ -154,6 +173,11 @@
|
||||
<div class="week-versions-info" style="margin-top: 5px;">
|
||||
<span class="version-count"><%= week.total_versions %> Version<%= week.total_versions !== 1 ? 'en' : '' %></span>
|
||||
</div>
|
||||
<% if (week.has_new_version_after_download) { %>
|
||||
<div class="new-version-warning" style="margin-top: 10px;">
|
||||
<strong>ACHTUNG: Neue version eingereicht</strong>
|
||||
</div>
|
||||
<% } %>
|
||||
</div>
|
||||
<button class="btn btn-secondary btn-sm toggle-versions-btn" data-employee-index="<%= employeeIndex %>" data-week-index="<%= weekIndex %>">
|
||||
<span class="toggle-icon">▼</span> Versionen
|
||||
@@ -169,7 +193,6 @@
|
||||
<th>Eingereicht am</th>
|
||||
<th>Grund</th>
|
||||
<th>Kommentar</th>
|
||||
<th>Status</th>
|
||||
<th>Aktionen</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -225,7 +248,6 @@
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
<td><span class="status-badge status-<%= ts.status %>"><%= ts.status %></span></td>
|
||||
<td>
|
||||
<button class="btn btn-info btn-sm toggle-pdf-btn" data-timesheet-id="<%= ts.id %>">
|
||||
<span class="arrow-icon">▶</span> PDF anzeigen
|
||||
@@ -236,7 +258,7 @@
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="pdf-preview-row" data-timesheet-id="<%= ts.id %>" style="display: none;">
|
||||
<td colspan="6">
|
||||
<td colspan="5">
|
||||
<div class="pdf-preview-container">
|
||||
<div class="pdf-preview-header">
|
||||
<h3>PDF-Vorschau: <%= employee.user.firstname %> <%= employee.user.lastname %> - Version <%= ts.version || 1 %> - <%= new Date(ts.week_start).toLocaleDateString('de-DE') %> bis <%= new Date(ts.week_end).toLocaleDateString('de-DE') %></h3>
|
||||
@@ -317,6 +339,12 @@
|
||||
${data.remainingVacation !== undefined ? `<span style="color: #28a745;">(verbleibend: ${data.remainingVacation.toFixed(1)} Tage)</span>` : ''}
|
||||
</div>`;
|
||||
}
|
||||
if (data.vacationOffsetDays !== undefined && data.vacationOffsetDays !== 0) {
|
||||
statsHTML += `<div class="stats-inline" style="display: inline-block; margin-right: 20px;">
|
||||
<strong>Urlaubstage-Offset:</strong> <span>${Number(data.vacationOffsetDays).toFixed(1)} Tag${Math.abs(data.vacationOffsetDays) !== 1 ? 'e' : ''}</span>
|
||||
${data.remainingVacation !== undefined ? `<span style="color: #28a745;">(verbleibend inkl. Offset: ${Number(data.remainingVacation).toFixed(1)} Tage)</span>` : ''}
|
||||
</div>`;
|
||||
}
|
||||
if (data.sickDays !== undefined && data.sickDays > 0) {
|
||||
statsHTML += `<div class="stats-inline" style="display: inline-block; margin-right: 20px;">
|
||||
<strong>Krankheitstage:</strong> <span style="color: #e74c3c;">${data.sickDays} Tag${data.sickDays !== 1 ? 'e' : ''}</span>
|
||||
@@ -431,6 +459,68 @@
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Urlaubstage-Offset speichern
|
||||
document.querySelectorAll('.save-vacation-offset-btn').forEach(btn => {
|
||||
btn.addEventListener('click', async function() {
|
||||
const userId = this.dataset.userId;
|
||||
const input = document.querySelector(`.vacation-offset-input[data-user-id="${userId}"]`);
|
||||
if (!input) return;
|
||||
|
||||
const originalText = this.textContent;
|
||||
this.disabled = true;
|
||||
this.textContent = '...';
|
||||
|
||||
// leere Eingabe => 0 (Backend macht das auch, aber UI soll sauber sein)
|
||||
const raw = (input.value || '').trim();
|
||||
const value = raw === '' ? '' : Number(raw);
|
||||
|
||||
try {
|
||||
const resp = await fetch(`/api/verwaltung/user/${userId}/vacation-offset`, {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ vacation_offset_days: value })
|
||||
});
|
||||
const data = await resp.json();
|
||||
if (!resp.ok) {
|
||||
alert(data.error || 'Fehler beim Speichern des Offsets');
|
||||
return;
|
||||
}
|
||||
|
||||
// Normalisiere Input auf Zahl (Backend gibt number zurück)
|
||||
input.value = (data.vacation_offset_days !== undefined && data.vacation_offset_days !== null)
|
||||
? Number(data.vacation_offset_days)
|
||||
: 0;
|
||||
|
||||
// Stats für diesen User neu laden
|
||||
const statDivs = document.querySelectorAll(`.group-stats[data-user-id="${userId}"]`);
|
||||
statDivs.forEach(div => {
|
||||
// loading indicator optional wieder anzeigen
|
||||
const loading = div.querySelector('.stats-loading');
|
||||
if (loading) {
|
||||
loading.style.display = 'inline-block';
|
||||
loading.style.color = '#666';
|
||||
loading.textContent = 'Lade Statistiken...';
|
||||
}
|
||||
loadStatsForDiv(div);
|
||||
});
|
||||
|
||||
this.textContent = '✓';
|
||||
setTimeout(() => {
|
||||
this.textContent = originalText;
|
||||
this.disabled = false;
|
||||
}, 900);
|
||||
} catch (e) {
|
||||
console.error('Fehler beim Speichern des Offsets:', e);
|
||||
alert('Fehler beim Speichern des Offsets');
|
||||
} finally {
|
||||
if (this.textContent === '...') {
|
||||
this.textContent = originalText;
|
||||
this.disabled = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Mitarbeiter-Gruppen auf-/zuklappen (zeigt/versteckt Wochen)
|
||||
document.querySelectorAll('.toggle-employee-btn').forEach(btn => {
|
||||
|
||||
Reference in New Issue
Block a user