remove alert
Deploy Nexus Portal to HestiaCP (FTP) / build-and-deploy (push) Successful in 31s

This commit is contained in:
maximus
2026-06-22 22:27:39 +02:00
parent bdcb6cb3f9
commit 421b97156a
3 changed files with 224 additions and 82 deletions
+76 -40
View File
@@ -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 (
<div className="fixed inset-0 bg-black/80 backdrop-blur-sm z-[100] flex items-center justify-center p-4" style={{ position: 'fixed', top: 0, left: 0, right: 0, bottom: 0, backgroundColor: 'rgba(0,0,0,0.8)', backdropFilter: 'blur(4px)', zIndex: 100, display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
<div style={{
backgroundColor: '#1A1A1A',
border: `1px solid ${isError ? '#ff003c' : '#00E5FF'}`,
boxShadow: `0 10px 30px ${isError ? 'rgba(255, 0, 60, 0.2)' : 'rgba(0, 229, 255, 0.2)'}`,
borderRadius: '8px',
maxWidth: '400px',
width: '100%',
padding: '24px',
color: '#FFF',
fontFamily: 'monospace'
}}>
<h4 style={{ fontSize: '1.1rem', fontWeight: 'bold', letterSpacing: '1px', marginBottom: '10px', color: isError ? '#ff003c' : '#00E5FF' }}>
{notification.title.toUpperCase()}
</h4>
<p style={{ fontSize: '0.9rem', color: '#AAA', marginBottom: '24px', whiteSpace: 'pre-line', lineHeight: '1.5' }}>
{notification.message}
</p>
<button onClick={onClose} style={{
width: '100%', padding: '10px',
backgroundColor: isError ? '#ff003c' : '#00E5FF',
color: isError ? '#FFF' : '#000',
border: 'none', borderRadius: '4px',
fontFamily: 'monospace', fontWeight: 'bold', cursor: 'pointer', letterSpacing: '1px'
}}>
COMPRIS
</button>
</div>
</div>
);
};
// ============================================================================
// 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 ]
</p>
{/* Affichage des alertes système */}
{error && (
<div style={{
color: '#ff003c', // Un rouge néon agressif pour les erreurs
border: '1px solid #ff003c',
backgroundColor: 'rgba(255, 0, 60, 0.1)',
padding: '10px',
marginBottom: '15px',
fontFamily: 'monospace'
}}>
[ ALERTE SYSTÈME ] : {error}
</div>
)}
<form onSubmit={handleRegister}>
<div style={{ display: 'flex', gap: '15px' }}>
<div style={{ flex: 1 }}>
@@ -140,6 +173,9 @@ export default function Register() {
</Link>
</div>
</div>
{/* Affichage du Modal par-dessus le formulaire */}
<NotificationModal notification={customAlert} onClose={handleCloseModal} />
</div>
);
}