126 líneas
4.2 KiB
TypeScript
126 líneas
4.2 KiB
TypeScript
import { useState, useEffect } from 'react'
|
||
import { useCSFApi } from '@/hooks/use-csf-api'
|
||
|
||
interface FirewallStatusCardProps {
|
||
className?: string
|
||
}
|
||
|
||
export function FirewallStatusCard({ className = '' }: FirewallStatusCardProps) {
|
||
const {
|
||
status,
|
||
getStatus,
|
||
startFirewall,
|
||
stopFirewall,
|
||
restartFirewall,
|
||
loading,
|
||
error
|
||
} = useCSFApi()
|
||
|
||
const [actionLoading, setActionLoading] = useState(false)
|
||
|
||
useEffect(() => {
|
||
getStatus()
|
||
}, [getStatus])
|
||
|
||
const handleAction = async (action: () => Promise<boolean>) => {
|
||
setActionLoading(true)
|
||
try {
|
||
await action()
|
||
} finally {
|
||
setActionLoading(false)
|
||
}
|
||
}
|
||
|
||
const getStatusColor = () => {
|
||
if (!status) return 'bg-gray-500'
|
||
return status.running ? 'bg-green-500' : 'bg-red-500'
|
||
}
|
||
|
||
const getStatusText = () => {
|
||
if (!status) return 'Desconocido'
|
||
return status.running ? 'Activo' : 'Inactivo'
|
||
}
|
||
|
||
return (
|
||
<div className={`bg-white rounded-lg shadow-md p-6 ${className}`}>
|
||
<div className="flex items-center justify-between mb-4">
|
||
<h3 className="text-lg font-semibold text-gray-900">Estado del Firewall</h3>
|
||
<div className={`w-3 h-3 rounded-full ${getStatusColor()}`}></div>
|
||
</div>
|
||
|
||
{error && (
|
||
<div className="bg-red-50 border border-red-200 rounded-md p-3 mb-4">
|
||
<p className="text-red-600 text-sm">{error}</p>
|
||
</div>
|
||
)}
|
||
|
||
<div className="space-y-3">
|
||
<div className="flex justify-between">
|
||
<span className="text-gray-600">Estado:</span>
|
||
<span className={`font-medium ${status?.running ? 'text-green-600' : 'text-red-600'}`}>
|
||
{getStatusText()}
|
||
</span>
|
||
</div>
|
||
|
||
{status && (
|
||
<>
|
||
<div className="flex justify-between">
|
||
<span className="text-gray-600">Versión:</span>
|
||
<span className="font-medium">{status.version}</span>
|
||
</div>
|
||
|
||
<div className="flex justify-between">
|
||
<span className="text-gray-600">IPv4:</span>
|
||
<span className={`font-medium ${status.ipv4_active ? 'text-green-600' : 'text-red-600'}`}>
|
||
{status.ipv4_active ? 'Activo' : 'Inactivo'}
|
||
</span>
|
||
</div>
|
||
|
||
<div className="flex justify-between">
|
||
<span className="text-gray-600">IPv6:</span>
|
||
<span className={`font-medium ${status.ipv6_active ? 'text-green-600' : 'text-red-600'}`}>
|
||
{status.ipv6_active ? 'Activo' : 'Inactivo'}
|
||
</span>
|
||
</div>
|
||
|
||
{status.testing_mode && (
|
||
<div className="bg-yellow-50 border border-yellow-200 rounded-md p-3">
|
||
<p className="text-yellow-700 text-sm font-medium">
|
||
⚠️ Modo de prueba activado
|
||
</p>
|
||
</div>
|
||
)}
|
||
</>
|
||
)}
|
||
</div>
|
||
|
||
<div className="mt-6 space-y-2">
|
||
<div className="flex gap-2">
|
||
<button
|
||
onClick={() => handleAction(startFirewall)}
|
||
disabled={loading || actionLoading || status?.running}
|
||
className="flex-1 bg-green-600 text-white px-4 py-2 rounded-md hover:bg-green-700 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
||
>
|
||
{actionLoading ? 'Procesando...' : 'Iniciar'}
|
||
</button>
|
||
|
||
<button
|
||
onClick={() => handleAction(stopFirewall)}
|
||
disabled={loading || actionLoading || !status?.running}
|
||
className="flex-1 bg-red-600 text-white px-4 py-2 rounded-md hover:bg-red-700 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
||
>
|
||
{actionLoading ? 'Procesando...' : 'Detener'}
|
||
</button>
|
||
</div>
|
||
|
||
<button
|
||
onClick={() => handleAction(restartFirewall)}
|
||
disabled={loading || actionLoading}
|
||
className="w-full bg-blue-600 text-white px-4 py-2 rounded-md hover:bg-blue-700 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
||
>
|
||
{actionLoading ? 'Reiniciando...' : 'Reiniciar'}
|
||
</button>
|
||
</div>
|
||
</div>
|
||
)
|
||
} |