"use client"; import { useEffect, useState, useCallback } from "react"; import { useRouter } from "next/navigation"; import { useAuth } from "@/lib/auth"; import { getTasks, getGroups, Task, Group } from "@/lib/api"; import Link from "next/link"; type WidgetType = "current_tasks" | "category_time" | "today_progress" | "next_task" | "motivace" | "calendar_mini"; const DEFAULT_WIDGETS: WidgetType[] = ["current_tasks", "category_time", "today_progress"]; const WIDGET_LABELS: Record = { current_tasks: "Aktualni ukoly", category_time: "Aktivni kategorie", today_progress: "Dnesni pokrok", next_task: "Pristi ukol", motivace: "Motivace", calendar_mini: "Mini kalendar", }; const MOTIVACE_LIST = [ "Kazdy ukol, ktery dokoncis, te priblizuje k cili.", "Male kroky vedou k velkym vysledkum.", "Dnes je skvely den na splneni ukolu.", "Soustred se na to, co muzes ovlivnit.", "Tvoje prace ma smysl. Pokracuj!", "Disciplina premaha talent, kdyz talent nema disciplinu.", "Nejlepsi cas zacit byl vcera. Druhy nejlepsi je ted.", ]; function getActiveGroup(groups: Group[]): Group | null { const now = new Date(); const currentDay = now.getDay(); const pad = (n: number) => String(n).padStart(2, "0"); const currentTime = `${pad(now.getHours())}:${pad(now.getMinutes())}`; for (const group of groups) { for (const tz of group.time_zones || []) { if (tz.days?.length && !tz.days.includes(currentDay)) continue; if (tz.from && tz.to && tz.from <= currentTime && currentTime <= tz.to) return group; } } return groups[0] || null; } function getTodayProgress(tasks: Task[]) { const today = new Date().toISOString().slice(0, 10); const all = tasks.filter(t => t.due_at?.startsWith(today) || t.scheduled_at?.startsWith(today) || (t.status !== "cancelled" && t.created_at?.startsWith(today)) ); const done = all.filter(t => t.status === "done").length; return { done, total: all.length }; } function getNextTask(tasks: Task[]): Task | null { const now = new Date().toISOString(); return tasks .filter(t => t.status !== "done" && t.status !== "cancelled" && t.due_at && t.due_at > now) .sort((a, b) => (a.due_at! > b.due_at! ? 1 : -1))[0] || null; } export default function DashboardPage() { const { token } = useAuth(); const router = useRouter(); const [tasks, setTasks] = useState([]); const [groups, setGroups] = useState([]); const [loading, setLoading] = useState(true); const [enabledWidgets, setEnabledWidgets] = useState(DEFAULT_WIDGETS); const [motivace] = useState(() => MOTIVACE_LIST[Math.floor(Math.random() * MOTIVACE_LIST.length)]); const [now, setNow] = useState(new Date()); useEffect(() => { if (!token) { router.replace("/login"); return; } try { const stored = localStorage.getItem("widget_config"); if (stored) { const cfg = JSON.parse(stored); if (Array.isArray(cfg.enabled) && cfg.enabled.length > 0) { setEnabledWidgets(cfg.enabled); } } } catch { /* ignore */ } }, [token, router]); const loadData = useCallback(async () => { if (!token) return; try { const [tasksRes, groupsRes] = await Promise.all([getTasks(token), getGroups(token)]); setTasks(tasksRes.data || []); setGroups(groupsRes.data || []); } catch { /* ignore */ } setLoading(false); }, [token]); useEffect(() => { loadData(); }, [loadData]); // Refresh every 2 minutes useEffect(() => { const i = setInterval(loadData, 2 * 60 * 1000); return () => clearInterval(i); }, [loadData]); // Update clock every minute useEffect(() => { const t = setInterval(() => setNow(new Date()), 60000); return () => clearInterval(t); }, []); if (!token) return null; const activeGroup = getActiveGroup(groups); const activeTasks = tasks.filter(t => t.status === "pending" || t.status === "in_progress"); const progress = getTodayProgress(tasks); const nextTask = getNextTask(tasks); const acColor = activeGroup?.color || "#4F46E5"; const pad = (n: number) => String(n).padStart(2, "0"); const timeStr = `${pad(now.getHours())}:${pad(now.getMinutes())}`; function renderWidget(wType: WidgetType) { switch (wType) { case "current_tasks": return (
Aktualni ukoly Zobrazit vse →
{loading ? (
Nacitam...
) : activeTasks.length === 0 ? (
Zadne aktivni ukoly
) : (
{activeTasks.slice(0, 5).map(task => (
{task.title} {task.group_icon && {task.group_icon}}
))}
)}
); case "category_time": if (!activeGroup) return null; return (
Aktivni kategorie
{activeGroup.icon || "\uD83D\uDCC1"}
{activeGroup.display_name || activeGroup.name}
{activeGroup.time_zones?.[0] && (
{activeGroup.time_zones[0].from} \u2013 {activeGroup.time_zones[0].to}
)}
); case "today_progress": return (
Dnesni pokrok
{progress.done} / {progress.total} ukolu
{progress.total > 0 && (
)} {progress.total === 0 && (
Zadne ukoly na dnes
)}
); case "next_task": return (
Pristi ukol
{nextTask ? (
{nextTask.title}
{nextTask.due_at && (
{new Date(nextTask.due_at).toLocaleString("cs-CZ", { day: "numeric", month: "short", hour: "2-digit", minute: "2-digit" })}
)}
) : (
Zadne nadchazejici ukoly
)}
); case "motivace": return (
Motivace
{motivace}
); case "calendar_mini": { const DAYS = ["Po", "Ut", "St", "Ct", "Pa", "So", "Ne"]; return (
Tento tyden
{Array.from({ length: 7 }, (_, i) => { const d = new Date(now); const dow = (d.getDay() + 6) % 7; // Mon=0 d.setDate(d.getDate() + (i - dow)); const dateStr = d.toISOString().slice(0, 10); const isToday = dateStr === now.toISOString().slice(0, 10); const taskCount = tasks.filter(t => t.due_at?.startsWith(dateStr) || t.scheduled_at?.startsWith(dateStr)).length; return (
{DAYS[i]}
0 ? "bg-blue-600/10 dark:text-[#F0F0F5] text-gray-800" : "border border-gray-200 dark:border-[#2A2A3A] dark:text-[#F0F0F5] text-gray-800" }`} > {d.getDate()}
{taskCount > 0 &&
}
); })}
); } } } return (
{/* Header with time */}
{timeStr}
{now.toLocaleDateString("cs-CZ", { weekday: "long", day: "numeric", month: "long" })}
{/* Widgets */}
{enabledWidgets.map(wType => renderWidget(wType))}
); }