Some checks failed
PR Review Automation / review (pull_request) Has been cancelled
Test / rust-fmt-check (pull_request) Has been cancelled
Test / rust-clippy (pull_request) Has been cancelled
Test / frontend-typecheck (pull_request) Has been cancelled
Test / rust-tests (pull_request) Has been cancelled
Test / frontend-tests (pull_request) Has been cancelled
Deployment/StatefulSet/DaemonSet action handlers were passing namespace='all' to kubectl when All Namespaces was selected. Actions now use the resource's own .namespace field for openEdit, handleRestart, handleRollback, handleDelete, ScaleModal, and EditResourceModal. Adds 21 TDD tests in WorkloadListActions.test.tsx covering all action handlers across DeploymentList, StatefulSetList, DaemonSetList, ReplicaSetList, JobList, and CronJobList. Tests verify IPC calls receive the item's actual namespace even when the filter prop is 'all'.
188 lines
6.2 KiB
TypeScript
188 lines
6.2 KiB
TypeScript
import React, { useState } from "react";
|
|
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui";
|
|
import { Scale, RotateCcw, Pencil, Trash2 } from "lucide-react";
|
|
import type { StatefulSetInfo } from "@/lib/tauriCommands";
|
|
import {
|
|
scaleStatefulsetCmd,
|
|
restartStatefulsetCmd,
|
|
deleteResourceCmd,
|
|
getResourceYamlCmd,
|
|
} from "@/lib/tauriCommands";
|
|
import { ResourceActionMenu } from "./ResourceActionMenu";
|
|
import { ConfirmDeleteDialog } from "./ConfirmDeleteDialog";
|
|
import { ScaleModal } from "./ScaleModal";
|
|
import { EditResourceModal } from "./EditResourceModal";
|
|
|
|
interface StatefulSetListProps {
|
|
statefulsets: StatefulSetInfo[];
|
|
clusterId: string;
|
|
namespace: string;
|
|
onRefresh?: () => void;
|
|
}
|
|
|
|
type ActiveModal =
|
|
| { type: "scale"; ss: StatefulSetInfo }
|
|
| { type: "restart"; ss: StatefulSetInfo }
|
|
| { type: "edit"; ss: StatefulSetInfo; yaml: string }
|
|
| { type: "delete"; ss: StatefulSetInfo }
|
|
| null;
|
|
|
|
export function StatefulSetList({ statefulsets, clusterId, namespace: _namespace, onRefresh }: StatefulSetListProps) {
|
|
const [activeModal, setActiveModal] = useState<ActiveModal>(null);
|
|
const [isActing, setIsActing] = useState(false);
|
|
const [actionError, setActionError] = useState<string | null>(null);
|
|
|
|
const openEdit = async (ss: StatefulSetInfo) => {
|
|
setActionError(null);
|
|
try {
|
|
const yaml = await getResourceYamlCmd(clusterId, "statefulsets", ss.namespace, ss.name);
|
|
setActiveModal({ type: "edit", ss, yaml });
|
|
} catch (err) {
|
|
setActionError(err instanceof Error ? err.message : String(err));
|
|
}
|
|
};
|
|
|
|
const handleRestart = async () => {
|
|
if (activeModal?.type !== "restart") return;
|
|
setIsActing(true);
|
|
try {
|
|
await restartStatefulsetCmd(clusterId, activeModal.ss.namespace, activeModal.ss.name);
|
|
setActiveModal(null);
|
|
onRefresh?.();
|
|
} catch (err) {
|
|
setActionError(err instanceof Error ? err.message : String(err));
|
|
} finally {
|
|
setIsActing(false);
|
|
}
|
|
};
|
|
|
|
const handleDelete = async () => {
|
|
if (activeModal?.type !== "delete") return;
|
|
setIsActing(true);
|
|
try {
|
|
await deleteResourceCmd(clusterId, "statefulsets", activeModal.ss.namespace, activeModal.ss.name);
|
|
setActiveModal(null);
|
|
onRefresh?.();
|
|
} finally {
|
|
setIsActing(false);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<>
|
|
{actionError && (
|
|
<p className="mb-2 text-sm text-destructive">{actionError}</p>
|
|
)}
|
|
<div className="overflow-x-auto">
|
|
<Table>
|
|
<TableHeader>
|
|
<TableRow>
|
|
<TableHead>Name</TableHead>
|
|
<TableHead>Ready</TableHead>
|
|
<TableHead>Replicas</TableHead>
|
|
<TableHead>Age</TableHead>
|
|
<TableHead className="text-right">Actions</TableHead>
|
|
</TableRow>
|
|
</TableHeader>
|
|
<TableBody>
|
|
{statefulsets.length === 0 ? (
|
|
<TableRow>
|
|
<TableCell colSpan={5} className="text-center text-muted-foreground">
|
|
No statefulsets found
|
|
</TableCell>
|
|
</TableRow>
|
|
) : (
|
|
statefulsets.map((ss) => (
|
|
<TableRow key={ss.name}>
|
|
<TableCell className="font-medium">{ss.name}</TableCell>
|
|
<TableCell>{ss.ready}</TableCell>
|
|
<TableCell>{ss.replicas}</TableCell>
|
|
<TableCell className="text-muted-foreground">{ss.age}</TableCell>
|
|
<TableCell className="text-right">
|
|
<ResourceActionMenu
|
|
actions={[
|
|
{
|
|
label: "Scale",
|
|
icon: Scale,
|
|
onClick: () => setActiveModal({ type: "scale", ss }),
|
|
},
|
|
{
|
|
label: "Restart",
|
|
icon: RotateCcw,
|
|
onClick: () => setActiveModal({ type: "restart", ss }),
|
|
},
|
|
{
|
|
label: "Edit",
|
|
icon: Pencil,
|
|
onClick: () => openEdit(ss),
|
|
},
|
|
{
|
|
label: "Delete",
|
|
icon: Trash2,
|
|
variant: "destructive",
|
|
onClick: () => setActiveModal({ type: "delete", ss }),
|
|
},
|
|
]}
|
|
/>
|
|
</TableCell>
|
|
</TableRow>
|
|
))
|
|
)}
|
|
</TableBody>
|
|
</Table>
|
|
</div>
|
|
|
|
{activeModal?.type === "scale" && (
|
|
<ScaleModal
|
|
open
|
|
onOpenChange={(o) => { if (!o) setActiveModal(null); }}
|
|
resourceType="StatefulSet"
|
|
resourceName={activeModal.ss.name}
|
|
currentReplicas={activeModal.ss.replicas}
|
|
onScale={(replicas) =>
|
|
scaleStatefulsetCmd(clusterId, activeModal.ss.namespace, activeModal.ss.name, replicas).then(() => {
|
|
setActiveModal(null);
|
|
onRefresh?.();
|
|
})
|
|
}
|
|
/>
|
|
)}
|
|
|
|
{activeModal?.type === "restart" && (
|
|
<ConfirmDeleteDialog
|
|
open
|
|
onOpenChange={(o) => { if (!o) setActiveModal(null); }}
|
|
resourceType="StatefulSet"
|
|
resourceName={activeModal.ss.name}
|
|
isLoading={isActing}
|
|
onConfirm={handleRestart}
|
|
variant="delete"
|
|
/>
|
|
)}
|
|
|
|
{activeModal?.type === "edit" && (
|
|
<EditResourceModal
|
|
isOpen
|
|
clusterId={clusterId}
|
|
namespace={activeModal.ss.namespace}
|
|
resourceType="statefulsets"
|
|
resourceName={activeModal.ss.name}
|
|
initialYaml={activeModal.yaml}
|
|
onClose={() => { setActiveModal(null); onRefresh?.(); }}
|
|
/>
|
|
)}
|
|
|
|
{activeModal?.type === "delete" && (
|
|
<ConfirmDeleteDialog
|
|
open
|
|
onOpenChange={(o) => { if (!o) setActiveModal(null); }}
|
|
resourceType="StatefulSet"
|
|
resourceName={activeModal.ss.name}
|
|
isLoading={isActing}
|
|
onConfirm={handleDelete}
|
|
/>
|
|
)}
|
|
</>
|
|
);
|
|
}
|