Skip to content

Commit f8d4ce8

Browse files
Feat: Display full email and add account settings
Display the full user email in the DashboardHeader and add a settings button for account management (email, password, delete).
1 parent ed19a6b commit f8d4ce8

2 files changed

Lines changed: 256 additions & 10 deletions

File tree

Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
2+
import { useState } from 'react';
3+
import { Button } from '@/components/ui/button';
4+
import { Input } from '@/components/ui/input';
5+
import { Label } from '@/components/ui/label';
6+
import {
7+
Dialog,
8+
DialogContent,
9+
DialogDescription,
10+
DialogFooter,
11+
DialogHeader,
12+
DialogTitle,
13+
DialogTrigger,
14+
} from '@/components/ui/dialog';
15+
import {
16+
AlertDialog,
17+
AlertDialogAction,
18+
AlertDialogCancel,
19+
AlertDialogContent,
20+
AlertDialogDescription,
21+
AlertDialogFooter,
22+
AlertDialogHeader,
23+
AlertDialogTitle,
24+
AlertDialogTrigger,
25+
} from '@/components/ui/alert-dialog';
26+
import { Settings, Trash2 } from 'lucide-react';
27+
import { useAuth } from '@/hooks/useAuth';
28+
import { updateEmail, updatePassword, deleteUser, reauthenticateWithCredential, EmailAuthProvider } from 'firebase/auth';
29+
import { useToast } from '@/hooks/use-toast';
30+
31+
export const AccountSettingsDialog = () => {
32+
const { user } = useAuth();
33+
const { toast } = useToast();
34+
const [open, setOpen] = useState(false);
35+
const [loading, setLoading] = useState(false);
36+
37+
const [newEmail, setNewEmail] = useState(user?.email || '');
38+
const [newPassword, setNewPassword] = useState('');
39+
const [currentPassword, setCurrentPassword] = useState('');
40+
41+
const handleUpdateEmail = async () => {
42+
if (!user || !currentPassword) {
43+
toast({
44+
title: "Erreur",
45+
description: "Mot de passe actuel requis",
46+
variant: "destructive",
47+
});
48+
return;
49+
}
50+
51+
setLoading(true);
52+
try {
53+
// Ré-authentification requise pour changer l'email
54+
const credential = EmailAuthProvider.credential(user.email!, currentPassword);
55+
await reauthenticateWithCredential(user, credential);
56+
57+
await updateEmail(user, newEmail);
58+
toast({
59+
title: "Email mis à jour",
60+
description: "Votre email a été modifié avec succès",
61+
});
62+
setCurrentPassword('');
63+
} catch (error: any) {
64+
toast({
65+
title: "Erreur",
66+
description: error.message || "Impossible de mettre à jour l'email",
67+
variant: "destructive",
68+
});
69+
} finally {
70+
setLoading(false);
71+
}
72+
};
73+
74+
const handleUpdatePassword = async () => {
75+
if (!user || !currentPassword || !newPassword) {
76+
toast({
77+
title: "Erreur",
78+
description: "Mot de passe actuel et nouveau mot de passe requis",
79+
variant: "destructive",
80+
});
81+
return;
82+
}
83+
84+
setLoading(true);
85+
try {
86+
// Ré-authentification requise pour changer le mot de passe
87+
const credential = EmailAuthProvider.credential(user.email!, currentPassword);
88+
await reauthenticateWithCredential(user, credential);
89+
90+
await updatePassword(user, newPassword);
91+
toast({
92+
title: "Mot de passe mis à jour",
93+
description: "Votre mot de passe a été modifié avec succès",
94+
});
95+
setCurrentPassword('');
96+
setNewPassword('');
97+
} catch (error: any) {
98+
toast({
99+
title: "Erreur",
100+
description: error.message || "Impossible de mettre à jour le mot de passe",
101+
variant: "destructive",
102+
});
103+
} finally {
104+
setLoading(false);
105+
}
106+
};
107+
108+
const handleDeleteAccount = async () => {
109+
if (!user || !currentPassword) {
110+
toast({
111+
title: "Erreur",
112+
description: "Mot de passe actuel requis",
113+
variant: "destructive",
114+
});
115+
return;
116+
}
117+
118+
setLoading(true);
119+
try {
120+
// Ré-authentification requise pour supprimer le compte
121+
const credential = EmailAuthProvider.credential(user.email!, currentPassword);
122+
await reauthenticateWithCredential(user, credential);
123+
124+
await deleteUser(user);
125+
toast({
126+
title: "Compte supprimé",
127+
description: "Votre compte a été supprimé avec succès",
128+
});
129+
} catch (error: any) {
130+
toast({
131+
title: "Erreur",
132+
description: error.message || "Impossible de supprimer le compte",
133+
variant: "destructive",
134+
});
135+
} finally {
136+
setLoading(false);
137+
}
138+
};
139+
140+
return (
141+
<Dialog open={open} onOpenChange={setOpen}>
142+
<DialogTrigger asChild>
143+
<Button variant="outline" size="sm" className="rounded-lg">
144+
<Settings className="h-4 w-4" />
145+
</Button>
146+
</DialogTrigger>
147+
<DialogContent className="sm:max-w-[425px]">
148+
<DialogHeader>
149+
<DialogTitle>Paramètres du compte</DialogTitle>
150+
<DialogDescription>
151+
Modifiez vos informations personnelles ou supprimez votre compte.
152+
</DialogDescription>
153+
</DialogHeader>
154+
155+
<div className="space-y-4">
156+
{/* Mot de passe actuel */}
157+
<div className="space-y-2">
158+
<Label htmlFor="currentPassword">Mot de passe actuel</Label>
159+
<Input
160+
id="currentPassword"
161+
type="password"
162+
value={currentPassword}
163+
onChange={(e) => setCurrentPassword(e.target.value)}
164+
placeholder="Entrez votre mot de passe actuel"
165+
/>
166+
</div>
167+
168+
{/* Nouvel email */}
169+
<div className="space-y-2">
170+
<Label htmlFor="newEmail">Nouvel email</Label>
171+
<Input
172+
id="newEmail"
173+
type="email"
174+
value={newEmail}
175+
onChange={(e) => setNewEmail(e.target.value)}
176+
placeholder="Entrez votre nouvel email"
177+
/>
178+
<Button
179+
onClick={handleUpdateEmail}
180+
disabled={loading || !currentPassword || newEmail === user?.email}
181+
size="sm"
182+
>
183+
Mettre à jour l'email
184+
</Button>
185+
</div>
186+
187+
{/* Nouveau mot de passe */}
188+
<div className="space-y-2">
189+
<Label htmlFor="newPassword">Nouveau mot de passe</Label>
190+
<Input
191+
id="newPassword"
192+
type="password"
193+
value={newPassword}
194+
onChange={(e) => setNewPassword(e.target.value)}
195+
placeholder="Entrez votre nouveau mot de passe"
196+
/>
197+
<Button
198+
onClick={handleUpdatePassword}
199+
disabled={loading || !currentPassword || !newPassword}
200+
size="sm"
201+
>
202+
Mettre à jour le mot de passe
203+
</Button>
204+
</div>
205+
206+
{/* Supprimer le compte */}
207+
<div className="pt-4 border-t">
208+
<AlertDialog>
209+
<AlertDialogTrigger asChild>
210+
<Button variant="destructive" size="sm" disabled={!currentPassword}>
211+
<Trash2 className="h-4 w-4 mr-2" />
212+
Supprimer le compte
213+
</Button>
214+
</AlertDialogTrigger>
215+
<AlertDialogContent>
216+
<AlertDialogHeader>
217+
<AlertDialogTitle>Êtes-vous sûr ?</AlertDialogTitle>
218+
<AlertDialogDescription>
219+
Cette action est irréversible. Votre compte et toutes vos données seront définitivement supprimés.
220+
</AlertDialogDescription>
221+
</AlertDialogHeader>
222+
<AlertDialogFooter>
223+
<AlertDialogCancel>Annuler</AlertDialogCancel>
224+
<AlertDialogAction onClick={handleDeleteAccount} className="bg-red-600 hover:bg-red-700">
225+
Supprimer définitivement
226+
</AlertDialogAction>
227+
</AlertDialogFooter>
228+
</AlertDialogContent>
229+
</AlertDialog>
230+
</div>
231+
</div>
232+
233+
<DialogFooter>
234+
<Button variant="outline" onClick={() => setOpen(false)}>
235+
Fermer
236+
</Button>
237+
</DialogFooter>
238+
</DialogContent>
239+
</Dialog>
240+
);
241+
};

src/components/DashboardHeader.tsx

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { LogOut, Menu } from 'lucide-react';
44
import { Link } from 'react-router-dom';
55
import { useState } from 'react';
66
import { RefSpringLogo } from '@/components/RefSpringLogo';
7+
import { AccountSettingsDialog } from '@/components/AccountSettingsDialog';
78

89
interface DashboardHeaderProps {
910
user: any;
@@ -32,9 +33,10 @@ export const DashboardHeader = ({ user, onLogout }: DashboardHeaderProps) => {
3233

3334
{/* Desktop Navigation */}
3435
<div className="hidden lg:flex items-center space-x-4">
35-
<div className="text-sm text-slate-700 bg-white/80 backdrop-blur-sm px-4 py-2 rounded-full border border-slate-200 max-w-[200px] truncate">
36+
<div className="text-sm text-slate-700 bg-white/80 backdrop-blur-sm px-4 py-2 rounded-full border border-slate-200">
3637
Bonjour, <span className="font-semibold">{user?.displayName || user?.email}</span>
3738
</div>
39+
<AccountSettingsDialog />
3840
<Button
3941
variant="outline"
4042
size="sm"
@@ -64,15 +66,18 @@ export const DashboardHeader = ({ user, onLogout }: DashboardHeaderProps) => {
6466
<div className="text-sm text-slate-700 px-4 py-2 bg-slate-50 rounded-lg">
6567
Bonjour, <span className="font-semibold">{user?.displayName || user?.email}</span>
6668
</div>
67-
<Button
68-
variant="outline"
69-
size="sm"
70-
onClick={onLogout}
71-
className="w-full justify-start rounded-lg"
72-
>
73-
<LogOut className="h-4 w-4 mr-2" />
74-
Déconnexion
75-
</Button>
69+
<div className="flex gap-2 px-4">
70+
<AccountSettingsDialog />
71+
<Button
72+
variant="outline"
73+
size="sm"
74+
onClick={onLogout}
75+
className="flex-1 justify-start rounded-lg"
76+
>
77+
<LogOut className="h-4 w-4 mr-2" />
78+
Déconnexion
79+
</Button>
80+
</div>
7681
</div>
7782
</div>
7883
)}

0 commit comments

Comments
 (0)