added habit daily completion target (#26)

This commit is contained in:
Doh
2025-01-05 16:56:04 -05:00
committed by GitHub
parent 86a517a859
commit aaa7e384bc
19 changed files with 577 additions and 132 deletions

View File

@@ -1,36 +1,37 @@
import { Circle, Coins, ArrowRight, CircleCheck, ChevronDown, ChevronUp } from 'lucide-react'
import { cn } from '@/lib/utils'
import Link from 'next/link'
import { useState } from 'react'
import { useAtom } from 'jotai'
import { settingsAtom } from '@/lib/atoms'
import { getTodayInTimezone } from '@/lib/utils'
import { getTodayInTimezone, isSameDate, t2d, d2t, getNow, getCompletedHabitsForDate, getCompletionsForDate } from '@/lib/utils'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { Badge } from '@/components/ui/badge'
import { Progress } from '@/components/ui/progress'
import { WishlistItemType } from '@/lib/types'
import { Habit } from '@/lib/types'
import Linkify from './linkify'
import { useHabits } from '@/hooks/useHabits'
interface UpcomingItemsProps {
habits: Habit[]
wishlistItems: WishlistItemType[]
coinBalance: number
onComplete: (habit: Habit) => void
onUndo: (habit: Habit) => void
}
export default function DailyOverview({
habits,
wishlistItems,
coinBalance,
onComplete,
onUndo
}: UpcomingItemsProps) {
const { completeHabit, undoComplete } = useHabits()
const [settings] = useAtom(settingsAtom)
const today = getTodayInTimezone(settings.system.timezone)
const todayCompletions = habits.filter(habit =>
habit.completions.includes(today)
)
const todayCompletions = getCompletedHabitsForDate({
habits,
date: getNow({ timezone: settings.system.timezone }),
timezone: settings.system.timezone
})
// Filter daily habits
const dailyHabits = habits.filter(habit => habit.frequency === 'daily')
@@ -54,7 +55,12 @@ export default function DailyOverview({
<div className="flex items-center justify-between mb-2">
<h3 className="font-semibold">Daily Habits</h3>
<Badge variant="secondary">
{todayCompletions.length}/{dailyHabits.length} Complete
{dailyHabits.reduce((sum, habit) => sum + getCompletionsForDate({
habit,
date: today,
timezone: settings.system.timezone
}), 0)}/
{dailyHabits.reduce((sum, habit) => sum + (habit.targetCompletions || 1), 0)} Completions
</Badge>
</div>
<ul className={`grid gap-2 transition-all duration-300 ease-in-out ${expandedHabits ? 'max-h-[500px] opacity-100' : 'max-h-[200px] opacity-100'} overflow-hidden`}>
@@ -66,7 +72,11 @@ export default function DailyOverview({
})
.slice(0, expandedHabits ? undefined : 3)
.map((habit) => {
const isCompleted = todayCompletions.includes(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
return (
<li
key={habit.id}
@@ -78,17 +88,30 @@ export default function DailyOverview({
onClick={(e) => {
e.preventDefault();
if (isCompleted) {
onUndo(habit);
undoComplete(habit);
} else {
onComplete(habit);
completeHabit(habit);
}
}}
className="hover:opacity-70 transition-opacity"
className="relative hover:opacity-70 transition-opacity w-4 h-4"
>
{isCompleted ? (
<CircleCheck className="h-4 w-4 text-green-500" />
) : (
<Circle className="h-4 w-4" />
<div className="relative h-4 w-4">
<Circle className="absolute h-4 w-4 text-muted-foreground" />
<div
className="absolute h-4 w-4 rounded-full overflow-hidden"
style={{
background: `conic-gradient(
currentColor ${(completionsToday / target) * 360}deg,
transparent ${(completionsToday / target) * 360}deg 360deg
)`,
mask: 'radial-gradient(transparent 50%, black 51%)',
WebkitMask: 'radial-gradient(transparent 50%, black 51%)'
}}
/>
</div>
)}
</button>
<span className={isCompleted ? 'line-through' : ''}>
@@ -97,9 +120,16 @@ export default function DailyOverview({
</Linkify>
</span>
</span>
<span className="flex items-center text-xs text-muted-foreground">
<Coins className="h-3 w-3 text-yellow-400 mr-1" />
{habit.coinReward}
<span className="flex items-center gap-2 text-xs text-muted-foreground">
{habit.targetCompletions && (
<span className="bg-secondary px-1.5 py-0.5 rounded-full">
{completionsToday}/{target}
</span>
)}
<span className="flex items-center">
<Coins className="h-3 w-3 text-yellow-400 mr-1" />
{habit.coinReward}
</span>
</span>
</li>
)