From afa7e59c34b63d6a97418dfd128675920c6c47dc Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 22 Feb 2026 14:32:42 +0100 Subject: [PATCH] feat: Implement Apple-inspired design system with global dark/light mode styles and utility classes. --- src/app/(dashboard)/settings/page.tsx | 37 ++-- src/app/globals.css | 19 +- .../shared/change-password-modal.tsx | 203 ++++++++++++++++++ .../admin/components/user-management.tsx | 77 ++++--- src/features/auth/components/login-form.tsx | 33 +-- 5 files changed, 294 insertions(+), 75 deletions(-) create mode 100644 src/components/shared/change-password-modal.tsx diff --git a/src/app/(dashboard)/settings/page.tsx b/src/app/(dashboard)/settings/page.tsx index 7756641..1677677 100644 --- a/src/app/(dashboard)/settings/page.tsx +++ b/src/app/(dashboard)/settings/page.tsx @@ -14,15 +14,12 @@ interface OpenRouterModel { id: string; name: string; free: boolean; } function Section({ title, subtitle, children }: { title: string; subtitle?: string; children: React.ReactNode }) { return ( -
-
-

{title}

- {subtitle &&

{subtitle}

} +
+
+

{title}

+ {subtitle &&

{subtitle}

}
-
+
{children}
@@ -32,9 +29,9 @@ function Section({ title, subtitle, children }: { title: string; subtitle?: stri function Field({ label, hint, children }: { label: string; hint?: string; children: React.ReactNode }) { return (
- + {children} - {hint &&

{hint}

} + {hint &&

{hint}

}
); } @@ -151,8 +148,8 @@ export default function SettingsPage() { return (
-

Paramètres

-

Configuration de la connexion et des services.

+

Paramètres

+

Configuration de la connexion et des services.

{/* PostgreSQL */} @@ -214,7 +211,7 @@ export default function SettingsPage() { onChange={(e) => setSsl(e.target.checked)} className="w-3.5 h-3.5 rounded accent-teal-500 cursor-pointer" /> - +
+
+
+ + {/* Confirm */} +
+ +
+ setConfirm(e.target.value)} + placeholder="Répétez le mot de passe" + className="apple-input pr-10" + required + /> + +
+
+ + {/* Password match indicator */} + {confirm.length > 0 && ( +
+ + {password === confirm ? "Les mots de passe correspondent" : "Les mots de passe ne correspondent pas"} +
+ )} + + {/* Error */} + {error && ( +
+ {error} +
+ )} + + {/* Actions */} +
+ + +
+ + + + + ); +} diff --git a/src/features/admin/components/user-management.tsx b/src/features/admin/components/user-management.tsx index 15bd7b9..04fa663 100644 --- a/src/features/admin/components/user-management.tsx +++ b/src/features/admin/components/user-management.tsx @@ -12,6 +12,7 @@ import { Key, } from "lucide-react"; import { SuccessModal } from "@/components/shared/success-modal"; +import { ChangePasswordModal } from "@/components/shared/change-password-modal"; export function UserManagement() { const [users, setUsers] = useState([]); @@ -21,6 +22,9 @@ export function UserManagement() { const [newPassword, setNewPassword] = useState(""); const [newRole, setNewRole] = useState<"admin" | "user">("user"); const [modal, setModal] = useState({ isOpen: false, title: "", message: "" }); + const [pwdModal, setPwdModal] = useState<{ isOpen: boolean; userId: number; username: string }>({ + isOpen: false, userId: 0, username: "" + }); const fetchUsers = async () => { setLoading(true); @@ -57,24 +61,20 @@ export function UserManagement() { setIsCreating(false); }; - const handleUpdatePassword = async (id: number, username: string) => { - const pass = window.prompt(`Nouveau mot de passe pour ${username} :`); - if (!pass) return; + const handleUpdatePassword = (id: number, username: string) => { + setPwdModal({ isOpen: true, userId: id, username }); + }; - if (pass.length < 4) { - alert("Le mot de passe doit faire au moins 4 caractères."); - return; - } - - const res = await updatePassword(id, pass); + const handleConfirmPassword = async (newPassword: string) => { + const res = await updatePassword(pwdModal.userId, newPassword); if (res.success) { setModal({ isOpen: true, title: "Mot de passe mis à jour", - message: `Le mot de passe de ${username} a été modifié avec succès.` + message: `Le mot de passe de ${pwdModal.username} a été modifié avec succès.` }); } else { - alert(res.error); + throw new Error(res.error ?? "Erreur lors de la mise à jour."); } }; @@ -91,8 +91,8 @@ export function UserManagement() { if (loading) { return (
- -

Chargement des utilisateurs...

+ +

Chargement des utilisateurs...

); } @@ -100,45 +100,45 @@ export function UserManagement() { return (
{/* Formulaire de création */} -
+
-
- +
+
-

Nouvel Utilisateur

-

Ajoutez un collaborateur à la plateforme.

+

Nouvel Utilisateur

+

Ajoutez un collaborateur à la plateforme.

- + setNewUsername(e.target.value)} - className="w-full bg-slate-900/50 border border-slate-700/50 rounded-xl px-4 py-3 text-sm text-slate-100 outline-none focus:border-indigo-500 transition-colors" + className="apple-input" placeholder="ex: jean.dupont" />
- + setNewPassword(e.target.value)} - className="w-full bg-slate-900/50 border border-slate-700/50 rounded-xl px-4 py-3 text-sm text-slate-100 outline-none focus:border-indigo-500 transition-colors" + className="apple-input" placeholder="••••••••" />
- +
-
{error && ( -
-
+
+
{error}
)} @@ -64,7 +64,8 @@ export function LoginForm() { -
-

+

+

Plateforme d'Arbitrage Magasin
Propulsée par l'Intelligence Artificielle