refactor: Unify all settings into single Save Preferences button
Simplified the settings page by consolidating all save buttons into one global "Save Preferences" button at the bottom of the page. ## Changes Made ### Unified Save Handler - Merged `handleSaveProfile` and `handleSavePreferences` into single `handleSaveAll` function - Single save handler now updates: * Profile name * Timezone * Time format (12h/24h) * Notification preferences - One API call to save all settings at once ### Removed Individual Save Buttons - Removed "Save Profile" button from Profile Information section - Removed "Save Preferences" button from Preferences section - Removed "Save Notification Settings" button from Notifications section ### Added Global Save Button - Centered "Save Preferences" button at bottom of settings - Positioned above Account Actions (Logout) section - Large, prominent button (minWidth: 200px) - Single source of truth for all settings changes ### User Experience Benefits - **Simpler**: One clear action to save all changes - **Fewer clicks**: No need to save each section separately - **Clear feedback**: Single success/error message for all updates - **Better UX**: Users can make multiple changes and save once - **Consistent**: All settings treated as unified preferences Files changed: 1 file (settings page) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -56,34 +56,7 @@ export default function SettingsPage() {
|
||||
}
|
||||
}, [user]);
|
||||
|
||||
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 () => {
|
||||
const handleSaveAll = async () => {
|
||||
// Validate name
|
||||
if (!name || name.trim() === '') {
|
||||
setNameError('Name cannot be empty');
|
||||
@@ -97,17 +70,22 @@ export default function SettingsPage() {
|
||||
try {
|
||||
const response = await usersApi.updateProfile({
|
||||
name: name.trim(),
|
||||
timezone,
|
||||
preferences: {
|
||||
...settings,
|
||||
timeFormat,
|
||||
}
|
||||
});
|
||||
console.log('✅ Profile updated successfully:', response);
|
||||
console.log('✅ All settings saved successfully:', response);
|
||||
|
||||
// Refresh user to get latest data from server
|
||||
await refreshUser();
|
||||
|
||||
setSuccessMessage('Profile updated successfully!');
|
||||
setSuccessMessage('Settings saved successfully!');
|
||||
} catch (err: any) {
|
||||
console.error('❌ Failed to update profile:', err);
|
||||
console.error('❌ Failed to save settings:', err);
|
||||
console.error('Error response:', err.response);
|
||||
setError(err.response?.data?.message || err.message || 'Failed to update profile. Please try again.');
|
||||
setError(err.response?.data?.message || err.message || 'Failed to save settings. Please try again.');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
@@ -172,15 +150,6 @@ export default function SettingsPage() {
|
||||
disabled
|
||||
helperText="Email cannot be changed"
|
||||
/>
|
||||
<Button
|
||||
variant="contained"
|
||||
startIcon={isLoading ? <CircularProgress size={20} color="inherit" /> : <Save />}
|
||||
onClick={handleSaveProfile}
|
||||
disabled={isLoading}
|
||||
sx={{ alignSelf: 'flex-start' }}
|
||||
>
|
||||
{isLoading ? 'Saving...' : 'Save Profile'}
|
||||
</Button>
|
||||
</Box>
|
||||
</CardContent>
|
||||
</Card>
|
||||
@@ -206,15 +175,6 @@ export default function SettingsPage() {
|
||||
<Divider />
|
||||
<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>
|
||||
@@ -252,15 +212,6 @@ export default function SettingsPage() {
|
||||
label="Email Updates"
|
||||
/>
|
||||
</Box>
|
||||
<Button
|
||||
variant="contained"
|
||||
startIcon={isLoading ? <CircularProgress size={20} color="inherit" /> : <Save />}
|
||||
onClick={handleSavePreferences}
|
||||
disabled={isLoading}
|
||||
sx={{ mt: 2, alignSelf: 'flex-start' }}
|
||||
>
|
||||
{isLoading ? 'Saving...' : 'Save Notification Settings'}
|
||||
</Button>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</motion.div>
|
||||
@@ -358,6 +309,26 @@ export default function SettingsPage() {
|
||||
</Box>
|
||||
</motion.div>
|
||||
|
||||
{/* Global Save Button */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.4, delay: 0.44 }}
|
||||
>
|
||||
<Box sx={{ mb: 3, display: 'flex', justifyContent: 'center' }}>
|
||||
<Button
|
||||
variant="contained"
|
||||
size="large"
|
||||
startIcon={isLoading ? <CircularProgress size={20} color="inherit" /> : <Save />}
|
||||
onClick={handleSaveAll}
|
||||
disabled={isLoading}
|
||||
sx={{ minWidth: 200 }}
|
||||
>
|
||||
{isLoading ? 'Saving...' : 'Save Preferences'}
|
||||
</Button>
|
||||
</Box>
|
||||
</motion.div>
|
||||
|
||||
{/* Account Actions */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
|
||||
Reference in New Issue
Block a user