Update UI and add user personalization features

- Rename app from QuitTrack to QuitTraq
- Add metallic dark gradient background
- Change header to light purple gradient
- Add name and age collection in setup wizard
- Display personalized "Welcome {name}, you got this!" message
- Hide username/email, show only profile picture
- Change calendar to red gradient for usage days
- Update logging prompt to "just took" instead of daily total
- Add floating "Log Puff" button for easy access
- Fix calendar editing to properly update values
- Add glass-morphism effect to cards

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Avery Felts 2026-01-23 21:40:59 -07:00
parent a812e6342c
commit 9918432686
11 changed files with 319 additions and 150 deletions

View File

@ -170,8 +170,31 @@
@apply border-border outline-ring/50;
}
body {
@apply bg-background text-foreground;
@apply text-foreground;
font-family: var(--font-sans);
letter-spacing: var(--tracking-normal);
background: linear-gradient(135deg,
#1a1a2e 0%,
#16213e 25%,
#1a1a2e 50%,
#0f0f1a 75%,
#1a1a2e 100%);
background-attachment: fixed;
min-height: 100vh;
}
body::before {
content: '';
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background:
radial-gradient(ellipse at 20% 20%, rgba(120, 119, 198, 0.15) 0%, transparent 50%),
radial-gradient(ellipse at 80% 80%, rgba(74, 85, 104, 0.2) 0%, transparent 50%),
radial-gradient(ellipse at 40% 60%, rgba(45, 55, 72, 0.15) 0%, transparent 40%);
pointer-events: none;
z-index: -1;
}
}

View File

@ -2,7 +2,7 @@ import type { Metadata } from "next";
import "./globals.css";
export const metadata: Metadata = {
title: "QuitTrack - Track Your Journey to Quit Smoking",
title: "QuitTraq - Track Your Journey to Quit Smoking",
description: "Track and manage your smoking habits, set goals, and quit safely with personalized plans.",
};

View File

@ -15,9 +15,9 @@ export default function LoginPage() {
return (
<div className="min-h-screen flex items-center justify-center p-4">
<Card className="w-full max-w-md">
<Card className="w-full max-w-md bg-card/80 backdrop-blur-sm border-white/10">
<CardHeader className="text-center">
<CardTitle className="text-3xl font-bold">QuitTrack</CardTitle>
<CardTitle className="text-3xl font-bold">QuitTraq</CardTitle>
<CardDescription className="text-lg">
Track your journey to a smoke-free life
</CardDescription>

View File

@ -8,7 +8,6 @@ import {
savePreferences,
saveUsageEntry,
shouldShowUsagePrompt,
setLastPromptDate,
hasOneWeekOfData,
calculateWeeklyAverage,
generateQuitPlan,
@ -21,6 +20,8 @@ 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;
@ -53,14 +54,16 @@ export function Dashboard({ user }: DashboardProps) {
setIsLoading(false);
}, []);
const handleSetupComplete = (substance: 'nicotine' | 'weed') => {
const handleSetupComplete = (data: { substance: 'nicotine' | 'weed'; name: string; age: number }) => {
const today = new Date().toISOString().split('T')[0];
const newPrefs: UserPreferences = {
substance,
substance: data.substance,
trackingStartDate: today,
hasCompletedSetup: true,
dailyGoal: null,
quitPlan: null,
userName: data.name,
userAge: data.age,
};
savePreferences(newPrefs);
setPreferences(newPrefs);
@ -69,7 +72,11 @@ export function Dashboard({ user }: DashboardProps) {
};
const handleUsageSubmit = (count: number) => {
if (!preferences) return;
if (!preferences || count === 0) {
setShowUsagePrompt(false);
loadData();
return;
}
const today = new Date().toISOString().split('T')[0];
saveUsageEntry({
@ -77,7 +84,6 @@ export function Dashboard({ user }: DashboardProps) {
count,
substance: preferences.substance,
});
setLastPromptDate(today);
setShowUsagePrompt(false);
loadData();
};
@ -101,36 +107,50 @@ export function Dashboard({ user }: DashboardProps) {
if (isLoading) {
return (
<div className="min-h-screen flex items-center justify-center">
<div className="animate-pulse text-lg">Loading...</div>
<div className="animate-pulse text-lg text-white">Loading...</div>
</div>
);
}
return (
<div className="min-h-screen bg-background">
<div className="min-h-screen">
<UserHeader user={user} />
<main className="container mx-auto px-4 py-8">
{preferences && (
<div className="grid gap-6 md:grid-cols-2">
<div className="space-y-6">
<UsageCalendar
usageData={usageData}
substance={preferences.substance}
onDataUpdate={loadData}
/>
<>
{/* 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="space-y-6">
<StatsCard usageData={usageData} substance={preferences.substance} />
<QuitPlanCard
plan={preferences.quitPlan}
onGeneratePlan={handleGeneratePlan}
hasEnoughData={hasOneWeekOfData(preferences.substance)}
daysTracked={getDaysTracked()}
currentAverage={calculateWeeklyAverage(preferences.substance)}
/>
<div className="grid gap-6 md:grid-cols-2">
<div className="space-y-6">
<UsageCalendar
usageData={usageData}
substance={preferences.substance}
onDataUpdate={loadData}
/>
</div>
<div className="space-y-6">
<StatsCard usageData={usageData} substance={preferences.substance} />
<QuitPlanCard
plan={preferences.quitPlan}
onGeneratePlan={handleGeneratePlan}
hasEnoughData={hasOneWeekOfData(preferences.substance)}
daysTracked={getDaysTracked()}
currentAverage={calculateWeeklyAverage(preferences.substance)}
/>
</div>
</div>
</div>
</>
)}
</main>

View File

@ -23,7 +23,7 @@ export function QuitPlanCard({
if (!plan) {
return (
<Card>
<Card className="bg-card/80 backdrop-blur-sm">
<CardHeader>
<CardTitle>Your Quit Plan</CardTitle>
<CardDescription>
@ -72,7 +72,7 @@ export function QuitPlanCard({
const totalWeeks = plan.weeklyTargets.length;
return (
<Card>
<Card className="bg-card/80 backdrop-blur-sm">
<CardHeader>
<CardTitle>Your Quit Plan</CardTitle>
<CardDescription>

View File

@ -9,6 +9,7 @@ import {
DialogTitle,
} from '@/components/ui/dialog';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import {
Select,
@ -20,15 +21,32 @@ import {
interface SetupWizardProps {
open: boolean;
onComplete: (substance: 'nicotine' | 'weed') => void;
onComplete: (data: { substance: 'nicotine' | 'weed'; name: string; age: number }) => void;
}
export function SetupWizard({ open, onComplete }: SetupWizardProps) {
const [step, setStep] = useState(1);
const [name, setName] = useState('');
const [age, setAge] = useState('25');
const [substance, setSubstance] = useState<'nicotine' | 'weed' | ''>('');
const ages = Array.from({ length: 83 }, (_, i) => (i + 18).toString());
const handleNext = () => {
if (step === 1 && name.trim()) {
setStep(2);
} else if (step === 2 && age) {
setStep(3);
}
};
const handleComplete = () => {
if (substance) {
onComplete(substance);
if (substance && name.trim() && age) {
onComplete({
substance,
name: name.trim(),
age: parseInt(age, 10),
});
}
};
@ -36,39 +54,101 @@ export function SetupWizard({ open, onComplete }: SetupWizardProps) {
<Dialog open={open}>
<DialogContent className="sm:max-w-md" onInteractOutside={(e) => e.preventDefault()}>
<DialogHeader>
<DialogTitle>Welcome to QuitTrack</DialogTitle>
<DialogTitle>Welcome to QuitTraq</DialogTitle>
<DialogDescription>
Let&apos;s set up your tracking preferences to help you on your journey.
{step === 1 && "Let's get to know you a little better."}
{step === 2 && "Just one more thing about you."}
{step === 3 && "Set up your tracking preferences."}
</DialogDescription>
</DialogHeader>
<div className="space-y-6 py-4">
<div className="space-y-3">
<Label htmlFor="substance">What would you like to track?</Label>
<Select value={substance} onValueChange={(v) => setSubstance(v as 'nicotine' | 'weed')}>
<SelectTrigger id="substance">
<SelectValue placeholder="Select substance" />
</SelectTrigger>
<SelectContent>
<SelectItem value="nicotine">Nicotine (Vaping/Cigarettes)</SelectItem>
<SelectItem value="weed">Cannabis/Weed</SelectItem>
</SelectContent>
</Select>
</div>
{step === 1 && (
<div className="space-y-4">
<div className="space-y-2">
<Label htmlFor="name">What&apos;s your name?</Label>
<Input
id="name"
placeholder="Enter your name"
value={name}
onChange={(e) => setName(e.target.value)}
className="text-lg"
autoFocus
/>
</div>
<Button
onClick={handleNext}
disabled={!name.trim()}
className="w-full"
>
Continue
</Button>
</div>
)}
<div className="bg-muted p-4 rounded-lg space-y-2">
<h4 className="font-medium">How it works</h4>
<ol className="text-sm text-muted-foreground space-y-1 list-decimal list-inside">
<li>Track your daily usage for one week</li>
<li>We&apos;ll analyze your patterns</li>
<li>Get a personalized quit plan based on your habits</li>
<li>Gradually reduce your intake safely</li>
</ol>
</div>
{step === 2 && (
<div className="space-y-4">
<div className="space-y-2">
<Label htmlFor="age">How old are you?</Label>
<Select value={age} onValueChange={setAge}>
<SelectTrigger id="age" className="text-lg">
<SelectValue placeholder="Select your age" />
</SelectTrigger>
<SelectContent className="max-h-60">
{ages.map((a) => (
<SelectItem key={a} value={a}>
{a} years old
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="flex gap-2">
<Button variant="outline" onClick={() => setStep(1)} className="flex-1">
Back
</Button>
<Button onClick={handleNext} disabled={!age} className="flex-1">
Continue
</Button>
</div>
</div>
)}
<Button onClick={handleComplete} disabled={!substance} className="w-full">
Start Tracking
</Button>
{step === 3 && (
<div className="space-y-4">
<div className="space-y-3">
<Label htmlFor="substance">What would you like to track?</Label>
<Select value={substance} onValueChange={(v) => setSubstance(v as 'nicotine' | 'weed')}>
<SelectTrigger id="substance">
<SelectValue placeholder="Select substance" />
</SelectTrigger>
<SelectContent>
<SelectItem value="nicotine">Nicotine (Vaping/Cigarettes)</SelectItem>
<SelectItem value="weed">Cannabis/Weed</SelectItem>
</SelectContent>
</Select>
</div>
<div className="bg-muted p-4 rounded-lg space-y-2">
<h4 className="font-medium">How it works</h4>
<ol className="text-sm text-muted-foreground space-y-1 list-decimal list-inside">
<li>Log each puff throughout the day</li>
<li>Track your patterns for one week</li>
<li>Get a personalized quit plan</li>
<li>Gradually reduce your intake safely</li>
</ol>
</div>
<div className="flex gap-2">
<Button variant="outline" onClick={() => setStep(2)} className="flex-1">
Back
</Button>
<Button onClick={handleComplete} disabled={!substance} className="flex-1">
Start Tracking
</Button>
</div>
</div>
)}
</div>
</DialogContent>
</Dialog>

View File

@ -49,7 +49,7 @@ export function StatsCard({ usageData, substance }: StatsCardProps) {
const totalDays = substanceData.length;
return (
<Card>
<Card className="bg-card/80 backdrop-blur-sm">
<CardHeader>
<CardTitle>Your Stats</CardTitle>
</CardHeader>

View File

@ -44,11 +44,19 @@ export function UsageCalendar({ usageData, substance, onDataUpdate }: UsageCalen
const handleSave = () => {
if (selectedDate) {
const dateStr = selectedDate.toISOString().split('T')[0];
setUsageForDate(dateStr, parseInt(editCount, 10) || 0, substance);
const newCount = parseInt(editCount, 10) || 0;
setUsageForDate(dateStr, newCount, substance);
onDataUpdate();
}
setIsEditing(false);
setSelectedDate(undefined);
setEditCount('');
};
const handleCancel = () => {
setIsEditing(false);
setSelectedDate(undefined);
setEditCount('');
};
const getUsageCount = useCallback((date: Date): number => {
@ -57,11 +65,21 @@ export function UsageCalendar({ usageData, substance, onDataUpdate }: UsageCalen
return entry?.count ?? 0;
}, [usageData, substance]);
const getColorClass = useCallback((count: number): string => {
if (count === 0) return 'bg-green-100 dark:bg-green-900/30 text-green-900 dark:text-green-100 hover:bg-green-200 dark:hover:bg-green-900/50';
if (count <= 3) return 'bg-yellow-100 dark:bg-yellow-900/30 text-yellow-900 dark:text-yellow-100 hover:bg-yellow-200 dark:hover:bg-yellow-900/50';
if (count <= 6) return 'bg-orange-100 dark:bg-orange-900/30 text-orange-900 dark:text-orange-100 hover:bg-orange-200 dark:hover:bg-orange-900/50';
return 'bg-red-100 dark:bg-red-900/30 text-red-900 dark:text-red-100 hover:bg-red-200 dark:hover:bg-red-900/50';
const getColorStyle = useCallback((count: number): React.CSSProperties => {
if (count === 0) {
return {
background: 'linear-gradient(135deg, #10b981 0%, #059669 100%)',
color: 'white',
};
}
// Red gradient for any usage - more intense red for higher counts
const intensity = Math.min(count / 10, 1); // Max intensity at 10+ uses
const lightRed = `rgba(239, 68, 68, ${0.6 + intensity * 0.4})`;
const darkRed = `rgba(185, 28, 28, ${0.7 + intensity * 0.3})`;
return {
background: `linear-gradient(135deg, ${lightRed} 0%, ${darkRed} 100%)`,
color: 'white',
};
}, []);
const CustomDayButton = useCallback(({ day, modifiers, ...props }: DayButtonProps) => {
@ -72,30 +90,31 @@ export function UsageCalendar({ usageData, substance, onDataUpdate }: UsageCalen
dateToCheck.setHours(0, 0, 0, 0);
const isFuture = dateToCheck > today;
const count = isFuture ? -1 : getUsageCount(date);
const colorClass = count >= 0 ? getColorClass(count) : '';
const colorStyle = count >= 0 ? getColorStyle(count) : {};
return (
<button
{...props}
className={`relative w-full h-full p-2 text-sm rounded-md transition-colors ${
isFuture ? 'text-muted-foreground opacity-50' : colorClass
} ${modifiers.today ? 'ring-2 ring-primary' : ''}`}
onClick={() => handleDateSelect(date)}
style={count >= 0 ? colorStyle : undefined}
className={`relative w-full h-full p-2 text-sm rounded-md transition-all hover:opacity-80 ${
isFuture ? 'text-muted-foreground opacity-30 cursor-not-allowed' : 'cursor-pointer shadow-sm'
} ${modifiers.today ? 'ring-2 ring-white ring-offset-2 ring-offset-background' : ''}`}
onClick={() => !isFuture && handleDateSelect(date)}
disabled={isFuture}
>
<span>{date.getDate()}</span>
<span className="font-medium">{date.getDate()}</span>
{count > 0 && (
<span className="absolute bottom-0.5 right-1 text-[10px] font-bold">
<span className="absolute bottom-0.5 right-1 text-[10px] font-bold bg-black/20 px-1 rounded">
{count}
</span>
)}
</button>
);
}, [getUsageCount, getColorClass]);
}, [getUsageCount, getColorStyle]);
return (
<>
<Card>
<Card className="bg-card/80 backdrop-blur-sm">
<CardHeader>
<CardTitle>Usage Calendar</CardTitle>
</CardHeader>
@ -104,7 +123,7 @@ export function UsageCalendar({ usageData, substance, onDataUpdate }: UsageCalen
mode="single"
selected={selectedDate}
onSelect={handleDateSelect}
className="rounded-md border p-3"
className="rounded-md border p-3 bg-background/50"
showOutsideDays={false}
components={{
DayButton: CustomDayButton,
@ -116,26 +135,21 @@ export function UsageCalendar({ usageData, substance, onDataUpdate }: UsageCalen
/>
<div className="mt-4 flex flex-wrap gap-4 text-sm">
<div className="flex items-center gap-2">
<div className="w-4 h-4 rounded bg-green-100 dark:bg-green-900/30 border" />
<span>0 uses</span>
<div className="w-4 h-4 rounded" style={{ background: 'linear-gradient(135deg, #10b981, #059669)' }} />
<span>No usage</span>
</div>
<div className="flex items-center gap-2">
<div className="w-4 h-4 rounded bg-yellow-100 dark:bg-yellow-900/30 border" />
<span>1-3</span>
</div>
<div className="flex items-center gap-2">
<div className="w-4 h-4 rounded bg-orange-100 dark:bg-orange-900/30 border" />
<span>4-6</span>
</div>
<div className="flex items-center gap-2">
<div className="w-4 h-4 rounded bg-red-100 dark:bg-red-900/30 border" />
<span>7+</span>
<div className="w-4 h-4 rounded" style={{ background: 'linear-gradient(135deg, rgba(239,68,68,0.7), rgba(185,28,28,0.8))' }} />
<span>Has usage</span>
</div>
</div>
<p className="mt-2 text-xs text-muted-foreground">
Click any day to edit the count
</p>
</CardContent>
</Card>
<Dialog open={isEditing} onOpenChange={setIsEditing}>
<Dialog open={isEditing} onOpenChange={(open) => !open && handleCancel()}>
<DialogContent className="sm:max-w-sm">
<DialogHeader>
<DialogTitle>
@ -144,7 +158,7 @@ export function UsageCalendar({ usageData, substance, onDataUpdate }: UsageCalen
</DialogHeader>
<div className="space-y-4 py-4">
<div className="space-y-2">
<Label htmlFor="editCount">Number of uses</Label>
<Label htmlFor="editCount">Total puffs for this day</Label>
<Input
id="editCount"
type="number"
@ -153,9 +167,12 @@ export function UsageCalendar({ usageData, substance, onDataUpdate }: UsageCalen
onChange={(e) => setEditCount(e.target.value)}
className="text-center text-lg"
/>
<p className="text-xs text-muted-foreground text-center">
This will replace the current value
</p>
</div>
<div className="flex gap-2">
<Button variant="outline" onClick={() => setIsEditing(false)} className="flex-1">
<Button variant="outline" onClick={handleCancel} className="flex-1">
Cancel
</Button>
<Button onClick={handleSave} className="flex-1">

View File

@ -11,6 +11,7 @@ import {
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { getUsageForDate } from '@/lib/storage';
interface UsagePromptDialogProps {
open: boolean;
@ -25,24 +26,32 @@ export function UsagePromptDialog({
onSubmit,
substance,
}: UsagePromptDialogProps) {
const [hasUsed, setHasUsed] = useState<boolean | null>(null);
const [count, setCount] = useState('');
const [wantsToLog, setWantsToLog] = useState<boolean | null>(null);
const [count, setCount] = useState('1');
const substanceLabel = substance === 'nicotine' ? 'nicotine (vape/cigarettes)' : 'cannabis';
const substanceLabel = substance === 'nicotine' ? 'a puff or cigarette' : 'a hit';
const substanceLabelPlural = substance === 'nicotine' ? 'puffs/cigarettes' : 'hits';
const today = new Date().toISOString().split('T')[0];
const todayCount = typeof window !== 'undefined' ? getUsageForDate(today, substance) : 0;
const handleSubmit = () => {
if (hasUsed === false) {
onSubmit(0);
} else if (hasUsed === true && count) {
if (wantsToLog === true && count) {
onSubmit(parseInt(count, 10));
}
setHasUsed(null);
setCount('');
setWantsToLog(null);
setCount('1');
};
const handleSkip = () => {
setWantsToLog(null);
setCount('1');
onClose();
};
const handleClose = () => {
setHasUsed(null);
setCount('');
setWantsToLog(null);
setCount('1');
onClose();
};
@ -50,78 +59,72 @@ export function UsagePromptDialog({
<Dialog open={open} onOpenChange={handleClose}>
<DialogContent className="sm:max-w-md">
<DialogHeader>
<DialogTitle>Daily Check-in</DialogTitle>
<DialogTitle>Log Your Usage</DialogTitle>
<DialogDescription>
Tracking your usage helps you understand your habits and work towards your goals.
Log each time you smoke to track your progress. You can log multiple times throughout the day.
</DialogDescription>
</DialogHeader>
<div className="space-y-6 py-4">
{hasUsed === null ? (
{todayCount > 0 && (
<div className="bg-muted/50 p-3 rounded-lg text-center">
<p className="text-sm text-muted-foreground">Today&apos;s total so far</p>
<p className="text-2xl font-bold">{todayCount} {substanceLabelPlural}</p>
</div>
)}
{wantsToLog === null ? (
<div className="space-y-4">
<p className="text-center font-medium">
Have you consumed any {substanceLabel} today?
Did you just have {substanceLabel}?
</p>
<div className="flex gap-4 justify-center">
<Button
variant="outline"
size="lg"
onClick={() => setHasUsed(true)}
className="w-24"
onClick={() => setWantsToLog(true)}
className="w-32"
>
Yes
Yes, log it
</Button>
<Button
variant="outline"
size="lg"
onClick={() => setHasUsed(false)}
className="w-24"
onClick={handleSkip}
className="w-32"
>
No
No, skip
</Button>
</div>
</div>
) : hasUsed === true ? (
) : (
<div className="space-y-4">
<div className="space-y-2">
<Label htmlFor="count">
How many times did you use {substanceLabel} today?
How many {substanceLabelPlural} did you just have?
</Label>
<Input
id="count"
type="number"
min="1"
placeholder="Enter number"
placeholder="1"
value={count}
onChange={(e) => setCount(e.target.value)}
className="text-center text-lg"
/>
<p className="text-xs text-muted-foreground">
{substance === 'nicotine'
? 'Count each vape session or cigarette'
: 'Count each smoking/consumption session'}
<p className="text-xs text-muted-foreground text-center">
This will be added to today&apos;s total
</p>
</div>
<div className="flex gap-2">
<Button variant="outline" onClick={() => setHasUsed(null)} className="flex-1">
<Button variant="outline" onClick={() => setWantsToLog(null)} className="flex-1">
Back
</Button>
<Button onClick={handleSubmit} disabled={!count} className="flex-1">
Log Usage
<Button onClick={handleSubmit} disabled={!count || parseInt(count) < 1} className="flex-1">
Log {count || 1} {parseInt(count) === 1 ? (substance === 'nicotine' ? 'puff' : 'hit') : substanceLabelPlural}
</Button>
</div>
</div>
) : (
<div className="space-y-4 text-center">
<div className="text-4xl">🎉</div>
<p className="font-medium">Great job staying smoke-free today!</p>
<p className="text-sm text-muted-foreground">
Every day without smoking is a victory. Keep it up!
</p>
<Button onClick={handleSubmit} className="w-full">
Continue
</Button>
</div>
)}
</div>
</DialogContent>

View File

@ -3,12 +3,21 @@
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
import { Button } from '@/components/ui/button';
import { User } from '@/lib/session';
import { getPreferences } from '@/lib/storage';
import { useEffect, useState } from 'react';
interface UserHeaderProps {
user: User;
}
export function UserHeader({ user }: UserHeaderProps) {
const [userName, setUserName] = useState<string | null>(null);
useEffect(() => {
const prefs = getPreferences();
setUserName(prefs.userName);
}, []);
const initials = [user.firstName?.[0], user.lastName?.[0]]
.filter(Boolean)
.join('')
@ -19,30 +28,42 @@ export function UserHeader({ user }: UserHeaderProps) {
};
return (
<header className="border-b bg-card">
<header className="border-b border-white/10" style={{
background: 'linear-gradient(135deg, rgba(147, 112, 219, 0.9) 0%, rgba(138, 99, 210, 0.85) 50%, rgba(123, 82, 195, 0.9) 100%)',
backdropFilter: 'blur(10px)',
}}>
<div className="container mx-auto px-4 py-4 flex items-center justify-between">
<div className="flex items-center gap-4">
<h1 className="text-2xl font-bold text-primary">QuitTrack</h1>
<h1 className="text-2xl font-bold text-white">QuitTraq</h1>
{userName && (
<p className="text-white/90 text-lg hidden sm:block">
Welcome {userName}, you got this!
</p>
)}
</div>
<div className="flex items-center gap-4">
<div className="flex items-center gap-3">
<Avatar>
<AvatarImage src={user.profilePictureUrl ?? undefined} alt={user.firstName ?? 'User'} />
<AvatarFallback>{initials}</AvatarFallback>
</Avatar>
<div className="hidden sm:block">
<p className="text-sm font-medium">
{user.firstName ? `${user.firstName} ${user.lastName ?? ''}`.trim() : user.email}
</p>
<p className="text-xs text-muted-foreground">{user.email}</p>
</div>
</div>
<Button variant="outline" size="sm" onClick={handleLogout}>
<Avatar className="h-10 w-10 ring-2 ring-white/30">
<AvatarImage src={user.profilePictureUrl ?? undefined} alt={userName || 'User'} />
<AvatarFallback className="bg-white/20 text-white">{initials}</AvatarFallback>
</Avatar>
<Button
variant="outline"
size="sm"
onClick={handleLogout}
className="bg-white/10 border-white/20 text-white hover:bg-white/20 hover:text-white"
>
Sign out
</Button>
</div>
</div>
{userName && (
<div className="sm:hidden container mx-auto px-4 pb-3">
<p className="text-white/90 text-base">
Welcome {userName}, you got this!
</p>
</div>
)}
</header>
);
}

View File

@ -12,6 +12,8 @@ export interface UserPreferences {
hasCompletedSetup: boolean;
dailyGoal: number | null;
quitPlan: QuitPlan | null;
userName: string | null;
userAge: number | null;
}
export interface QuitPlan {
@ -77,6 +79,8 @@ export function getPreferences(): UserPreferences {
hasCompletedSetup: false,
dailyGoal: null,
quitPlan: null,
userName: null,
userAge: null,
};
}
@ -88,6 +92,8 @@ export function getPreferences(): UserPreferences {
hasCompletedSetup: false,
dailyGoal: null,
quitPlan: null,
userName: null,
userAge: null,
};
}
@ -110,9 +116,8 @@ export function setLastPromptDate(date: string): void {
}
export function shouldShowUsagePrompt(): boolean {
const lastPrompt = getLastPromptDate();
const today = new Date().toISOString().split('T')[0];
return lastPrompt !== today;
// Always show the prompt - users can log multiple times throughout the day
return true;
}
export function getWeeklyData(substance: 'nicotine' | 'weed'): UsageEntry[] {