function isInteractiveTouchTarget(target) { if (!target || typeof target.closest !== 'function') { return false; } return Boolean(target.closest('a, button, input, select, textarea, summary, details, label, form')); } function attachSwipeNavigation(target, prevUrl, nextUrl) { if (!target || !prevUrl || !nextUrl) { return; } const minSwipeDistance = 60; const maxVerticalRatio = 1.25; const maxSwipeDuration = 900; let startX = 0; let startY = 0; let startAt = 0; let tracking = false; let navigating = false; target.addEventListener('touchstart', (event) => { if (event.touches.length !== 1 || isInteractiveTouchTarget(event.target)) { tracking = false; return; } const touch = event.touches[0]; startX = touch.clientX; startY = touch.clientY; startAt = Date.now(); tracking = true; }, { passive: true }); target.addEventListener('touchend', (event) => { if (!tracking || navigating || event.changedTouches.length !== 1) { tracking = false; return; } tracking = false; const touch = event.changedTouches[0]; const deltaX = touch.clientX - startX; const deltaY = touch.clientY - startY; const absX = Math.abs(deltaX); const absY = Math.abs(deltaY); const duration = Date.now() - startAt; if (duration > maxSwipeDuration || absX < minSwipeDistance || absX <= absY * maxVerticalRatio) { return; } navigating = true; if (deltaX < 0) { window.location.assign(nextUrl); } else { window.location.assign(prevUrl); } }, { passive: true }); } function initSwipeNavigation() { if (!window.matchMedia('(pointer: coarse)').matches && !('ontouchstart' in window)) { return; } document.querySelectorAll('[data-component="swipe-nav"]').forEach((node) => { attachSwipeNavigation(node, node.dataset.prevUrl, node.dataset.nextUrl); }); } function initWarningBanner() { const warningBanner = document.querySelector('[data-component="workhours-warning"]'); if (!warningBanner) { return; } const warningKey = warningBanner.getAttribute('data-workhours-warning') || ''; const storageKey = warningKey ? `workhours-warning-dismissed:${warningKey}` : ''; if (storageKey && window.localStorage.getItem(storageKey) === '1') { warningBanner.remove(); return; } const closeButton = warningBanner.querySelector('[data-action="warning-close"]'); if (closeButton) { closeButton.addEventListener('click', () => { warningBanner.remove(); if (storageKey) { window.localStorage.setItem(storageKey, '1'); } }); } } function initWeeklyTargetEditor() { const form = document.querySelector('.weekly-target-form'); const editor = document.querySelector('[data-component="weekly-target-editor"]'); const toggleButtons = document.querySelectorAll('.js-toggle-weekly-target-editor'); if (form && toggleButtons.length && editor) { toggleButtons.forEach((toggleButton) => { toggleButton.addEventListener('click', () => { editor.classList.toggle('is-hidden'); }); }); } if (!form) { return; } form.addEventListener('submit', (event) => { const scopeSelect = form.querySelector("select[name='scope']"); const hoursInput = form.querySelector("input[name='weekly_target_hours']"); if (!scopeSelect || !hoursInput) { return; } const scope = scopeSelect.value; const hours = hoursInput.value; let scopeText = ''; if (scope === 'current_week') { scopeText = 'Nur die aktuell ausgewählte Woche'; } else if (scope === 'all_weeks') { scopeText = 'Alle Wochen (Vergangenheit und Zukunft)'; } else if (scope === 'from_current_week') { scopeText = 'Aktuelle Woche und alle zukünftigen Wochen'; } if (!scopeText) { return; } const confirmed = window.confirm(`Wochen-Soll wirklich ändern?\nNeuer Wert: ${hours} h\nGültigkeit: ${scopeText}`); if (!confirmed) { event.preventDefault(); return; } if (editor) { editor.classList.add('is-hidden'); } }); } export function initDashboard() { initSwipeNavigation(); initWarningBanner(); initWeeklyTargetEditor(); }