@@ -540,7 +575,7 @@ export default function Services() {
{vpc.name.toUpperCase()}
{vpcServices.length} INSTANCES
- deleteVPC(vpc.id)} className="text-gray-500 hover:text-red-400 transition-colors flex items-center text-sm">
+ { deleteVPC(vpc.id); triggerAlert("VPC Démantelé", "Le groupe de routage a été dissous. Les instances ont été reversées dans le pool libre.", "info"); }} className="text-gray-500 hover:text-red-400 transition-colors flex items-center text-sm">
Démanteler VPC
@@ -591,7 +626,8 @@ export default function Services() {
ACCÈS AUTORISÉ
- setSsoVault(null)} className="text-gray-400 hover:text-white transition">✖
+ {/* La croix permet de fermer manuellement le modal après copie */}
+ setSsoVault(null)} className="text-gray-400 hover:text-white transition text-lg">✖
Le pare-feu HestiaCP bloque les injections directes. Un mot de passe jetable a été généré. Copiez-le et connectez-vous.
@@ -610,8 +646,9 @@ export default function Services() {
- setSsoVault(null)} className="block w-full bg-emerald-500 hover:bg-emerald-400 text-gray-950 font-bold py-3 text-center rounded transition font-mono tracking-widest uppercase">
- Ouvrir le Panel Web
+ {/* REFIXÉ : Pas de setSsoVault(null) ici pour garder le modal ouvert au retour de l'onglet */}
+
+ Ouvrir le Panel Web ↗
@@ -632,19 +669,21 @@ export default function Services() {
setActiveServiceModal(null)} className="text-gray-400 hover:text-white transition text-xl">✖
- {/* ROUTAGE DU COMPOSANT INTERNE SELON L'ÉTAT DU SERVICE */}
{activeServiceModal.type === 'vps' ? (
activeServiceModal.details?.ip && activeServiceModal.details.ip !== '127.0.0.1' ? (
-
+
) : (
- { setActiveServiceModal(null); fetchServices(); }} />
+ { setActiveServiceModal(null); fetchServices(); }} onAlert={triggerAlert} />
)
) : (
-
+
)}
)}
+
+ {/* VRAI MODAL DE NOTIFICATION REACT SURCHARGE */}
+ setCustomAlert(null)} />
);
}
\ No newline at end of file
diff --git a/src/pages/public/Register.jsx b/src/pages/public/Register.jsx
index 58c2d19..560f67f 100644
--- a/src/pages/public/Register.jsx
+++ b/src/pages/public/Register.jsx
@@ -1,8 +1,50 @@
-// src/pages/public/Register.jsx
import { useState } from 'react';
import { useNavigate, Link } from 'react-router-dom';
import { registerUnifiedClient } from '../../services/api';
+// ============================================================================
+// COMPOSANT : MODAL DE NOTIFICATION (Succès / Erreur)
+// ============================================================================
+const NotificationModal = ({ notification, onClose }) => {
+ if (!notification) return null;
+ const isError = notification.type === 'error';
+
+ return (
+
+
+
+ {notification.title.toUpperCase()}
+
+
+ {notification.message}
+
+
+ COMPRIS
+
+
+
+ );
+};
+
+// ============================================================================
+// COMPOSANT PRINCIPAL : REGISTER
+// ============================================================================
export default function Register() {
const [firstName, setFirstName] = useState('');
const [lastName, setLastName] = useState('');
@@ -10,60 +52,65 @@ export default function Register() {
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const [confirmPassword, setConfirmPassword] = useState('');
- const [error, setError] = useState(null);
+
const [loading, setLoading] = useState(false);
+ const [customAlert, setCustomAlert] = useState(null); // Remplace l'état "error" et "alert()"
const navigate = useNavigate();
const handleRegister = async (e) => {
e.preventDefault();
- setError(null);
+ setCustomAlert(null);
- // 1. DÉFINITION DE LA POLITIQUE DE MOT DE PASSE (Le Checkpoint)
- // Explication de la Regex :
- // (?=.*[a-z]) : Au moins une minuscule
- // (?=.*[A-Z]) : Au moins une majuscule
- // (?=.*\d) : Au moins un chiffre
- // (?=.*[\W_]) : Au moins un caractère spécial (non-alphanumérique ou underscore)
- // .{8,} : Minimum 8 caractères au total
const passwordPolicy = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[\W_]).{8,}$/;
- // 2. VÉRIFICATION
+ // VÉRIFICATIONS FRONTEND (Affiche le Modal d'Erreur)
if (!passwordPolicy.test(password)) {
- setError("ERREUR : Le mot de passe doit contenir 8 caractères min, une majuscule, une minuscule, un chiffre et un caractère spécial.");
- return; // On stoppe l'exécution ici. La requête ne part pas vers le serveur.
- }
-
- // Sécurité Frontend : Validation des mots de passe avant envoi au serveur
- if (password !== confirmPassword) {
- setError("Les clés d'accès (mots de passe) ne correspondent pas.");
+ setCustomAlert({ type: 'error', title: 'Sécurité compromise', message: "Le mot de passe doit contenir 8 caractères min, une majuscule, une minuscule, un chiffre et un caractère spécial." });
+ return;
+ }
+
+ if (password !== confirmPassword) {
+ setCustomAlert({ type: 'error', title: 'Erreur de saisie', message: "Les clés d'accès (mots de passe) ne correspondent pas." });
return;
}
- // Sécurité Frontend : Regex simple pour valider le format exigé par HestiaCP
const validUsername = /^[a-zA-Z0-9]{3,12}$/.test(username);
if (!validUsername) {
- setError("Le nom d'utilisateur doit contenir uniquement des lettres ou chiffres (entre 3 et 12 caractères, sans espace).");
+ setCustomAlert({ type: 'error', title: 'Identifiant invalide', message: "Le nom d'utilisateur doit contenir uniquement des lettres ou chiffres (entre 3 et 12 caractères, sans espace)." });
return;
}
setLoading(true);
try {
- // Envoi de la requête groupée à notre orchestrateur PHP backend
+ // API BACKEND
await registerUnifiedClient(email, username, password, firstName, lastName);
- alert("[ PROVISIONNEMENT RÉUSSI ]\nVos comptes FOSSBilling, HestiaCP et Nextcloud ont été initialisés.\nVous pouvez maintenant vous connecter.");
+ // SUCCÈS (Affiche le Modal de Succès)
+ setCustomAlert({
+ type: 'success',
+ title: 'PROVISIONNEMENT RÉUSSI',
+ message: "Vos comptes FOSSBilling, HestiaCP et Nextcloud ont été initialisés.\n\nVous pouvez maintenant vous connecter à l'infrastructure."
+ });
- // Redirection automatique vers la page de login après succès
- navigate('/login');
} catch (err) {
- setError(err.message || "Échec de l'initialisation de l'infrastructure.");
+ // ERREUR API (Affiche le Modal d'Erreur API)
+ setCustomAlert({ type: 'error', title: 'Échec du Déploiement', message: err.message || "Échec de l'initialisation de l'infrastructure." });
} finally {
setLoading(false);
}
};
- // Réutilisation de ton Design System "Bunker"
+ // Fermeture du Modal : Redirige vers le Login SI c'était un succès
+ const handleCloseModal = () => {
+ if (customAlert?.type === 'success') {
+ navigate('/login');
+ } else {
+ setCustomAlert(null);
+ }
+ };
+
+ // Styles
const inputStyle = {
width: '100%', padding: '10px', marginBottom: '15px',
backgroundColor: '#1A1A1A', color: '#00E5FF',
@@ -91,20 +138,6 @@ export default function Register() {
[ INITIALISATION DU PROVISIONNEMENT TRIPLE EN CASCADE ]
- {/* Affichage des alertes système */}
- {error && (
-