mirror of
https://github.com/ManInDark/HabitTrove.git
synced 2026-03-09 12:09:48 +01:00
added habit daily completion target (#26)
This commit is contained in:
@@ -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>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user