import { useMemo, useState } from 'preact/hooks'; import { animated, useSpring } from '@react-spring/web'; import { getStatusLabel } from '../../stores/taskStore'; const WEEKDAY_LABELS = ['Po', 'Ut', 'Sr', 'Če', 'Pe', 'Su', 'Ne']; const MONTH_NAMES = [ 'Siječanj', 'Veljača', 'Ožujak', 'Travanj', 'Svibanj', 'Lipanj', 'Srpanj', 'Kolovoz', 'Rujan', 'Listopad', 'Studeni', 'Prosinac', ]; const PANEL_WIDTH = 300; function toIsoDateKey(dateStr) { if (!dateStr) return null; const d = new Date(dateStr); if (Number.isNaN(d.getTime())) return null; return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`; } function formatDayLabel(year, month, day) { return `${String(day).padStart(2, '0')}.${String(month + 1).padStart(2, '0')}.${year}`; } export default function TaskCalendarWidget({ tasks = [], onTaskClick }) { const [open, setOpen] = useState(false); const [viewYear, setViewYear] = useState(() => new Date().getFullYear()); const [viewMonth, setViewMonth] = useState(() => new Date().getMonth()); const [selectedDay, setSelectedDay] = useState(null); const panelSpring = useSpring({ left: open ? 0 : -PANEL_WIDTH, config: { tension: 260, friction: 28 }, }); const buttonSpring = useSpring({ left: open ? PANEL_WIDTH : 0, config: { tension: 260, friction: 28 }, }); // Map: "YYYY-MM-DD" -> task[] const tasksByDate = useMemo(() => { const map = {}; for (const task of tasks) { const key = toIsoDateKey(task.scheduled_date); if (!key) continue; if (!map[key]) map[key] = []; map[key].push(task); } return map; }, [tasks]); // Calendar grid cells for current month const calendarDays = useMemo(() => { const firstDay = new Date(viewYear, viewMonth, 1); let startDow = firstDay.getDay(); // 0=Sun startDow = (startDow + 6) % 7; // shift to Mon=0 const daysInMonth = new Date(viewYear, viewMonth + 1, 0).getDate(); const cells = []; for (let i = 0; i < startDow; i++) cells.push(null); for (let d = 1; d <= daysInMonth; d++) cells.push(d); return cells; }, [viewYear, viewMonth]); const selectedDayKey = selectedDay ? `${viewYear}-${String(viewMonth + 1).padStart(2, '0')}-${String(selectedDay).padStart(2, '0')}` : null; const selectedDayTasks = selectedDayKey ? (tasksByDate[selectedDayKey] ?? []) : []; const today = new Date(); function prevMonth() { setSelectedDay(null); if (viewMonth === 0) { setViewYear((y) => y - 1); setViewMonth(11); } else setViewMonth((m) => m - 1); } function nextMonth() { setSelectedDay(null); if (viewMonth === 11) { setViewYear((y) => y + 1); setViewMonth(0); } else setViewMonth((m) => m + 1); } function handleDayClick(day) { setSelectedDay((prev) => (prev === day ? null : day)); } function handleTaskClick(task) { onTaskClick?.(task); setOpen(false); } const totalScheduled = Object.values(tasksByDate).flat().length; return ( <> {/* Backdrop */} {open && (
setOpen(false)} /> )} {/* Clipping wrapper — animates width 0→300, clips shadow+border completely */} `${Math.max(0, v + PANEL_WIDTH)}px`), pointerEvents: open ? 'auto' : 'none', }} className="fixed left-0 top-0 z-[46] h-screen overflow-hidden" > {/* Inner panel — always 300px, anchored to right edge of wrapper */}
{/* Header */}

📅 Kalendar zadataka

{/* Month navigation */}
{MONTH_NAMES[viewMonth]} {viewYear}
{/* Calendar grid */}
{WEEKDAY_LABELS.map((label) => (
{label}
))}
{calendarDays.map((day, idx) => { if (!day) return
; const key = `${viewYear}-${String(viewMonth + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}`; const hasTasks = Boolean(tasksByDate[key]?.length); const taskCount = tasksByDate[key]?.length ?? 0; const isToday = today.getFullYear() === viewYear && today.getMonth() === viewMonth && today.getDate() === day; const isSelected = selectedDay === day; return ( ); })}
{/* Divider */}
{/* Task list for selected day OR hint */}
{selectedDay ? ( <>

{formatDayLabel(viewYear, viewMonth, selectedDay)}

{selectedDayTasks.length === 0 ? (

Nema zadataka za odabrani datum.

) : (
    {selectedDayTasks.map((task) => (
  • ))}
)} ) : ( <>

Odaberite datum u kalendaru za prikaz zadataka.

{totalScheduled > 0 && (

{totalScheduled} zadatak/a s planiranim datumom.

)} {totalScheduled === 0 && (

Nijedan zadatak nema postavljen planirani datum.

)} )}
{/* Toggle tab button — slides right as panel opens */} `${v}px`) }} onClick={() => setOpen((o) => !o)} className="fixed top-1/2 z-[47] -translate-y-1/2 rounded-r-xl border border-l-0 border-border-hairline bg-canvas-elevated px-1.5 py-4 shadow-md hover:bg-indigo-50 focus:outline-none" aria-label={open ? 'Zatvori kalendar' : 'Otvori kalendar'} title="Kalendar zadataka" > 📅 Kalendar ); }