diff --git a/backend/src/controllers/TimefixController.js b/backend/src/controllers/TimefixController.js index bff31cd..da799a4 100755 --- a/backend/src/controllers/TimefixController.js +++ b/backend/src/controllers/TimefixController.js @@ -30,7 +30,7 @@ class TimefixController { async getTodayTimefixes(req, res) { try { const userId = req.user.userId; - const timefixes = await timefixService.getTodayTimefixes(userId); + const timefixes = await timefixService.getTodayTimefixes(userId, req.query.date); res.json(timefixes); } catch (error) { console.error('Fehler beim Abrufen der Timefixes:', error); diff --git a/backend/src/repositories/WorklogRepository.js b/backend/src/repositories/WorklogRepository.js index ab31e56..0339a68 100755 --- a/backend/src/repositories/WorklogRepository.js +++ b/backend/src/repositories/WorklogRepository.js @@ -386,6 +386,7 @@ class WorklogRepository { if (startEntry) { pairs.push({ id: startEntry.id, + end_id: entry.id, start_time: startEntry.tstamp, end_time: entry.tstamp, start_state: startEntry.state, diff --git a/backend/src/services/TimeEntryService.js b/backend/src/services/TimeEntryService.js index ef37537..0b9643a 100755 --- a/backend/src/services/TimeEntryService.js +++ b/backend/src/services/TimeEntryService.js @@ -1387,11 +1387,11 @@ class TimeEntryService { let workEndUTC = new Date(pair.end_time); // Prüfe auf Timefix-Korrekturen - const endFixEntry = allEntries.find(e => { - const action = (typeof e.state === 'string' ? JSON.parse(e.state) : e.state)?.action || e.state; - return action === 'stop work' && e.relatedTo_id === pair.id; - }); - const endFix = endFixEntry ? timefixMap.get(endFixEntry.id)?.find(f => f.fix_type === 'stop work') : null; + // Direkt über die vom Repository gelieferte Stop-ID nachschlagen. + // So wird eine Datumsänderung des Stop-Eintrags zuverlässig erkannt. + const endFix = pair.end_id + ? timefixMap.get(pair.end_id)?.find(f => f.fix_type === 'stop work') + : null; // Verwende korrigierte Zeiten falls vorhanden const originalStartTime = workStartUTC; diff --git a/backend/src/services/TimefixService.js b/backend/src/services/TimefixService.js index 625a11e..4e00d96 100755 --- a/backend/src/services/TimefixService.js +++ b/backend/src/services/TimefixService.js @@ -131,21 +131,21 @@ class TimefixService { } /** - * Holt alle Zeitkorrekturen für den heutigen Tag + * Holt alle Zeitkorrekturen für ein korrigiertes Datum * @param {number} userId - Benutzer-ID + * @param {string} date - Optionales Datum im Format YYYY-MM-DD * @returns {Promise} Array von Zeitkorrekturen */ - async getTodayTimefixes(userId) { + async getTodayTimefixes(userId, date = null) { const sequelize = database.sequelize; - - // Berechne Start und Ende des heutigen Tages (lokale Zeit) - const now = new Date(); - const todayStart = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 0, 0, 0); - const todayEnd = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 23, 59, 59); - - // Hole alle Timefixes für heute mit Raw SQL - const todayStartStr = `${todayStart.getFullYear()}-${String(todayStart.getMonth() + 1).padStart(2, '0')}-${String(todayStart.getDate()).padStart(2, '0')} 00:00:00`; - const todayEndStr = `${todayEnd.getFullYear()}-${String(todayEnd.getMonth() + 1).padStart(2, '0')}-${String(todayEnd.getDate()).padStart(2, '0')} 23:59:59`; + const requestedDate = date || new Date().toISOString().split('T')[0]; + + if (!/^\d{4}-\d{2}-\d{2}$/.test(requestedDate)) { + throw new Error('Ungültiges Datum für Zeitkorrekturen'); + } + + const todayStartStr = `${requestedDate} 00:00:00`; + const todayEndStr = `${requestedDate} 23:59:59`; const timefixes = await sequelize.query( `SELECT id, user_id, worklog_id, fix_type, fix_date_time diff --git a/deploy.sh b/deploy.sh index 3b0d909..bd74118 100755 --- a/deploy.sh +++ b/deploy.sh @@ -184,6 +184,11 @@ copy_project_files() { print_info "Kopiere Frontend..." rsync -av --exclude 'node_modules' --exclude 'dist' --exclude '.env*' --delete "$CURRENT_DIR/frontend/" "$FRONTEND_DIR/" + + # package.json und package-lock.json gehören zum npm-Workspace im Root und + # gelten gemeinsam für Backend und Frontend. + print_info "Kopiere Workspace-Metadaten..." + rsync -av "$CURRENT_DIR/package.json" "$CURRENT_DIR/package-lock.json" "$PROJECT_DIR/" print_info "Kopiere Root-Dateien..." rsync -av --exclude 'node_modules' --exclude '.git' --exclude 'frontend' --exclude 'backend' \ @@ -480,34 +485,23 @@ do_update() { print_info "Kopiere Konfigurations-Dateien..." rsync -av "$CURRENT_DIR"/*.{sh,conf,md,service} "$PROJECT_DIR/" 2>/dev/null || true - # Backend aktualisieren - print_info "Aktualisiere Backend Dependencies..." - cd $BACKEND_DIR - - # Prüfe ob package-lock.json existiert - if [ ! -f "package-lock.json" ]; then - print_warning "package-lock.json fehlt! Verwende npm install statt npm ci" - npm install --no-audit --no-fund --loglevel=warn - else - npm config set fund false >/dev/null 2>&1 || true - npm config set audit false >/dev/null 2>&1 || true - npm config set progress false >/dev/null 2>&1 || true - npm config set loglevel warn >/dev/null 2>&1 || true - - # Backend braucht alle Dependencies (auch dev für Build-Tools) - if ! npm ci --no-audit --no-fund --loglevel=warn 2>&1; then - print_warning "npm ci fehlgeschlagen. Fallback auf npm install..." - rm -rf node_modules - npm install --no-audit --no-fund --loglevel=warn || { - print_error "npm install (Backend Update) fehlgeschlagen" - exit 1 - } - fi - fi + # Dependencies einmal im Workspace-Root installieren. Das Lockfile enthält + # beide Teilprojekte; getrennte package-lock.json-Dateien sind absichtlich + # nicht vorhanden. + print_info "Aktualisiere Workspace-Dependencies..." + cd "$PROJECT_DIR" + npm config set fund false >/dev/null 2>&1 || true + npm config set audit false >/dev/null 2>&1 || true + npm config set progress false >/dev/null 2>&1 || true + npm config set loglevel warn >/dev/null 2>&1 || true + npm ci --include=dev --no-audit --no-fund --loglevel=warn || { + print_error "npm ci (Workspace-Update) fehlgeschlagen" + exit 1 + } # Frontend aktualisieren print_info "Aktualisiere Frontend..." - cd $FRONTEND_DIR + cd "$FRONTEND_DIR" # .env.production prüfen/erstellen if [ ! -f ".env.production" ]; then @@ -518,27 +512,6 @@ VITE_API_URL=/api EOF fi - # Prüfe ob package-lock.json existiert - if [ ! -f "package-lock.json" ]; then - print_warning "package-lock.json fehlt! Verwende npm install statt npm ci" - npm install --no-audit --no-fund --loglevel=warn - else - npm config set fund false >/dev/null 2>&1 || true - npm config set audit false >/dev/null 2>&1 || true - npm config set progress false >/dev/null 2>&1 || true - npm config set loglevel warn >/dev/null 2>&1 || true - - # Frontend braucht dev-Dependencies für den Build (vite, etc.) - if ! npm ci --no-audit --no-fund --loglevel=warn 2>&1; then - print_warning "npm ci fehlgeschlagen. Fallback auf npm install..." - rm -rf node_modules - npm install --no-audit --no-fund --loglevel=warn || { - print_error "npm install (Frontend Update) fehlgeschlagen" - exit 1 - } - fi - fi - # Sauberer Build rm -rf dist/ npm run build @@ -824,4 +797,3 @@ case "${1:-help}" in esac exit 0 - diff --git a/frontend/src/views/Timefix.vue b/frontend/src/views/Timefix.vue index e294a9e..8ff9924 100755 --- a/frontend/src/views/Timefix.vue +++ b/frontend/src/views/Timefix.vue @@ -98,16 +98,22 @@ - +
-

Zeitkorrekturen von heute

+
+

Zeitkorrekturen

+ +
Lade Zeitkorrekturen...
- Keine Zeitkorrekturen für heute vorhanden. + Keine Zeitkorrekturen für dieses Datum vorhanden.
@@ -164,13 +170,15 @@ const authStore = useAuthStore() const timefixes = ref([]) const availableEntries = ref([]) const loading = ref(false) +const today = new Date().toISOString().split('T')[0] +const timefixDate = ref(today) const { showModal, modalConfig, alert, confirm, onConfirm, onCancel } = useModal() const form = ref({ - originalDate: new Date().toISOString().split('T')[0], + originalDate: today, worklogId: '', - newDate: new Date().toISOString().split('T')[0], + newDate: today, newTime: '', newAction: '' }) @@ -223,11 +231,11 @@ function onEntrySelected() { } } -// Lade alle Zeitkorrekturen für heute +// Lade alle Zeitkorrekturen für das ausgewählte korrigierte Datum async function loadTimefixes() { try { loading.value = true - const response = await fetch(`${API_URL}/timefix`, { + const response = await fetch(`${API_URL}/timefix?date=${encodeURIComponent(timefixDate.value)}`, { headers: authStore.getAuthHeaders() }) @@ -251,6 +259,7 @@ async function createTimefix() { try { loading.value = true + const correctedDate = form.value.newDate const response = await fetch(`${API_URL}/timefix`, { method: 'POST', headers: { @@ -271,6 +280,7 @@ async function createTimefix() { } resetForm() + timefixDate.value = correctedDate await Promise.all([ loadTimefixes(), loadWorklogEntries() // Lade auch die Dropdown-Liste neu @@ -339,9 +349,9 @@ function formatAction(action) { // Formular zurücksetzen function resetForm() { form.value = { - originalDate: new Date().toISOString().split('T')[0], + originalDate: today, worklogId: '', - newDate: new Date().toISOString().split('T')[0], + newDate: today, newTime: '', newAction: '' } @@ -464,6 +474,24 @@ input:disabled, select:disabled { } /* Tabelle */ +.timefix-list-header { + display: flex; + justify-content: space-between; + align-items: end; + gap: 16px; + margin-bottom: 20px; +} + +.timefix-list-header h2 { + margin-bottom: 0; +} + +.timefix-list-header label { + display: flex; + flex-direction: column; + margin-bottom: 0; +} + .timefix-table { width: 100%; border-collapse: collapse; @@ -515,4 +543,3 @@ input:disabled, select:disabled { color: #7f8c8d; } - diff --git a/package-lock.json b/package-lock.json index e5475d5..ec8f6db 100755 --- a/package-lock.json +++ b/package-lock.json @@ -18,7 +18,7 @@ }, "backend": { "name": "timeclock-backend", - "version": "3.0.0", + "version": "3.0.1", "license": "ISC", "dependencies": { "bcrypt": "^5.1.1", @@ -29,10 +29,10 @@ "express": "^4.18.2", "express-session": "^1.18.0", "helmet": "^7.1.0", - "jsonwebtoken": "^9.0.3", + "jsonwebtoken": "^9.0.2", "morgan": "^1.10.0", "mysql2": "^3.6.5", - "nodemailer": "^7.0.11", + "nodemailer": "^7.0.9", "passport": "^0.7.0", "passport-google-oauth20": "^2.0.0", "sequelize": "^6.37.7"