Fix per-user data isolation by passing userId explicitly to all storage operations

- 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>
This commit is contained in:
Avery Felts 2026-01-23 22:01:16 -07:00
parent a244e740c9
commit 39a1e858fb
4 changed files with 42 additions and 27 deletions

View File

@ -1,6 +1,6 @@
'use client';
import { useState, useEffect } from 'react';
import { useState, useEffect, useCallback } from 'react';
import { User } from '@/lib/session';
import {
getUsageData,
@ -34,14 +34,17 @@ export function Dashboard({ user }: DashboardProps) {
const [showSetup, setShowSetup] = useState(false);
const [showUsagePrompt, setShowUsagePrompt] = useState(false);
const [isLoading, setIsLoading] = useState(true);
const [refreshKey, setRefreshKey] = useState(0);
const loadData = () => {
const prefs = getPreferences();
const usage = getUsageData();
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
@ -56,7 +59,7 @@ export function Dashboard({ user }: DashboardProps) {
}
setIsLoading(false);
}, [user.id]);
}, [user.id, loadData]);
const handleSetupComplete = (data: { substance: 'nicotine' | 'weed'; name: string; age: number }) => {
const today = new Date().toISOString().split('T')[0];
@ -69,35 +72,41 @@ export function Dashboard({ user }: DashboardProps) {
userName: data.name,
userAge: data.age,
};
savePreferences(newPrefs);
savePreferences(newPrefs, user.id);
setPreferences(newPrefs);
setShowSetup(false);
setShowUsagePrompt(true);
setRefreshKey(prev => prev + 1);
};
const handleUsageSubmit = (count: number) => {
if (!preferences || count === 0) {
if (!preferences) {
setShowUsagePrompt(false);
loadData();
return;
}
const today = new Date().toISOString().split('T')[0];
saveUsageEntry({
date: today,
count,
substance: preferences.substance,
});
if (count > 0) {
const today = new Date().toISOString().split('T')[0];
saveUsageEntry({
date: today,
count,
substance: preferences.substance,
}, user.id);
}
setShowUsagePrompt(false);
loadData();
// 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);
const plan = generateQuitPlan(preferences.substance, user.id);
const updatedPrefs = { ...preferences, quitPlan: plan };
savePreferences(updatedPrefs);
savePreferences(updatedPrefs, user.id);
setPreferences(updatedPrefs);
};
@ -138,9 +147,11 @@ export function Dashboard({ user }: DashboardProps) {
<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">
@ -148,9 +159,9 @@ export function Dashboard({ user }: DashboardProps) {
<QuitPlanCard
plan={preferences.quitPlan}
onGeneratePlan={handleGeneratePlan}
hasEnoughData={hasOneWeekOfData(preferences.substance)}
hasEnoughData={hasOneWeekOfData(preferences.substance, user.id)}
daysTracked={getDaysTracked()}
currentAverage={calculateWeeklyAverage(preferences.substance)}
currentAverage={calculateWeeklyAverage(preferences.substance, user.id)}
/>
</div>
</div>
@ -166,6 +177,7 @@ export function Dashboard({ user }: DashboardProps) {
onClose={() => setShowUsagePrompt(false)}
onSubmit={handleUsageSubmit}
substance={preferences.substance}
userId={user.id}
/>
)}
</div>

View File

@ -19,9 +19,10 @@ interface UsageCalendarProps {
usageData: UsageEntry[];
substance: 'nicotine' | 'weed';
onDataUpdate: () => void;
userId: string;
}
export function UsageCalendar({ usageData, substance, onDataUpdate }: UsageCalendarProps) {
export function UsageCalendar({ usageData, substance, onDataUpdate, userId }: UsageCalendarProps) {
const [selectedDate, setSelectedDate] = useState<Date | undefined>(undefined);
const [editCount, setEditCount] = useState('');
const [isEditing, setIsEditing] = useState(false);
@ -36,7 +37,7 @@ export function UsageCalendar({ usageData, substance, onDataUpdate }: UsageCalen
setSelectedDate(date);
const dateStr = date.toISOString().split('T')[0];
const currentCount = getUsageForDate(dateStr, substance);
const currentCount = getUsageForDate(dateStr, substance, userId);
setEditCount(currentCount.toString());
setIsEditing(true);
};
@ -45,7 +46,7 @@ export function UsageCalendar({ usageData, substance, onDataUpdate }: UsageCalen
if (selectedDate) {
const dateStr = selectedDate.toISOString().split('T')[0];
const newCount = parseInt(editCount, 10) || 0;
setUsageForDate(dateStr, newCount, substance);
setUsageForDate(dateStr, newCount, substance, userId);
onDataUpdate();
}
setIsEditing(false);
@ -62,7 +63,7 @@ export function UsageCalendar({ usageData, substance, onDataUpdate }: UsageCalen
const handleClearDay = () => {
if (selectedDate) {
const dateStr = selectedDate.toISOString().split('T')[0];
clearDayData(dateStr, substance);
clearDayData(dateStr, substance, userId);
onDataUpdate();
}
setIsEditing(false);

View File

@ -18,6 +18,7 @@ interface UsagePromptDialogProps {
onClose: () => void;
onSubmit: (count: number) => void;
substance: 'nicotine' | 'weed';
userId: string;
}
export function UsagePromptDialog({
@ -25,6 +26,7 @@ export function UsagePromptDialog({
onClose,
onSubmit,
substance,
userId,
}: UsagePromptDialogProps) {
const [wantsToLog, setWantsToLog] = useState<boolean | null>(null);
const [count, setCount] = useState('1');
@ -33,7 +35,7 @@ export function UsagePromptDialog({
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 todayCount = typeof window !== 'undefined' ? getUsageForDate(today, substance, userId) : 0;
const handleSubmit = () => {
if (wantsToLog === true && count) {

View File

@ -14,9 +14,9 @@ export function UserHeader({ user }: UserHeaderProps) {
const [userName, setUserName] = useState<string | null>(null);
useEffect(() => {
const prefs = getPreferences();
const prefs = getPreferences(user.id);
setUserName(prefs.userName);
}, []);
}, [user.id]);
const initials = [user.firstName?.[0], user.lastName?.[0]]
.filter(Boolean)