- Pass user.id explicitly to all storage function calls instead of relying on global state - Add userId prop to UsageCalendar and UsagePromptDialog components - Fix UserHeader to use user.id when fetching preferences - Add refreshKey to force calendar re-render after logging usage Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
186 lines
5.5 KiB
TypeScript
186 lines
5.5 KiB
TypeScript
'use client';
|
|
|
|
import { useState, useEffect, useCallback } from 'react';
|
|
import { User } from '@/lib/session';
|
|
import {
|
|
getUsageData,
|
|
getPreferences,
|
|
savePreferences,
|
|
saveUsageEntry,
|
|
shouldShowUsagePrompt,
|
|
hasOneWeekOfData,
|
|
calculateWeeklyAverage,
|
|
generateQuitPlan,
|
|
setCurrentUserId,
|
|
UserPreferences,
|
|
UsageEntry,
|
|
} from '@/lib/storage';
|
|
import { UserHeader } from './UserHeader';
|
|
import { SetupWizard } from './SetupWizard';
|
|
import { UsagePromptDialog } from './UsagePromptDialog';
|
|
import { UsageCalendar } from './UsageCalendar';
|
|
import { QuitPlanCard } from './QuitPlanCard';
|
|
import { StatsCard } from './StatsCard';
|
|
import { Button } from '@/components/ui/button';
|
|
import { PlusCircle } from 'lucide-react';
|
|
|
|
interface DashboardProps {
|
|
user: User;
|
|
}
|
|
|
|
export function Dashboard({ user }: DashboardProps) {
|
|
const [preferences, setPreferences] = useState<UserPreferences | null>(null);
|
|
const [usageData, setUsageData] = useState<UsageEntry[]>([]);
|
|
const [showSetup, setShowSetup] = useState(false);
|
|
const [showUsagePrompt, setShowUsagePrompt] = useState(false);
|
|
const [isLoading, setIsLoading] = useState(true);
|
|
const [refreshKey, setRefreshKey] = useState(0);
|
|
|
|
const loadData = useCallback(() => {
|
|
// Always pass user.id explicitly to ensure correct data is loaded
|
|
const prefs = getPreferences(user.id);
|
|
const usage = getUsageData(user.id);
|
|
setPreferences(prefs);
|
|
setUsageData(usage);
|
|
setRefreshKey(prev => prev + 1);
|
|
return prefs;
|
|
}, [user.id]);
|
|
|
|
useEffect(() => {
|
|
// Set the current user ID for all storage operations
|
|
setCurrentUserId(user.id);
|
|
|
|
const prefs = loadData();
|
|
|
|
if (!prefs.hasCompletedSetup) {
|
|
setShowSetup(true);
|
|
} else if (shouldShowUsagePrompt()) {
|
|
setShowUsagePrompt(true);
|
|
}
|
|
|
|
setIsLoading(false);
|
|
}, [user.id, loadData]);
|
|
|
|
const handleSetupComplete = (data: { substance: 'nicotine' | 'weed'; name: string; age: number }) => {
|
|
const today = new Date().toISOString().split('T')[0];
|
|
const newPrefs: UserPreferences = {
|
|
substance: data.substance,
|
|
trackingStartDate: today,
|
|
hasCompletedSetup: true,
|
|
dailyGoal: null,
|
|
quitPlan: null,
|
|
userName: data.name,
|
|
userAge: data.age,
|
|
};
|
|
savePreferences(newPrefs, user.id);
|
|
setPreferences(newPrefs);
|
|
setShowSetup(false);
|
|
setShowUsagePrompt(true);
|
|
setRefreshKey(prev => prev + 1);
|
|
};
|
|
|
|
const handleUsageSubmit = (count: number) => {
|
|
if (!preferences) {
|
|
setShowUsagePrompt(false);
|
|
return;
|
|
}
|
|
|
|
if (count > 0) {
|
|
const today = new Date().toISOString().split('T')[0];
|
|
saveUsageEntry({
|
|
date: today,
|
|
count,
|
|
substance: preferences.substance,
|
|
}, user.id);
|
|
}
|
|
|
|
setShowUsagePrompt(false);
|
|
// Reload data and force calendar refresh
|
|
const usage = getUsageData(user.id);
|
|
setUsageData(usage);
|
|
setRefreshKey(prev => prev + 1);
|
|
};
|
|
|
|
const handleGeneratePlan = () => {
|
|
if (!preferences) return;
|
|
|
|
const plan = generateQuitPlan(preferences.substance, user.id);
|
|
const updatedPrefs = { ...preferences, quitPlan: plan };
|
|
savePreferences(updatedPrefs, user.id);
|
|
setPreferences(updatedPrefs);
|
|
};
|
|
|
|
const getDaysTracked = (): number => {
|
|
if (!preferences?.trackingStartDate) return 0;
|
|
const startDate = new Date(preferences.trackingStartDate);
|
|
const today = new Date();
|
|
return Math.floor((today.getTime() - startDate.getTime()) / (1000 * 60 * 60 * 24)) + 1;
|
|
};
|
|
|
|
if (isLoading) {
|
|
return (
|
|
<div className="min-h-screen flex items-center justify-center">
|
|
<div className="animate-pulse text-lg text-white">Loading...</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="min-h-screen">
|
|
<UserHeader user={user} />
|
|
|
|
<main className="container mx-auto px-4 py-8">
|
|
{preferences && (
|
|
<>
|
|
{/* Floating Log Button */}
|
|
<div className="fixed bottom-6 right-6 z-50">
|
|
<Button
|
|
size="lg"
|
|
onClick={() => setShowUsagePrompt(true)}
|
|
className="h-14 px-6 rounded-full shadow-lg bg-gradient-to-r from-primary to-primary/80 hover:from-primary/90 hover:to-primary/70"
|
|
>
|
|
<PlusCircle className="mr-2 h-5 w-5" />
|
|
Log Puff
|
|
</Button>
|
|
</div>
|
|
|
|
<div className="grid gap-6 md:grid-cols-2">
|
|
<div className="space-y-6">
|
|
<UsageCalendar
|
|
key={refreshKey}
|
|
usageData={usageData}
|
|
substance={preferences.substance}
|
|
onDataUpdate={loadData}
|
|
userId={user.id}
|
|
/>
|
|
</div>
|
|
<div className="space-y-6">
|
|
<StatsCard usageData={usageData} substance={preferences.substance} />
|
|
<QuitPlanCard
|
|
plan={preferences.quitPlan}
|
|
onGeneratePlan={handleGeneratePlan}
|
|
hasEnoughData={hasOneWeekOfData(preferences.substance, user.id)}
|
|
daysTracked={getDaysTracked()}
|
|
currentAverage={calculateWeeklyAverage(preferences.substance, user.id)}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</>
|
|
)}
|
|
</main>
|
|
|
|
<SetupWizard open={showSetup} onComplete={handleSetupComplete} />
|
|
|
|
{preferences && (
|
|
<UsagePromptDialog
|
|
open={showUsagePrompt}
|
|
onClose={() => setShowUsagePrompt(false)}
|
|
onSubmit={handleUsageSubmit}
|
|
substance={preferences.substance}
|
|
userId={user.id}
|
|
/>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|