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:
2025-10-03 11:59:27 +00:00
parent 49ac1dd58a
commit c27f72e41d
5 changed files with 878 additions and 129 deletions

View File

@@ -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>