Files
portail-gise/src/pages/app/Dashboard.jsx
T
2026-06-11 22:39:45 +02:00

162 lines
6.4 KiB
React

import { useState, useEffect } from 'react';
import { getClientProfile, getClientOrders, getOrderService } from '../../services/api';
export default function Dashboard() {
const [profile, setProfile] = useState(null);
const [orders, setOrders] = useState([]);
const [error, setError] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const fetchDashboardData = async () => {
try {
// On lance les deux requêtes en même temps pour aller plus vite
const profileData = await getClientProfile();
const ordersData = await getClientOrders();
setProfile(profileData);
// FOSSBilling renvoie une liste paginée, on prend le tableau 'list'
setOrders(ordersData.list || []);
} catch (err) {
setError(err.message || "Erreur de synchronisation avec le serveur central.");
} finally {
setLoading(false);
}
};
fetchDashboardData();
}, []);
const cardStyle = {
backgroundColor: '#1A1A1A',
border: '1px solid #333',
padding: '20px',
borderRadius: '4px',
display: 'flex',
flexDirection: 'column',
gap: '10px'
};
const handleManageInstance = async (orderId) => {
try {
console.log(`[SYS] Demande d'accès au service #${orderId}...`);
const serviceData = await getOrderService(orderId);
if (serviceData && serviceData.server && serviceData.server.login_url) {
window.open(serviceData.server.login_url, '_blank');
}
else if (serviceData && serviceData.server) {
console.log(serviceData);
// L'URL d'action du formulaire de connexion HestiaCP
const hestiaLoginUrl = "https://panel.gise.be/login/";
const username = serviceData.username;
const password = serviceData.password;
if (!username || !password) {
alert("Identifiants introuvables. Le serveur est-il bien provisionné ?");
return;
}
console.log("[SYS] Création du pont SSO vers HestiaCP...");
// 1. On crée un formulaire invisible
const form = document.createElement('form');
form.method = 'POST';
form.action = hestiaLoginUrl;
form.target = '_blank'; // Pour ouvrir dans un nouvel onglet
// 2. On crée le champ utilisateur (Hestia attend le nom 'user')
const userField = document.createElement('input');
userField.type = 'hidden';
userField.name = 'user';
userField.value = username;
// 3. On crée le champ mot de passe (Hestia attend le nom 'password')
const passField = document.createElement('input');
passField.type = 'hidden';
passField.name = 'password';
passField.value = password;
// 4. On assemble et on injecte dans la page
form.appendChild(userField);
form.appendChild(passField);
document.body.appendChild(form);
// 5. On valide le formulaire (BAM ! Connexion)
form.submit();
// 6. On efface les traces du formulaire fantôme pour la sécurité
document.body.removeChild(form);
} else {
alert("Configuration serveur introuvable pour cette instance.");
}
} catch (err) {
alert(`[ERREUR D'ACCÈS] : ${err.message}`);
}
};
return (
<div style={{ padding: '40px', color: '#E0E0E0', fontFamily: 'monospace', backgroundColor: '#121212', minHeight: '100vh' }}>
{/* HEADER DU DASHBOARD */}
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', borderBottom: '1px solid #333', paddingBottom: '20px', marginBottom: '30px' }}>
<h2 style={{ color: '#00E5FF', margin: 0 }}>CENTRE DE CONTRÔLE GISE</h2>
{profile && (
<div style={{ textAlign: 'right' }}>
<div>Opérateur : <span style={{ color: '#FFF' }}>{profile.email}</span></div>
<div>Crédits : <span style={{ color: '#00FF00' }}>{profile.balance} {profile.currency}</span></div>
</div>
)}
</div>
{loading && <p style={{ color: '#888' }}>[ Synchronisation des données en cours... ]</p>}
{error && (
<div style={{ backgroundColor: 'rgba(255, 68, 68, 0.1)', color: '#FF4444', padding: '15px', border: '1px solid #FF4444', marginBottom: '20px' }}>
[ ERREUR ] : {error}
</div>
)}
{/* ZONE DES SERVICES ACTIFS */}
{!loading && !error && (
<>
<h3 style={{ color: '#FFF', marginBottom: '20px' }}>INFRASTRUCTURE ACTIVE</h3>
{orders.length === 0 ? (
<div style={{ padding: '30px', textAlign: 'center', border: '1px dashed #333', color: '#888' }}>
Aucun service actif détecté. <br/><br/>
<button style={{ backgroundColor: '#00E5FF', color: '#000', border: 'none', padding: '10px 20px', cursor: 'pointer', fontFamily: 'monospace', fontWeight: 'bold' }}>
+ DÉPLOYER UN NOUVEAU SERVICE
</button>
</div>
) : (
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(300px, 1fr))', gap: '20px' }}>
{orders.map((order) => (
<div key={order.id} style={cardStyle}>
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<strong style={{ color: '#FFF', fontSize: '1.1rem' }}>{order.title}</strong>
<span style={{
color: order.status === 'active' ? '#00FF00' : '#FF4444',
fontSize: '0.8rem', textTransform: 'uppercase', border: `1px solid ${order.status === 'active' ? '#00FF00' : '#FF4444'}`, padding: '2px 6px'
}}>
{order.status}
</span>
</div>
<div style={{ fontSize: '0.9rem', color: '#A0A0A0' }}>Renouvellement : {order.expires_at || 'N/A'}</div>
<div style={{ fontSize: '0.9rem', color: '#A0A0A0' }}>Montant : {order.total} {order.currency}</div>
<button
onClick={() => handleManageInstance(order.id)}
style={{ marginTop: '10px', backgroundColor: 'transparent', color: '#00E5FF', border: '1px solid #00E5FF', padding: '8px', cursor: 'pointer', fontFamily: 'monospace' }}>
GÉRER L'INSTANCE
</button>
</div>
))}
</div>
)}
</>
)}
</div>
);
}