refactor: Consolidate settings page save buttons for better UX
Improved the settings page by removing individual save buttons from each preference component and adding unified save buttons per section: ## Changes Made ### Component Updates - **TimeZoneSelector**: Converted to controlled component with value/onChange props * Removed internal state management and save button * Removed success/error alerts (now handled by parent) * Added auto-detect as simple button without save - **TimeFormatSelector**: Converted to controlled component with value/onChange props * Removed internal state management and save button * Removed success/error alerts (now handled by parent) * Simplified to just radio buttons with preview ### Settings Page Improvements - Added timezone and timeFormat to local state - Created separate save handlers: * `handleSaveProfile` - for name/email changes * `handleSavePreferences` - for timezone and time format - Three clear sections with dedicated save buttons: 1. **Profile Information** → "Save Profile" button 2. **Preferences** (Language, Units, Timezone, Time Format) → "Save Preferences" button 3. **Notifications** → "Save Notification Settings" button ### User Experience Benefits - Clearer separation between different types of settings - Single save action per logical section instead of multiple buttons - Consistent save pattern across all settings cards - Reduced visual clutter with fewer buttons on page - Better organization: related settings grouped with one save action Files changed: 3 files (TimeZoneSelector, TimeFormatSelector, settings page) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -22,6 +22,8 @@ import { motion } from 'framer-motion';
|
||||
export default function SettingsPage() {
|
||||
const { user, logout, refreshUser } = useAuth();
|
||||
const [name, setName] = useState(user?.name || '');
|
||||
const [timezone, setTimezone] = useState(user?.timezone || 'UTC');
|
||||
const [timeFormat, setTimeFormat] = useState<'12h' | '24h'>(user?.preferences?.timeFormat || '12h');
|
||||
const [settings, setSettings] = useState({
|
||||
notifications: true,
|
||||
emailUpdates: false,
|
||||
@@ -40,17 +42,48 @@ export default function SettingsPage() {
|
||||
emailUpdates: user.preferences.emailUpdates ?? false,
|
||||
darkMode: user.preferences.darkMode ?? false,
|
||||
});
|
||||
setTimeFormat(user.preferences.timeFormat || '12h');
|
||||
}
|
||||
}, [user?.preferences]);
|
||||
|
||||
// Sync name state when user data changes
|
||||
// Sync name and timezone state when user data changes
|
||||
useEffect(() => {
|
||||
if (user?.name) {
|
||||
setName(user.name);
|
||||
}
|
||||
if (user?.timezone) {
|
||||
setTimezone(user.timezone);
|
||||
}
|
||||
}, [user]);
|
||||
|
||||
const handleSave = async () => {
|
||||
const handleSavePreferences = async () => {
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
|
||||
try {
|
||||
const response = await usersApi.updateProfile({
|
||||
timezone,
|
||||
preferences: {
|
||||
...settings,
|
||||
timeFormat,
|
||||
}
|
||||
});
|
||||
console.log('✅ Preferences updated successfully:', response);
|
||||
|
||||
// Refresh user to get latest data from server
|
||||
await refreshUser();
|
||||
|
||||
setSuccessMessage('Preferences saved successfully!');
|
||||
} catch (err: any) {
|
||||
console.error('❌ Failed to update preferences:', err);
|
||||
console.error('Error response:', err.response);
|
||||
setError(err.response?.data?.message || err.message || 'Failed to save preferences. Please try again.');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSaveProfile = async () => {
|
||||
// Validate name
|
||||
if (!name || name.trim() === '') {
|
||||
setNameError('Name cannot be empty');
|
||||
@@ -64,7 +97,6 @@ export default function SettingsPage() {
|
||||
try {
|
||||
const response = await usersApi.updateProfile({
|
||||
name: name.trim(),
|
||||
preferences: settings
|
||||
});
|
||||
console.log('✅ Profile updated successfully:', response);
|
||||
|
||||
@@ -143,11 +175,11 @@ export default function SettingsPage() {
|
||||
<Button
|
||||
variant="contained"
|
||||
startIcon={isLoading ? <CircularProgress size={20} color="inherit" /> : <Save />}
|
||||
onClick={handleSave}
|
||||
onClick={handleSaveProfile}
|
||||
disabled={isLoading}
|
||||
sx={{ alignSelf: 'flex-start' }}
|
||||
>
|
||||
{isLoading ? 'Saving...' : 'Save Changes'}
|
||||
{isLoading ? 'Saving...' : 'Save Profile'}
|
||||
</Button>
|
||||
</Box>
|
||||
</CardContent>
|
||||
@@ -170,10 +202,19 @@ export default function SettingsPage() {
|
||||
<Divider />
|
||||
<MeasurementUnitSelector />
|
||||
<Divider />
|
||||
<TimeZoneSelector />
|
||||
<TimeZoneSelector value={timezone} onChange={setTimezone} />
|
||||
<Divider />
|
||||
<TimeFormatSelector />
|
||||
<TimeFormatSelector value={timeFormat} onChange={setTimeFormat} />
|
||||
</Box>
|
||||
<Button
|
||||
variant="contained"
|
||||
startIcon={isLoading ? <CircularProgress size={20} color="inherit" /> : <Save />}
|
||||
onClick={handleSavePreferences}
|
||||
disabled={isLoading}
|
||||
sx={{ mt: 3, alignSelf: 'flex-start' }}
|
||||
>
|
||||
{isLoading ? 'Saving...' : 'Save Preferences'}
|
||||
</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</motion.div>
|
||||
@@ -214,11 +255,11 @@ export default function SettingsPage() {
|
||||
<Button
|
||||
variant="contained"
|
||||
startIcon={isLoading ? <CircularProgress size={20} color="inherit" /> : <Save />}
|
||||
onClick={handleSave}
|
||||
onClick={handleSavePreferences}
|
||||
disabled={isLoading}
|
||||
sx={{ mt: 2, alignSelf: 'flex-start' }}
|
||||
>
|
||||
{isLoading ? 'Saving...' : 'Save Preferences'}
|
||||
{isLoading ? 'Saving...' : 'Save Notification Settings'}
|
||||
</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
Reference in New Issue
Block a user