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:
@@ -1,53 +1,33 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import { useEffect } from 'react';
|
||||
import {
|
||||
Box,
|
||||
FormControl,
|
||||
RadioGroup,
|
||||
FormControlLabel,
|
||||
Radio,
|
||||
Button,
|
||||
CircularProgress,
|
||||
Alert,
|
||||
Typography,
|
||||
} from '@mui/material';
|
||||
import { Save, Schedule } from '@mui/icons-material';
|
||||
import { Schedule } from '@mui/icons-material';
|
||||
import { useAuth } from '@/lib/auth/AuthContext';
|
||||
import { usersApi } from '@/lib/api/users';
|
||||
import { useTranslation } from '@/hooks/useTranslation';
|
||||
|
||||
export function TimeFormatSelector() {
|
||||
const { user, refreshUser } = useAuth();
|
||||
interface TimeFormatSelectorProps {
|
||||
value: '12h' | '24h';
|
||||
onChange: (timeFormat: '12h' | '24h') => void;
|
||||
}
|
||||
|
||||
export function TimeFormatSelector({ value, onChange }: TimeFormatSelectorProps) {
|
||||
const { user } = useAuth();
|
||||
const { t } = useTranslation('settings');
|
||||
const [timeFormat, setTimeFormat] = useState<'12h' | '24h'>(
|
||||
user?.preferences?.timeFormat || '12h'
|
||||
);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [successMessage, setSuccessMessage] = useState<string | null>(null);
|
||||
|
||||
const handleSave = async () => {
|
||||
setIsLoading(true);
|
||||
setError(null);
|
||||
setSuccessMessage(null);
|
||||
|
||||
try {
|
||||
await usersApi.updateProfile({
|
||||
preferences: {
|
||||
...user?.preferences,
|
||||
timeFormat,
|
||||
},
|
||||
});
|
||||
await refreshUser();
|
||||
setSuccessMessage('Time format updated successfully');
|
||||
} catch (err: any) {
|
||||
console.error('Failed to update time format:', err);
|
||||
setError(err.response?.data?.message || 'Failed to update time format');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
// Initialize with user's time format on mount
|
||||
useEffect(() => {
|
||||
if (user?.preferences?.timeFormat && !value) {
|
||||
onChange(user.preferences.timeFormat);
|
||||
}
|
||||
};
|
||||
}, [user?.preferences?.timeFormat]);
|
||||
|
||||
const currentTime = new Date();
|
||||
const preview12h = currentTime.toLocaleTimeString('en-US', {
|
||||
@@ -70,26 +50,14 @@ export function TimeFormatSelector() {
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
{error && (
|
||||
<Alert severity="error" sx={{ mb: 2 }} onClose={() => setError(null)}>
|
||||
{error}
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
{successMessage && (
|
||||
<Alert severity="success" sx={{ mb: 2 }} onClose={() => setSuccessMessage(null)}>
|
||||
{successMessage}
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
<FormControl component="fieldset" sx={{ mb: 2 }}>
|
||||
<FormControl component="fieldset">
|
||||
<RadioGroup
|
||||
value={timeFormat}
|
||||
onChange={(e) => setTimeFormat(e.target.value as '12h' | '24h')}
|
||||
value={value || user?.preferences?.timeFormat || '12h'}
|
||||
onChange={(e) => onChange(e.target.value as '12h' | '24h')}
|
||||
>
|
||||
<FormControlLabel
|
||||
value="12h"
|
||||
control={<Radio disabled={isLoading} />}
|
||||
control={<Radio />}
|
||||
label={
|
||||
<Box>
|
||||
<Typography variant="body1">12-hour format</Typography>
|
||||
@@ -101,7 +69,7 @@ export function TimeFormatSelector() {
|
||||
/>
|
||||
<FormControlLabel
|
||||
value="24h"
|
||||
control={<Radio disabled={isLoading} />}
|
||||
control={<Radio />}
|
||||
label={
|
||||
<Box>
|
||||
<Typography variant="body1">24-hour format</Typography>
|
||||
@@ -113,15 +81,6 @@ export function TimeFormatSelector() {
|
||||
/>
|
||||
</RadioGroup>
|
||||
</FormControl>
|
||||
|
||||
<Button
|
||||
variant="contained"
|
||||
startIcon={isLoading ? <CircularProgress size={20} color="inherit" /> : <Save />}
|
||||
onClick={handleSave}
|
||||
disabled={isLoading || timeFormat === user?.preferences?.timeFormat}
|
||||
>
|
||||
{isLoading ? 'Saving...' : 'Save'}
|
||||
</Button>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user