Collabsable automations
This commit is contained in:
@@ -113,6 +113,29 @@ function registerVerwaltungRoutes(app) {
|
||||
});
|
||||
});
|
||||
|
||||
// API: Krankheitstage für einen User im aktuellen Jahr abrufen
|
||||
app.get('/api/verwaltung/user/:id/sick-days', requireVerwaltung, (req, res) => {
|
||||
const userId = req.params.id;
|
||||
const currentYear = new Date().getFullYear();
|
||||
const yearStart = `${currentYear}-01-01`;
|
||||
const yearEnd = `${currentYear}-12-31`;
|
||||
|
||||
db.all(`SELECT DISTINCT date
|
||||
FROM timesheet_entries
|
||||
WHERE user_id = ? AND date >= ? AND date <= ? AND sick_status = 1
|
||||
ORDER BY date`,
|
||||
[userId, yearStart, yearEnd],
|
||||
(err, entries) => {
|
||||
if (err) {
|
||||
return res.status(500).json({ error: 'Fehler beim Abrufen der Krankheitstage' });
|
||||
}
|
||||
|
||||
const sickDays = entries ? entries.length : 0;
|
||||
res.json({ sickDays: sickDays, year: currentYear });
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
// API: Überstunden- und Urlaubsstatistiken für einen User abrufen
|
||||
app.get('/api/verwaltung/user/:id/stats', requireVerwaltung, (req, res) => {
|
||||
const userId = req.params.id;
|
||||
|
||||
@@ -73,9 +73,16 @@
|
||||
<div class="stat-unit">von <span id="totalVacation">-</span> Tagen</div>
|
||||
</div>
|
||||
|
||||
<!-- URL-Erfassung -->
|
||||
<!-- Zeiterfassung (URL & IP) -->
|
||||
<div style="margin-top: 20px; padding-top: 20px; border-top: 1px solid #e0e0e0;">
|
||||
<h3 style="font-size: 14px; margin-bottom: 15px; color: #2c3e50;">Zeiterfassung per URL</h3>
|
||||
<h3 style="font-size: 14px; margin-bottom: 0; color: #2c3e50; cursor: pointer; user-select: none; display: flex; align-items: center; gap: 8px;" onclick="toggleTimeCapture()">
|
||||
<span class="toggle-icon-time-capture" style="display: inline-block; transition: transform 0.3s;">▶</span>
|
||||
Automatische Zeiterfassung
|
||||
</h3>
|
||||
<div id="timeCaptureContent" style="display: none; margin-top: 15px;">
|
||||
<!-- URL-Erfassung -->
|
||||
<div style="margin-bottom: 20px;">
|
||||
<h4 style="font-size: 13px; margin-bottom: 10px; color: #555;">Zeiterfassung per URL</h4>
|
||||
<div class="form-group" style="margin-bottom: 15px;">
|
||||
<label style="font-size: 12px; color: #666; margin-bottom: 5px;">Check-in URL</label>
|
||||
<div style="display: flex; gap: 5px;">
|
||||
@@ -93,8 +100,8 @@
|
||||
</div>
|
||||
|
||||
<!-- IP-Erfassung -->
|
||||
<div style="margin-top: 20px; padding-top: 20px; border-top: 1px solid #e0e0e0;">
|
||||
<h3 style="font-size: 14px; margin-bottom: 15px; color: #2c3e50;">IP-basierte Zeiterfassung</h3>
|
||||
<div style="padding-top: 15px; border-top: 1px solid #e0e0e0;">
|
||||
<h4 style="font-size: 13px; margin-bottom: 10px; color: #555;">IP-basierte Zeiterfassung</h4>
|
||||
<div class="form-group" style="margin-bottom: 15px;">
|
||||
<label style="font-size: 12px; color: #666; margin-bottom: 5px;">Ping-IP Adresse</label>
|
||||
<div style="display: flex; gap: 5px;">
|
||||
@@ -107,6 +114,8 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="/js/dashboard.js"></script>
|
||||
<script>
|
||||
@@ -126,6 +135,22 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Zeiterfassung ein-/ausklappen
|
||||
function toggleTimeCapture() {
|
||||
const content = document.getElementById('timeCaptureContent');
|
||||
const icon = document.querySelector('.toggle-icon-time-capture');
|
||||
|
||||
if (content && icon) {
|
||||
if (content.style.display === 'none') {
|
||||
content.style.display = 'block';
|
||||
icon.style.transform = 'rotate(90deg)';
|
||||
} else {
|
||||
content.style.display = 'none';
|
||||
icon.style.transform = 'rotate(0deg)';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// URL-Kopier-Funktion
|
||||
function copyToClipboard(inputId) {
|
||||
const input = document.getElementById(inputId);
|
||||
|
||||
@@ -113,6 +113,10 @@
|
||||
<div style="display: inline-block; margin-right: 20px;">
|
||||
<strong>Kalenderwochen:</strong> <span><%= employee.weeks.length %></span>
|
||||
</div>
|
||||
<div style="display: inline-block; margin-right: 20px;">
|
||||
<strong>Krankheitstage (<span class="sick-days-year"><%= new Date().getFullYear() %></span>):</strong>
|
||||
<span class="sick-days-count" data-user-id="<%= employee.user.id %>" style="color: #e74c3c;">-</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button class="btn btn-secondary btn-sm toggle-employee-btn" data-employee-index="<%= employeeIndex %>">
|
||||
@@ -330,6 +334,36 @@
|
||||
// Statistiken für alle Wochen initial laden
|
||||
document.querySelectorAll('.group-stats').forEach(statsDiv => loadStatsForDiv(statsDiv));
|
||||
|
||||
// Krankheitstage für alle Mitarbeiter laden
|
||||
async function loadSickDays() {
|
||||
const sickDaysElements = document.querySelectorAll('.sick-days-count');
|
||||
const userIds = Array.from(sickDaysElements).map(el => el.dataset.userId);
|
||||
const uniqueUserIds = [...new Set(userIds)];
|
||||
|
||||
for (const userId of uniqueUserIds) {
|
||||
try {
|
||||
const response = await fetch(`/api/verwaltung/user/${userId}/sick-days`);
|
||||
if (!response.ok) {
|
||||
throw new Error('Fehler beim Laden der Krankheitstage');
|
||||
}
|
||||
const data = await response.json();
|
||||
|
||||
// Alle Elemente für diesen User aktualisieren
|
||||
document.querySelectorAll(`.sick-days-count[data-user-id="${userId}"]`).forEach(el => {
|
||||
el.textContent = data.sickDays || 0;
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Fehler beim Laden der Krankheitstage für User', userId, ':', error);
|
||||
document.querySelectorAll(`.sick-days-count[data-user-id="${userId}"]`).forEach(el => {
|
||||
el.textContent = '-';
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Krankheitstage beim Laden der Seite abrufen
|
||||
loadSickDays();
|
||||
|
||||
// Überstunden-Offset speichern
|
||||
document.querySelectorAll('.save-overtime-offset-btn').forEach(btn => {
|
||||
btn.addEventListener('click', async function() {
|
||||
|
||||
Reference in New Issue
Block a user