tftsr-devops_investigation/src/components/Kubernetes/WorkloadOverview.tsx
Shaun Arman c871318009 fix(ui): replace hardcoded colors with semantic Tailwind vars for dark mode
Non-adaptive text-gray-* and bg-white classes replaced with text-foreground,
text-muted-foreground, bg-card, bg-background — ensuring readable contrast
in both light and dark themes.
2026-06-08 21:52:01 -05:00

149 lines
5.1 KiB
TypeScript

import React from "react";
import { Layers, Box, Server, Activity } from "lucide-react";
import type {
PodInfo,
DeploymentInfo,
StatefulSetInfo,
DaemonSetInfo,
JobInfo,
CronJobInfo,
} from "@/lib/tauriCommands";
interface WorkloadOverviewProps {
clusterId: string;
resources: {
pods: PodInfo[];
deployments: DeploymentInfo[];
statefulsets: StatefulSetInfo[];
daemonsets: DaemonSetInfo[];
jobs: JobInfo[];
cronjobs: CronJobInfo[];
};
}
interface SummaryCardProps {
title: string;
value: number;
subtitle?: string;
icon: React.ReactNode;
}
function SummaryCard({ title, value, subtitle, icon }: SummaryCardProps) {
return (
<div className="bg-card rounded-lg p-4 border">
<div className="flex items-center justify-between pb-2">
<h3 className="text-sm font-medium">{title}</h3>
{icon}
</div>
<div className="text-2xl font-bold">{value}</div>
{subtitle && (
<p className="text-xs text-muted-foreground mt-1">{subtitle}</p>
)}
</div>
);
}
export function WorkloadOverview({ resources }: WorkloadOverviewProps) {
const { pods, deployments, statefulsets, daemonsets, jobs, cronjobs } = resources;
const runningPods = pods.filter((p) => p.status === "Running").length;
const pendingPods = pods.filter((p) => p.status === "Pending").length;
const failedPods = pods.filter((p) => p.status === "Failed").length;
const readyDeployments = deployments.filter((d) => d.ready === `${d.replicas}/${d.replicas}`).length;
const readyStatefulSets = statefulsets.filter((s) => {
const parts = s.ready.split("/");
return parts.length === 2 && parts[0] === parts[1];
}).length;
const healthyDaemonSets = daemonsets.filter(
(ds) => ds.desired === ds.ready
).length;
const completedJobs = jobs.filter((j) => {
const parts = j.completions.split("/");
return parts.length === 2 && parts[0] === parts[1];
}).length;
return (
<div className="h-full overflow-y-auto space-y-6 p-6">
<div>
<h2 className="text-2xl font-semibold">Workload Overview</h2>
<p className="text-muted-foreground text-sm mt-0.5">
Summary of all workload resources in the selected namespace
</p>
</div>
<div className="grid grid-cols-2 md:grid-cols-3 gap-4">
<SummaryCard
title="Pods"
value={pods.length}
subtitle={`Running: ${runningPods} · Pending: ${pendingPods} · Failed: ${failedPods}`}
icon={<Box className="h-4 w-4 text-muted-foreground" />}
/>
<SummaryCard
title="Deployments"
value={deployments.length}
subtitle={`Ready: ${readyDeployments}/${deployments.length}`}
icon={<Layers className="h-4 w-4 text-muted-foreground" />}
/>
<SummaryCard
title="StatefulSets"
value={statefulsets.length}
subtitle={`Ready: ${readyStatefulSets}/${statefulsets.length}`}
icon={<Server className="h-4 w-4 text-muted-foreground" />}
/>
<SummaryCard
title="DaemonSets"
value={daemonsets.length}
subtitle={`Healthy: ${healthyDaemonSets}/${daemonsets.length}`}
icon={<Activity className="h-4 w-4 text-muted-foreground" />}
/>
<SummaryCard
title="Jobs"
value={jobs.length}
subtitle={`Completed: ${completedJobs}/${jobs.length}`}
icon={<Activity className="h-4 w-4 text-muted-foreground" />}
/>
<SummaryCard
title="Cron Jobs"
value={cronjobs.length}
subtitle={cronjobs.length > 0 ? `Active: ${cronjobs.reduce((acc, cj) => acc + cj.active, 0)}` : undefined}
icon={<Activity className="h-4 w-4 text-muted-foreground" />}
/>
</div>
{pods.length > 0 && (
<div className="bg-card rounded-lg border">
<div className="border-b px-6 py-4">
<h3 className="font-semibold">Pod Status Breakdown</h3>
</div>
<div className="p-6">
<div className="flex gap-6 text-sm">
<div className="flex items-center gap-2">
<span className="inline-block w-3 h-3 rounded-full bg-green-500" />
<span>Running: {runningPods}</span>
</div>
<div className="flex items-center gap-2">
<span className="inline-block w-3 h-3 rounded-full bg-yellow-500" />
<span>Pending: {pendingPods}</span>
</div>
<div className="flex items-center gap-2">
<span className="inline-block w-3 h-3 rounded-full bg-red-500" />
<span>Failed: {failedPods}</span>
</div>
{pods.length - runningPods - pendingPods - failedPods > 0 && (
<div className="flex items-center gap-2">
<span className="inline-block w-3 h-3 rounded-full bg-muted-foreground" />
<span>Other: {pods.length - runningPods - pendingPods - failedPods}</span>
</div>
)}
</div>
</div>
</div>
)}
</div>
);
}