import { useEffect, useMemo, useState } from 'react' import { AnimatePresence, motion } from 'framer-motion' import { Plus, Pause, Play, Flame, Activity, TrendingUp } from 'lucide-react' import { useAppStore } from '../store/appStore' import { ExerciseCard } from '../components/ExerciseCard' import { ExerciseEditor } from '../components/ExerciseEditor' import { HistoryHeatmap } from '../components/HistoryHeatmap' import { Button } from '../components/ui/Button' import type { Exercise, HistoryEntry } from '@shared/types' import { formatCountdown } from '../lib/format' import { useT } from '../i18n' import { currentStreak, dailyReps, todayKey } from '../lib/history' export default function Dashboard(): JSX.Element { const state = useAppStore((s) => s.state) const ticks = useAppStore((s) => s.ticks) const [editorOpen, setEditorOpen] = useState(false) const [editing, setEditing] = useState(null) const { t, lang } = useT() const exercises = state?.exercises ?? [] const settings = state?.settings const gamesEnabled = Object.values(state?.gamesEnabled ?? {}).some(Boolean) // Local history mirror; reloaded whenever app-state changes. const [history, setHistory] = useState([]) useEffect(() => { void window.api.getHistory().then(setHistory) }, [state]) const todayDone = useMemo( () => dailyReps(history, exercises, todayKey()), [history, exercises] ) const streak = useMemo(() => currentStreak(history), [history]) const stats = useMemo(() => { const enabled = exercises.filter((e) => e.enabled) const next = enabled .map((e) => ({ id: e.id, ms: e.nextFireAt - Date.now() })) .sort((a, b) => a.ms - b.ms)[0] return { total: exercises.length, active: enabled.length, nextMs: next?.ms ?? Infinity, totalReps: enabled.reduce((s, e) => s + e.reps, 0) } }, [exercises, ticks]) const paused = !settings?.globalEnabled function openCreate(): void { setEditing(null) setEditorOpen(true) } function openEdit(ex: Exercise): void { setEditing(ex) setEditorOpen(true) } async function handleSave(draft: { name: string reps: number icon: string intervalMinutes: number enabled: boolean }): Promise { if (editing) await window.api.updateExercise(editing.id, draft) else await window.api.addExercise(draft) setEditorOpen(false) } async function togglePause(): Promise { if (!settings) return await window.api.updateSettings({ globalEnabled: !settings.globalEnabled }) } const today = new Date().toLocaleDateString( lang === 'en' ? 'en-US' : 'ru-RU', { weekday: 'long', day: 'numeric', month: 'long' } ) return (
{today}

{t('dashboard.title')}

} /> 0 ? 'warning' : 'muted'} label={t('dashboard.stat.streak')} value={`${streak}`} subvalue={t('dashboard.stat.streak.subtitle', { n: streak })} icon={} /> } /> } />
{history.length > 0 && (
)} {paused && (
{t('dashboard.paused.title')}
{t('dashboard.paused.hint')}
)}
{exercises.map((ex) => ( openEdit(ex)} onDelete={() => window.api.deleteExercise(ex.id)} onToggle={(v) => window.api.toggleExercise(ex.id, v)} onMarkDone={() => window.api.markDone(ex.id)} /> ))}
{exercises.length === 0 && (
{t('dashboard.empty.title')}

{t('dashboard.empty.hint')}

)} setEditorOpen(false)} onSave={handleSave} />
) } function HeroStat({ tone, label, value, subvalue, icon }: { tone: 'accent' | 'info' | 'success' | 'warning' | 'muted' label: string value: string subvalue?: string icon?: React.ReactNode }): JSX.Element { const toneBg = tone === 'accent' ? 'bg-accent' : tone === 'info' ? 'bg-info' : tone === 'success' ? 'bg-success' : tone === 'warning' ? 'bg-warning' : 'bg-text/40' return (
{icon}
{label}
{value}
{subvalue && (
{subvalue}
)}
) }