upgrade design

This commit is contained in:
LathanDevers
2026-06-25 10:50:49 +02:00
parent 323667e835
commit d83b7ad4e7
9 changed files with 226 additions and 442 deletions
+1 -12
View File
@@ -16,7 +16,6 @@ export default function Services() {
const [error, setError] = useState(null);
const [newVpcName, setNewVpcName] = useState("");
const [isConnecting, setIsConnecting] = useState(null);
const [ssoVault, setSsoVault] = useState(null);
const [activeServiceModal, setActiveServiceModal] = useState(null);
const [customAlert, setCustomAlert] = useState(null);
@@ -125,16 +124,6 @@ export default function Services() {
</>
)}
{/* MODAUX */}
{ssoVault && ( /* ... Gérer le ssoVault ici, ou l'extraire aussi si tu le souhaites ... */
<div className="fixed inset-0 bg-black/80 backdrop-blur-sm z-[60] flex items-center justify-center p-4">
<div className="bg-gray-900 border border-emerald-500/50 rounded-lg shadow-2xl shadow-emerald-500/20 max-w-md w-full p-6 text-gray-200">
<h3 className="text-xl font-bold text-emerald-400 font-mono tracking-wider mb-4">ACCÈS AUTORISÉ</h3>
<p className="text-sm text-gray-400 mb-6">Utilisateur : {ssoVault.username}<br/>Clé : {ssoVault.password}</p>
<button onClick={() => setSsoVault(null)}>Fermer</button>
</div>
</div>
)}
{activeServiceModal && (
<div className="fixed inset-0 bg-black/80 backdrop-blur-sm z-50 flex items-center justify-center p-4">
@@ -148,7 +137,7 @@ export default function Services() {
{activeServiceModal.type === 'vps' ? (
activeServiceModal.details?.ip && activeServiceModal.details.ip !== '127.0.0.1' ? <VpsManager details={activeServiceModal.details} orderId={activeServiceModal.data.id} onRefresh={fetchServices} onAlert={triggerAlert} /> : <VpsDeployer serviceId={activeServiceModal.data.id} onDeploySuccess={() => { setActiveServiceModal(null); fetchServices(); }} onAlert={triggerAlert} />
) : (
<WebManager details={activeServiceModal.details} orderId={activeServiceModal.data.id} onRefresh={fetchServices} onOpenSso={setSsoVault} onAlert={triggerAlert} />
<WebManager details={activeServiceModal.details} orderId={activeServiceModal.data.id} onRefresh={fetchServices} onAlert={triggerAlert} />
)}
</div>
</div>
+10 -50
View File
@@ -2,68 +2,28 @@ import { Link } from 'react-router-dom';
export default function Home() {
return (
<div style={{ textAlign: 'center', marginTop: '12vh' }}>
<h1 style={{
color: '#FFF',
fontSize: '4rem',
letterSpacing: '6px',
display: 'flex',
alignItems: 'baseline',
justifyContent: 'center',
gap: '12px',
margin: '0 0 20px 0'
}}>
<div className="text-center mt-[12vh]">
<h1 className="text-white text-5xl md:text-6xl tracking-[6px] flex items-baseline justify-center gap-3 mb-5 font-black">
NEXUS
<span style={{
color: '#00E5FF',
fontSize: '1.4rem',
letterSpacing: '2px',
fontWeight: 'normal'
}}>
<span className="text-cyan-400 text-xl md:text-2xl tracking-[2px] font-normal">
by GISE
</span>
</h1>
<h2 style={{
color: '#FFFFFF',
fontSize: '1.5rem',
letterSpacing: '2px',
textTransform: 'uppercase',
marginBottom: '20px',
fontWeight: 'normal'
}}>
<h2 className="text-white text-xl md:text-2xl tracking-[2px] uppercase mb-5 font-normal">
Bienvenue dans l'infrastructure
</h2>
<p style={{
color: '#A0A0A0',
fontSize: '1.2rem',
maxWidth: '600px',
margin: '0 auto',
lineHeight: '1.6'
}}>
<p className="text-[#A0A0A0] text-lg max-w-[600px] mx-auto leading-relaxed">
Hébergement web, instances VPS et Stockage Cloud en Belgique.
<br />Propulsé par une architecture bare-metal locale.
</p>
{/* LE BOUTON D'ACTION PRINCIPAL REDIRIGE VERS /REGISTER */}
<div style={{ marginTop: '50px' }}>
<Link to="/register" style={{
display: 'inline-block',
backgroundColor: 'transparent',
color: '#00E5FF',
border: '1px solid #00E5FF',
padding: '12px 24px',
fontSize: '1rem',
fontFamily: 'monospace',
textDecoration: 'none',
textTransform: 'uppercase',
letterSpacing: '1px',
fontWeight: 'bold',
transition: 'all 0.3s ease',
boxShadow: '0 0 15px rgba(0, 229, 255, 0.1)',
}}>
<div className="mt-12">
<Link
to="/register"
className="inline-block bg-transparent text-cyan-400 border border-cyan-400 px-6 py-3 font-mono text-base uppercase tracking-[1px] font-bold transition-all duration-300 shadow-[0_0_15px_rgba(0,229,255,0.1)] hover:bg-cyan-400/10 hover:shadow-[0_0_20px_rgba(0,229,255,0.3)] no-underline rounded-sm"
>
Démarrer le déploiement
</Link>
</div>
+19 -49
View File
@@ -15,23 +15,9 @@ export default function Login() {
setLoading(true);
try {
// 1. APPEL À TON API BACKEND (FOSSBilling / PHP)
// Ici, tu mettras ton vrai 'fetch' vers ton serveur pour vérifier le mot de passe.
// Pour l'instant, on simule un délai réseau d'une seconde.
await new Promise(resolve => setTimeout(resolve, 1000));
// --- SIMULATION D'AUTHENTIFICATION ---
// (À remplacer par la vraie validation de ton serveur)
await loginClient(email, password);
// 2. LA CLÉ DU PROBLÈME EST ICI : L'ATTRIBUTION DU BADGE
// On sauvegarde le token (généralement renvoyé par ton API) dans le navigateur
localStorage.setItem('gise_token', 'secure_token_alphanumerique_factice');
// 3. AUTORISATION ET REDIRECTION
// Maintenant que le token est en poche, ProtectedRoute nous laissera passer !
navigate('/dashboard');
} catch (err) {
setError(err.message);
} finally {
@@ -39,72 +25,56 @@ export default function Login() {
}
};
// --- DESIGN SYSTEM "BUNKER" ---
const inputStyle = {
width: '100%', padding: '10px', marginBottom: '20px',
backgroundColor: '#1A1A1A', color: '#00E5FF',
border: '1px solid #333', fontFamily: 'monospace', outline: 'none',
boxSizing: 'border-box'
};
const buttonStyle = {
width: '100%', padding: '12px', backgroundColor: loading ? '#333' : '#00E5FF',
color: loading ? '#888' : '#000', border: 'none', cursor: loading ? 'not-allowed' : 'pointer',
fontFamily: 'monospace', fontWeight: 'bold', textTransform: 'uppercase', letterSpacing: '1px',
marginTop: '10px'
};
return (
<div style={{ display: 'flex', justifyContent: 'center', marginTop: '10vh', padding: '0 20px' }}>
<div style={{
width: '100%', maxWidth: '400px', backgroundColor: '#242424',
padding: '30px', borderTop: '4px solid #00E5FF', boxShadow: '0 10px 30px rgba(0,0,0,0.5)'
}}>
<h2 style={{ color: '#FFF', textTransform: 'uppercase', marginBottom: '5px', fontSize: '1.5rem', textAlign: 'center' }}>
<div className="flex justify-center mt-[10vh] px-5">
<div className="w-full max-w-[400px] bg-[#242424] p-8 border-t-4 border-cyan-400 shadow-[0_10px_30px_rgba(0,0,0,0.5)]">
<h2 className="text-white uppercase mb-1 text-2xl text-center font-sans font-bold">
Connexion au Nexus
</h2>
<p style={{ color: '#888', fontFamily: 'monospace', fontSize: '0.85rem', marginBottom: '25px', textAlign: 'center' }}>
<p className="text-[#888] font-mono text-sm mb-6 text-center">
[ IDENTIFICATION REQUISE ]
</p>
{error && (
<div style={{
color: '#ff003c', border: '1px solid #ff003c', backgroundColor: 'rgba(255, 0, 60, 0.1)',
padding: '10px', marginBottom: '20px', fontFamily: 'monospace', fontSize: '0.85rem'
}}>
<div className="text-[#ff003c] border border-[#ff003c] bg-[#ff003c]/10 p-3 mb-5 font-mono text-sm">
[ ALERTE ] : {error}
</div>
)}
<form onSubmit={handleLogin}>
<label style={{ display: 'block', color: '#888', marginBottom: '5px', fontSize: '0.8rem' }}>IDENTIFIANT (E-MAIL)</label>
<form onSubmit={handleLogin} className="font-mono">
<label className="block text-[#888] mb-1 text-xs tracking-widest uppercase">Identifiant (E-mail)</label>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
style={inputStyle}
required
placeholder="admin@gise.be"
className="w-full p-3 mb-5 bg-[#1A1A1A] text-cyan-400 border border-[#333] outline-none focus:border-cyan-400 transition-colors"
/>
<label style={{ display: 'block', color: '#888', marginBottom: '5px', fontSize: '0.8rem' }}>C D'ACCÈS SÉCURISÉE</label>
<label className="block text-[#888] mb-1 text-xs tracking-widest uppercase">C d'accès sécurisée</label>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
style={inputStyle}
required
placeholder="••••••••"
className="w-full p-3 mb-5 bg-[#1A1A1A] text-cyan-400 border border-[#333] outline-none focus:border-cyan-400 transition-colors"
/>
<button type="submit" style={buttonStyle} disabled={loading}>
<button
type="submit"
disabled={loading}
className="w-full p-3 mt-2 font-bold uppercase tracking-widest transition-colors disabled:bg-[#333] disabled:text-[#888] disabled:cursor-not-allowed bg-cyan-400 hover:bg-cyan-300 text-black shadow-[0_0_15px_rgba(0,229,255,0.2)]"
>
{loading ? 'VÉRIFICATION...' : 'OUVRIR LE SAS'}
</button>
</form>
<div style={{ marginTop: '20px', textAlign: 'center', fontSize: '0.85rem' }}>
<Link to="/register" style={{ color: '#888', textDecoration: 'none' }}>
Aucun accès réseau ? <span style={{ color: '#00E5FF' }}>S'enregistrer &gt;</span>
<div className="mt-5 text-center text-sm font-mono text-[#888]">
Aucun accès réseau ?{' '}
<Link to="/register" className="text-cyan-400 hover:text-cyan-300 transition-colors no-underline">
S'enregistrer &gt;
</Link>
</div>
</div>
+34 -75
View File
@@ -3,9 +3,6 @@ import { useNavigate, Link } from 'react-router-dom';
import { registerUnifiedClient } from '../../services/api';
import NotificationModal from '../../components/ui/NotificationModal';
// ============================================================================
// COMPOSANT PRINCIPAL : REGISTER
// ============================================================================
export default function Register() {
const [firstName, setFirstName] = useState('');
const [lastName, setLastName] = useState('');
@@ -15,7 +12,7 @@ export default function Register() {
const [confirmPassword, setConfirmPassword] = useState('');
const [loading, setLoading] = useState(false);
const [customAlert, setCustomAlert] = useState(null); // Remplace l'état "error" et "alert()"
const [customAlert, setCustomAlert] = useState(null);
const navigate = useNavigate();
const handleRegister = async (e) => {
@@ -23,119 +20,81 @@ export default function Register() {
setCustomAlert(null);
const passwordPolicy = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[\W_]).{8,}$/;
// VÉRIFICATIONS FRONTEND (Affiche le Modal d'Erreur)
if (!passwordPolicy.test(password)) {
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." });
setCustomAlert({ type: 'error', title: 'Erreur de saisie', message: "Les clés d'accès ne correspondent pas." });
return;
}
const validUsername = /^[a-zA-Z0-9]{3,12}$/.test(username);
if (!validUsername) {
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)." });
if (!/^[a-zA-Z0-9]{3,12}$/.test(username)) {
setCustomAlert({ type: 'error', title: 'Identifiant invalide', message: "Le nom d'utilisateur doit contenir uniquement des lettres ou chiffres (entre 3 et 12 caractères)." });
return;
}
setLoading(true);
try {
// API BACKEND
await registerUnifiedClient(email, username, password, firstName, lastName);
// 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."
});
setCustomAlert({ type: 'success', title: 'PROVISIONNEMENT RÉUSSI', message: "Vos comptes FOSSBilling, HestiaCP et Nextcloud ont été initialisés.\n\nVous pouvez maintenant vous connecter." });
} catch (err) {
// 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." });
setCustomAlert({ type: 'error', title: 'Échec du Déploiement', message: err.message || "Échec de l'initialisation." });
} finally {
setLoading(false);
}
};
// 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',
border: '1px solid #333', fontFamily: 'monospace', outline: 'none',
boxSizing: 'border-box'
};
const buttonStyle = {
width: '100%', padding: '12px', backgroundColor: loading ? '#333' : '#00E5FF',
color: loading ? '#888' : '#000', border: 'none', cursor: loading ? 'not-allowed' : 'pointer',
fontFamily: 'monospace', fontWeight: 'bold', textTransform: 'uppercase', letterSpacing: '1px',
marginTop: '10px'
if (customAlert?.type === 'success') navigate('/login');
else setCustomAlert(null);
};
return (
<div style={{ display: 'flex', justifyContent: 'center', marginTop: '5vh', padding: '0 20px' }}>
<div style={{
width: '100%', maxWidth: '500px', backgroundColor: '#242424',
padding: '30px', borderTop: '4px solid #00E5FF', boxShadow: '0 10px 30px rgba(0,0,0,0.5)'
}}>
<h2 style={{ color: '#FFF', textTransform: 'uppercase', marginBottom: '5px', fontSize: '1.5rem' }}>
<div className="flex justify-center mt-[5vh] px-5">
<div className="w-full max-w-[500px] bg-[#242424] p-8 border-t-4 border-cyan-400 shadow-[0_10px_30px_rgba(0,0,0,0.5)]">
<h2 className="text-white uppercase mb-1 text-2xl font-sans font-bold">
Créer un accès réseau
</h2>
<p style={{ color: '#888', fontFamily: 'monospace', fontSize: '0.85rem', marginBottom: '25px' }}>
[ INITIALISATION DU PROVISIONNEMENT TRIPLE EN CASCADE ]
<p className="text-[#888] font-mono text-sm mb-6">
[ INITIALISATION DU PROVISIONNEMENT TRIPLE ]
</p>
<form onSubmit={handleRegister}>
<div style={{ display: 'flex', gap: '15px' }}>
<div style={{ flex: 1 }}>
<label style={{ display: 'block', color: '#888', marginBottom: '5px', fontSize: '0.8rem' }}>PRÉNOM</label>
<input type="text" value={firstName} onChange={(e) => setFirstName(e.target.value)} style={inputStyle} required placeholder="John" />
<form onSubmit={handleRegister} className="font-mono">
<div className="flex gap-4">
<div className="flex-1">
<label className="block text-[#888] mb-1 text-xs tracking-widest uppercase">Prénom</label>
<input type="text" value={firstName} onChange={(e) => setFirstName(e.target.value)} required placeholder="John" className="w-full p-3 mb-4 bg-[#1A1A1A] text-cyan-400 border border-[#333] outline-none focus:border-cyan-400 transition-colors" />
</div>
<div style={{ flex: 1 }}>
<label style={{ display: 'block', color: '#888', marginBottom: '5px', fontSize: '0.8rem' }}>NOM</label>
<input type="text" value={lastName} onChange={(e) => setLastName(e.target.value)} style={inputStyle} required placeholder="Doe" />
<div className="flex-1">
<label className="block text-[#888] mb-1 text-xs tracking-widest uppercase">Nom</label>
<input type="text" value={lastName} onChange={(e) => setLastName(e.target.value)} required placeholder="Doe" className="w-full p-3 mb-4 bg-[#1A1A1A] text-cyan-400 border border-[#333] outline-none focus:border-cyan-400 transition-colors" />
</div>
</div>
<label style={{ display: 'block', color: '#888', marginBottom: '5px', fontSize: '0.8rem' }}>ADRESSE E-MAIL (IDENTIFIANT FACTURATION)</label>
<input type="email" value={email} onChange={(e) => setEmail(e.target.value)} style={inputStyle} required placeholder="john.doe@gise.be" />
<label className="block text-[#888] mb-1 text-xs tracking-widest uppercase">E-mail (Facturation)</label>
<input type="email" value={email} onChange={(e) => setEmail(e.target.value)} required placeholder="john.doe@gise.be" className="w-full p-3 mb-4 bg-[#1A1A1A] text-cyan-400 border border-[#333] outline-none focus:border-cyan-400 transition-colors" />
<label style={{ display: 'block', color: '#888', marginBottom: '5px', fontSize: '0.8rem' }}>NOM D'UTILISATEUR SÉCURISÉ (ACCÈS HESTIA & CLOUD)</label>
<input type="text" value={username} onChange={(e) => setUsername(e.target.value)} style={inputStyle} required placeholder="ex: jdoe42" />
<label className="block text-[#888] mb-1 text-xs tracking-widest uppercase">Identifiant sécurisé (Cloud)</label>
<input type="text" value={username} onChange={(e) => setUsername(e.target.value)} required placeholder="ex: jdoe42" className="w-full p-3 mb-4 bg-[#1A1A1A] text-cyan-400 border border-[#333] outline-none focus:border-cyan-400 transition-colors" />
<label style={{ display: 'block', color: '#888', marginBottom: '5px', fontSize: '0.8rem' }}>CLÉ D'ACCÈS (MOT DE PASSE UNIQUE)</label>
<input type="password" value={password} onChange={(e) => setPassword(e.target.value)} style={inputStyle} required placeholder="••••••••" />
<label className="block text-[#888] mb-1 text-xs tracking-widest uppercase">Clé d'accès unique</label>
<input type="password" value={password} onChange={(e) => setPassword(e.target.value)} required placeholder="••••••••" className="w-full p-3 mb-4 bg-[#1A1A1A] text-cyan-400 border border-[#333] outline-none focus:border-cyan-400 transition-colors" />
<label style={{ display: 'block', color: '#888', marginBottom: '5px', fontSize: '0.8rem' }}>CONFIRMER LA CLÉ D'ACCÈS</label>
<input type="password" value={confirmPassword} onChange={(e) => setConfirmPassword(e.target.value)} style={inputStyle} required placeholder="••••••••" />
<label className="block text-[#888] mb-1 text-xs tracking-widest uppercase">Confirmer la clé</label>
<input type="password" value={confirmPassword} onChange={(e) => setConfirmPassword(e.target.value)} required placeholder="••••••••" className="w-full p-3 mb-5 bg-[#1A1A1A] text-cyan-400 border border-[#333] outline-none focus:border-cyan-400 transition-colors" />
<button type="submit" style={buttonStyle} disabled={loading}>
<button type="submit" disabled={loading} className="w-full p-3 mt-2 font-bold uppercase tracking-widest transition-colors disabled:bg-[#333] disabled:text-[#888] disabled:cursor-not-allowed bg-cyan-400 hover:bg-cyan-300 text-black shadow-[0_0_15px_rgba(0,229,255,0.2)]">
{loading ? 'DÉPLOIEMENT EN COURS...' : 'LANCER LE PROVISIONNEMENT'}
</button>
</form>
<div style={{ marginTop: '20px', textAlign: 'center', fontSize: '0.85rem' }}>
<Link to="/login" style={{ color: '#888', textDecoration: 'none' }}>
Déjà enregistré ? <span style={{ color: '#00E5FF' }}>Se connecter au terminal &gt;</span>
<div className="mt-5 text-center text-sm font-mono text-[#888]">
Déjà enregistré ?{' '}
<Link to="/login" className="text-cyan-400 hover:text-cyan-300 transition-colors no-underline">
Se connecter au terminal &gt;
</Link>
</div>
</div>
{/* Affichage du Modal par-dessus le formulaire */}
<NotificationModal notification={customAlert} onClose={handleCloseModal} />
</div>
);
-90
View File
@@ -1,90 +0,0 @@
import { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { loginClient } from '../../services/api';
export default function Login() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState(null);
const [loading, setLoading] = useState(false);
const navigate = useNavigate();
const handleLogin = async (e) => {
e.preventDefault(); // Empêche la page de se rafraîchir
setError(null);
setLoading(true);
try {
// On envoie la requête à FOSSBilling
await loginClient(email, password);
// Si on arrive ici, le login est un succès !
// On redirige vers l'espace client sécurisé
navigate('/dashboard');
} catch (err) {
setError(err.message || "Accès refusé. Vérifiez vos identifiants.");
} finally {
setLoading(false);
}
};
// Styles CSS en variables pour garder le code lisible
const inputStyle = {
width: '100%', padding: '12px', marginBottom: '15px',
backgroundColor: '#1A1A1A', color: '#00E5FF',
border: '1px solid #333', fontFamily: 'monospace', outline: 'none'
};
const buttonStyle = {
width: '100%', padding: '12px', backgroundColor: loading ? '#333' : '#00E5FF',
color: loading ? '#888' : '#000', border: 'none', cursor: loading ? 'not-allowed' : 'pointer',
fontFamily: 'monospace', fontWeight: 'bold', textTransform: 'uppercase', letterSpacing: '1px'
};
return (
<div style={{ display: 'flex', justifyContent: 'center', marginTop: '10vh' }}>
<div style={{
width: '100%', maxWidth: '400px', backgroundColor: '#242424',
padding: '30px', borderTop: '4px solid #00E5FF', boxShadow: '0 10px 30px rgba(0,0,0,0.5)'
}}>
<h2 style={{ color: '#FFF', textTransform: 'uppercase', marginBottom: '20px', fontSize: '1.5rem' }}>
Authentification
</h2>
{/* Zone d'erreur qui s'affiche si le mot de passe est faux */}
{error && (
<div style={{ backgroundColor: 'rgba(255, 68, 68, 0.1)', color: '#FF4444', padding: '10px', marginBottom: '15px', border: '1px solid #FF4444', fontSize: '0.9rem' }}>
[ ERREUR ] : {error}
</div>
)}
<form onSubmit={handleLogin}>
<label style={{ display: 'block', color: '#888', marginBottom: '5px', fontSize: '0.8rem' }}>IDENTIFIANT (EMAIL)</label>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
style={inputStyle}
required
placeholder="admin@gise.be"
/>
<label style={{ display: 'block', color: '#888', marginBottom: '5px', fontSize: '0.8rem' }}>CLÉ D'ACCÈS (MOT DE PASSE)</label>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
style={inputStyle}
required
placeholder="••••••••"
/>
<button type="submit" style={buttonStyle} disabled={loading}>
{loading ? 'VÉRIFICATION...' : 'INITIALISER LA CONNEXION'}
</button>
</form>
</div>
</div>
);
}