import { useState } from "react"; import { Link } from "react-router-dom"; import { Clock, Calendar, Briefcase, TrendingUp, Target, Edit2, Trash2 } from "lucide-react"; import { useTimeEntries } from "@/hooks/useTimeEntries"; import { useClientTargets } from "@/hooks/useClientTargets"; import { ProjectColorDot } from "@/components/ProjectColorDot"; import { StatCard } from "@/components/StatCard"; import { TimeEntryFormModal } from "@/components/TimeEntryFormModal"; import { ConfirmModal } from "@/components/ConfirmModal"; import { formatDate, formatTime, formatDurationFromDatesHoursMinutes, formatDurationHoursMinutes, calculateDuration, } from "@/utils/dateUtils"; import { startOfDay, endOfDay } from "date-fns"; import type { TimeEntry } from "@/types"; export function DashboardPage() { const today = new Date(); const { data: todayEntries } = useTimeEntries({ startDate: startOfDay(today).toISOString(), endDate: endOfDay(today).toISOString(), limit: 5, }); const { data: recentEntries, createTimeEntry, updateTimeEntry, deleteTimeEntry } = useTimeEntries({ limit: 10, }); const { targets } = useClientTargets(); const [isModalOpen, setIsModalOpen] = useState(false); const [editingEntry, setEditingEntry] = useState(null); const [confirmEntry, setConfirmEntry] = useState(null); const handleOpenModal = (entry: TimeEntry) => { setEditingEntry(entry); setIsModalOpen(true); }; const handleCloseModal = () => { setIsModalOpen(false); setEditingEntry(null); }; const handleDeleteConfirmed = async () => { if (!confirmEntry) return; try { await deleteTimeEntry.mutateAsync(confirmEntry.id); } catch (err) { alert(err instanceof Error ? err.message : 'Failed to delete'); } }; const totalTodaySeconds = todayEntries?.entries.reduce((total, entry) => { return total + calculateDuration(entry.startTime, entry.endTime, entry.breakMinutes); }, 0) || 0; const targetsWithData = targets?.filter(t => t.periods.length > 0) ?? []; return (
{/* Page Header */}

Dashboard

Overview of your time tracking activity

{/* Stats Grid */}
e.projectId), ).size.toString() || "0" } color="purple" />
{/* Overtime / Targets Widget */} {targetsWithData.length > 0 && (

Targets

{targetsWithData.map(target => { const balance = target.totalBalanceSeconds; const absBalance = Math.abs(balance); const isOver = balance > 0; const isEven = balance === 0; const currentPeriodTracked = formatDurationHoursMinutes(target.currentPeriodTrackedSeconds); const currentPeriodTarget = formatDurationHoursMinutes(target.currentPeriodTargetSeconds); const periodLabel = target.periodType === 'weekly' ? 'This week' : 'This month'; return (

{target.clientName}

{periodLabel}: {currentPeriodTracked} / {currentPeriodTarget}

{isEven ? '±0' : (isOver ? '+' : '−') + formatDurationHoursMinutes(absBalance)}

running balance

); })}

Manage targets →

)} {/* Recent Activity */}

Recent Activity

View all →
{recentEntries?.entries.length === 0 ? (

No time entries yet. Start tracking time using the timer below.

) : (
{recentEntries?.entries.slice(0, 5).map((entry) => ( ))}
Project Date Duration Actions
{entry.project.name}
{entry.project.client.name}
{formatDate(entry.startTime)}
{formatTime(entry.startTime)} – {formatTime(entry.endTime)}
{formatDurationFromDatesHoursMinutes(entry.startTime, entry.endTime, entry.breakMinutes)} {entry.breakMinutes > 0 && ( (−{entry.breakMinutes}m break) )}
)}
{isModalOpen && ( )} {confirmEntry && ( setConfirmEntry(null)} /> )}
); }