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'.
170 lines
5.6 KiB
TypeScript
170 lines
5.6 KiB
TypeScript
import React, { useState } from "react";
|
|
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui";
|
|
import { RotateCcw, Pencil, Trash2 } from "lucide-react";
|
|
import type { DaemonSetInfo } from "@/lib/tauriCommands";
|
|
import {
|
|
restartDaemonsetCmd,
|
|
deleteResourceCmd,
|
|
getResourceYamlCmd,
|
|
} from "@/lib/tauriCommands";
|
|
import { ResourceActionMenu } from "./ResourceActionMenu";
|
|
import { ConfirmDeleteDialog } from "./ConfirmDeleteDialog";
|
|
import { EditResourceModal } from "./EditResourceModal";
|
|
|
|
interface DaemonSetListProps {
|
|
daemonsets: DaemonSetInfo[];
|
|
clusterId: string;
|
|
namespace: string;
|
|
onRefresh?: () => void;
|
|
}
|
|
|
|
type ActiveModal =
|
|
| { type: "restart"; ds: DaemonSetInfo }
|
|
| { type: "edit"; ds: DaemonSetInfo; yaml: string }
|
|
| { type: "delete"; ds: DaemonSetInfo }
|
|
| null;
|
|
|
|
export function DaemonSetList({ daemonsets, clusterId, namespace: _namespace, onRefresh }: DaemonSetListProps) {
|
|
const [activeModal, setActiveModal] = useState<ActiveModal>(null);
|
|
const [isActing, setIsActing] = useState(false);
|
|
const [actionError, setActionError] = useState<string | null>(null);
|
|
|
|
const openEdit = async (ds: DaemonSetInfo) => {
|
|
setActionError(null);
|
|
try {
|
|
const yaml = await getResourceYamlCmd(clusterId, "daemonsets", ds.namespace, ds.name);
|
|
setActiveModal({ type: "edit", ds, yaml });
|
|
} catch (err) {
|
|
setActionError(err instanceof Error ? err.message : String(err));
|
|
}
|
|
};
|
|
|
|
const handleRestart = async () => {
|
|
if (activeModal?.type !== "restart") return;
|
|
setIsActing(true);
|
|
try {
|
|
await restartDaemonsetCmd(clusterId, activeModal.ds.namespace, activeModal.ds.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, "daemonsets", activeModal.ds.namespace, activeModal.ds.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>Desired</TableHead>
|
|
<TableHead>Current</TableHead>
|
|
<TableHead>Ready</TableHead>
|
|
<TableHead>Up-to-date</TableHead>
|
|
<TableHead>Available</TableHead>
|
|
<TableHead>Age</TableHead>
|
|
<TableHead className="text-right">Actions</TableHead>
|
|
</TableRow>
|
|
</TableHeader>
|
|
<TableBody>
|
|
{daemonsets.length === 0 ? (
|
|
<TableRow>
|
|
<TableCell colSpan={8} className="text-center text-muted-foreground">
|
|
No daemonsets found
|
|
</TableCell>
|
|
</TableRow>
|
|
) : (
|
|
daemonsets.map((ds) => (
|
|
<TableRow key={ds.name}>
|
|
<TableCell className="font-medium">{ds.name}</TableCell>
|
|
<TableCell>{ds.desired}</TableCell>
|
|
<TableCell>{ds.current}</TableCell>
|
|
<TableCell>{ds.ready}</TableCell>
|
|
<TableCell>{ds.up_to_date}</TableCell>
|
|
<TableCell>{ds.available}</TableCell>
|
|
<TableCell className="text-muted-foreground">{ds.age}</TableCell>
|
|
<TableCell className="text-right">
|
|
<ResourceActionMenu
|
|
actions={[
|
|
{
|
|
label: "Restart",
|
|
icon: RotateCcw,
|
|
onClick: () => setActiveModal({ type: "restart", ds }),
|
|
},
|
|
{
|
|
label: "Edit",
|
|
icon: Pencil,
|
|
onClick: () => openEdit(ds),
|
|
},
|
|
{
|
|
label: "Delete",
|
|
icon: Trash2,
|
|
variant: "destructive",
|
|
onClick: () => setActiveModal({ type: "delete", ds }),
|
|
},
|
|
]}
|
|
/>
|
|
</TableCell>
|
|
</TableRow>
|
|
))
|
|
)}
|
|
</TableBody>
|
|
</Table>
|
|
</div>
|
|
|
|
{activeModal?.type === "restart" && (
|
|
<ConfirmDeleteDialog
|
|
open
|
|
onOpenChange={(o) => { if (!o) setActiveModal(null); }}
|
|
resourceType="DaemonSet"
|
|
resourceName={activeModal.ds.name}
|
|
isLoading={isActing}
|
|
onConfirm={handleRestart}
|
|
variant="delete"
|
|
/>
|
|
)}
|
|
|
|
{activeModal?.type === "edit" && (
|
|
<EditResourceModal
|
|
isOpen
|
|
clusterId={clusterId}
|
|
namespace={activeModal.ds.namespace}
|
|
resourceType="daemonsets"
|
|
resourceName={activeModal.ds.name}
|
|
initialYaml={activeModal.yaml}
|
|
onClose={() => { setActiveModal(null); onRefresh?.(); }}
|
|
/>
|
|
)}
|
|
|
|
{activeModal?.type === "delete" && (
|
|
<ConfirmDeleteDialog
|
|
open
|
|
onOpenChange={(o) => { if (!o) setActiveModal(null); }}
|
|
resourceType="DaemonSet"
|
|
resourceName={activeModal.ds.name}
|
|
isLoading={isActing}
|
|
onConfirm={handleDelete}
|
|
/>
|
|
)}
|
|
</>
|
|
);
|
|
}
|