import { Habit, SafeUser, User, Permission } from '@/lib/types' import { useAtom } from 'jotai' import { settingsAtom, pomodoroAtom, browserSettingsAtom, usersAtom } from '@/lib/atoms' import { getTodayInTimezone, isSameDate, t2d, d2t, getNow, parseRRule, d2s, getCompletionsForToday, isTaskOverdue, getFrequencyDisplayText } from '@/lib/utils' import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card' import { Button } from '@/components/ui/button' import { Coins, Edit, Trash2, Check, Undo2, MoreVertical, Timer, Archive, ArchiveRestore, Calendar } from 'lucide-react' import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu' import { useEffect, useState } from 'react' import { useHabits } from '@/hooks/useHabits' import { INITIAL_RECURRENCE_RULE, RECURRENCE_RULE_MAP } from '@/lib/constants' import { DateTime } from 'luxon' import { Avatar, AvatarFallback, AvatarImage } from './ui/avatar' import { useHelpers } from '@/lib/client-helpers' interface HabitItemProps { habit: Habit onEdit: () => void onDelete: () => void } const renderUserAvatars = (habit: Habit, currentUser: User | null, usersData: { users: User[] }) => { if (!habit.userIds || habit.userIds.length <= 1) return null; return (
{habit.userIds?.filter((u) => u !== currentUser?.id).map(userId => { const user = usersData.users.find(u => u.id === userId) if (!user) return null return ( {user.username[0]} ) })}
); }; export default function HabitItem({ habit, onEdit, onDelete }: HabitItemProps) { const { completeHabit, undoComplete, archiveHabit, unarchiveHabit, saveHabit } = useHabits() const [settings] = useAtom(settingsAtom) const [_, setPomo] = useAtom(pomodoroAtom) const completionsToday = getCompletionsForToday({ habit, timezone: settings.system.timezone }) const target = habit.targetCompletions || 1 const isCompletedToday = completionsToday >= target const [isHighlighted, setIsHighlighted] = useState(false) const [usersData] = useAtom(usersAtom) const { currentUser, hasPermission } = useHelpers() const canWrite = hasPermission('habit', 'write') const canInteract = hasPermission('habit', 'interact') const [browserSettings] = useAtom(browserSettingsAtom) const isTasksView = browserSettings.viewType === 'tasks' const isRecurRule = !isTasksView useEffect(() => { const params = new URLSearchParams(window.location.search) const highlightId = params.get('highlight') if (highlightId === habit.id) { setIsHighlighted(true) // Scroll the element into view after a short delay to ensure rendering setTimeout(() => { const element = document.getElementById(`habit-${habit.id}`) if (element) { element.scrollIntoView({ behavior: 'smooth', block: 'center' }) } }, 100) // Remove highlight after animation const timer = setTimeout(() => setIsHighlighted(false), 2000) return () => clearTimeout(timer) } }, [habit.id]) return (
{habit.name} {isTaskOverdue(habit, settings.system.timezone) && ( Overdue )} {renderUserAvatars(habit, currentUser as User, usersData)}
{habit.description && ( {habit.description} )}

When: {getFrequencyDisplayText(habit.frequency, isRecurRule, settings.system.timezone)}

{habit.coinReward} coins per completion
{completionsToday > 0 && !habit.archived && ( )}
{!habit.archived && ( )} {!habit.archived && ( { if (!canInteract) return setPomo((prev) => ({ ...prev, show: true, selectedHabitId: habit.id })) }}> Start Pomodoro )} {!habit.archived && ( <> {habit.isTask && ( { saveHabit({...habit, frequency: d2t({ dateTime: getNow({ timezone: settings.system.timezone })})}) }}> Move to Today )} archiveHabit(habit.id)}> Archive )} {habit.archived && ( unarchiveHabit(habit.id)}> Unarchive )} Edit Delete
) }