99 lines
3.3 KiB
TypeScript
99 lines
3.3 KiB
TypeScript
import { useState, useEffect } from 'react';
|
|
import { pb } from '../../config/pocketbase';
|
|
|
|
interface InfrastructureNode {
|
|
id: string;
|
|
label: string;
|
|
type: string;
|
|
status: string;
|
|
uptime_sla: string;
|
|
}
|
|
|
|
export default function UptimeWidget() {
|
|
const [nodes, setNodes] = useState<InfrastructureNode[]>([]);
|
|
const [isLoading, setIsLoading] = useState(true);
|
|
|
|
useEffect(() => {
|
|
const fetchNodes = async () => {
|
|
try {
|
|
// Récupération sécurisée filtrée automatiquement par le Token JWT de la compagnie
|
|
const records = await pb.collection('aegis_infrastructure_nodes').getFullList<InfrastructureNode>({
|
|
sort: 'created',
|
|
});
|
|
setNodes(records);
|
|
} catch (error) {
|
|
console.error("Erreur lors de la récupération des nœuds :", error);
|
|
} finally {
|
|
setIsLoading(false);
|
|
}
|
|
};
|
|
|
|
fetchNodes();
|
|
}, []);
|
|
|
|
// Fonction pour adapter dynamiquement la couleur du badge selon le statut renvoyé par la base
|
|
const getStatusStyle = (status: string) => {
|
|
switch (status) {
|
|
case 'Opérationnel':
|
|
return {
|
|
bg: 'bg-emerald-50 text-emerald-600 border-emerald-100',
|
|
dot: 'bg-emerald-500'
|
|
};
|
|
case 'Dégradé':
|
|
return {
|
|
bg: 'bg-amber-50 text-amber-600 border-amber-100',
|
|
dot: 'bg-amber-500'
|
|
};
|
|
case 'Hors Ligne':
|
|
return {
|
|
bg: 'bg-red-50 text-red-600 border-red-100',
|
|
dot: 'bg-red-500'
|
|
};
|
|
default:
|
|
return {
|
|
bg: 'bg-slate-50 text-slate-600 border-slate-100',
|
|
dot: 'bg-slate-400'
|
|
};
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div className="bg-white rounded-xl shadow-sm border border-slate-200 p-6 flex flex-col">
|
|
<h2 className="text-lg font-semibold text-slate-900 mb-4">État des Services & SLA</h2>
|
|
|
|
{isLoading ? (
|
|
<div className="flex-1 flex justify-center items-center py-8">
|
|
<div className="animate-spin rounded-full h-6 w-6 border-b-2 border-slate-900"></div>
|
|
</div>
|
|
) : nodes.length === 0 ? (
|
|
<div className="flex-1 flex justify-center items-center py-8 text-sm text-slate-500 italic">
|
|
Aucun équipement enregistré pour ce contrat.
|
|
</div>
|
|
) : (
|
|
<div className="divide-y divide-slate-100 flex-1">
|
|
{nodes.map((node) => {
|
|
const style = getStatusStyle(node.status);
|
|
return (
|
|
<div key={node.id} className="py-4 flex items-center justify-between first:pt-0 last:pb-0">
|
|
<div>
|
|
<p className="font-medium text-slate-900">{node.label}</p>
|
|
<p className="text-xs text-slate-500 mt-0.5">{node.type}</p>
|
|
</div>
|
|
<div className="flex items-center space-x-6">
|
|
<div className="text-right">
|
|
<p className="text-xs text-slate-500">SLA</p>
|
|
<p className="text-sm font-semibold text-slate-900">{node.uptime_sla}%</p>
|
|
</div>
|
|
<div className={`${style.bg} px-3 py-1 rounded-full text-xs font-medium flex items-center border`}>
|
|
<div className={`w-1.5 h-1.5 rounded-full ${style.dot} mr-2`}></div>
|
|
{node.status}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
} |