Notification updates
This commit is contained in:
@@ -7,6 +7,7 @@ import { useAuth0 } from '@auth0/auth0-react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useUnits } from '../core/units/UnitsContext';
|
||||
import { useAdminAccess } from '../core/auth/useAdminAccess';
|
||||
import { useProfile, useUpdateProfile } from '../features/settings/hooks/useProfile';
|
||||
import {
|
||||
Box,
|
||||
Typography,
|
||||
@@ -21,7 +22,9 @@ import {
|
||||
Button as MuiButton,
|
||||
Select,
|
||||
MenuItem,
|
||||
FormControl
|
||||
FormControl,
|
||||
TextField,
|
||||
CircularProgress
|
||||
} from '@mui/material';
|
||||
import AccountCircleIcon from '@mui/icons-material/AccountCircle';
|
||||
import NotificationsIcon from '@mui/icons-material/Notifications';
|
||||
@@ -29,6 +32,9 @@ import PaletteIcon from '@mui/icons-material/Palette';
|
||||
import SecurityIcon from '@mui/icons-material/Security';
|
||||
import StorageIcon from '@mui/icons-material/Storage';
|
||||
import AdminPanelSettingsIcon from '@mui/icons-material/AdminPanelSettings';
|
||||
import EditIcon from '@mui/icons-material/Edit';
|
||||
import SaveIcon from '@mui/icons-material/Save';
|
||||
import CancelIcon from '@mui/icons-material/Cancel';
|
||||
import { Card } from '../shared-minimal/components/Card';
|
||||
|
||||
export const SettingsPage: React.FC = () => {
|
||||
@@ -40,10 +46,59 @@ export const SettingsPage: React.FC = () => {
|
||||
const [emailUpdates, setEmailUpdates] = useState(false);
|
||||
const [darkMode, setDarkMode] = useState(false);
|
||||
|
||||
// Profile state
|
||||
const { data: profile, isLoading: profileLoading } = useProfile();
|
||||
const updateProfileMutation = useUpdateProfile();
|
||||
const [isEditingProfile, setIsEditingProfile] = useState(false);
|
||||
const [editedDisplayName, setEditedDisplayName] = useState('');
|
||||
const [editedNotificationEmail, setEditedNotificationEmail] = useState('');
|
||||
|
||||
// Initialize edit form when profile loads or edit mode starts
|
||||
React.useEffect(() => {
|
||||
if (profile && isEditingProfile) {
|
||||
setEditedDisplayName(profile.displayName || '');
|
||||
setEditedNotificationEmail(profile.notificationEmail || '');
|
||||
}
|
||||
}, [profile, isEditingProfile]);
|
||||
|
||||
const handleLogout = () => {
|
||||
logout({ logoutParams: { returnTo: window.location.origin } });
|
||||
};
|
||||
|
||||
const handleEditProfile = () => {
|
||||
setIsEditingProfile(true);
|
||||
};
|
||||
|
||||
const handleCancelEdit = () => {
|
||||
setIsEditingProfile(false);
|
||||
setEditedDisplayName(profile?.displayName || '');
|
||||
setEditedNotificationEmail(profile?.notificationEmail || '');
|
||||
};
|
||||
|
||||
const handleSaveProfile = async () => {
|
||||
const updates: { displayName?: string; notificationEmail?: string } = {};
|
||||
|
||||
if (editedDisplayName !== (profile?.displayName || '')) {
|
||||
updates.displayName = editedDisplayName;
|
||||
}
|
||||
|
||||
if (editedNotificationEmail !== (profile?.notificationEmail || '')) {
|
||||
updates.notificationEmail = editedNotificationEmail || undefined;
|
||||
}
|
||||
|
||||
if (Object.keys(updates).length === 0) {
|
||||
setIsEditingProfile(false);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await updateProfileMutation.mutateAsync(updates);
|
||||
setIsEditingProfile(false);
|
||||
} catch (error) {
|
||||
// Error is handled by the mutation hook
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Box sx={{ py: 2 }}>
|
||||
<Typography variant="h4" sx={{ fontWeight: 700, color: 'text.primary', mb: 4 }}>
|
||||
@@ -51,69 +106,149 @@ export const SettingsPage: React.FC = () => {
|
||||
</Typography>
|
||||
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 3 }}>
|
||||
{/* Account Section */}
|
||||
{/* Profile Section */}
|
||||
<Card>
|
||||
<Typography variant="h6" sx={{ fontWeight: 600, mb: 3 }}>
|
||||
Account
|
||||
</Typography>
|
||||
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', mb: 3 }}>
|
||||
<Avatar
|
||||
sx={{
|
||||
width: 64,
|
||||
height: 64,
|
||||
bgcolor: 'primary.main',
|
||||
fontSize: '1.5rem',
|
||||
fontWeight: 600,
|
||||
mr: 3
|
||||
}}
|
||||
>
|
||||
{user?.name?.charAt(0) || user?.email?.charAt(0)}
|
||||
</Avatar>
|
||||
<Box>
|
||||
<Typography variant="h6" sx={{ fontWeight: 500 }}>
|
||||
{user?.name || 'User'}
|
||||
</Typography>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
{user?.email}
|
||||
</Typography>
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
Verified account
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 3 }}>
|
||||
<Typography variant="h6" sx={{ fontWeight: 600 }}>
|
||||
Profile
|
||||
</Typography>
|
||||
{!isEditingProfile && !profileLoading && (
|
||||
<MuiButton
|
||||
variant="outlined"
|
||||
size="small"
|
||||
startIcon={<EditIcon />}
|
||||
onClick={handleEditProfile}
|
||||
>
|
||||
Edit
|
||||
</MuiButton>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
<List disablePadding>
|
||||
<ListItem>
|
||||
<ListItemIcon>
|
||||
<AccountCircleIcon />
|
||||
</ListItemIcon>
|
||||
<ListItemText
|
||||
primary="Profile Information"
|
||||
secondary="Manage your account details"
|
||||
/>
|
||||
<ListItemSecondaryAction>
|
||||
<MuiButton variant="outlined" size="small">
|
||||
Edit
|
||||
</MuiButton>
|
||||
</ListItemSecondaryAction>
|
||||
</ListItem>
|
||||
<Divider />
|
||||
<ListItem>
|
||||
<ListItemIcon>
|
||||
<SecurityIcon />
|
||||
</ListItemIcon>
|
||||
<ListItemText
|
||||
primary="Security & Privacy"
|
||||
secondary="Password, two-factor authentication"
|
||||
/>
|
||||
<ListItemSecondaryAction>
|
||||
<MuiButton variant="outlined" size="small">
|
||||
Manage
|
||||
</MuiButton>
|
||||
</ListItemSecondaryAction>
|
||||
</ListItem>
|
||||
</List>
|
||||
{profileLoading ? (
|
||||
<Box sx={{ display: 'flex', justifyContent: 'center', py: 4 }}>
|
||||
<CircularProgress />
|
||||
</Box>
|
||||
) : (
|
||||
<>
|
||||
<Box sx={{ display: 'flex', alignItems: 'center', mb: 3 }}>
|
||||
<Avatar
|
||||
sx={{
|
||||
width: 64,
|
||||
height: 64,
|
||||
bgcolor: 'primary.main',
|
||||
fontSize: '1.5rem',
|
||||
fontWeight: 600,
|
||||
mr: 3
|
||||
}}
|
||||
>
|
||||
{profile?.displayName?.charAt(0) || user?.name?.charAt(0) || user?.email?.charAt(0)}
|
||||
</Avatar>
|
||||
<Box>
|
||||
<Typography variant="h6" sx={{ fontWeight: 500 }}>
|
||||
{profile?.displayName || user?.name || 'User'}
|
||||
</Typography>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
{profile?.email || user?.email}
|
||||
</Typography>
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
Verified account
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
{isEditingProfile ? (
|
||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 3 }}>
|
||||
<TextField
|
||||
label="Email"
|
||||
value={profile?.email || ''}
|
||||
disabled
|
||||
fullWidth
|
||||
helperText="Email is managed by your account provider and cannot be changed here"
|
||||
variant="outlined"
|
||||
/>
|
||||
<TextField
|
||||
label="Display Name"
|
||||
value={editedDisplayName}
|
||||
onChange={(e) => setEditedDisplayName(e.target.value)}
|
||||
fullWidth
|
||||
placeholder="Enter your display name"
|
||||
variant="outlined"
|
||||
/>
|
||||
<TextField
|
||||
label="Notification Email"
|
||||
value={editedNotificationEmail}
|
||||
onChange={(e) => setEditedNotificationEmail(e.target.value)}
|
||||
fullWidth
|
||||
placeholder="Leave blank to use your primary email"
|
||||
helperText="Optional: Use a different email address for notifications"
|
||||
variant="outlined"
|
||||
type="email"
|
||||
/>
|
||||
<Box sx={{ display: 'flex', gap: 2, justifyContent: 'flex-end' }}>
|
||||
<MuiButton
|
||||
variant="outlined"
|
||||
startIcon={<CancelIcon />}
|
||||
onClick={handleCancelEdit}
|
||||
disabled={updateProfileMutation.isPending}
|
||||
>
|
||||
Cancel
|
||||
</MuiButton>
|
||||
<MuiButton
|
||||
variant="contained"
|
||||
startIcon={updateProfileMutation.isPending ? <CircularProgress size={20} /> : <SaveIcon />}
|
||||
onClick={handleSaveProfile}
|
||||
disabled={updateProfileMutation.isPending}
|
||||
>
|
||||
Save
|
||||
</MuiButton>
|
||||
</Box>
|
||||
</Box>
|
||||
) : (
|
||||
<List disablePadding>
|
||||
<ListItem>
|
||||
<ListItemIcon>
|
||||
<AccountCircleIcon />
|
||||
</ListItemIcon>
|
||||
<ListItemText
|
||||
primary="Email"
|
||||
secondary={profile?.email || user?.email || 'Not available'}
|
||||
/>
|
||||
</ListItem>
|
||||
<Divider />
|
||||
<ListItem>
|
||||
<ListItemText
|
||||
primary="Display Name"
|
||||
secondary={profile?.displayName || 'Not set'}
|
||||
sx={{ pl: 7 }}
|
||||
/>
|
||||
</ListItem>
|
||||
<Divider />
|
||||
<ListItem>
|
||||
<ListItemText
|
||||
primary="Notification Email"
|
||||
secondary={profile?.notificationEmail || 'Using primary email'}
|
||||
sx={{ pl: 7 }}
|
||||
/>
|
||||
</ListItem>
|
||||
<Divider />
|
||||
<ListItem>
|
||||
<ListItemIcon>
|
||||
<SecurityIcon />
|
||||
</ListItemIcon>
|
||||
<ListItemText
|
||||
primary="Security & Privacy"
|
||||
secondary="Password, two-factor authentication"
|
||||
/>
|
||||
<ListItemSecondaryAction>
|
||||
<MuiButton variant="outlined" size="small">
|
||||
Manage
|
||||
</MuiButton>
|
||||
</ListItemSecondaryAction>
|
||||
</ListItem>
|
||||
</List>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Card>
|
||||
|
||||
{/* Notifications Section */}
|
||||
@@ -306,6 +441,23 @@ export const SettingsPage: React.FC = () => {
|
||||
</MuiButton>
|
||||
</ListItemSecondaryAction>
|
||||
</ListItem>
|
||||
<Divider />
|
||||
<ListItem>
|
||||
<ListItemText
|
||||
primary="Email Templates"
|
||||
secondary="Manage notification email templates"
|
||||
sx={{ pl: 7 }}
|
||||
/>
|
||||
<ListItemSecondaryAction>
|
||||
<MuiButton
|
||||
variant="outlined"
|
||||
size="small"
|
||||
onClick={() => navigate('/garage/settings/admin/email-templates')}
|
||||
>
|
||||
Manage
|
||||
</MuiButton>
|
||||
</ListItemSecondaryAction>
|
||||
</ListItem>
|
||||
</List>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user