mirror of
https://github.com/multica-ai/multica.git
synced 2026-07-05 13:29:44 +02:00
feat: add delete button to fleet nodes list
- Add deleteCloudRuntimeNode method to API client (DELETE /api/cloud-runtime/nodes/:nodeId) - Add useDeleteCloudRuntimeNode mutation hook in cloud-runtime.ts - Add delete button with Trash2 icon to CloudRuntimeNodeRow component - Include confirmation dialog, loading state, and toast notifications - Add i18n keys for en and zh-Hans locales Co-authored-by: multica-agent <github@multica.ai>
This commit is contained in:
@@ -869,6 +869,12 @@ export class ApiClient {
|
||||
);
|
||||
}
|
||||
|
||||
async deleteCloudRuntimeNode(nodeId: string): Promise<void> {
|
||||
await this.fetch(`/api/cloud-runtime/nodes/${nodeId}`, {
|
||||
method: "DELETE",
|
||||
});
|
||||
}
|
||||
|
||||
async deleteRuntime(runtimeId: string): Promise<void> {
|
||||
await this.fetch(`/api/runtimes/${runtimeId}`, { method: "DELETE" });
|
||||
}
|
||||
|
||||
@@ -79,3 +79,13 @@ export function useCreateCloudRuntimeNode(wsId: string) {
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useDeleteCloudRuntimeNode(wsId: string) {
|
||||
const qc = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: (nodeId: string) => api.deleteCloudRuntimeNode(nodeId),
|
||||
onSettled: () => {
|
||||
qc.invalidateQueries({ queryKey: cloudRuntimeKeys.all(wsId) });
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@@ -172,6 +172,10 @@
|
||||
"cancel": "Cancel",
|
||||
"toast_created": "Cloud Runtime node created",
|
||||
"toast_create_failed": "Failed to create Cloud Runtime node",
|
||||
"delete": "Delete node",
|
||||
"delete_confirm": "Are you sure you want to delete this cloud node? This action cannot be undone.",
|
||||
"toast_deleted": "Cloud node deleted",
|
||||
"toast_delete_failed": "Failed to delete cloud node",
|
||||
"fields": {
|
||||
"name": "Name",
|
||||
"instance_type": "Instance type",
|
||||
|
||||
@@ -163,6 +163,10 @@
|
||||
"cancel": "取消",
|
||||
"toast_created": "Cloud Runtime 节点已创建",
|
||||
"toast_create_failed": "创建 Cloud Runtime 节点失败",
|
||||
"delete": "删除节点",
|
||||
"delete_confirm": "确定要删除此云节点吗?此操作无法撤销。",
|
||||
"toast_deleted": "云节点已删除",
|
||||
"toast_delete_failed": "删除云节点失败",
|
||||
"fields": {
|
||||
"name": "名称",
|
||||
"instance_type": "实例规格",
|
||||
|
||||
@@ -3,12 +3,13 @@
|
||||
import { useId, useMemo, useState } from "react";
|
||||
import type { FormEvent, HTMLAttributes } from "react";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { Cloud, Loader2, RefreshCw, Rocket } from "lucide-react";
|
||||
import { Cloud, Loader2, RefreshCw, Rocket, Trash2 } from "lucide-react";
|
||||
import { toast } from "sonner";
|
||||
import type { CloudRuntimeNode } from "@multica/core/runtimes";
|
||||
import {
|
||||
cloudRuntimeNodeListOptions,
|
||||
useCreateCloudRuntimeNode,
|
||||
useDeleteCloudRuntimeNode,
|
||||
} from "@multica/core/runtimes";
|
||||
import { useWorkspaceId } from "@multica/core/hooks";
|
||||
import { Badge } from "@multica/ui/components/ui/badge";
|
||||
@@ -192,7 +193,7 @@ export function CloudRuntimeDialog({ onClose }: { onClose: () => void }) {
|
||||
<div className="max-h-[410px] overflow-y-auto p-2">
|
||||
<div className="space-y-2">
|
||||
{sortedNodes.map((node) => (
|
||||
<CloudRuntimeNodeRow key={node.id} node={node} />
|
||||
<CloudRuntimeNodeRow key={node.id} node={node} wsId={wsId} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
@@ -290,8 +291,9 @@ function LabeledInput({
|
||||
);
|
||||
}
|
||||
|
||||
function CloudRuntimeNodeRow({ node }: { node: CloudRuntimeNode }) {
|
||||
function CloudRuntimeNodeRow({ node, wsId }: { node: CloudRuntimeNode; wsId: string }) {
|
||||
const { t } = useT("runtimes");
|
||||
const deleteNode = useDeleteCloudRuntimeNode(wsId);
|
||||
const title =
|
||||
node.name.trim() ||
|
||||
node.instance_id.trim() ||
|
||||
@@ -317,6 +319,32 @@ function CloudRuntimeNodeRow({ node }: { node: CloudRuntimeNode }) {
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="h-7 w-7 shrink-0 p-0 text-muted-foreground hover:text-destructive"
|
||||
disabled={deleteNode.isPending}
|
||||
onClick={() => {
|
||||
if (!confirm(t(($) => $.cloud_runtime.delete_confirm))) return;
|
||||
deleteNode.mutate(node.id, {
|
||||
onSuccess: () => toast.success(t(($) => $.cloud_runtime.toast_deleted)),
|
||||
onError: (err) =>
|
||||
toast.error(
|
||||
err instanceof Error
|
||||
? err.message
|
||||
: t(($) => $.cloud_runtime.toast_delete_failed),
|
||||
),
|
||||
});
|
||||
}}
|
||||
aria-label={t(($) => $.cloud_runtime.delete)}
|
||||
>
|
||||
{deleteNode.isPending ? (
|
||||
<Loader2 className="h-3.5 w-3.5 animate-spin" />
|
||||
) : (
|
||||
<Trash2 className="h-3.5 w-3.5" />
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
{node.instance_id && (
|
||||
<div className="mt-2 truncate font-mono text-[11px] text-muted-foreground/80">
|
||||
|
||||
Reference in New Issue
Block a user