// assets/scripts/calendar.js // Strings aus window.TT.i18n – keine hardcodierten deutschen Texte mehr function t(key) { return window.TT?.i18n?.[key] ?? key; } class WeekCalendar { constructor() { this.nav = document.querySelector('.week-nav'); this.daysContainer = document.querySelector('.week-nav__days'); this.calBtn = document.querySelector('.week-nav__cal'); this.prevBtn = document.querySelector('.week-nav__arrow--prev'); this.nextBtn = document.querySelector('.week-nav__arrow--next'); this.header = document.querySelector('.tt-header'); const raw = this.nav?.dataset.activeDate; this.activeDate = raw ? new Date(raw + 'T00:00:00') : new Date(); this.today = new Date(); this.today.setHours(0, 0, 0, 0); this.monthOpen = false; this.monthDate = new Date(this.activeDate); this.monthEl = null; if (!this.nav) return; this.init(); } get months() { return t('months') || []; } get monthsShort() { return t('monthsShort') || []; } get weekdays() { return t('weekdays') || []; } get weekdaysShort() { return t('weekdaysShort') || []; } init() { this.prevBtn?.addEventListener('click', e => { e.preventDefault(); this.navigateWeek(-1); }); this.nextBtn?.addEventListener('click', e => { e.preventDefault(); this.navigateWeek(1); }); this.calBtn?.addEventListener('click', e => { e.preventDefault(); this.toggleMonth(); }); this.daysContainer?.addEventListener('click', e => { const dayEl = e.target.closest('.week-nav__day'); if (!dayEl) return; e.preventDefault(); const dateStr = dayEl.dataset.date; if (dateStr) this.goToDate(new Date(dateStr + 'T00:00:00')); }); } // ── Wochen-Navigation ───────────────────────────────────────────────────── navigateWeek(direction) { const slideOut = direction > 0 ? 'slide-out-left' : 'slide-out-right'; const slideIn = direction > 0 ? 'slide-in-right' : 'slide-in-left'; this.daysContainer.classList.add(slideOut); setTimeout(() => { const newDate = new Date(this.activeDate); newDate.setDate(newDate.getDate() + direction * 7); this.activeDate = newDate; this.renderWeekDays(); this.updateHeaderMeta(); this.daysContainer.classList.remove(slideOut); this.daysContainer.classList.add(slideIn); requestAnimationFrame(() => requestAnimationFrame(() => { this.daysContainer.classList.remove(slideIn); })); window.history.pushState({}, '', `/week/${this.formatDate(this.getMonday(this.activeDate))}`); window.entryManager?.loadEntriesForDate(this.formatDate(this.activeDate)); }, 180); } renderWeekDays() { const monday = this.getMonday(this.activeDate); this.daysContainer.innerHTML = ''; for (let i = 0; i < 7; i++) { const d = new Date(monday); d.setDate(d.getDate() + i); const isActive = this.isSameDay(d, this.activeDate); const isToday = this.isSameDay(d, this.today); // Führungsnull: padStart(2, '0') const dayNum = String(d.getDate()).padStart(2, '0'); const monthShort = this.monthsShort[d.getMonth()] ?? ''; const a = document.createElement('a'); a.href = `/week/${this.formatDate(d)}`; a.className = 'week-nav__day' + (isActive ? ' week-nav__day--active' : '') + (isToday ? ' week-nav__day--today' : ''); a.dataset.date = this.formatDate(d); a.innerHTML = ` ${this.weekdaysShort[i] ?? ''} ${dayNum}. ${monthShort} `; this.daysContainer.appendChild(a); } } goToDate(date) { this.activeDate = date; this.renderWeekDays(); this.updateHeaderMeta(); window.history.pushState({}, '', `/week/${this.formatDate(date)}`); window.entryManager?.loadEntriesForDate(this.formatDate(date)); if (this.monthOpen) this.closeMonth(); } updateHeaderMeta() { const dateEl = document.querySelector('.tt-header__date'); const kwEl = document.querySelector('.tt-header__kw'); if (dateEl) { const d = this.activeDate; const day = d.getDate(); const month = this.months[d.getMonth()] ?? ''; const tomorrow = new Date(this.today); tomorrow.setDate(this.today.getDate() + 1); const yesterday = new Date(this.today); yesterday.setDate(this.today.getDate() - 1); // JS getDay(): 0=So, 1=Mo...6=Sa → weekdays[0]=Montag, also index = getDay()-1, So=6 const jsDay = d.getDay(); const isoIdx = jsDay === 0 ? 6 : jsDay - 1; const weekday = this.weekdays[isoIdx] ?? ''; let prefix; if (this.isSameDay(d, this.today)) prefix = t('today'); else if (this.isSameDay(d, tomorrow)) prefix = t('tomorrow'); else if (this.isSameDay(d, yesterday)) prefix = t('yesterday'); else prefix = weekday; dateEl.textContent = `${prefix}, ${day}. ${month}`; document.title = `${prefix}, ${day}. ${month}`; } if (kwEl) kwEl.textContent = `${t('weekLabel')} ${this.getWeekNumber(this.activeDate)}`; } // ── Monats-Ansicht ──────────────────────────────────────────────────────── toggleMonth() { this.monthOpen ? this.closeMonth() : this.openMonth(); } openMonth() { this.monthOpen = true; this.monthDate = new Date(this.activeDate); this.monthEl = document.createElement('div'); this.monthEl.className = 'month-calendar month-calendar--hidden'; this.header.appendChild(this.monthEl); this.renderMonthGrid(); requestAnimationFrame(() => requestAnimationFrame(() => { this.monthEl.classList.remove('month-calendar--hidden'); this.monthEl.classList.add('month-calendar--visible'); })); this.calBtn.classList.add('week-nav__cal--active'); } closeMonth() { if (!this.monthEl) return; this.monthEl.classList.remove('month-calendar--visible'); this.monthEl.classList.add('month-calendar--hidden'); setTimeout(() => { this.monthEl?.remove(); this.monthEl = null; }, 280); this.monthOpen = false; this.calBtn.classList.remove('week-nav__cal--active'); } renderMonthGrid() { if (!this.monthEl) return; const year = this.monthDate.getFullYear(); const month = this.monthDate.getMonth(); const firstDay = new Date(year, month, 1); let startDow = firstDay.getDay(); startDow = startDow === 0 ? 6 : startDow - 1; const daysInMonth = new Date(year, month + 1, 0).getDate(); const daysInPrev = new Date(year, month, 0).getDate(); let html = `