From 323667e8353b754c632ac648ed3c9767162c0ae0 Mon Sep 17 00:00:00 2001 From: LathanDevers Date: Thu, 25 Jun 2026 09:16:47 +0200 Subject: [PATCH] separate components --- .gitignore | 25 + .../dashboard/DashboardServiceCard.jsx | 49 ++ src/components/dashboard/NewServiceCard.jsx | 20 + src/components/services/InstanceCard.jsx | 68 ++ src/components/services/VpsDeployer.jsx | 41 ++ src/components/services/VpsManager.jsx | 90 +++ src/components/services/WebManager.jsx | 82 +++ src/components/store/CategorySection.jsx | 55 ++ src/components/store/ProductCard.jsx | 112 ++++ src/components/store/StoreHelpers.js | 31 + src/components/support/CreateTicketModal.jsx | 60 ++ src/components/support/SupportHelpers.jsx | 11 + src/components/support/TicketCard.jsx | 36 + src/components/support/TicketThreadModal.jsx | 80 +++ src/components/ui/ConfirmLogoutModal.jsx | 32 + src/components/ui/NotificationModal.jsx | 18 + src/layouts/AppLayout.jsx | 53 +- src/pages/app/Dashboard.jsx | 76 +-- src/pages/app/Services.jsx | 622 ++---------------- src/pages/app/Store.jsx | 288 +------- src/pages/app/Support.jsx | 347 ++-------- src/pages/app/VpsDeployer.jsx | 110 ---- src/pages/app/VpsManager.jsx | 112 ---- src/pages/app/VpsServiceDetail.jsx | 34 - src/pages/public/Register.jsx | 41 +- 25 files changed, 921 insertions(+), 1572 deletions(-) create mode 100644 .gitignore create mode 100644 src/components/dashboard/DashboardServiceCard.jsx create mode 100644 src/components/dashboard/NewServiceCard.jsx create mode 100644 src/components/services/InstanceCard.jsx create mode 100644 src/components/services/VpsDeployer.jsx create mode 100644 src/components/services/VpsManager.jsx create mode 100644 src/components/services/WebManager.jsx create mode 100644 src/components/store/CategorySection.jsx create mode 100644 src/components/store/ProductCard.jsx create mode 100644 src/components/store/StoreHelpers.js create mode 100644 src/components/support/CreateTicketModal.jsx create mode 100644 src/components/support/SupportHelpers.jsx create mode 100644 src/components/support/TicketCard.jsx create mode 100644 src/components/support/TicketThreadModal.jsx create mode 100644 src/components/ui/ConfirmLogoutModal.jsx create mode 100644 src/components/ui/NotificationModal.jsx delete mode 100644 src/pages/app/VpsDeployer.jsx delete mode 100644 src/pages/app/VpsManager.jsx delete mode 100644 src/pages/app/VpsServiceDetail.jsx diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d81a7fb --- /dev/null +++ b/.gitignore @@ -0,0 +1,25 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? +*api-keys* \ No newline at end of file diff --git a/src/components/dashboard/DashboardServiceCard.jsx b/src/components/dashboard/DashboardServiceCard.jsx new file mode 100644 index 0000000..2cdd770 --- /dev/null +++ b/src/components/dashboard/DashboardServiceCard.jsx @@ -0,0 +1,49 @@ +import { Server, Database, Cloud, Globe } from 'lucide-react'; + +export default function DashboardServiceCard({ order, onClick }) { + // Fonction Radar : Détecte le type de service selon son nom pour afficher la bonne icône + const getServiceIcon = (title) => { + const t = title.toLowerCase(); + if (t.includes('vps') || t.includes('serveur')) return ; + if (t.includes('cloud') || t.includes('nextcloud')) return ; + if (t.includes('db') || t.includes('base') || t.includes('sql')) return ; + return ; // Par défaut : Web / Hestia + }; + + // Fonction d'état : Formate le statut du service + const getStatusBadge = (status) => { + switch (status) { + case 'active': return ACTIF; + case 'pending_setup': return EN PRÉPARATION; + case 'suspended': return SUSPENDU; + default: return {status.toUpperCase()}; + } + }; + + return ( +
+
+
+
+ {getServiceIcon(order.title)} +
+ {getStatusBadge(order.status)} +
+

+ {order.title} +

+

+ Facturation : {order.period} +

+
+ +
+ ID Réseau: #{order.id} + Gérer > +
+
+ ); +} \ No newline at end of file diff --git a/src/components/dashboard/NewServiceCard.jsx b/src/components/dashboard/NewServiceCard.jsx new file mode 100644 index 0000000..9533ca3 --- /dev/null +++ b/src/components/dashboard/NewServiceCard.jsx @@ -0,0 +1,20 @@ +import { Plus } from 'lucide-react'; + +export default function NewServiceCard({ onClick }) { + return ( +
+
+ +
+

+ Demander une accréditation +

+

+ Déployer un nouveau serveur Web, VPS ou Cloud. +

+
+ ); +} \ No newline at end of file diff --git a/src/components/services/InstanceCard.jsx b/src/components/services/InstanceCard.jsx new file mode 100644 index 0000000..fe31d99 --- /dev/null +++ b/src/components/services/InstanceCard.jsx @@ -0,0 +1,68 @@ +import { Server, Database, Cloud, Globe, ExternalLink, Play, Loader } from 'lucide-react'; + +export default function InstanceCard({ service, vpcs, onAssignVpc, onRemoveVpc, onOpenConsole, isConnecting }) { + const titleLower = (service.title || '').toLowerCase(); + const isVPS = titleLower.includes('vps') || titleLower.includes('compute'); + const isCloud = titleLower.includes('cloud') || titleLower.includes('nextcloud'); + const isDB = titleLower.includes('db') || titleLower.includes('base') || titleLower.includes('sql'); + const isWeb = !isVPS && !isCloud && !isDB; + const hasIP = service.hostingDetails?.ip && service.hostingDetails.ip !== '127.0.0.1' && service.hostingDetails.ip !== ''; + + let buttonText = "CONSOLE D'ADMINISTRATION"; + let ButtonIcon = ExternalLink; + let buttonStyle = "bg-cyan-400/10 hover:bg-cyan-400 text-cyan-400 hover:text-gray-900 border-cyan-400"; + + if (isVPS) { + if (hasIP) { buttonText = "GÉRER L'INSTANCE"; ButtonIcon = ExternalLink; } + else { buttonText = "INITIALISATION"; ButtonIcon = Play; buttonStyle = "bg-yellow-500/10 text-yellow-500 hover:bg-yellow-500 hover:text-gray-900 border-yellow-500/50"; } + } else if (isWeb) { buttonText = "GÉRER L'HÉBERGEMENT"; ButtonIcon = Globe; buttonStyle = "bg-emerald-400/10 hover:bg-emerald-400 text-emerald-400 hover:text-gray-900 border-emerald-400"; } + else if (isCloud) { buttonText = "ACCÉDER AU CLOUD"; buttonStyle = "bg-blue-400/10 hover:bg-blue-400 text-blue-400 hover:text-gray-900 border-blue-400"; } + else if (isDB) { buttonText = "PHPMYADMIN / CLUSTER"; ButtonIcon = Database; buttonStyle = "bg-purple-400/10 hover:bg-purple-400 text-purple-400 hover:text-gray-900 border-purple-400"; } + + const getServiceIcon = () => { + if (isVPS) return ; + if (isCloud) return ; + if (isDB) return ; + return ; + }; + + const baseTitle = (service.title || '').split(/(?: for | pour )/i)[0].trim(); + const shortTitle = baseTitle.split(' ').slice(0, 2).join(' '); + const titleMatch = (service.title || '').match(/(?: for | pour )(.+)$/i); + const domainFromTitle = titleMatch ? titleMatch[1].trim() : null; + let displayDomain = service.hostingDetails?.domain && service.hostingDetails.domain !== '127.0.0.1' ? service.hostingDetails.domain : domainFromTitle || service.domain; + if (!displayDomain || displayDomain === '127.0.0.1') displayDomain = null; + + return ( +
+
+
+
{getServiceIcon()}
+ {service.status === 'active' ? ( + + {isVPS && !hasIP ? 'AWAITING INIT' : 'ONLINE'} + + ) : ( + DEPLOYING + )} +
+

{shortTitle}

+
+ {displayDomain ? (

{displayDomain}

) : (

En attente de déploiement

)} +
+
+
+
+ Projet VPC: + +
+ +
+
+ ); +} \ No newline at end of file diff --git a/src/components/services/VpsDeployer.jsx b/src/components/services/VpsDeployer.jsx new file mode 100644 index 0000000..de77a5e --- /dev/null +++ b/src/components/services/VpsDeployer.jsx @@ -0,0 +1,41 @@ +import { useState } from 'react'; + +export default function VpsDeployer({ serviceId, onDeploySuccess, onAlert }) { + const [domain, setDomain] = useState(''); + const [password, setPassword] = useState(''); + const [isDeploying, setIsDeploying] = useState(false); + + const handleDeploy = async () => { + if (password.length < 8) { onAlert("Sécurité", "Le mot de passe doit faire au moins 8 caractères.", "error"); return; } + setIsDeploying(true); + try { + const response = await fetch('https://web.gise.be/custom_api/proxmox_create_vps.php', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ service_id: serviceId, domain: domain, password: password }) }); + const data = await response.json(); + if (data.status === 'success') { onDeploySuccess({ ip: data.ip, domain: data.domain, password: password }); } + else { onAlert("Échec", "Erreur de provisionnement : " + (data.message || 'Inconnue'), "error"); } + } catch (err) { onAlert("Erreur Réseau", "Erreur de communication avec l'API Proxmox Gateway.", "error"); } + finally { setIsDeploying(false); } + }; + + return ( +
+
+ PHASE D'INITIALISATION REQUISE
+ La facturation est activée. Veuillez définir un mot de passe Root pour lancer la création de l'instance. +
+
+
+ + setDomain(e.target.value.toLowerCase())} placeholder="Optionnel" className="w-full bg-gray-950 border border-gray-800 text-white p-3 rounded font-mono outline-none focus:border-cyan-500 transition-colors" /> +
+
+ + setPassword(e.target.value)} placeholder="••••••••" className="w-full bg-gray-950 border border-gray-800 text-white p-3 rounded font-mono outline-none focus:border-cyan-500 transition-colors" /> +
+
+ +
+ ); +} \ No newline at end of file diff --git a/src/components/services/VpsManager.jsx b/src/components/services/VpsManager.jsx new file mode 100644 index 0000000..f4890d5 --- /dev/null +++ b/src/components/services/VpsManager.jsx @@ -0,0 +1,90 @@ +import { useState } from 'react'; +import { Folder, ExternalLink, Play, Maximize2, Lock } from 'lucide-react'; + +export default function VpsManager({ details, orderId, onRefresh, onAlert }) { + const [showTerminal, setShowTerminal] = useState(false); + const [isEditingDomain, setIsEditingDomain] = useState(false); + const [newDomain, setNewDomain] = useState(details.domain); + const [isUpdating, setIsUpdating] = useState(false); + const [sslStatus, setSslStatus] = useState(null); + + const isInternal = details.domain && details.domain.endsWith('.gise.be'); + const fileManagerUrl = isInternal ? `https://file-${details.domain}` : `http://${details.domain}:8080`; + const terminalUrl = isInternal ? `https://terminal-${details.domain}` : `http://${details.domain}:8081`; + + const handleUpdateDomain = async () => { + if (!newDomain || newDomain === details.domain) return; + setIsUpdating(true); + try { + const response = await fetch('https://web.gise.be/custom_api/update_service_domain.php', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ order_id: orderId, new_domain: newDomain }) }); + const data = await response.json(); + if (data.status === 'success') { setIsEditingDomain(false); if (onRefresh) onRefresh(); onAlert("Succès", "Domaine VPS mis à jour !", "success"); } + else onAlert("Erreur", data.error, "error"); + } catch (err) { onAlert("Erreur", "Erreur de connexion avec l'API.", "error"); } finally { setIsUpdating(false); } + }; + + const handleGenerateSSL = async () => { + setSslStatus('loading'); + try { + const response = await fetch('https://web.gise.be/custom_api/generate_custom_ssl.php', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ domain: details.domain }) }); + const data = await response.json(); + if (data.status === 'success') { setSslStatus('success'); onAlert("SSL Activé", "Certificat SSL généré !", "success"); } + else { setSslStatus('error'); onAlert("Erreur DNS", "Vérifiez votre pointage.", "error"); } + } catch (e) { setSslStatus('error'); } + }; + + return ( +
+ {!showTerminal ? ( + <> +
+ + {!isEditingDomain ? ( +
+ + {details.domain} + + +
+ ) : ( +
+ setNewDomain(e.target.value.toLowerCase())} className="flex-1 bg-black border border-gray-700 rounded px-3 py-1.5 text-white font-mono text-sm focus:border-cyan-500 outline-none" disabled={isUpdating} /> + + +
+ )} + {!isInternal && ( +
+

Domaine externe détecté.

+ +
+ )} +
+
+ + +
+ + ) : ( +
+
+ TERMINAL ({details.domain}) +
+ + +
+
+
+