2026-06-06 17:46:33 +00:00
|
|
|
import React, { useState, useEffect } from "react";
|
|
|
|
|
import { ClusterList } from "@/components/Kubernetes/ClusterList";
|
|
|
|
|
import { PortForwardList } from "@/components/Kubernetes/PortForwardList";
|
|
|
|
|
import { AddClusterModal } from "@/components/Kubernetes/AddClusterModal";
|
|
|
|
|
import { PortForwardForm } from "@/components/Kubernetes/PortForwardForm";
|
|
|
|
|
import type { ClusterInfo, PortForwardResponse } from "@/lib/tauriCommands";
|
|
|
|
|
import {
|
|
|
|
|
listClustersCmd,
|
|
|
|
|
removeClusterCmd,
|
|
|
|
|
listPortForwardsCmd,
|
|
|
|
|
stopPortForwardCmd,
|
2026-06-06 18:01:35 +00:00
|
|
|
deletePortForwardCmd,
|
2026-06-06 17:46:33 +00:00
|
|
|
} from "@/lib/tauriCommands";
|
|
|
|
|
|
|
|
|
|
export function KubernetesPage() {
|
|
|
|
|
const [clusters, setClusters] = useState<ClusterInfo[]>([]);
|
|
|
|
|
const [portForwards, setPortForwards] = useState<PortForwardResponse[]>([]);
|
|
|
|
|
const [isLoading, setIsLoading] = useState(true);
|
|
|
|
|
const [isAddClusterOpen, setIsAddClusterOpen] = useState(false);
|
|
|
|
|
const [isStartPortForwardOpen, setIsStartPortForwardOpen] = useState(false);
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
loadData();
|
|
|
|
|
}, []);
|
|
|
|
|
|
|
|
|
|
const loadData = async () => {
|
|
|
|
|
setIsLoading(true);
|
|
|
|
|
try {
|
|
|
|
|
const [clustersData, portForwardsData] = await Promise.all([
|
|
|
|
|
listClustersCmd(),
|
|
|
|
|
listPortForwardsCmd(),
|
|
|
|
|
]);
|
|
|
|
|
setClusters(clustersData);
|
|
|
|
|
setPortForwards(portForwardsData);
|
|
|
|
|
} catch (err) {
|
|
|
|
|
console.error("Failed to load data:", err);
|
|
|
|
|
} finally {
|
|
|
|
|
setIsLoading(false);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleRemoveCluster = async (clusterId: string) => {
|
|
|
|
|
try {
|
|
|
|
|
await removeClusterCmd(clusterId);
|
|
|
|
|
setClusters((prev) => prev.filter((c) => c.id !== clusterId));
|
|
|
|
|
} catch (err) {
|
|
|
|
|
console.error("Failed to remove cluster:", err);
|
|
|
|
|
alert("Failed to remove cluster");
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleStopPortForward = async (id: string) => {
|
|
|
|
|
try {
|
|
|
|
|
await stopPortForwardCmd(id);
|
|
|
|
|
setPortForwards((prev) => prev.filter((pf) => pf.id !== id));
|
|
|
|
|
} catch (err) {
|
|
|
|
|
console.error("Failed to stop port forward:", err);
|
|
|
|
|
alert("Failed to stop port forward");
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2026-06-06 17:55:23 +00:00
|
|
|
const handleDeletePortForward = async (id: string) => {
|
|
|
|
|
try {
|
2026-06-06 18:01:35 +00:00
|
|
|
await deletePortForwardCmd(id);
|
2026-06-06 17:55:23 +00:00
|
|
|
setPortForwards((prev) => prev.filter((pf) => pf.id !== id));
|
|
|
|
|
} catch (err) {
|
|
|
|
|
console.error("Failed to delete port forward:", err);
|
|
|
|
|
alert("Failed to delete port forward");
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2026-06-06 17:46:33 +00:00
|
|
|
const handleAddCluster = (cluster: ClusterInfo) => {
|
|
|
|
|
setClusters((prev) => [...prev, cluster]);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleStartPortForward = (portForward: PortForwardResponse) => {
|
|
|
|
|
setPortForwards((prev) => [...prev, portForward]);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (isLoading) {
|
|
|
|
|
return (
|
|
|
|
|
<div className="flex items-center justify-center h-full">
|
|
|
|
|
<div className="flex flex-col items-center gap-4">
|
|
|
|
|
<div className="w-8 h-8 border-4 border-primary border-t-transparent rounded-full animate-spin" />
|
|
|
|
|
<p className="text-muted-foreground">Loading Kubernetes resources...</p>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className="h-full overflow-y-auto p-6 space-y-8">
|
|
|
|
|
<div className="flex flex-col gap-2">
|
|
|
|
|
<h1 className="text-3xl font-bold tracking-tight">Kubernetes Management</h1>
|
|
|
|
|
<p className="text-muted-foreground">
|
|
|
|
|
Manage your Kubernetes clusters and port forwarding sessions
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div className="grid gap-8">
|
|
|
|
|
<ClusterList
|
|
|
|
|
clusters={clusters}
|
|
|
|
|
onAdd={() => setIsAddClusterOpen(true)}
|
|
|
|
|
onRemove={handleRemoveCluster}
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
<PortForwardList
|
|
|
|
|
portForwards={portForwards}
|
|
|
|
|
onStart={() => setIsStartPortForwardOpen(true)}
|
|
|
|
|
onStop={handleStopPortForward}
|
2026-06-06 17:55:23 +00:00
|
|
|
onDelete={handleDeletePortForward}
|
2026-06-06 17:46:33 +00:00
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<AddClusterModal
|
|
|
|
|
isOpen={isAddClusterOpen}
|
|
|
|
|
onClose={() => setIsAddClusterOpen(false)}
|
|
|
|
|
onAdd={handleAddCluster}
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
<PortForwardForm
|
|
|
|
|
isOpen={isStartPortForwardOpen}
|
|
|
|
|
onClose={() => setIsStartPortForwardOpen(false)}
|
|
|
|
|
onStart={handleStartPortForward}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|