ui update and some backend functionality adding in accordance with research on adhd and ux design

This commit is contained in:
2026-02-14 17:21:37 -06:00
parent 4d3a9fbd54
commit fb480eacb2
32 changed files with 9549 additions and 248 deletions

View File

@@ -0,0 +1,172 @@
'use client';
import { useEffect, useState } from 'react';
import api from '@/lib/api';
import { VolumeIcon, VolumeOffIcon, SparklesIcon } from '@/components/ui/Icons';
import { playStepComplete } from '@/lib/sounds';
import { hapticTap } from '@/lib/haptics';
interface Preferences {
sound_enabled: boolean;
haptic_enabled: boolean;
show_launch_screen: boolean;
celebration_style: string;
}
export default function SettingsPage() {
const [prefs, setPrefs] = useState<Preferences>({
sound_enabled: false,
haptic_enabled: true,
show_launch_screen: true,
celebration_style: 'standard',
});
const [isLoading, setIsLoading] = useState(true);
const [saved, setSaved] = useState(false);
useEffect(() => {
api.preferences.get()
.then((data: Preferences) => setPrefs(data))
.catch(() => {})
.finally(() => setIsLoading(false));
}, []);
const updatePref = async (key: keyof Preferences, value: boolean | string) => {
const updated = { ...prefs, [key]: value };
setPrefs(updated);
try {
await api.preferences.update({ [key]: value });
setSaved(true);
setTimeout(() => setSaved(false), 1500);
} catch {
// revert on failure
setPrefs(prefs);
}
};
if (isLoading) {
return (
<div className="flex items-center justify-center min-h-[50vh]">
<div className="w-8 h-8 border-4 border-indigo-500 border-t-transparent rounded-full animate-spin"></div>
</div>
);
}
return (
<div className="p-4 space-y-6">
<div className="flex items-center justify-between">
<h1 className="text-2xl font-bold text-gray-900">Settings</h1>
{saved && (
<span className="text-sm text-green-600 animate-fade-in-up">Saved</span>
)}
</div>
{/* Session Experience */}
<div>
<h2 className="text-lg font-semibold text-gray-900 mb-3">Session Experience</h2>
<div className="bg-white rounded-xl shadow-sm divide-y divide-gray-100">
{/* Sound */}
<div className="flex items-center justify-between p-4">
<div className="flex items-center gap-3">
{prefs.sound_enabled ? (
<VolumeIcon size={20} className="text-indigo-500" />
) : (
<VolumeOffIcon size={20} className="text-gray-400" />
)}
<div>
<p className="font-medium text-gray-900">Sound effects</p>
<p className="text-sm text-gray-500">Subtle audio cues on step completion</p>
</div>
</div>
<button
onClick={() => {
updatePref('sound_enabled', !prefs.sound_enabled);
if (!prefs.sound_enabled) playStepComplete();
}}
className={`w-12 h-7 rounded-full transition-colors ${
prefs.sound_enabled ? 'bg-indigo-500' : 'bg-gray-300'
}`}
>
<div className={`w-5 h-5 bg-white rounded-full shadow transition-transform ml-1 ${
prefs.sound_enabled ? 'translate-x-5' : ''
}`} />
</button>
</div>
{/* Haptics */}
<div className="flex items-center justify-between p-4">
<div className="flex items-center gap-3">
<SparklesIcon size={20} className={prefs.haptic_enabled ? 'text-indigo-500' : 'text-gray-400'} />
<div>
<p className="font-medium text-gray-900">Haptic feedback</p>
<p className="text-sm text-gray-500">Gentle vibration on actions</p>
</div>
</div>
<button
onClick={() => {
updatePref('haptic_enabled', !prefs.haptic_enabled);
if (!prefs.haptic_enabled) hapticTap();
}}
className={`w-12 h-7 rounded-full transition-colors ${
prefs.haptic_enabled ? 'bg-indigo-500' : 'bg-gray-300'
}`}
>
<div className={`w-5 h-5 bg-white rounded-full shadow transition-transform ml-1 ${
prefs.haptic_enabled ? 'translate-x-5' : ''
}`} />
</button>
</div>
{/* Launch Screen */}
<div className="flex items-center justify-between p-4">
<div>
<p className="font-medium text-gray-900">Pre-routine launch screen</p>
<p className="text-sm text-gray-500">Environment check and emotion bridge</p>
</div>
<button
onClick={() => updatePref('show_launch_screen', !prefs.show_launch_screen)}
className={`w-12 h-7 rounded-full transition-colors ${
prefs.show_launch_screen ? 'bg-indigo-500' : 'bg-gray-300'
}`}
>
<div className={`w-5 h-5 bg-white rounded-full shadow transition-transform ml-1 ${
prefs.show_launch_screen ? 'translate-x-5' : ''
}`} />
</button>
</div>
</div>
</div>
{/* Celebration Style */}
<div>
<h2 className="text-lg font-semibold text-gray-900 mb-3">Celebration Style</h2>
<div className="bg-white rounded-xl shadow-sm divide-y divide-gray-100">
{[
{ value: 'standard', label: 'Standard', desc: 'Full animated celebration with stats and rewards' },
{ value: 'quick', label: 'Quick', desc: 'Brief confirmation, then back to dashboard' },
{ value: 'none', label: 'None', desc: 'No celebration screen, return immediately' },
].map(option => (
<button
key={option.value}
onClick={() => updatePref('celebration_style', option.value)}
className="w-full flex items-center justify-between p-4 text-left"
>
<div>
<p className="font-medium text-gray-900">{option.label}</p>
<p className="text-sm text-gray-500">{option.desc}</p>
</div>
<div className={`w-5 h-5 rounded-full border-2 flex items-center justify-center ${
prefs.celebration_style === option.value
? 'border-indigo-500'
: 'border-gray-300'
}`}>
{prefs.celebration_style === option.value && (
<div className="w-2.5 h-2.5 rounded-full bg-indigo-500" />
)}
</div>
</button>
))}
</div>
</div>
</div>
);
}