diff --git a/frontend-prep/src/pages/ChangePassword/ChangePassword.css b/frontend-prep/src/pages/ChangePassword/ChangePassword.css index 92bc854f..da333f4f 100644 --- a/frontend-prep/src/pages/ChangePassword/ChangePassword.css +++ b/frontend-prep/src/pages/ChangePassword/ChangePassword.css @@ -216,3 +216,109 @@ font-size: 0.85rem; font-weight: 400 !important; } + +/* Modal feedback */ +.cp-modal-overlay { + position: fixed; + inset: 0; + background: rgba(0, 0, 0, 0.6); + backdrop-filter: blur(4px); + display: flex; + align-items: center; + justify-content: center; + z-index: 1000; + padding: 1rem; +} + +.cp-modal { + background: var(--surface); + border: 1px solid var(--border); + border-radius: 1rem; + padding: 2rem 1.75rem 1.75rem; + width: 100%; + max-width: 22rem; + position: relative; + display: flex; + flex-direction: column; + align-items: center; + gap: 0.75rem; + text-align: center; + box-shadow: 0 25px 60px rgba(0, 0, 0, 0.5); +} + +.cp-modal-close { + position: absolute; + top: 0.75rem; + right: 0.75rem; + background: transparent; + border: none; + color: var(--text-muted); + cursor: pointer; + padding: 0.25rem; + display: flex; + align-items: center; + border-radius: 0.375rem; + transition: color 0.2s, background 0.2s; +} +.cp-modal-close:hover { color: var(--text); background: var(--bg); } + +.cp-modal-icon { + width: 3.5rem; + height: 3.5rem; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + margin-bottom: 0.25rem; +} +.cp-modal-icon--success { + background: rgba(34, 197, 94, 0.12); + border: 2px solid rgba(34, 197, 94, 0.35); + color: #4ade80; +} +.cp-modal-icon--error { + background: rgba(239, 68, 68, 0.12); + border: 2px solid rgba(239, 68, 68, 0.35); + color: #f87171; +} + +.cp-modal-title { + font-size: 1.1rem; + font-weight: 700; + color: var(--text); + margin: 0; +} + +.cp-modal-body { + font-size: 0.9rem; + color: var(--text-muted); + margin: 0; + line-height: 1.5; +} + +.cp-modal-btn { + margin-top: 0.5rem; + width: 100%; + padding: 0.7rem 1rem; + border: none; + border-radius: 0.5rem; + font-weight: 600; + font-size: 0.9rem; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + gap: 0.4rem; + transition: opacity 0.2s; +} +.cp-modal-btn:hover { opacity: 0.88; } +.cp-modal-btn--success { + background: linear-gradient(135deg, #7c3aed, #6d28d9); + color: #fff; + box-shadow: 0 4px 14px rgba(109, 40, 217, 0.4); +} +.cp-modal-btn--error { + background: rgba(239, 68, 68, 0.12); + color: #f87171; + border: 1px solid rgba(239, 68, 68, 0.3); +} diff --git a/frontend-prep/src/pages/ChangePassword/ChangePassword.tsx b/frontend-prep/src/pages/ChangePassword/ChangePassword.tsx index 615c43df..e3b636f4 100644 --- a/frontend-prep/src/pages/ChangePassword/ChangePassword.tsx +++ b/frontend-prep/src/pages/ChangePassword/ChangePassword.tsx @@ -1,5 +1,5 @@ import { useState } from "react"; -import { Lock, Eye, EyeOff, ShieldCheck } from "lucide-react"; +import { Lock, Eye, EyeOff, ShieldCheck, CheckCircle, AlertTriangle, X } from "lucide-react"; import "./ChangePassword.css"; import { changePassword } from "../../api/api"; import { useNavigate } from "react-router-dom"; @@ -14,8 +14,7 @@ const ChangePasswordPage = () => { const [showNew, setShowNew] = useState(false); const [showConfirm, setShowConfirm] = useState(false); const [isLoading, setIsLoading] = useState(false); - const [error, setError] = useState(""); - const [success, setSuccess] = useState(false); + const [modal, setModal] = useState<{ show: boolean; type: 'success' | 'error'; message: string }>({ show: false, type: 'success', message: '' }); const validate = (): string | null => { if (!currentPassword) return "Le mot de passe actuel est requis"; @@ -28,11 +27,10 @@ const ChangePasswordPage = () => { const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); - setError(""); const validationError = validate(); if (validationError) { - setError(validationError); + setModal({ show: true, type: 'error', message: validationError }); return; } @@ -40,16 +38,23 @@ const ChangePasswordPage = () => { try { const result = await changePassword(currentPassword, newPassword); if (result.success) { - setSuccess(true); - setTimeout(() => navigate("/user/accueil"), 1800); + setModal({ show: true, type: 'success', message: 'Mot de passe mis à jour avec succès !' }); } else { - setError(result.message); + setModal({ show: true, type: 'error', message: result.message || 'Erreur lors du changement de mot de passe.' }); } } finally { setIsLoading(false); } }; + const handleCloseModal = () => { + if (modal.type === 'success') { + navigate("/user/accueil"); + } else { + setModal(m => ({ ...m, show: false })); + } + }; + return (
@@ -65,20 +70,7 @@ const ChangePasswordPage = () => {
- {success ? ( -
- -

Mot de passe mis à jour avec succès !

-

Redirection en cours…

-
- ) : (
- {error && ( -
- ⚠️ {error} -
- )} - {/* Mot de passe actuel */}
@@ -180,10 +172,32 @@ const ChangePasswordPage = () => { {isLoading ? "Mise à jour…" : "Confirmer le nouveau mot de passe"} - )}
+ + {modal.show && ( +
+
e.stopPropagation()}> + +
+ {modal.type === 'error' + ? + : + } +
+

+ {modal.type === 'error' ? 'Erreur' : 'Succès'} +

+

{modal.message}

+ +
+
+ )} ); }; diff --git a/frontend-prep/src/pages/User/ProfilePage.tsx b/frontend-prep/src/pages/User/ProfilePage.tsx index eb2f6c40..e19618c8 100644 --- a/frontend-prep/src/pages/User/ProfilePage.tsx +++ b/frontend-prep/src/pages/User/ProfilePage.tsx @@ -28,9 +28,10 @@ export default function ProfilePage() { const [signalPseudo, setSignalPseudo] = useState(() => localStorage.getItem(STORAGE_SIGNAL) ?? ''); // Feedback - const [savingContact, setSavingContact] = useState(false); - const [successMsg, setSuccessMsg] = useState(''); - const [errorMsg, setErrorMsg] = useState(''); + const [savingContact, setSavingContact] = useState(false); + const [feedbackModal, setFeedbackModal] = useState<{ + show: boolean; type: 'success' | 'error'; title: string; body: string; + }>({ show: false, type: 'success', title: '', body: '' }); // Telegram const [tgLinked, setTgLinked] = useState(false); @@ -78,20 +79,13 @@ export default function ProfilePage() { }); }, [navigate]); - const showSuccess = (msg: string) => { - setSuccessMsg(msg); - setErrorMsg(''); - setTimeout(() => setSuccessMsg(''), 3000); - }; - const showError = (msg: string) => { - setErrorMsg(msg); - setSuccessMsg(''); - setTimeout(() => setErrorMsg(''), 4000); + const showFeedback = (type: 'success' | 'error', title: string, body: string) => { + setFeedbackModal({ show: true, type, title, body }); }; const saveAddress = () => { localStorage.setItem(STORAGE_ADDRESS, defaultAddress.trim()); - showSuccess('Adresse par défaut enregistrée'); + showFeedback('success', 'Adresse enregistrée', 'Votre adresse par défaut a été sauvegardée et sera pré-remplie à votre prochaine commande.'); }; const saveLocal = () => { @@ -99,7 +93,7 @@ export default function ProfilePage() { localStorage.setItem(STORAGE_PHONE, defaultPhone.trim()); localStorage.setItem(STORAGE_SIGNAL, signalPseudo.trim()); setShowSaveModal(false); - showSuccess('Informations par défaut enregistrées'); + showFeedback('success', 'Infos enregistrées', 'Adresse, téléphone et pseudo Signal sauvegardés. Ils seront pré-remplis à votre prochaine commande.'); }; const handleLinkTelegram = async () => { @@ -132,9 +126,9 @@ export default function ProfilePage() { setTwoFALoading(false); if (res.success) { setTwoFAEnabled(newVal); - showSuccess(newVal ? 'Double authentification activée' : 'Double authentification désactivée'); + showFeedback('success', newVal ? '2FA activée' : '2FA désactivée', newVal ? 'La double authentification est maintenant active. Un code vous sera envoyé sur Telegram à chaque connexion.' : 'La double authentification a été désactivée.'); } else { - showError(res.error || 'Erreur lors de la modification'); + showFeedback('error', 'Erreur', res.error || 'Erreur lors de la modification'); } }; @@ -143,9 +137,9 @@ export default function ProfilePage() { const res = await updateMyProfile({ nom: nom.trim(), prenom: prenom.trim(), telephone: telephone.trim() }); setSavingContact(false); if (res.success) { - showSuccess('Profil mis à jour'); + showFeedback('success', 'Profil mis à jour', 'Vos informations de compte ont été enregistrées avec succès.'); } else { - showError(res.message ?? 'Erreur lors de la mise à jour'); + showFeedback('error', 'Erreur', res.message ?? 'Erreur lors de la mise à jour du profil.'); } }; @@ -174,17 +168,6 @@ export default function ProfilePage() { - {successMsg && ( -
- {successMsg} -
- )} - {errorMsg && ( -
- {errorMsg} -
- )} - {/* Section compte */}

@@ -403,6 +386,29 @@ export default function ProfilePage() {

)} + {feedbackModal.show && ( +
setFeedbackModal(m => ({ ...m, show: false }))}> +
e.stopPropagation()}> + +
+ +
+

{feedbackModal.title}

+

{feedbackModal.body}

+
+ +
+
+
+ )} + {showUnlinkSuccessModal && (
setShowUnlinkSuccessModal(false)}>
e.stopPropagation()}>