import { Badge } from '@/components/ui/badge' import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' import { ContextMenu, ContextMenuContent, ContextMenuTrigger } from "@/components/ui/context-menu" import { Progress } from '@/components/ui/progress' import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from "@/components/ui/tooltip" import { useHabits } from '@/hooks/useHabits' import { browserSettingsAtom, completedHabitsMapAtom, hasTasksAtom, pomodoroAtom, settingsAtom } from '@/lib/atoms' import { Habit, WishlistItemType } from '@/lib/types' import { cn, d2t, getNow, getTodayInTimezone, isHabitDue, isSameDate, isTaskOverdue, t2d } from '@/lib/utils' import { useAtom } from 'jotai' import { AlertTriangle, ArrowRight, ChevronDown, ChevronUp, Circle, CircleCheck, Coins, Pin, Plus } from 'lucide-react'; // Removed unused icons import Link from 'next/link' import { useState } from 'react' import AddEditHabitModal from './AddEditHabitModal' import CompletionCountBadge from './CompletionCountBadge' import ConfirmDialog from './ConfirmDialog' import { HabitContextMenuItems } from './HabitContextMenuItems' import Linkify from './linkify' import { Button } from './ui/button' interface UpcomingItemsProps { habits: Habit[] wishlistItems: WishlistItemType[] coinBalance: number } interface ItemSectionProps { title: string; items: Habit[]; emptyMessage: string; isTask: boolean; viewLink: string; addNewItem: () => void; } const ItemSection = ({ title, items, emptyMessage, isTask, viewLink, addNewItem, }: ItemSectionProps) => { const { completeHabit, undoComplete, saveHabit, deleteHabit, archiveHabit, habitFreqMap } = useHabits(); const [_, setPomo] = useAtom(pomodoroAtom); const [browserSettings, setBrowserSettings] = useAtom(browserSettingsAtom); const [settings] = useAtom(settingsAtom); const [completedHabitsMap] = useAtom(completedHabitsMapAtom); const today = getTodayInTimezone(settings.system.timezone); const currentTodayCompletions = completedHabitsMap.get(today) || []; const currentBadgeType = isTask ? 'tasks' : 'habits'; const currentExpanded = isTask ? browserSettings.expandedTasks : browserSettings.expandedHabits; const setCurrentExpanded = (value: boolean) => { setBrowserSettings(prev => ({ ...prev, [isTask ? 'expandedTasks' : 'expandedHabits']: value })); }; const [isConfirmDeleteDialogOpen, setIsConfirmDeleteDialogOpen] = useState(false); const [habitToDelete, setHabitToDelete] = useState(null); const [habitToEdit, setHabitToEdit] = useState(null); const handleDeleteClick = (habit: Habit) => { setHabitToDelete(habit); setIsConfirmDeleteDialogOpen(true); }; const confirmDelete = async () => { if (habitToDelete) { await deleteHabit(habitToDelete.id); setHabitToDelete(null); setIsConfirmDeleteDialogOpen(false); } }; const handleEditClick = (habit: Habit) => { setHabitToEdit(habit); }; if (items.length === 0) { return (

{title}

{emptyMessage}
); } return (

{title}

    {items .sort((a, b) => { // First by pinned status if (a.pinned !== b.pinned) { return a.pinned ? -1 : 1; } // Then by completion status const aCompleted = currentTodayCompletions.includes(a); const bCompleted = currentTodayCompletions.includes(b); if (aCompleted !== bCompleted) { return aCompleted ? 1 : -1; } // Then by frequency (daily first) const aFreq = habitFreqMap.get(a.id) || 'daily'; const bFreq = habitFreqMap.get(b.id) || 'daily'; const freqOrder = ['daily', 'weekly', 'monthly', 'yearly']; if (freqOrder.indexOf(aFreq) !== freqOrder.indexOf(bFreq)) { return freqOrder.indexOf(aFreq) - freqOrder.indexOf(bFreq); } // Then by coin reward (higher first) if (a.coinReward !== b.coinReward) { return b.coinReward - a.coinReward; } // Finally by target completions (higher first) const aTarget = a.targetCompletions || 1; const bTarget = b.targetCompletions || 1; return bTarget - aTarget; }) .slice(0, currentExpanded ? undefined : 5) .map((habit) => { const completionsToday = habit.completions.filter(completion => isSameDate(t2d({ timestamp: completion, timezone: settings.system.timezone }), t2d({ timestamp: d2t({ dateTime: getNow({ timezone: settings.system.timezone }) }), timezone: settings.system.timezone })) ).length const target = habit.targetCompletions || 1 const isCompleted = completionsToday >= target || (isTask && habit.archived) return (
  • {habit.pinned && ( )} { const newViewType = isTask ? 'tasks' : 'habits'; if (browserSettings.viewType !== newViewType) { setBrowserSettings(prev => ({ ...prev, viewType: newViewType })); } }} > {isTask && isTaskOverdue(habit, settings.system.timezone) && !isCompleted && ( {/* The AlertTriangle itself doesn't need hover styles if the parent Link handles it */}

    Overdue

    )} {habit.name}
    handleEditClick(habit)} onDeleteRequest={() => handleDeleteClick(habit)} context="daily-overview" />
    {habit.targetCompletions && ( {completionsToday}/{target} )} {habitFreqMap.get(habit.id) !== 'daily' && ( {habitFreqMap.get(habit.id)} )} {habit.coinReward}
  • ) })}
{ const newViewType = isTask ? 'tasks' : 'habits'; if (browserSettings.viewType !== newViewType) { setBrowserSettings(prev => ({ ...prev, viewType: newViewType })); } }} > View
{habitToDelete && ( setIsConfirmDeleteDialogOpen(false)} onConfirm={confirmDelete} title={`Delete ${isTask ? 'Task' : 'Habit'}`} message={`Are you sure you want to delete "${habitToDelete.name}"? This action cannot be undone.`} confirmText="Delete" /> )} {habitToEdit && ( setHabitToEdit(null)} onSave={async (updatedHabit) => { await saveHabit({ ...habitToEdit, ...updatedHabit }); setHabitToEdit(null); }} habit={habitToEdit} isTask={habitToEdit.isTask || false} /> )}
); }; export default function DailyOverview({ habits, wishlistItems, coinBalance, }: UpcomingItemsProps) { const { completeHabit, undoComplete } = useHabits() const [settings] = useAtom(settingsAtom) const [completedHabitsMap] = useAtom(completedHabitsMapAtom) const [browserSettings, setBrowserSettings] = useAtom(browserSettingsAtom) const today = getTodayInTimezone(settings.system.timezone) const todayCompletions = completedHabitsMap.get(today) || [] const { saveHabit } = useHabits() const timezone = settings.system.timezone const todayDateObj = getNow({ timezone }) const dailyTasks = habits.filter(habit => habit.isTask && !habit.archived && (isHabitDue({ habit, timezone, date: todayDateObj }) || isTaskOverdue(habit, timezone)) ) const dailyHabits = habits.filter(habit => !habit.isTask && !habit.archived && isHabitDue({ habit, timezone, date: todayDateObj }) ) // Get all wishlist items sorted by redeemable status (non-redeemable first) then by coin cost // Filter out archived wishlist items const sortedWishlistItems = wishlistItems .filter(item => !item.archived) .sort((a, b) => { const aRedeemable = a.coinCost <= coinBalance const bRedeemable = b.coinCost <= coinBalance // Non-redeemable items first if (aRedeemable !== bRedeemable) { return aRedeemable ? 1 : -1 } // Then sort by coin cost (lower cost first) return a.coinCost - b.coinCost }) const [hasTasks] = useAtom(hasTasksAtom) const [, setPomo] = useAtom(pomodoroAtom) const [modalConfig, setModalConfig] = useState<{ isOpen: boolean, isTask: boolean }>({ isOpen: false, isTask: false }); return ( <> Today's Overview
{/* Tasks Section */} {hasTasks && ( setModalConfig({ isOpen: true, isTask: true })} /> )} {/* Habits Section */} setModalConfig({ isOpen: true, isTask: false })} />

Wishlist Goals

{wishlistItems.filter(item => item.coinCost <= coinBalance).length}/{wishlistItems.length} Redeemable
{sortedWishlistItems.length === 0 ? (
No wishlist items yet. Add some goals to work towards!
) : ( <> {sortedWishlistItems .slice(0, browserSettings.expandedWishlist ? undefined : 5) .map((item) => { const isRedeemable = item.coinCost <= coinBalance return (
{item.name} {item.coinCost}

{isRedeemable ? "Ready to redeem!" : `${item.coinCost - coinBalance} coins to go` }

) })} )}
View
{modalConfig.isOpen && ( setModalConfig({ isOpen: false, isTask: false })} onSave={async (habit) => { await saveHabit({ ...habit, isTask: modalConfig.isTask }) setModalConfig({ isOpen: false, isTask: false }); }} habit={null} isTask={modalConfig.isTask} /> )} ) }