- Restore QuitPlanCard component under calendar on dashboard - Yellow gradient during tracking phase (before 7 days of data) - Pink gradient when quit plan is active - Track unique days with logged data for countdown - Generate 4-week plan with 25% weekly reduction - White text throughout for readability Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
180 lines
5.1 KiB
TypeScript
180 lines
5.1 KiB
TypeScript
'use client';
|
|
|
|
import { useState, useEffect, useCallback } from 'react';
|
|
import { User } from '@/lib/session';
|
|
import {
|
|
fetchPreferences,
|
|
fetchUsageData,
|
|
savePreferencesAsync,
|
|
saveUsageEntryAsync,
|
|
shouldShowUsagePrompt,
|
|
generateQuitPlan,
|
|
UserPreferences,
|
|
UsageEntry,
|
|
} from '@/lib/storage';
|
|
import { UserHeader } from './UserHeader';
|
|
import { SetupWizard } from './SetupWizard';
|
|
import { UsagePromptDialog } from './UsagePromptDialog';
|
|
import { UsageCalendar } from './UsageCalendar';
|
|
import { StatsCard } from './StatsCard';
|
|
import { QuitPlanCard } from './QuitPlanCard';
|
|
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(async () => {
|
|
const [prefs, usage] = await Promise.all([
|
|
fetchPreferences(),
|
|
fetchUsageData(),
|
|
]);
|
|
setPreferences(prefs);
|
|
setUsageData(usage);
|
|
setRefreshKey(prev => prev + 1);
|
|
return prefs;
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
const init = async () => {
|
|
const prefs = await loadData();
|
|
|
|
if (!prefs.hasCompletedSetup) {
|
|
setShowSetup(true);
|
|
} else if (shouldShowUsagePrompt()) {
|
|
setShowUsagePrompt(true);
|
|
}
|
|
|
|
setIsLoading(false);
|
|
};
|
|
|
|
init();
|
|
}, [loadData]);
|
|
|
|
const handleSetupComplete = async (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,
|
|
};
|
|
await savePreferencesAsync(newPrefs);
|
|
setPreferences(newPrefs);
|
|
setShowSetup(false);
|
|
setShowUsagePrompt(true);
|
|
setRefreshKey(prev => prev + 1);
|
|
};
|
|
|
|
const handleUsageSubmit = async (count: number, substance: 'nicotine' | 'weed') => {
|
|
if (!preferences) {
|
|
setShowUsagePrompt(false);
|
|
return;
|
|
}
|
|
|
|
if (count > 0) {
|
|
const today = new Date().toISOString().split('T')[0];
|
|
await saveUsageEntryAsync({
|
|
date: today,
|
|
count,
|
|
substance,
|
|
});
|
|
}
|
|
|
|
setShowUsagePrompt(false);
|
|
// Reload data and force calendar refresh
|
|
const usage = await fetchUsageData();
|
|
setUsageData(usage);
|
|
setRefreshKey(prev => prev + 1);
|
|
};
|
|
|
|
const handleGeneratePlan = async () => {
|
|
if (!preferences) return;
|
|
|
|
const plan = generateQuitPlan(preferences.substance);
|
|
const updatedPrefs = {
|
|
...preferences,
|
|
quitPlan: plan,
|
|
};
|
|
await savePreferencesAsync(updatedPrefs);
|
|
setPreferences(updatedPrefs);
|
|
setRefreshKey(prev => prev + 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-16 px-8 text-lg rounded-full shadow-xl bg-gradient-to-r from-primary to-primary/80 hover:from-primary/90 hover:to-primary/70 drop-shadow-lg"
|
|
>
|
|
<PlusCircle className="mr-2 h-6 w-6" />
|
|
Log Usage
|
|
</Button>
|
|
</div>
|
|
|
|
<div className="grid gap-6 md:grid-cols-2">
|
|
<div className="space-y-6">
|
|
<UsageCalendar
|
|
key={refreshKey}
|
|
usageData={usageData}
|
|
onDataUpdate={loadData}
|
|
userId={user.id}
|
|
/>
|
|
<QuitPlanCard
|
|
key={`quit-plan-${refreshKey}`}
|
|
plan={preferences.quitPlan}
|
|
onGeneratePlan={handleGeneratePlan}
|
|
usageData={usageData}
|
|
/>
|
|
</div>
|
|
<div className="space-y-6">
|
|
<StatsCard key={`stats-nicotine-${refreshKey}`} usageData={usageData} substance="nicotine" />
|
|
<StatsCard key={`stats-weed-${refreshKey}`} usageData={usageData} substance="weed" />
|
|
</div>
|
|
</div>
|
|
</>
|
|
)}
|
|
</main>
|
|
|
|
<SetupWizard open={showSetup} onComplete={handleSetupComplete} />
|
|
|
|
{preferences && (
|
|
<UsagePromptDialog
|
|
open={showUsagePrompt}
|
|
onClose={() => setShowUsagePrompt(false)}
|
|
onSubmit={handleUsageSubmit}
|
|
userId={user.id}
|
|
/>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|