|
|
|
@@ -1,7 +1,7 @@
|
|
|
|
|
import { useState, useEffect, useCallback } from 'react';
|
|
|
|
|
import { getMyServices, getServiceDetails, getHostingServiceDetails, resetHostingPassword } from '../../services/api';
|
|
|
|
|
import { useVPC } from '../../services/useVPC';
|
|
|
|
|
import { Server, Database, Cloud, Globe, Folder, Trash2, ExternalLink, Loader, Plus, Play, Lock, Maximize2 } from 'lucide-react';
|
|
|
|
|
import { Server, Database, Cloud, Globe, Folder, Trash2, ExternalLink, Loader, Plus, Play, Lock, Maximize2, AlertCircle } from 'lucide-react'; // Ajout de AlertCircle
|
|
|
|
|
|
|
|
|
|
// ============================================================================
|
|
|
|
|
// COMPOSANT 0 : MODAL DE NOTIFICATION (Remplace les alert() natifs)
|
|
|
|
@@ -290,7 +290,6 @@ const VpsManager = ({ details, orderId, onRefresh, onAlert }) => {
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{/* ACCÈS DE FAUT REMIS À JOUR */}
|
|
|
|
|
<div className="mt-4 bg-black/60 border border-gray-900 rounded p-3 text-[11px] space-y-1">
|
|
|
|
|
<span className="text-gray-500 font-bold uppercase tracking-wider block mb-1 text-[10px]">Identifiants d'usine :</span>
|
|
|
|
|
<p className="text-gray-400 font-mono">Console SSH : <span className="text-cyan-400 font-bold">root</span> / <span className="text-gray-500 italic">Clé choisie à l'initialisation</span></p>
|
|
|
|
@@ -320,7 +319,6 @@ const VpsManager = ({ details, orderId, onRefresh, onAlert }) => {
|
|
|
|
|
<div className="flex justify-between items-center mb-4 border-b border-gray-800 pb-2">
|
|
|
|
|
<span className="text-cyan-400 font-mono text-sm tracking-widest">NEXUS TERMINAL ({details.domain})</span>
|
|
|
|
|
<div className="flex items-center space-x-2">
|
|
|
|
|
{/* OPTION AJOUTÉE : OUVRIR EN PLEIN ÉCRAN DANS UN NOUVEL ONGLET */}
|
|
|
|
|
<button onClick={() => { window.open(terminalUrl, '_blank'); setShowTerminal(false); }} className="text-xs bg-cyan-600/30 text-cyan-400 hover:bg-cyan-600 hover:text-white border border-cyan-500/30 px-3 py-1 rounded transition flex items-center gap-1">
|
|
|
|
|
<Maximize2 className="w-3 h-3" /> Plein Écran ↗
|
|
|
|
|
</button>
|
|
|
|
@@ -471,7 +469,7 @@ export default function Services() {
|
|
|
|
|
const [isConnecting, setIsConnecting] = useState(null);
|
|
|
|
|
const [ssoVault, setSsoVault] = useState(null);
|
|
|
|
|
const [activeServiceModal, setActiveServiceModal] = useState(null);
|
|
|
|
|
const [customAlert, setCustomAlert] = useState(null); // Gère les pop-ups personnalisés
|
|
|
|
|
const [customAlert, setCustomAlert] = useState(null);
|
|
|
|
|
|
|
|
|
|
const { vpcs, createVPC, deleteVPC, assignToVPC, removeFromVPC } = useVPC();
|
|
|
|
|
|
|
|
|
@@ -544,14 +542,16 @@ export default function Services() {
|
|
|
|
|
const assignedServiceIds = vpcs.flatMap(vpc => vpc.services);
|
|
|
|
|
const freeServices = services.filter(s => !assignedServiceIds.includes(s.id));
|
|
|
|
|
|
|
|
|
|
if (isLoading) return <div className="text-center mt-20 text-cyan-400 animate-pulse font-mono tracking-widest">ANALYSE DU RÉSEAU...</div>;
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className="max-w-7xl mx-auto mt-8 p-6">
|
|
|
|
|
<div className="flex flex-col md:flex-row justify-between items-start md:items-end mb-10 gap-4">
|
|
|
|
|
<div className="w-full max-w-6xl p-6 mx-auto"> {/* Alignement avec le Dashboard */}
|
|
|
|
|
<div className="flex flex-col md:flex-row justify-between items-start md:items-end mb-8 gap-4">
|
|
|
|
|
<div>
|
|
|
|
|
<h1 className="text-3xl font-black text-white tracking-wider">INVENTAIRE RÉSEAU</h1>
|
|
|
|
|
<p className="text-gray-400 mt-1">Orchestration des environnements et des Virtual Private Clouds.</p>
|
|
|
|
|
<header>
|
|
|
|
|
<h1 className="text-3xl font-black text-white tracking-wider"> {/* Typographie Dashboard */}
|
|
|
|
|
TERMINAL <span className="text-cyan-400">NEXUS</span>
|
|
|
|
|
</h1>
|
|
|
|
|
<p className="text-gray-400 mt-2">Orchestration des environnements et des Virtual Private Clouds.</p>
|
|
|
|
|
</header>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div className="flex space-x-2 bg-gray-900 p-2 rounded-xl border border-gray-800">
|
|
|
|
@@ -562,8 +562,25 @@ export default function Services() {
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{error && <div className="text-red-400 bg-red-400/10 p-4 rounded-xl border border-red-500/20 mb-6">{error}</div>}
|
|
|
|
|
{/* GESTION DU CHARGEMENT IDENTIQUE AU DASHBOARD */}
|
|
|
|
|
{isLoading && (
|
|
|
|
|
<div className="flex items-center space-x-3 text-cyan-400 mb-8">
|
|
|
|
|
<Loader className="w-6 h-6 animate-spin" />
|
|
|
|
|
<span>Analyse du réseau et des instances en cours...</span>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{/* GESTION DES ERREURS IDENTIQUE AU DASHBOARD */}
|
|
|
|
|
{error && (
|
|
|
|
|
<div className="flex items-center space-x-3 text-red-400 bg-red-400/10 border border-red-400 p-4 rounded-lg mb-8">
|
|
|
|
|
<AlertCircle className="w-6 h-6" />
|
|
|
|
|
<span>{error}</span>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{/* CONTENU (Caché pendant le chargement pour rester propre) */}
|
|
|
|
|
{!isLoading && !error && (
|
|
|
|
|
<>
|
|
|
|
|
<div className="space-y-8 mb-12">
|
|
|
|
|
{vpcs.map(vpc => {
|
|
|
|
|
const vpcServices = services.filter(s => vpc.services.includes(s.id));
|
|
|
|
@@ -619,6 +636,8 @@ export default function Services() {
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{/* MODAL 1 : HESTIACP SSO VAULT */}
|
|
|
|
|
{ssoVault && (
|
|
|
|
@@ -626,7 +645,6 @@ export default function Services() {
|
|
|
|
|
<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">
|
|
|
|
|
<div className="flex justify-between items-center mb-6">
|
|
|
|
|
<h3 className="text-xl font-bold text-emerald-400 font-mono tracking-wider">ACCÈS AUTORISÉ</h3>
|
|
|
|
|
{/* La croix permet de fermer manuellement le modal après copie */}
|
|
|
|
|
<button onClick={() => setSsoVault(null)} className="text-gray-400 hover:text-white transition text-lg">✖</button>
|
|
|
|
|
</div>
|
|
|
|
|
<p className="text-sm text-gray-400 mb-6">Le pare-feu HestiaCP bloque les injections directes. Un mot de passe <strong>jetable</strong> a été généré. Copiez-le et connectez-vous.</p>
|
|
|
|
@@ -646,7 +664,6 @@ export default function Services() {
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
{/* REFIXÉ : Pas de setSsoVault(null) ici pour garder le modal ouvert au retour de l'onglet */}
|
|
|
|
|
<a href={ssoVault.url} target="_blank" rel="noopener noreferrer" 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 ↗
|
|
|
|
|
</a>
|
|
|
|
|