const MONTHS_SHORT = [ 'sty', 'lut', 'mar', 'kwi', 'maj', 'cze', 'lip', 'sie', 'wrz', 'paź', 'lis', 'gru', ]; const WEEKDAYS_SHORT = ['pn', 'wt', 'śr', 'cz', 'pt', 'so', 'nd']; function startOfDay(d) { const x = new Date(d); x.setHours(0, 0, 0, 0); return x; } function sameDay(a, b) { return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate(); } function addDays(d, n) { const x = new Date(d); x.setDate(x.getDate() + n); return startOfDay(x); } /** Poniedziałek jako pierwszy dzień tygodnia (PL) */ function startOfWeekMonday(d) { const date = startOfDay(d); const day = date.getDay(); const diff = day === 0 ? -6 : 1 - day; return addDays(date, diff); } function startOfMonth(d) { const x = new Date(d.getFullYear(), d.getMonth(), 1); return startOfDay(x); } function addMonths(d, n) { const x = new Date(d); x.setMonth(x.getMonth() + n); return startOfDay(x); } function addWeeks(d, n) { return addDays(d, n * 7); } function weekContains(weekStart, d) { const t = startOfDay(d).getTime(); const ws = weekStart.getTime(); const we = addDays(weekStart, 6).getTime(); return t >= ws && t <= we; } function sameMonth(a, b) { return a.getMonth() === b.getMonth() && a.getFullYear() === b.getFullYear(); } function isCalendarOnToday(mode, weekStart, monthAnchor, selected) { const today = startOfDay(new Date()); if (!sameDay(selected, today)) return false; if (mode === 'week') return weekContains(weekStart, today); return sameMonth(monthAnchor, today); } function syncTodayButton(mode, weekStart, monthAnchor, selected) { const btn = document.getElementById('cal-go-today'); if (!btn) return; const onToday = isCalendarOnToday(mode, weekStart, monthAnchor, selected); const active = 'h-6 shrink-0 inline-flex items-center justify-center gap-1 rounded-md border border-gray-200 bg-white px-2 text-[10px] font-semibold text-gray-700 shadow-sm hover:bg-gray-50 hover:text-gray-900 transition-colors'; const dim = 'h-6 shrink-0 inline-flex items-center justify-center gap-1 rounded-md border border-gray-100 bg-gray-50 px-2 text-[10px] font-semibold text-gray-400 shadow-none cursor-default transition-colors'; btn.className = onToday ? dim : active; btn.disabled = onToday; } export function getMealPlannerHTML() { return ` `; } function renderWeekGrid(weekStart, selected) { const grid = document.getElementById('calendar-week-grid'); if (!grid) return; const cells = []; for (let i = 0; i < 7; i++) { const day = addDays(weekStart, i); const isSel = selected && sameDay(day, selected); const isToday = sameDay(day, new Date()); cells.push(` `); } grid.innerHTML = cells.join(''); } function renderMonthGrid(monthAnchor, selected) { const grid = document.getElementById('calendar-month-grid'); if (!grid) return; const first = startOfMonth(monthAnchor); const startGrid = startOfWeekMonday(first); const cells = []; for (let i = 0; i < 42; i++) { const day = addDays(startGrid, i); const inMonth = day.getMonth() === first.getMonth(); const isSel = selected && sameDay(day, selected); const isToday = sameDay(day, new Date()); cells.push(` `); } grid.innerHTML = cells.join(''); } function updatePeriodLabel(mode, weekStart, monthAnchor) { const el = document.getElementById('cal-period-label'); if (!el) return; if (mode === 'week') { const end = addDays(weekStart, 6); const y = weekStart.getFullYear(); if (weekStart.getMonth() === end.getMonth()) { el.textContent = `${weekStart.getDate()}–${end.getDate()} ${MONTHS_SHORT[weekStart.getMonth()]} ${y}`; } else { el.textContent = `${weekStart.getDate()} ${MONTHS_SHORT[weekStart.getMonth()]} – ${end.getDate()} ${MONTHS_SHORT[end.getMonth()]} ${y}`; } } else { const m = monthAnchor.getMonth(); const y = monthAnchor.getFullYear(); const monthLong = [ 'Styczeń', 'Luty', 'Marzec', 'Kwiecień', 'Maj', 'Czerwiec', 'Lipiec', 'Sierpień', 'Wrzesień', 'Październik', 'Listopad', 'Grudzień', ][m]; el.textContent = `${monthLong} ${y}`; } } function syncModeToggle(mode) { const w = document.getElementById('planner-mode-week'); const m = document.getElementById('planner-mode-month'); const weekWrap = document.getElementById('calendar-week-wrap'); const monthWrap = document.getElementById('calendar-month-wrap'); const base = 'planner-cal-mode-btn w-7 h-6 flex items-center justify-center rounded-[0.3125rem] transition-colors'; const active = `${base} bg-white text-gray-900 shadow-sm`; const idle = `${base} text-gray-400 hover:text-gray-600`; if (w && m) { if (mode === 'week') { w.className = active; m.className = idle; } else { w.className = idle; m.className = active; } } if (weekWrap && monthWrap) { weekWrap.classList.toggle('hidden', mode !== 'week'); monthWrap.classList.toggle('hidden', mode !== 'month'); } } function bindDayClicks(container, state, rerender) { container?.addEventListener('click', (e) => { const btn = e.target.closest('[data-planner-day]'); if (!btn) return; const ts = Number(btn.getAttribute('data-planner-day')); state.selected = new Date(ts); rerender(); }); } export function setupMealPlanner() { const state = { mode: 'week', weekStart: startOfWeekMonday(new Date()), monthAnchor: startOfDay(new Date()), selected: startOfDay(new Date()), }; const weekGrid = document.getElementById('calendar-week-grid'); const monthGrid = document.getElementById('calendar-month-grid'); const rerender = () => { syncModeToggle(state.mode); updatePeriodLabel(state.mode, state.weekStart, state.monthAnchor); syncTodayButton(state.mode, state.weekStart, state.monthAnchor, state.selected); if (state.mode === 'week') { renderWeekGrid(state.weekStart, state.selected); } else { renderMonthGrid(state.monthAnchor, state.selected); } }; bindDayClicks(weekGrid?.parentElement, state, rerender); bindDayClicks(monthGrid?.parentElement, state, rerender); document.getElementById('planner-cal-mode')?.addEventListener('click', (e) => { const btn = e.target.closest('[data-cal-mode]'); if (!btn) return; const mode = btn.getAttribute('data-cal-mode'); if (mode !== 'week' && mode !== 'month') return; state.mode = mode; if (mode === 'week') { state.weekStart = startOfWeekMonday(state.selected); } else { state.monthAnchor = startOfMonth(state.selected); } rerender(); }); document.getElementById('cal-prev')?.addEventListener('click', () => { if (state.mode === 'week') { state.weekStart = addWeeks(state.weekStart, -1); if (!weekContains(state.weekStart, state.selected)) { state.selected = new Date(state.weekStart); } } else { state.monthAnchor = addMonths(state.monthAnchor, -1); if (!sameMonth(state.monthAnchor, state.selected)) { state.selected = startOfMonth(state.monthAnchor); } } rerender(); }); document.getElementById('cal-next')?.addEventListener('click', () => { if (state.mode === 'week') { state.weekStart = addWeeks(state.weekStart, 1); if (!weekContains(state.weekStart, state.selected)) { state.selected = new Date(state.weekStart); } } else { state.monthAnchor = addMonths(state.monthAnchor, 1); if (!sameMonth(state.monthAnchor, state.selected)) { state.selected = startOfMonth(state.monthAnchor); } } rerender(); }); document.getElementById('cal-go-today')?.addEventListener('click', () => { const today = startOfDay(new Date()); state.selected = today; state.weekStart = startOfWeekMonday(today); state.monthAnchor = startOfMonth(today); rerender(); }); rerender(); }