// Calendar.jsx — Monthly calendar with tasks (function () { const { useState } = React; const MONTHS = ['Styczeń','Luty','Marzec','Kwiecień','Maj','Czerwiec','Lipiec','Sierpień','Wrzesień','Październik','Listopad','Grudzień']; const DOW = ['Pn','Wt','Śr','Cz','Pt','Sb','Nd']; const priorityColor = { high:'#EF4444', medium:'#F59E0B', low:'#22C55E' }; window.Calendar = function Calendar({ tasks, setTasks }) { const [year, setYear] = useState(2026); const [month, setMonth] = useState(6); // lipiec const [selDay, setSelDay] = useState(21); const [gcalConnected, setGcalConnected] = useState(false); const [gcalSyncing, setGcalSyncing] = useState(false); const connectGcal = () => { if (gcalConnected) { setGcalConnected(false); return; } setGcalSyncing(true); setTimeout(() => { setGcalSyncing(false); setGcalConnected(true); }, 1200); }; const firstDow = (new Date(year, month, 1).getDay() + 6) % 7; // Mon=0 const daysInMon = new Date(year, month + 1, 0).getDate(); const today = { y:2026, m:6, d:21 }; const prevMonth = () => { if (month===0){setYear(y=>y-1);setMonth(11);}else setMonth(m=>m-1); }; const nextMonth = () => { if (month===11){setYear(y=>y+1);setMonth(0);}else setMonth(m=>m+1); }; const pad = n => String(n).padStart(2,'0'); const dateStr = d => `${year}-${pad(month+1)}-${pad(d)}`; const tasksForDay = d => tasks.filter(t => t.due === dateStr(d)); const selDateStr = dateStr(selDay); const selTasks = tasks.filter(t => t.due === selDateStr); const toggle = id => setTasks(p => p.map(t => t.id===id ? {...t,done:!t.done} : t)); const monthPrefix = `${year}-${pad(month+1)}`; const todayStr = `${today.y}-${pad(today.m+1)}-${pad(today.d)}`; const cells = [...Array(firstDow).fill(null), ...Array.from({length:daysInMon},(_,i)=>i+1)]; return (
{/* ── Calendar grid ── */}
{/* Google Calendar sync bar */}
{gcalSyncing ? 'Łączenie z Kalendarzem Google…' : gcalConnected ? 'Zsynchronizowano z Kalendarzem Google' : 'Kalendarz Google niepołączony'}
{/* Nav */}
{MONTHS[month]}
{year}
{/* DOW headers */}
{DOW.map(d => (
{d}
))}
{/* Day cells */}
{cells.map((day, i) => { if (!day) return
; const dt = tasksForDay(day); const isToday = day===today.d && month===today.m && year===today.y; const isSel = day===selDay; const hasTasks = dt.length > 0; return (
setSelDay(day)} style={{ aspectRatio:'1',borderRadius:12,cursor:'pointer', display:'flex',flexDirection:'column',alignItems:'center',justifyContent:'center',gap:4, background: isSel ? 'linear-gradient(135deg,rgba(124,92,255,.3),rgba(77,163,255,.2))' : isToday ? 'rgba(124,92,255,.12)' : 'transparent', border: isSel ? '1.5px solid rgba(124,92,255,.55)' : isToday ? '1.5px solid rgba(124,92,255,.28)' : '1.5px solid transparent', boxShadow: isSel ? '0 0 16px rgba(124,92,255,.2)' : 'none', transition:'all .15s', }} onMouseEnter={e=>{ if(!isSel) e.currentTarget.style.background='var(--bg-hover)'; }} onMouseLeave={e=>{ if(!isSel) e.currentTarget.style.background=isToday?'rgba(124,92,255,.12)':'transparent'; }} > {day} {hasTasks && (
{dt.slice(0,3).map((t,ti) => (
))}
)}
); })}
{/* ── Right panel ── */}
{/* Selected day header */}
{selDay} {MONTHS[month]} {year} {gcalConnected && Google ✓}
{selTasks.length===0 ? 'Brak zadań tego dnia' : `${selTasks.length} ${selTasks.length===1?'zadanie':selTasks.length<5?'zadania':'zadań'}`}
{selTasks.length===0 ? (
📅
Wolny dzień!
) : (
{selTasks.map(t => { const pc = priorityColor[t.priority]; return (
toggle(t.id)} style={{ display:'flex',alignItems:'flex-start',gap:10,padding:'10px 12px', borderRadius:10,cursor:'pointer',transition:'background .15s', background:'var(--bg1)',border:'1px solid var(--bdr)', borderLeft:`3px solid ${t.done?'var(--bdr)':pc}`, opacity:t.done?0.5:1, }} onMouseEnter={e=>e.currentTarget.style.background='var(--bg2)'} onMouseLeave={e=>e.currentTarget.style.background='var(--bg1)'} >
{t.done && }
{t.text}
); })}
)}
{/* Month summary */}
Podsumowanie miesiąca
{[ { label:'Zaplanowane', val: tasks.filter(t=>t.due.startsWith(monthPrefix)).length, color:'#7C5CFF' }, { label:'Ukończone', val: tasks.filter(t=>t.due.startsWith(monthPrefix)&&t.done).length, color:'#22C55E' }, { label:'Zaległe', val: tasks.filter(t=>t.due (
{s.label}
{s.val}
))}
{/* Upcoming this week */}
Najbliższe 7 dni
{tasks.filter(t => { const d = new Date(t.due); const today7 = new Date('2026-07-21'); today7.setDate(today7.getDate()+7); return d >= new Date('2026-07-21') && d <= today7 && !t.done; }).slice(0,4).map((t,i,a) => (
{t.text} {t.due.slice(8)}.{t.due.slice(5,7)}
))}
); }; })();