fix(ui): add missing delete buttons for agents and teams (#63)

The list pages had ConfirmDialog for deletion wired up but no UI
element to trigger it. Detail pages had no delete option at all.

- Add delete button with trash icon to agent and team cards
- Wire onDelete callbacks to existing setDeleteTarget state
- Add delete button + confirm dialog to agent detail page header
- Add delete button + confirm dialog to team detail page header

Closes #17

Co-authored-by: Luvu182 <208665161+Luvu182@users.noreply.github.com>
This commit is contained in:
Luan VuandLuvu182 authored and GitHub committed 2026-03-06 22:56:40 +07:00
1 parent 6041c683cb
commit 1ebbe53093
6 files changed
+90 -7

No files matched your search

+17 -2
View File
@@ -1,4 +1,4 @@
import { Bot, Star, RotateCcw } from "lucide-react";
import { Bot, Star, RotateCcw, Trash2 } from "lucide-react";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import type { AgentData } from "@/types/agent";
@@ -7,11 +7,12 @@ interface AgentCardProps {
agent: AgentData;
onClick: () => void;
onResummon?: () => void;
onDelete?: () => void;
}
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
export function AgentCard({ agent, onClick, onResummon }: AgentCardProps) {
export function AgentCard({ agent, onClick, onResummon, onDelete }: AgentCardProps) {
const displayName = agent.display_name
|| (UUID_RE.test(agent.agent_key) ? "Unnamed Agent" : agent.agent_key);
@@ -91,6 +92,20 @@ export function AgentCard({ agent, onClick, onResummon }: AgentCardProps) {
Resummon
</Button>
)}
{onDelete && (
<Button
variant="ghost"
size="xs"
className={`text-muted-foreground hover:text-destructive ${agent.status === "summon_failed" && onResummon ? "" : "ml-auto"}`}
onClick={(e) => {
e.stopPropagation();
onDelete();
}}
>
<Trash2 className="h-3.5 w-3.5" />
Delete
</Button>
)}
</div>
</button>
);
@@ -2,8 +2,9 @@ import { useState } from "react";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import { ArrowLeft, Bot, Star } from "lucide-react";
import { ArrowLeft, Bot, Star, Trash2 } from "lucide-react";
import { useAgentDetail } from "../hooks/use-agent-detail";
import { useAgents } from "../hooks/use-agents";
import { AgentGeneralTab } from "./agent-general-tab";
import { AgentConfigTab } from "./agent-config-tab";
import { AgentFilesTab } from "./agent-files-tab";
@@ -11,6 +12,7 @@ import { AgentSharesTab } from "./agent-shares-tab";
import { AgentLinksTab } from "./agent-links-tab";
import { AgentSkillsTab } from "./agent-skills-tab";
import { SummoningModal } from "../summoning-modal";
import { ConfirmDialog } from "@/components/shared/confirm-dialog";
import { DeferredSpinner } from "@/components/shared/loading-skeleton";
interface AgentDetailPageProps {
@@ -37,8 +39,10 @@ function agentSubtitle(agent: { display_name?: string; agent_key: string; id: st
export function AgentDetailPage({ agentId, onBack }: AgentDetailPageProps) {
const { agent, files, loading, updateAgent, getFile, setFile, regenerateAgent, resummonAgent, refresh } =
useAgentDetail(agentId);
const { deleteAgent } = useAgents();
const [summoningOpen, setSummoningOpen] = useState(false);
const [activeTab, setActiveTab] = useState("general");
const [deleteOpen, setDeleteOpen] = useState(false);
const handleRegenerate = async (prompt: string) => {
await regenerateAgent(prompt);
@@ -106,6 +110,14 @@ export function AgentDetailPage({ agentId, onBack }: AgentDetailPageProps) {
)}
</div>
</div>
<Button
variant="ghost"
size="icon"
className="shrink-0 text-muted-foreground hover:text-destructive"
onClick={() => setDeleteOpen(true)}
>
<Trash2 className="h-4 w-4" />
</Button>
</div>
{/* Tabs */}
@@ -161,6 +173,20 @@ export function AgentDetailPage({ agentId, onBack }: AgentDetailPageProps) {
onCompleted={() => {}}
onResummon={async () => { await resummonAgent(); }}
/>
<ConfirmDialog
open={deleteOpen}
onOpenChange={setDeleteOpen}
title="Delete Agent"
description={`Are you sure you want to delete "${title}"? This action cannot be undone.`}
confirmLabel="Delete"
variant="destructive"
onConfirm={async () => {
await deleteAgent(agentId);
setDeleteOpen(false);
onBack();
}}
/>
</div>
);
}
+1
View File
@@ -114,6 +114,7 @@ export function AgentsPage() {
}
}}
onResummon={() => handleResummon(agent)}
onDelete={() => setDeleteTarget(agent.id)}
/>
))}
</div>
+18 -2
View File
@@ -1,13 +1,15 @@
import { Users } from "lucide-react";
import { Users, Trash2 } from "lucide-react";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import type { TeamData } from "@/types/team";
interface TeamCardProps {
team: TeamData;
onClick: () => void;
onDelete?: () => void;
}
export function TeamCard({ team, onClick }: TeamCardProps) {
export function TeamCard({ team, onClick, onDelete }: TeamCardProps) {
return (
<button
type="button"
@@ -41,6 +43,20 @@ export function TeamCard({ team, onClick }: TeamCardProps) {
Lead: {team.lead_agent_key}
</Badge>
)}
{onDelete && (
<Button
variant="ghost"
size="xs"
className="ml-auto text-muted-foreground hover:text-destructive"
onClick={(e) => {
e.stopPropagation();
onDelete();
}}
>
<Trash2 className="h-3.5 w-3.5" />
Delete
</Button>
)}
</div>
</button>
);
+26 -2
View File
@@ -2,8 +2,9 @@ import { useState, useEffect, useCallback } from "react";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import { ArrowLeft, Users } from "lucide-react";
import { ArrowLeft, Users, Trash2 } from "lucide-react";
import { DeferredSpinner } from "@/components/shared/loading-skeleton";
import { ConfirmDialog } from "@/components/shared/confirm-dialog";
import { useTeams } from "./hooks/use-teams";
import { TeamMembersTab } from "./team-members-tab";
import { TeamTasksTab } from "./team-tasks-tab";
@@ -17,11 +18,12 @@ interface TeamDetailPageProps {
}
export function TeamDetailPage({ teamId, onBack }: TeamDetailPageProps) {
const { getTeam, getTeamTasks, addMember, removeMember } = useTeams();
const { getTeam, getTeamTasks, addMember, removeMember, deleteTeam } = useTeams();
const [team, setTeam] = useState<TeamData | null>(null);
const [members, setMembers] = useState<TeamMemberData[]>([]);
const [loading, setLoading] = useState(true);
const [activeTab, setActiveTab] = useState("members");
const [deleteOpen, setDeleteOpen] = useState(false);
const reload = useCallback(async () => {
try {
@@ -103,6 +105,14 @@ export function TeamDetailPage({ teamId, onBack }: TeamDetailPageProps) {
<p className="mt-1 text-sm text-muted-foreground/70">{team.description}</p>
)}
</div>
<Button
variant="ghost"
size="icon"
className="shrink-0 text-muted-foreground hover:text-destructive"
onClick={() => setDeleteOpen(true)}
>
<Trash2 className="h-4 w-4" />
</Button>
</div>
{/* Tabs */}
@@ -137,6 +147,20 @@ export function TeamDetailPage({ teamId, onBack }: TeamDetailPageProps) {
</TabsContent>
</Tabs>
</div>
<ConfirmDialog
open={deleteOpen}
onOpenChange={setDeleteOpen}
title="Delete Team"
description={`Are you sure you want to delete "${team.name}"? This action cannot be undone.`}
confirmLabel="Delete"
variant="destructive"
onConfirm={async () => {
await deleteTeam(teamId);
setDeleteOpen(false);
onBack();
}}
/>
</div>
);
}
+1
View File
@@ -97,6 +97,7 @@ export function TeamsPage() {
key={team.id}
team={team}
onClick={() => navigate(`/teams/${team.id}`)}
onDelete={() => setDeleteTarget({ id: team.id, name: team.name })}
/>
))}
</div>