165 lines
5.9 KiB
JavaScript
165 lines
5.9 KiB
JavaScript
import React, { useState, useEffect } from 'react';
|
|
import { Container, RefreshCw, Clock } from 'lucide-react';
|
|
import { getContainers } from '../utils/api';
|
|
|
|
function formatDate(dateStr) {
|
|
if (!dateStr) return 'N/A';
|
|
try {
|
|
return new Date(dateStr).toLocaleString('fr-FR');
|
|
} catch {
|
|
return dateStr;
|
|
}
|
|
}
|
|
|
|
export default function DockerPage() {
|
|
const [containers, setContainers] = useState([]);
|
|
const [loading, setLoading] = useState(true);
|
|
|
|
const fetchContainers = async () => {
|
|
setLoading(true);
|
|
try {
|
|
const res = await getContainers();
|
|
setContainers(res.data);
|
|
} catch (err) {
|
|
console.error('Erreur chargement conteneurs:', err);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
fetchContainers();
|
|
const interval = setInterval(fetchContainers, 15000);
|
|
return () => clearInterval(interval);
|
|
}, []);
|
|
|
|
const stateColors = {
|
|
running: 'text-emerald-400 bg-emerald-500/10 border-emerald-500/20',
|
|
exited: 'text-red-400 bg-red-500/10 border-red-500/20',
|
|
created: 'text-amber-400 bg-amber-500/10 border-amber-500/20',
|
|
restarting: 'text-amber-400 bg-amber-500/10 border-amber-500/20',
|
|
};
|
|
|
|
return (
|
|
<div className="space-y-6">
|
|
{/* Header */}
|
|
<div className="flex items-center justify-between">
|
|
<div>
|
|
<h2 className="text-2xl font-bold text-white flex items-center gap-3">
|
|
<Container className="w-7 h-7 text-primary-400" />
|
|
Conteneurs Docker
|
|
</h2>
|
|
<p className="text-gray-400 mt-1">
|
|
Vue d'ensemble de tous les conteneurs sur le serveur
|
|
</p>
|
|
</div>
|
|
<button
|
|
onClick={fetchContainers}
|
|
className="btn-secondary flex items-center gap-2"
|
|
>
|
|
<RefreshCw className={`w-4 h-4 ${loading ? 'animate-spin' : ''}`} />
|
|
Rafraîchir
|
|
</button>
|
|
</div>
|
|
|
|
{/* Containers Table */}
|
|
<div className="card overflow-hidden">
|
|
<div className="overflow-x-auto">
|
|
<table className="w-full">
|
|
<thead>
|
|
<tr className="border-b border-dark-700">
|
|
<th className="text-left text-xs font-medium text-gray-500 uppercase tracking-wider px-6 py-4">
|
|
Nom
|
|
</th>
|
|
<th className="text-left text-xs font-medium text-gray-500 uppercase tracking-wider px-6 py-4">
|
|
Image
|
|
</th>
|
|
<th className="text-left text-xs font-medium text-gray-500 uppercase tracking-wider px-6 py-4">
|
|
État
|
|
</th>
|
|
<th className="text-left text-xs font-medium text-gray-500 uppercase tracking-wider px-6 py-4">
|
|
Statut
|
|
</th>
|
|
<th className="text-left text-xs font-medium text-gray-500 uppercase tracking-wider px-6 py-4">
|
|
ID
|
|
</th>
|
|
<th className="text-left text-xs font-medium text-gray-500 uppercase tracking-wider px-6 py-4">
|
|
Créé
|
|
</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody className="divide-y divide-dark-700">
|
|
{loading && containers.length === 0 ? (
|
|
<tr>
|
|
<td colSpan="6" className="px-6 py-8 text-center">
|
|
<RefreshCw className="w-6 h-6 text-gray-600 mx-auto animate-spin" />
|
|
</td>
|
|
</tr>
|
|
) : containers.length === 0 ? (
|
|
<tr>
|
|
<td
|
|
colSpan="6"
|
|
className="px-6 py-8 text-center text-gray-500"
|
|
>
|
|
Aucun conteneur trouvé
|
|
</td>
|
|
</tr>
|
|
) : (
|
|
containers.map((c) => (
|
|
<tr
|
|
key={c.id}
|
|
className="hover:bg-dark-700/30 transition-colors"
|
|
>
|
|
<td className="px-6 py-3">
|
|
<span className="text-sm font-medium text-gray-200">
|
|
{c.names.join(', ')}
|
|
</span>
|
|
</td>
|
|
<td className="px-6 py-3">
|
|
<code className="text-xs text-gray-400 bg-dark-700 px-2 py-0.5 rounded">
|
|
{c.image}
|
|
</code>
|
|
</td>
|
|
<td className="px-6 py-3">
|
|
<span
|
|
className={`inline-flex items-center px-2 py-0.5 rounded-full text-xs font-medium border ${
|
|
stateColors[c.state] || stateColors.created
|
|
}`}
|
|
>
|
|
<span
|
|
className={`w-1.5 h-1.5 rounded-full mr-1.5 ${
|
|
c.state === 'running'
|
|
? 'bg-emerald-400'
|
|
: c.state === 'exited'
|
|
? 'bg-red-400'
|
|
: 'bg-amber-400'
|
|
}`}
|
|
/>
|
|
{c.state}
|
|
</span>
|
|
</td>
|
|
<td className="px-6 py-3">
|
|
<span className="text-xs text-gray-400">{c.status}</span>
|
|
</td>
|
|
<td className="px-6 py-3">
|
|
<code className="text-xs text-gray-500 font-mono">
|
|
{c.id}
|
|
</code>
|
|
</td>
|
|
<td className="px-6 py-3">
|
|
<span className="text-xs text-gray-500 flex items-center gap-1">
|
|
<Clock className="w-3 h-3" />
|
|
{formatDate(c.created)}
|
|
</span>
|
|
</td>
|
|
</tr>
|
|
))
|
|
)}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|