rebuild api fossbilling

This commit is contained in:
maximus
2026-07-15 14:42:44 +02:00
parent a4f9663f60
commit 074e3ab195
4 changed files with 253 additions and 113 deletions
+95 -45
View File
@@ -1,11 +1,13 @@
import { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { getClientOrders } from '../../services/api';
import { AlertCircle, Loader } from 'lucide-react';
import { AlertCircle, Loader, FileText, X } from 'lucide-react';
// Importation des composants isolés
// Importation des composants
import DashboardServiceCard from '../../components/dashboard/DashboardServiceCard';
import NewServiceCard from '../../components/dashboard/NewServiceCard';
import NotificationModal from '../../components/ui/NotificationModal';
import WebServiceSubscriptionManager from '../../components/dashboard/WebServiceSubscriptionManager';
export default function Dashboard() {
const navigate = useNavigate();
@@ -13,54 +15,82 @@ export default function Dashboard() {
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState(null);
// Chargement des données à l'ouverture du Sas
useEffect(() => {
const fetchInventory = async () => {
try {
const data = await getClientOrders();
if (data.list) {
// LE FILTRE CHIRURGICAL PAR PREFIXE
const filteredOrders = data.list.filter(order => {
const title = (order.title || '').toLowerCase();
const type = (order.type || '').toLowerCase();
const [activeSubscriptionModal, setActiveSubscriptionModal] = useState(null);
const [customAlert, setCustomAlert] = useState(null);
const isGhostProduct =
type === 'domain' ||
title.startsWith('domain ') ||
title.startsWith('domaine ') ||
title.startsWith('enregistrement ');
return !isGhostProduct;
});
const triggerAlert = (title, message, type = "info") => setCustomAlert({ title, message, type });
setOrders(filteredOrders);
} else {
setOrders([]);
}
} catch (err) {
setError(err.message || "Impossible de récupérer la télémétrie des services.");
} finally {
setIsLoading(false);
const fetchInventory = async () => {
setIsLoading(true);
try {
const data = await getClientOrders();
if (data.list) {
// LE FILTRE CHIRURGICAL PAR PREFIXE
const filteredOrders = data.list.filter(order => {
const title = (order.title || '').toLowerCase();
const type = (order.type || '').toLowerCase();
const isGhostProduct =
type === 'domain' ||
title.startsWith('domain ') ||
title.startsWith('domaine ') ||
title.startsWith('enregistrement ');
return !isGhostProduct;
});
setOrders(filteredOrders);
} else {
setOrders([]);
}
};
} catch (err) {
setError(err.message || "Impossible de récupérer la télémétrie des services.");
} finally {
setIsLoading(false);
}
};
useEffect(() => {
fetchInventory();
}, []);
// 🌟 Plus besoin de charger les IPs, on ouvre juste la modale comptable !
const handleManageSubscription = (order) => {
// 1. Détection du type de service
const titleLower = (order.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');
// C'est un hébergement web s'il ne correspond à aucun des cas ci-dessus
const isWeb = !isVPS && !isCloud && !isDB;
// 2. Logique de blocage
if (isWeb) {
// On ouvre la modale comptable uniquement pour le WEB
setActiveSubscriptionModal(order);
} else {
// On affiche une notification pour les autres types d'instances
triggerAlert(
"Action non disponible",
"La modification autonome d'abonnement est actuellement exclusive aux Hébergements Web. Pour restructurer cette instance, veuillez contacter les ingénieurs via le Centre de Support.",
"info"
);
}
};
return (
<div className="w-full max-w-6xl p-6 mx-auto">
<div className="w-full max-w-6xl p-6 mx-auto relative">
<header className="mb-8">
<h1 className="text-3xl font-black text-white tracking-wider">TABLEAU DE <span className="text-cyan-400">BORD</span></h1>
<p className="text-gray-400 mt-2">Aperçu de vos accréditations réseau et infrastructures.</p>
<p className="text-gray-400 mt-2">Gestion financière et accréditations de vos infrastructures.</p>
</header>
{/* GESTION DES ERREURS & CHARGEMENT */}
{isLoading && (
<div className="flex items-center space-x-3 text-cyan-400">
<Loader className="w-6 h-6 animate-spin" />
<span>Synchronisation avec l'orchestrateur en cours...</span>
<span>Synchronisation avec le registre comptable...</span>
</div>
)}
@@ -71,24 +101,44 @@ export default function Dashboard() {
</div>
)}
{/* GRILLE DES SERVICES */}
{!isLoading && !error && (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-6">
{/* Boucle sur les composants isolés */}
{orders.map((order) => (
<DashboardServiceCard
key={order.id}
order={order}
onClick={() => navigate(`/services/${order.id}`)}
<DashboardServiceCard
key={order.id}
order={order}
onClick={() => handleManageSubscription(order)}
/>
))}
{/* Le composant carte d'ajout */}
<NewServiceCard onClick={() => navigate('/store')} />
</div>
)}
{/* 🌟 LA MODALE DE GESTION D'ABONNEMENT */}
{activeSubscriptionModal && (
<div className="fixed inset-0 bg-black/80 backdrop-blur-sm z-50 flex items-center justify-center p-4">
<div className="bg-gray-900 border border-cyan-500/50 shadow-2xl shadow-cyan-500/10 rounded-lg max-w-xxl w-full p-6 text-gray-200">
<div className="flex justify-between items-center mb-6 border-b border-gray-800 pb-4">
<h3 className="text-xl font-bold text-white tracking-wider flex items-center gap-3">
<FileText className="w-6 h-6 text-cyan-400" />
GESTION DE L'ABONNEMENT
</h3>
<button onClick={() => setActiveSubscriptionModal(null)} className="p-2 text-gray-500 hover:text-white bg-gray-900 hover:bg-gray-800 rounded-lg transition-colors">
<X className="w-5 h-5" />
</button>
</div>
<WebServiceSubscriptionManager
order={activeSubscriptionModal}
onClose={() => setActiveSubscriptionModal(null)}
onRefresh={fetchInventory}
onAlert={triggerAlert}
/>
</div>
</div>
)}
<NotificationModal notification={customAlert} onClose={() => setCustomAlert(null)} />
</div>
);
}