tftsr-devops_investigation/src/components/Kubernetes/WorkloadOverview.tsx

149 lines
5.1 KiB
TypeScript
Raw Normal View History

feat(kube): nav restructure, action menus, new resource lists, advanced components Navigation: - Restructure to match requested layout: Cluster, Nodes, Workloads, Config, Network, Storage, Namespaces, Events, Helm, Access Control, Custom Resources - Workloads: add Overview dashboard and Replication Controllers - Config: add PDB, PriorityClass, RuntimeClass, Lease, Mutating/Validating Webhooks - Network: add Endpoints, EndpointSlices, IngressClasses; move Port Forwarding here - Helm and Custom Resources sections wired through New shared components: - ResourceActionMenu: state-aware MoreHorizontal dropdown - ConfirmDeleteDialog: confirmation guard for all destructive operations - ScaleModal: replica count dialog (Deployments, StatefulSets, ReplicaSets, RCs) - LogsModal: container log viewer replacing PodList inline dialog - ShellExecModal: kubectl exec -it with container and shell selector - AttachModal: kubectl attach -it with container selector New resource list components (12): ReplicationControllerList, PodDisruptionBudgetList, PriorityClassList, RuntimeClassList, LeaseList, MutatingWebhookList, ValidatingWebhookList, EndpointList, EndpointSliceList, IngressClassList, NamespaceList, WorkloadOverview New advanced components (5): LogStreamPanel (Tauri-event streaming, follow/search/download), HelmChartList, HelmReleaseList, CrdList, CustomResourceList Updated 24 existing list components with context-appropriate action menus: - Pods: Logs, Shell, Attach, Edit, Delete, Force Delete (state-aware) - Deployments: Scale, Restart, Rollback, Edit, Delete - StatefulSets/ReplicaSets: Scale, Restart/none, Edit, Delete - DaemonSets: Restart, Edit, Delete - Jobs: Edit, Delete - CronJobs: Suspend/Resume (state-aware), Trigger, Edit, Delete - Services/Ingresses/ConfigMaps/Secrets/HPAs/PVCs/PVs/StorageClasses/ NetworkPolicies/ResourceQuotas/LimitRanges: Edit, Delete - Nodes: Cordon/Uncordon (state-aware), Drain, Edit - All RBAC resources: Edit, Delete Co-Authored-By: TFTSR Engineering <noreply@tftsr.com>
2026-06-09 01:38:05 +00:00
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-gray-400" />
<span>Other: {pods.length - runningPods - pendingPods - failedPods}</span>
</div>
)}
</div>
</div>
</div>
)}
</div>
);
}