diff --git a/DSGVO-Dokumentation.pdf b/DSGVO-Dokumentation.pdf deleted file mode 100644 index b1b1ac6..0000000 Binary files a/DSGVO-Dokumentation.pdf and /dev/null differ diff --git a/DSGVO-Dokumentation.md b/doc/DSGVO-Dokumentation.md similarity index 100% rename from DSGVO-Dokumentation.md rename to doc/DSGVO-Dokumentation.md diff --git a/README.md b/doc/README.md similarity index 100% rename from README.md rename to doc/README.md diff --git a/SCHNELLSTART.md b/doc/SCHNELLSTART.md similarity index 100% rename from SCHNELLSTART.md rename to doc/SCHNELLSTART.md diff --git a/Stundenregeln.txt b/doc/Stundenregeln.txt similarity index 100% rename from Stundenregeln.txt rename to doc/Stundenregeln.txt diff --git a/Stunderfassung todo.txt b/doc/Stunderfassung todo.txt similarity index 91% rename from Stunderfassung todo.txt rename to doc/Stunderfassung todo.txt index 77d24c5..64f102d 100644 --- a/Stunderfassung todo.txt +++ b/doc/Stunderfassung todo.txt @@ -10,7 +10,7 @@ - Wenn bereits heruntergeladen wurde und neue version da ist Meldung an Verwaltung. -> DONE Muss getestet werden - Wenn ganzer Tag Urlaub gesetzt wird steht erst 8h (Urlaub) und dann nur noch 8h -- Feiertage im PDF anzeigen -> DONE Testen noch nicht depoyed +- Feiertage im PDF anzeigen -> DONE - Oben wenn woche eingereicht anzeigen als hilfestellung -> DONE - Ausgefüllte Tage anhand der Tage pro woche gültig setzten -> DONE Testen - Überstunden müssen anhand der Tagesstunden auch auf gültig setzten (Tag ausgefüllt wenn weniger als 8h) -> DONE sollte passen diff --git a/generate-dsgvo-pdf.js b/generate-dsgvo-pdf.js deleted file mode 100644 index 1ead773..0000000 --- a/generate-dsgvo-pdf.js +++ /dev/null @@ -1,177 +0,0 @@ -#!/usr/bin/env node -/** - * Erzeugt aus DSGVO-Dokumentation.md eine PDF-Datei. - * Verwendung: node generate-dsgvo-pdf.js - */ - -const PDFDocument = require('pdfkit'); -const fs = require('fs'); -const path = require('path'); - -const MARGIN = 50; -const PAGE_WIDTH = 595; // A4 -const CONTENT_WIDTH = PAGE_WIDTH - 2 * MARGIN; - -function stripBold(text) { - return text.replace(/\*\*([^*]+)\*\*/g, '$1'); -} - -function isTableRow(line) { - return /^\|.+\|$/.test(line.trim()) && !/^[\s|:-]+$/.test(line.replace(/\s/g, '')); -} - -function parseTableRows(lines, startIndex) { - const rows = []; - let i = startIndex; - while (i < lines.length && isTableRow(lines[i])) { - const line = lines[i]; - if (/^[\s|:-]+$/.test(line.replace(/\s/g, ''))) { - i++; - continue; // separator line - } - const cells = line.split('|').slice(1, -1).map(c => c.trim()); - if (cells.some(c => c)) rows.push(cells); - i++; - } - return { rows, nextIndex: i }; -} - -function writeText(doc, text, options = {}) { - const opts = { width: CONTENT_WIDTH, ...options }; - doc.text(text, opts); -} - -function addParagraph(doc, line, fontSize = 10) { - doc.fontSize(fontSize).font('Helvetica'); - const text = stripBold(line.trim()); - if (!text) return; - writeText(doc, text); - doc.moveDown(0.5); -} - -function addBullet(doc, line, fontSize = 10) { - doc.fontSize(fontSize).font('Helvetica'); - const text = stripBold(line.replace(/^[-*]\s*/, '').trim()); - if (!text) return; - doc.text('• ', { continued: true }); - doc.text(text, { width: CONTENT_WIDTH - 20 }); - doc.moveDown(0.4); -} - -function addTable(doc, rows, fontSize = 9) { - if (rows.length === 0) return; - const colCount = rows[0].length; - const colWidth = CONTENT_WIDTH / colCount; - const rowHeight = fontSize * 1.4; - const startY = doc.y; - - doc.fontSize(fontSize); - rows.forEach((row, rowIndex) => { - const isHeader = rowIndex === 0; - if (isHeader) doc.font('Helvetica-Bold'); - if (doc.y > 750) { - doc.addPage(); - doc.y = MARGIN; - } - let x = MARGIN; - row.forEach((cell, cellIndex) => { - doc.text(cell, x, doc.y, { width: colWidth - 4, align: 'left' }); - x += colWidth; - }); - doc.y += rowHeight; - if (isHeader) doc.font('Helvetica'); - }); - doc.moveDown(0.5); -} - -function generateDSGVOPdf() { - const mdPath = path.join(__dirname, 'DSGVO-Dokumentation.md'); - const outPath = path.join(__dirname, 'DSGVO-Dokumentation.pdf'); - - if (!fs.existsSync(mdPath)) { - console.error('Datei nicht gefunden: DSGVO-Dokumentation.md'); - process.exit(1); - } - - const content = fs.readFileSync(mdPath, 'utf8'); - const lines = content.split(/\r?\n/); - - const doc = new PDFDocument({ margin: MARGIN, size: 'A4' }); - const stream = fs.createWriteStream(outPath); - doc.pipe(stream); - - let i = 0; - while (i < lines.length) { - const line = lines[i]; - const trimmed = line.trim(); - - // Neue Seite wenn nötig - if (doc.y > 750) doc.addPage(); - - if (trimmed.startsWith('# ')) { - doc.fontSize(22).font('Helvetica-Bold'); - writeText(doc, trimmed.slice(2).trim(), { align: 'center' }); - doc.moveDown(0.5); - doc.fontSize(12).text('Stundenerfassungssystem', { align: 'center' }); - doc.moveDown(1); - doc.font('Helvetica'); - i++; - continue; - } - - if (trimmed.startsWith('## ')) { - doc.fontSize(16).font('Helvetica-Bold'); - writeText(doc, trimmed.slice(3).trim()); - doc.moveDown(0.5); - doc.font('Helvetica').fontSize(10); - i++; - continue; - } - - if (trimmed.startsWith('### ')) { - doc.fontSize(13).font('Helvetica-Bold'); - writeText(doc, trimmed.slice(4).trim()); - doc.moveDown(0.4); - doc.font('Helvetica').fontSize(10); - i++; - continue; - } - - if (trimmed === '---') { - doc.moveDown(0.5); - i++; - continue; - } - - if (isTableRow(line)) { - const { rows, nextIndex } = parseTableRows(lines, i); - addTable(doc, rows); - i = nextIndex; - continue; - } - - if (trimmed.startsWith('- ') || trimmed.startsWith('* ')) { - addBullet(doc, trimmed); - i++; - continue; - } - - if (trimmed) { - addParagraph(doc, trimmed); - } else { - doc.moveDown(0.3); - } - i++; - } - - doc.end(); - stream.on('finish', () => { - console.log('PDF erstellt: ' + outPath); - }); - stream.on('error', (err) => { - console.error('Fehler beim Schreiben der PDF:', err); - process.exit(1); - }); -} - -generateDSGVOPdf(); diff --git a/public/images/icons/icon-120x120.png b/public/images/icons/icon-120x120.png new file mode 100644 index 0000000..480665b Binary files /dev/null and b/public/images/icons/icon-120x120.png differ diff --git a/public/images/icons/icon-152x152.png b/public/images/icons/icon-152x152.png new file mode 100644 index 0000000..6344387 Binary files /dev/null and b/public/images/icons/icon-152x152.png differ diff --git a/public/images/icons/icon-167x167.png b/public/images/icons/icon-167x167.png new file mode 100644 index 0000000..49ad599 Binary files /dev/null and b/public/images/icons/icon-167x167.png differ diff --git a/public/images/icons/icon-180x180.png b/public/images/icons/icon-180x180.png new file mode 100644 index 0000000..399db4f Binary files /dev/null and b/public/images/icons/icon-180x180.png differ diff --git a/public/images/icons/icon-192x192.png b/public/images/icons/icon-192x192.png new file mode 100644 index 0000000..f57ad2d Binary files /dev/null and b/public/images/icons/icon-192x192.png differ diff --git a/public/images/icons/icon-512x512.png b/public/images/icons/icon-512x512.png new file mode 100644 index 0000000..4f66a3f Binary files /dev/null and b/public/images/icons/icon-512x512.png differ diff --git a/public/js/dashboard.js b/public/js/dashboard.js index ef4697f..6d5e6bf 100644 --- a/public/js/dashboard.js +++ b/public/js/dashboard.js @@ -675,7 +675,9 @@ function renderWeek() { } // Sollstunden berechnen - const sollStunden = (userWochenstunden / userArbeitstage) * workdays; + // Die Sollstunden für eine Woche sind immer die Wochenstunden, unabhängig von den Arbeitstagen + // Die Arbeitstage pro Woche bestimmen nur die Stunden pro Tag (für Urlaub/Krank), nicht die Wochenstunden + const sollStunden = userWochenstunden || 0; // Urlaubsstunden berechnen (Urlaub zählt als normale Arbeitszeit) let vacationHours = 0; @@ -720,12 +722,14 @@ function updateOvertimeDisplay() { } // Sollstunden berechnen - const sollStunden = (userWochenstunden / userArbeitstage) * workdays; + // Die Sollstunden für eine Woche sind immer die Wochenstunden, unabhängig von den Arbeitstagen + // Die Arbeitstage pro Woche bestimmen nur die Stunden pro Tag (für Urlaub/Krank), nicht die Wochenstunden + const sollStunden = userWochenstunden || 0; // Gesamtstunden berechnen - direkt aus DOM-Elementen lesen für Echtzeit-Aktualisierung let totalHours = 0; let vacationHours = 0; - const fullDayHours = userWochenstunden ? (userWochenstunden / 5) : 8; + const fullDayHours = getFullDayHours(); // Verwende die Hilfsfunktion statt manueller Berechnung let fullDayOvertimeDays = 0; // Anzahl Tage mit 8 Überstunden (wie im Backend) const startDateObj = new Date(startDate); for (let i = 0; i < 7; i++) { @@ -929,6 +933,12 @@ function updateOvertimeDisplay() { overtimeHoursSpan.textContent = `${sign}${overtimeHours.toFixed(2)} h`; overtimeHoursSpan.style.color = overtimeHours >= 0 ? '#27ae60' : '#e74c3c'; } + + // Gesamtstunden-Anzeige aktualisieren + const totalHoursElement = document.getElementById('totalHours'); + if (totalHoursElement) { + totalHoursElement.textContent = totalHoursWithVacation.toFixed(2) + ' h'; + } } // Überstunden-Änderung verarbeiten diff --git a/public/manifest.json b/public/manifest.json new file mode 100644 index 0000000..5dd6ba7 --- /dev/null +++ b/public/manifest.json @@ -0,0 +1,20 @@ +{ + "name": "SDS Systemtechnik GmbH", + "short_name": "SDS", + "start_url": "/", + "display": "standalone", + "background_color": "#ffffff", + "theme_color": "#0a5ea8", + "icons": [ + { + "src": "/public/images/icons/icon-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/public/images/icons/icon-512x512.png", + "sizes": "512x512", + "type": "image/png" + } + ] + } \ No newline at end of file diff --git a/views/admin.ejs b/views/admin.ejs index b621f47..f85bb4f 100644 --- a/views/admin.ejs +++ b/views/admin.ejs @@ -6,6 +6,7 @@ Admin - Stundenerfassung + <%- include('header') %>