From 7847b71c4d44fe6da163e8438e2c4348955b48cd Mon Sep 17 00:00:00 2001 From: Avery Felts Date: Sun, 25 Jan 2026 12:30:09 -0700 Subject: [PATCH] Update quit smoking website --- prisma/schema.prisma | 3 + src/app/api/preferences/route.ts | 25 +++- src/components/Dashboard.tsx | 6 + src/components/HealthTimelineCard.tsx | 169 +++++++++++++++++--------- src/components/UsageCalendar.tsx | 26 +++- src/lib/storage.ts | 74 ----------- src/lib/tracker/NicotineTracker.ts | 77 ++++++++++++ src/lib/tracker/RecoveryTracker.ts | 33 +++++ src/lib/tracker/WeedTracker.ts | 69 +++++++++++ 9 files changed, 349 insertions(+), 133 deletions(-) create mode 100644 src/lib/tracker/NicotineTracker.ts create mode 100644 src/lib/tracker/RecoveryTracker.ts create mode 100644 src/lib/tracker/WeedTracker.ts diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 3160a02..01d3b74 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -19,6 +19,9 @@ model UserPreferences { dailyGoal Int? userName String? userAge Int? + religion String? + lastNicotineUsageTime String? + lastWeedUsageTime String? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt diff --git a/src/app/api/preferences/route.ts b/src/app/api/preferences/route.ts index 88bc976..7d592ee 100644 --- a/src/app/api/preferences/route.ts +++ b/src/app/api/preferences/route.ts @@ -33,6 +33,9 @@ export async function GET() { quitPlan: preferences.quitPlanJson ? JSON.parse(preferences.quitPlanJson) : null, userName: preferences.userName, userAge: preferences.userAge, + religion: preferences.religion, + lastNicotineUsageTime: preferences.lastNicotineUsageTime, + lastWeedUsageTime: preferences.lastWeedUsageTime, }); } catch (error) { console.error('Error fetching preferences:', error); @@ -48,7 +51,18 @@ export async function POST(request: NextRequest) { } const body = await request.json(); - const { substance, trackingStartDate, hasCompletedSetup, dailyGoal, quitPlan, userName, userAge } = body; + const { + substance, + trackingStartDate, + hasCompletedSetup, + dailyGoal, + quitPlan, + userName, + userAge, + religion, + lastNicotineUsageTime, + lastWeedUsageTime + } = body; const preferences = await prisma.userPreferences.upsert({ where: { userId: session.user.id }, @@ -60,6 +74,9 @@ export async function POST(request: NextRequest) { quitPlanJson: quitPlan ? JSON.stringify(quitPlan) : null, userName, userAge, + religion, + lastNicotineUsageTime, + lastWeedUsageTime, }, create: { userId: session.user.id, @@ -70,6 +87,9 @@ export async function POST(request: NextRequest) { quitPlanJson: quitPlan ? JSON.stringify(quitPlan) : null, userName, userAge, + religion, + lastNicotineUsageTime, + lastWeedUsageTime, }, }); @@ -81,6 +101,9 @@ export async function POST(request: NextRequest) { quitPlan: preferences.quitPlanJson ? JSON.parse(preferences.quitPlanJson) : null, userName: preferences.userName, userAge: preferences.userAge, + religion: preferences.religion, + lastNicotineUsageTime: preferences.lastNicotineUsageTime, + lastWeedUsageTime: preferences.lastWeedUsageTime, }); } catch (error) { console.error('Error saving preferences:', error); diff --git a/src/components/Dashboard.tsx b/src/components/Dashboard.tsx index 7e23ac6..9353671 100644 --- a/src/components/Dashboard.tsx +++ b/src/components/Dashboard.tsx @@ -64,6 +64,7 @@ export function Dashboard({ user }: DashboardProps) { setUsageData(usage); setAchievements(achvs); setSavingsConfig(savings); + console.log('[Dashboard] Loaded prefs:', prefs); setRefreshKey(prev => prev + 1); return { prefs, usage, achvs }; }, []); @@ -233,6 +234,11 @@ export function Dashboard({ user }: DashboardProps) { setPreferences(updatedPrefs); await savePreferencesAsync(updatedPrefs); }} + preferences={preferences} + onPreferencesUpdate={async (updatedPrefs) => { + await savePreferencesAsync(updatedPrefs); + setPreferences(updatedPrefs); + }} />
diff --git a/src/components/HealthTimelineCard.tsx b/src/components/HealthTimelineCard.tsx index d80b9d0..3146ae7 100644 --- a/src/components/HealthTimelineCard.tsx +++ b/src/components/HealthTimelineCard.tsx @@ -1,8 +1,8 @@ 'use client'; -import { useMemo, useState, useEffect } from 'react'; +import { useState, useEffect, useCallback } from 'react'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; -import { HEALTH_MILESTONES, getMinutesSinceQuit, UsageEntry, UserPreferences } from '@/lib/storage'; +import { HEALTH_MILESTONES, UsageEntry, UserPreferences } from '@/lib/storage'; import { useTheme } from '@/lib/theme-context'; import { Heart, @@ -14,13 +14,13 @@ import { Sparkles, HeartHandshake, CheckCircle2, - Clock, Cigarette, Leaf } from 'lucide-react'; interface HealthTimelineCardProps { usageData: UsageEntry[]; + preferences?: UserPreferences | null; } const iconMap: Record = { @@ -34,8 +34,60 @@ const iconMap: Record = { HeartHandshake, }; +// Simple, direct calculation of minutes since last usage +function calculateMinutesFree( + substance: 'nicotine' | 'weed', + usageData: UsageEntry[], + preferences: UserPreferences | null +): number { + const now = new Date(); + + // 1. Check for stored timestamp first (most accurate) + const lastUsageTime = substance === 'nicotine' + ? preferences?.lastNicotineUsageTime + : preferences?.lastWeedUsageTime; + + if (lastUsageTime) { + const lastTime = new Date(lastUsageTime); + const diffMs = now.getTime() - lastTime.getTime(); + return Math.max(0, diffMs / (1000 * 60)); + } + + // 2. Find last recorded usage from usage data + const substanceData = usageData + .filter(e => e.substance === substance && e.count > 0) + .sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()); + + if (substanceData.length > 0) { + const lastDateStr = substanceData[0].date; + const todayStr = now.toISOString().split('T')[0]; + + // If last usage was today but no timestamp, count from now (0 minutes) + if (lastDateStr === todayStr) { + return 0; + } + + // For past days, count from end of that day (23:59:59) + const lastDate = new Date(lastDateStr); + lastDate.setHours(23, 59, 59, 999); + const diffMs = now.getTime() - lastDate.getTime(); + return Math.max(0, diffMs / (1000 * 60)); + } + + // 3. No usage ever - count from tracking start date + if (preferences?.trackingStartDate) { + const startDate = new Date(preferences.trackingStartDate); + startDate.setHours(0, 0, 0, 0); + const diffMs = now.getTime() - startDate.getTime(); + return Math.max(0, diffMs / (1000 * 60)); + } + + return 0; +} + function formatDuration(minutes: number): string { - if (minutes < 60) return `${minutes} min`; + if (minutes < 1) return '< 1 min'; + if (minutes < 60) return `${Math.floor(minutes)} min`; if (minutes < 1440) return `${Math.floor(minutes / 60)} hrs`; if (minutes < 10080) return `${Math.floor(minutes / 1440)} days`; if (minutes < 43200) return `${Math.floor(minutes / 10080)} weeks`; @@ -51,55 +103,52 @@ function formatTimeRemaining(currentMinutes: number, targetMinutes: number): str interface TimelineColumnProps { substance: 'nicotine' | 'weed'; - minutesSinceQuit: number; + minutesFree: number; theme: 'light' | 'dark'; } -function TimelineColumn({ substance, minutesSinceQuit, theme }: TimelineColumnProps) { - const currentMilestoneIndex = useMemo(() => { - for (let i = HEALTH_MILESTONES.length - 1; i >= 0; i--) { - if (minutesSinceQuit >= HEALTH_MILESTONES[i].timeMinutes) { - return i; - } +function TimelineColumn({ substance, minutesFree, theme }: TimelineColumnProps) { + // Find current milestone + let currentMilestoneIndex = -1; + for (let i = HEALTH_MILESTONES.length - 1; i >= 0; i--) { + if (minutesFree >= HEALTH_MILESTONES[i].timeMinutes) { + currentMilestoneIndex = i; + break; } - return -1; - }, [minutesSinceQuit]); + } - const nextMilestone = useMemo(() => { - const nextIndex = currentMilestoneIndex + 1; - if (nextIndex < HEALTH_MILESTONES.length) { - return HEALTH_MILESTONES[nextIndex]; - } - return null; - }, [currentMilestoneIndex]); + // Find next milestone + const nextMilestoneIndex = currentMilestoneIndex + 1; + const nextMilestone = nextMilestoneIndex < HEALTH_MILESTONES.length + ? HEALTH_MILESTONES[nextMilestoneIndex] + : null; - const progressToNext = useMemo(() => { - if (!nextMilestone) return 100; - const prevMinutes = - currentMilestoneIndex >= 0 - ? HEALTH_MILESTONES[currentMilestoneIndex].timeMinutes - : 0; + // Calculate progress to next milestone + let progressToNext = 100; + if (nextMilestone) { + const prevMinutes = currentMilestoneIndex >= 0 + ? HEALTH_MILESTONES[currentMilestoneIndex].timeMinutes + : 0; const range = nextMilestone.timeMinutes - prevMinutes; - const progress = minutesSinceQuit - prevMinutes; - return Math.min(100, Math.max(0, (progress / range) * 100)); - }, [minutesSinceQuit, nextMilestone, currentMilestoneIndex]); + const progress = minutesFree - prevMinutes; + progressToNext = Math.min(100, Math.max(0, (progress / range) * 100)); + } const substanceLabel = substance === 'nicotine' ? 'Nicotine' : 'Marijuana'; const SubstanceIcon = substance === 'nicotine' ? Cigarette : Leaf; - const accentColor = substance === 'nicotine' ? 'red' : 'green'; const accentColorClass = substance === 'nicotine' ? 'text-red-500' : 'text-green-500'; const bgAccentClass = substance === 'nicotine' ? 'bg-red-500' : 'bg-green-500'; return (
- {/* Header */} + {/* Header with live timer */}
{substanceLabel} - - {formatDuration(Math.floor(minutesSinceQuit))} free + + {formatDuration(minutesFree)} free
@@ -110,7 +159,7 @@ function TimelineColumn({ substance, minutesSinceQuit, theme }: TimelineColumnPr
Next Up - {formatTimeRemaining(Math.floor(minutesSinceQuit), nextMilestone.timeMinutes)} + {formatTimeRemaining(minutesFree, nextMilestone.timeMinutes)}
@@ -127,7 +176,7 @@ function TimelineColumn({ substance, minutesSinceQuit, theme }: TimelineColumnPr {/* Timeline Items */} {HEALTH_MILESTONES.map((milestone, index) => { - const isAchieved = minutesSinceQuit >= milestone.timeMinutes; + const isAchieved = minutesFree >= milestone.timeMinutes; const isCurrent = index === currentMilestoneIndex; const Icon = iconMap[milestone.icon] || Heart; @@ -135,15 +184,15 @@ function TimelineColumn({ substance, minutesSinceQuit, theme }: TimelineColumnPr
{/* Icon */}
{isAchieved ? : } @@ -151,11 +200,9 @@ function TimelineColumn({ substance, minutesSinceQuit, theme }: TimelineColumnPr {/* Content */}
-
-

- {milestone.title} -

-
+

+ {milestone.title} +

{milestone.description}

@@ -168,20 +215,30 @@ function TimelineColumn({ substance, minutesSinceQuit, theme }: TimelineColumnPr ); } -export function HealthTimelineCard({ usageData, preferences }: HealthTimelineCardProps & { preferences?: UserPreferences | null }) { +export function HealthTimelineCard({ usageData, preferences }: HealthTimelineCardProps) { const { theme } = useTheme(); - const [now, setNow] = useState(Date.now()); + // State for live timer values + const [nicotineMinutes, setNicotineMinutes] = useState(0); + const [weedMinutes, setWeedMinutes] = useState(0); + + // Function to recalculate both timers + const updateTimers = useCallback(() => { + const prefs = preferences || null; + setNicotineMinutes(calculateMinutesFree('nicotine', usageData, prefs)); + setWeedMinutes(calculateMinutesFree('weed', usageData, prefs)); + }, [usageData, preferences]); + + // Initial calculation and start interval useEffect(() => { - const interval = setInterval(() => { - setNow(Date.now()); - }, 1000); // Update every second + // Calculate immediately + updateTimers(); + + // Update every second + const interval = setInterval(updateTimers, 1000); return () => clearInterval(interval); - }, []); - - const nicotineMinutes = useMemo(() => getMinutesSinceQuit(usageData, 'nicotine', true, preferences), [usageData, preferences, now]); - const weedMinutes = useMemo(() => getMinutesSinceQuit(usageData, 'weed', true, preferences), [usageData, preferences, now]); + }, [updateTimers]); const cardBackground = theme === 'light' @@ -207,8 +264,8 @@ export function HealthTimelineCard({ usageData, preferences }: HealthTimelineCar
- - + +
diff --git a/src/components/UsageCalendar.tsx b/src/components/UsageCalendar.tsx index 3eb19bc..7f72eaf 100644 --- a/src/components/UsageCalendar.tsx +++ b/src/components/UsageCalendar.tsx @@ -12,7 +12,7 @@ import { import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; -import { UsageEntry, setUsageForDateAsync, clearDayDataAsync } from '@/lib/storage'; +import { UsageEntry, UserPreferences, setUsageForDateAsync, clearDayDataAsync } from '@/lib/storage'; import { ChevronLeftIcon, ChevronRightIcon, Cigarette, Leaf, Sparkles } from 'lucide-react'; import { useTheme } from '@/lib/theme-context'; import { DailyInspirationCard } from './DailyInspirationCard'; @@ -25,9 +25,11 @@ interface UsageCalendarProps { userId: string; religion?: 'christian' | 'muslim' | 'jewish' | 'secular' | null; onReligionUpdate?: (religion: 'christian' | 'muslim' | 'jewish' | 'secular') => void; + preferences?: UserPreferences | null; + onPreferencesUpdate?: (prefs: UserPreferences) => Promise; } -export function UsageCalendar({ usageData, onDataUpdate, religion, onReligionUpdate }: UsageCalendarProps) { +export function UsageCalendar({ usageData, onDataUpdate, religion, onReligionUpdate, preferences, onPreferencesUpdate }: UsageCalendarProps) { const [selectedDate, setSelectedDate] = useState(undefined); const [editNicotineCount, setEditNicotineCount] = useState(''); const [editWeedCount, setEditWeedCount] = useState(''); @@ -59,6 +61,7 @@ export function UsageCalendar({ usageData, onDataUpdate, religion, onReligionUpd const handleSave = async () => { if (selectedDate) { const dateStr = selectedDate.toISOString().split('T')[0]; + const todayStr = new Date().toISOString().split('T')[0]; const newNicotineCount = parseInt(editNicotineCount, 10) || 0; const newWeedCount = parseInt(editWeedCount, 10) || 0; @@ -66,6 +69,25 @@ export function UsageCalendar({ usageData, onDataUpdate, religion, onReligionUpd setUsageForDateAsync(dateStr, newNicotineCount, 'nicotine'), setUsageForDateAsync(dateStr, newWeedCount, 'weed'), ]); + + // Update last usage time preferences if editing today's usage and count > 0 + if (dateStr === todayStr && preferences && onPreferencesUpdate) { + const now = new Date().toISOString(); + const updatedPrefs = { ...preferences }; + + if (newNicotineCount > 0) { + updatedPrefs.lastNicotineUsageTime = now; + } + if (newWeedCount > 0) { + updatedPrefs.lastWeedUsageTime = now; + } + + // Only update if we changed something + if (newNicotineCount > 0 || newWeedCount > 0) { + await onPreferencesUpdate(updatedPrefs); + } + } + onDataUpdate(); } setIsEditing(false); diff --git a/src/lib/storage.ts b/src/lib/storage.ts index 9dfb58d..8f0ae11 100644 --- a/src/lib/storage.ts +++ b/src/lib/storage.ts @@ -395,81 +395,7 @@ export function calculateTotalSaved( return Math.max(0, expectedSpend - actualSpend); } -export function getMinutesSinceQuit( - usageData: UsageEntry[], - substance: 'nicotine' | 'weed', - precise: boolean = false, - preferences?: UserPreferences | null -): number { - // Try to use precise timestamp from preferences first - if (preferences) { - const lastUsageTimeStr = substance === 'nicotine' - ? preferences.lastNicotineUsageTime - : preferences.lastWeedUsageTime; - if (lastUsageTimeStr) { - const now = new Date(); - const lastUsageTime = new Date(lastUsageTimeStr); - const diffMs = now.getTime() - lastUsageTime.getTime(); - const minutes = Math.max(0, diffMs / (1000 * 60)); - - // Sanity check: if the timestamp is OLDER than the last recorded date in usageData, - // it might mean the user manually added a later date in the calendar without a timestamp. - // In that case, we should fall back to the date-based logic. - - const substanceData = usageData - .filter((e) => e.substance === substance && e.count > 0) - .sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()); - - if (substanceData.length > 0) { - const lastDateStr = substanceData[0].date; - const lastDate = new Date(lastDateStr); - // Set lastDate to end of day to compare with timestamp - lastDate.setHours(23, 59, 59, 999); - - // If the timestamp is essentially on the same day or later than the last recorded date, rely on the timestamp - // (We allow the timestamp to be earlier in the same day, that's the whole point) - const lastDateStart = new Date(lastDateStr); - lastDateStart.setHours(0, 0, 0, 0); - - if (lastUsageTime >= lastDateStart) { - return precise ? minutes : Math.floor(minutes); - } - } else { - // No usage data but we have a timestamp? Trust the timestamp. - return precise ? minutes : Math.floor(minutes); - } - } - } - - // Find the last usage date for this substance - const substanceData = usageData - .filter((e) => e.substance === substance && e.count > 0) - .sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()); - - if (substanceData.length === 0) { - // No usage recorded, assume they just started - return 0; - } - - const now = new Date(); - const todayStr = now.toISOString().split('T')[0]; - const lastUsageDateStr = substanceData[0].date; - - // If the last usage was today, reset to 0 (just used, unknown time) - if (lastUsageDateStr === todayStr) { - return 0; - } - - // For past days, count from the end of that day - const lastUsageDate = new Date(lastUsageDateStr); - lastUsageDate.setHours(23, 59, 59, 999); - - const diffMs = now.getTime() - lastUsageDate.getTime(); - const minutes = Math.max(0, diffMs / (1000 * 60)); - - return precise ? minutes : Math.floor(minutes); -} export function checkBadgeEligibility( badgeId: string, diff --git a/src/lib/tracker/NicotineTracker.ts b/src/lib/tracker/NicotineTracker.ts new file mode 100644 index 0000000..e4fd9d2 --- /dev/null +++ b/src/lib/tracker/NicotineTracker.ts @@ -0,0 +1,77 @@ +import { RecoveryTracker } from './RecoveryTracker'; + +export class NicotineTracker extends RecoveryTracker { + calculateMinutesFree(precise: boolean = false): number { + // 1. Try to use precise timestamp from preferences first + if (this.preferences?.lastNicotineUsageTime) { + const now = new Date(); + const lastUsageTime = new Date(this.preferences.lastNicotineUsageTime); + console.log('[NicotineTracker] Found timestamp:', this.preferences.lastNicotineUsageTime); + + const diffMs = now.getTime() - lastUsageTime.getTime(); + const minutes = this.msToMinutes(diffMs, precise); + + // Validation: Ensure the timestamp aligns with the last recorded usage date. + // If the user manually edited usage for a LATER date, the timestamp might be stale. + const lastRecordedDateStr = this.getLastRecordedDate(); + console.log('[NicotineTracker] Last recorded date:', lastRecordedDateStr); + + if (lastRecordedDateStr) { + const lastRecordedDate = new Date(lastRecordedDateStr); + lastRecordedDate.setHours(0, 0, 0, 0); + + // If the timestamp is older than the start of the last recorded day, + // it means we have a newer manual entry without a timestamp. + // In this case, fall back to date-based logic. + if (lastUsageTime < lastRecordedDate) { + console.log('[NicotineTracker] Timestamp is stale, falling back to date logic'); + return this.calculateDateBasedMinutes(lastRecordedDateStr, precise); + } + } + + return minutes; + } + + // 2. Fallback to date-based logic if no timestamp exists + const lastDateStr = this.getLastRecordedDate(); + + // 3. If no nicotine usage ever recorded, use tracking start date + if (!lastDateStr) { + if (this.preferences?.trackingStartDate) { + const startDate = new Date(this.preferences.trackingStartDate); + startDate.setHours(0, 0, 0, 0); // Count from start of tracking day + const now = new Date(); + const diffMs = now.getTime() - startDate.getTime(); + return this.msToMinutes(diffMs, precise); + } + return 0; // No usage and no tracking start date + } + + return this.calculateDateBasedMinutes(lastDateStr, precise); + } + + private getLastRecordedDate(): string | null { + const nicotineData = this.usageData + .filter((e) => e.substance === 'nicotine' && e.count > 0) + .sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()); + + return nicotineData.length > 0 ? nicotineData[0].date : null; + } + + private calculateDateBasedMinutes(dateStr: string, precise: boolean): number { + const now = new Date(); + const todayStr = now.toISOString().split('T')[0]; + + // If the last usage was today but we have no timestamp, reset to 0 + if (dateStr === todayStr) { + return 0; + } + + // For past days, count from the END of that day (23:59:59) + const lastUsageDate = new Date(dateStr); + lastUsageDate.setHours(23, 59, 59, 999); + + const diffMs = now.getTime() - lastUsageDate.getTime(); + return this.msToMinutes(diffMs, precise); + } +} diff --git a/src/lib/tracker/RecoveryTracker.ts b/src/lib/tracker/RecoveryTracker.ts new file mode 100644 index 0000000..2be5593 --- /dev/null +++ b/src/lib/tracker/RecoveryTracker.ts @@ -0,0 +1,33 @@ +import { UsageEntry, UserPreferences } from '../storage'; + +export abstract class RecoveryTracker { + protected usageData: UsageEntry[]; + protected preferences: UserPreferences | null; + + constructor(usageData: UsageEntry[], preferences: UserPreferences | null) { + this.usageData = usageData; + this.preferences = preferences; + } + + /** + * Calculates the number of minutes elapsed since the last usage. + * This is the core logic that subclasses must support, but the implementation + * heavily depends on the specific substance's data source (preferences timestamp vs usage logs). + */ + abstract calculateMinutesFree(precise?: boolean): number; + + /** + * Helper to convert milliseconds to minutes with optional precision. + */ + protected msToMinutes(ms: number, precise: boolean = false): number { + const minutes = Math.max(0, ms / (1000 * 60)); + return precise ? minutes : Math.floor(minutes); + } + + /** + * Helper to check if a timestamp is valid and recent enough to rely on. + */ + protected isValidTimestamp(timestamp: string | null | undefined): boolean { + return !!timestamp && !isNaN(new Date(timestamp).getTime()); + } +} diff --git a/src/lib/tracker/WeedTracker.ts b/src/lib/tracker/WeedTracker.ts new file mode 100644 index 0000000..d52c620 --- /dev/null +++ b/src/lib/tracker/WeedTracker.ts @@ -0,0 +1,69 @@ +import { RecoveryTracker } from './RecoveryTracker'; + +export class WeedTracker extends RecoveryTracker { + calculateMinutesFree(precise: boolean = false): number { + // 1. Try to use precise timestamp from preferences first + if (this.preferences?.lastWeedUsageTime) { + const now = new Date(); + const lastUsageTime = new Date(this.preferences.lastWeedUsageTime); + const diffMs = now.getTime() - lastUsageTime.getTime(); + const minutes = this.msToMinutes(diffMs, precise); + + // Validation: Ensure the timestamp aligns with the last recorded usage date. + const lastRecordedDateStr = this.getLastRecordedDate(); + + if (lastRecordedDateStr) { + const lastRecordedDate = new Date(lastRecordedDateStr); + lastRecordedDate.setHours(0, 0, 0, 0); + + if (lastUsageTime < lastRecordedDate) { + return this.calculateDateBasedMinutes(lastRecordedDateStr, precise); + } + } + + return minutes; + } + + // 2. Fallback to date-based logic if no timestamp exists + const lastDateStr = this.getLastRecordedDate(); + + // 3. If no weed usage ever recorded, use tracking start date + if (!lastDateStr) { + if (this.preferences?.trackingStartDate) { + const startDate = new Date(this.preferences.trackingStartDate); + startDate.setHours(0, 0, 0, 0); // Count from start of tracking day + const now = new Date(); + const diffMs = now.getTime() - startDate.getTime(); + return this.msToMinutes(diffMs, precise); + } + return 0; // No usage and no tracking start date + } + + return this.calculateDateBasedMinutes(lastDateStr, precise); + } + + private getLastRecordedDate(): string | null { + const weedData = this.usageData + .filter((e) => e.substance === 'weed' && e.count > 0) + .sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()); + + return weedData.length > 0 ? weedData[0].date : null; + } + + private calculateDateBasedMinutes(dateStr: string, precise: boolean): number { + const now = new Date(); + const todayStr = now.toISOString().split('T')[0]; + + // If the last usage was today but we have no timestamp, reset to 0 + if (dateStr === todayStr) { + return 0; + } + + // For past days, count from the END of that day (23:59:59) + const lastUsageDate = new Date(dateStr); + lastUsageDate.setHours(23, 59, 59, 999); + + const diffMs = now.getTime() - lastUsageDate.getTime(); + return this.msToMinutes(diffMs, precise); + } +}