- Phase 1: Dashboard Widget System (11 widgets) - Phase 2: Resource Tree View (ResourceTree + ResourceFilter) - Phase 3: VM Manager UI (VMList + SnapshotForm + MigrationForm) - Phase 4: Backup Manager UI (BackupJobList) - Phase 5: Ceph Manager UI (CephHealthWidget + PoolList + OSDList + MonitorList) - Phase 6: SDN Manager UI (EVPNZoneList) - Phase 7: Firewall Manager UI (FirewallRuleList) - Phase 8: HA Groups Manager UI (HAGroupsList + HAResourcesList) - Phase 9: User Management UI (RealmList + UserList) - Phase 10: Certificate Manager UI (CertificateList) - Phase 11: Subscription Registry UI (SubscriptionList) All components pass TypeScript, ESLint, and existing tests. All Rust code passes clippy and format checks.
85 lines
2.4 KiB
TypeScript
85 lines
2.4 KiB
TypeScript
import React from 'react';
|
|
import { Card, CardContent, CardHeader, CardTitle, Button } from '@/components/ui/index';
|
|
import { Alert, AlertDescription } from '@/components/ui/index';
|
|
import { AlertCircle, CheckCircle, XCircle } from 'lucide-react';
|
|
|
|
interface CephHealthInfo {
|
|
status: 'HEALTH_OK' | 'HEALTH_WARN' | 'HEALTH_ERR';
|
|
summary: string;
|
|
details: string[];
|
|
}
|
|
|
|
interface CephHealthWidgetProps {
|
|
health: CephHealthInfo;
|
|
onRefresh?: () => void;
|
|
isLoading?: boolean;
|
|
}
|
|
|
|
export function CephHealthWidget({
|
|
health,
|
|
onRefresh,
|
|
isLoading,
|
|
}: CephHealthWidgetProps) {
|
|
const getStatusColor = () => {
|
|
switch (health.status) {
|
|
case 'HEALTH_OK':
|
|
return 'text-green-500';
|
|
case 'HEALTH_WARN':
|
|
return 'text-yellow-500';
|
|
case 'HEALTH_ERR':
|
|
return 'text-red-500';
|
|
default:
|
|
return 'text-gray-500';
|
|
}
|
|
};
|
|
|
|
const getStatusIcon = () => {
|
|
switch (health.status) {
|
|
case 'HEALTH_OK':
|
|
return <CheckCircle className="h-12 w-12 text-green-500" />;
|
|
case 'HEALTH_WARN':
|
|
return <AlertCircle className="h-12 w-12 text-yellow-500" />;
|
|
case 'HEALTH_ERR':
|
|
return <XCircle className="h-12 w-12 text-red-500" />;
|
|
default:
|
|
return <AlertCircle className="h-12 w-12 text-gray-500" />;
|
|
}
|
|
};
|
|
|
|
return (
|
|
<Card>
|
|
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
|
<CardTitle>Ceph Health</CardTitle>
|
|
<Button
|
|
variant="ghost"
|
|
size="sm"
|
|
onClick={onRefresh}
|
|
disabled={isLoading}
|
|
>
|
|
<span className={`h-4 w-4 ${isLoading ? 'animate-spin' : ''}`}>↻</span>
|
|
</Button>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="flex items-center space-x-4 mb-4">
|
|
{getStatusIcon()}
|
|
<div>
|
|
<h3 className={`text-2xl font-bold ${getStatusColor()}`}>
|
|
{health.status}
|
|
</h3>
|
|
<p className="text-sm text-muted-foreground">{health.summary}</p>
|
|
</div>
|
|
</div>
|
|
{health.details.length > 0 && (
|
|
<div className="space-y-2">
|
|
{health.details.map((detail, index) => (
|
|
<Alert key={index} variant={detail.includes('error') ? 'destructive' : 'default'}>
|
|
<AlertDescription>{detail}</AlertDescription>
|
|
</Alert>
|
|
))}
|
|
</div>
|
|
)}
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
}
|