Checkpoint: Ajout de l'identifiant de connexion et modification du mot de passe dans la gestion des utilisateurs

Modifications apportées :
- Ajout du champ username (identifiant de connexion) dans le schéma de la base de données avec contrainte UNIQUE
- Mise à jour des procédures tRPC create et update pour gérer username et password
- Implémentation du hashage du mot de passe avec bcryptjs dans createUser et updateUser
- Ajout de la colonne "Identifiant" dans le tableau de la page de gestion des utilisateurs
- Ajout des champs "Identifiant de connexion" et "Mot de passe" dans le formulaire de création
- Ajout des champs "Identifiant de connexion" et "Nouveau mot de passe" dans le formulaire d'édition avec notes explicatives
- Les mots de passe sont hashés avant d'être stockés en base de données pour la sécurité
- Dans le formulaire d'édition, les champs username et password sont optionnels (laissez vide pour ne pas modifier)
This commit is contained in:
Manus Sandbox
2025-11-21 10:08:56 -05:00
parent fad88c5257
commit 8a61f9b823
13 changed files with 1011 additions and 3 deletions

View File

@@ -44,11 +44,15 @@ export default function AdminUsers() {
openId: "",
name: "",
email: "",
username: "",
password: "",
role: "user" as "user" | "admin",
});
const [editForm, setEditForm] = useState({
name: "",
email: "",
username: "",
password: "",
role: "user" as "user" | "admin",
});
@@ -63,6 +67,8 @@ export default function AdminUsers() {
openId: "",
name: "",
email: "",
username: "",
password: "",
role: "user",
});
},
@@ -127,6 +133,8 @@ export default function AdminUsers() {
openId: createForm.openId,
name: createForm.name || undefined,
email: createForm.email || undefined,
username: createForm.username || undefined,
password: createForm.password || undefined,
role: createForm.role,
isActive: true,
});
@@ -137,6 +145,8 @@ export default function AdminUsers() {
setEditForm({
name: user.name || "",
email: user.email || "",
username: user.username || "",
password: "", // Toujours vide pour la sécurité
role: user.role,
});
setEditDialogOpen(true);
@@ -145,12 +155,23 @@ export default function AdminUsers() {
const handleSaveEdit = () => {
if (!selectedUser) return;
updateMutation.mutate({
// Ne pas envoyer username et password s'ils sont vides
const updateData: any = {
id: selectedUser.id,
name: editForm.name || undefined,
email: editForm.email || undefined,
role: editForm.role,
});
};
if (editForm.username) {
updateData.username = editForm.username;
}
if (editForm.password) {
updateData.password = editForm.password;
}
updateMutation.mutate(updateData);
};
const handleToggleStatus = (user: any) => {
@@ -281,6 +302,7 @@ export default function AdminUsers() {
<TableHead>ID</TableHead>
<TableHead>Nom</TableHead>
<TableHead>Email</TableHead>
<TableHead>Identifiant</TableHead>
<TableHead>Rôle</TableHead>
<TableHead>Statut</TableHead>
<TableHead>Dernière connexion</TableHead>
@@ -291,7 +313,7 @@ export default function AdminUsers() {
<TableBody>
{filteredUsers.length === 0 ? (
<TableRow>
<TableCell colSpan={8} className="text-center py-8 text-muted-foreground">
<TableCell colSpan={9} className="text-center py-8 text-muted-foreground">
Aucun utilisateur trouvé
</TableCell>
</TableRow>
@@ -301,6 +323,7 @@ export default function AdminUsers() {
<TableCell className="font-medium">{user.id}</TableCell>
<TableCell>{user.name || "N/A"}</TableCell>
<TableCell>{user.email || "N/A"}</TableCell>
<TableCell>{user.username || "N/A"}</TableCell>
<TableCell>
<Badge variant={user.role === "admin" ? "default" : "secondary"}>
{user.role === "admin" ? "Administrateur" : "Utilisateur"}
@@ -403,6 +426,27 @@ export default function AdminUsers() {
/>
</div>
<div>
<Label htmlFor="createUsername">Identifiant de connexion</Label>
<Input
id="createUsername"
value={createForm.username}
onChange={(e) => setCreateForm({ ...createForm, username: e.target.value })}
placeholder="Identifiant unique pour la connexion"
/>
</div>
<div>
<Label htmlFor="createPassword">Mot de passe</Label>
<Input
id="createPassword"
type="password"
value={createForm.password}
onChange={(e) => setCreateForm({ ...createForm, password: e.target.value })}
placeholder="Minimum 6 caractères"
/>
</div>
<div>
<Label htmlFor="createRole">Rôle</Label>
<Select
@@ -465,6 +509,29 @@ export default function AdminUsers() {
/>
</div>
<div>
<Label htmlFor="editUsername">Identifiant de connexion</Label>
<Input
id="editUsername"
value={editForm.username}
onChange={(e) => setEditForm({ ...editForm, username: e.target.value })}
placeholder="Identifiant unique pour la connexion"
/>
<p className="text-xs text-muted-foreground mt-1">Laissez vide pour ne pas modifier</p>
</div>
<div>
<Label htmlFor="editPassword">Nouveau mot de passe</Label>
<Input
id="editPassword"
type="password"
value={editForm.password}
onChange={(e) => setEditForm({ ...editForm, password: e.target.value })}
placeholder="Laissez vide pour ne pas modifier"
/>
<p className="text-xs text-muted-foreground mt-1">Minimum 6 caractères si modifié</p>
</div>
<div>
<Label htmlFor="editRole">Rôle</Label>
<Select