mirror of
https://github.com/block/buzz.git
synced 2026-10-31 22:41:12 +00:00
Adapt delete confirmation to profile redesign
Co-authored-by: Atish Patel <atish@squareup.com> Signed-off-by: Atish Patel <atish@squareup.com> Signed-off-by: Brainy Bumble <0ed7657b57c0e8a9f5288390dd6c8d5d0a3a06abe9b01b9006814f52077d6cdf@buzz.block.builderlab.xyz>
This commit is contained in:
@@ -11,6 +11,7 @@ import {
|
||||
|
||||
import type { IdentityArchiveActions } from "@/features/identity-archive/hooks";
|
||||
import { ArchiveConfirmDialog } from "@/features/profile/ui/ArchiveConfirmDialog";
|
||||
import { useAgentMemoryQuery } from "@/features/agent-memory/hooks";
|
||||
import type { ManagedAgent } from "@/shared/api/types";
|
||||
import {
|
||||
AlertDialog,
|
||||
@@ -31,6 +32,7 @@ export function UserProfileAgentManagementRows({
|
||||
canDeleteAgent,
|
||||
isDeletePending,
|
||||
managedAgent,
|
||||
channelCount,
|
||||
onCreateCard,
|
||||
onDeleteAgent,
|
||||
onDuplicateAgent,
|
||||
@@ -41,6 +43,7 @@ export function UserProfileAgentManagementRows({
|
||||
canDeleteAgent: boolean;
|
||||
isDeletePending: boolean;
|
||||
managedAgent?: ManagedAgent;
|
||||
channelCount: number;
|
||||
/** Mint an agent trading card. Present only for owner-managed personas. */
|
||||
onCreateCard?: () => void;
|
||||
onDeleteAgent: () => void;
|
||||
@@ -91,9 +94,11 @@ export function UserProfileAgentManagementRows({
|
||||
) : null}
|
||||
{canDeleteAgent ? (
|
||||
<ProfileDeleteAgentRow
|
||||
channelCount={channelCount}
|
||||
isPending={isDeletePending}
|
||||
managedAgent={managedAgent}
|
||||
onDelete={onDeleteAgent}
|
||||
onExport={onExportAgent}
|
||||
/>
|
||||
) : null}
|
||||
</PanelSectionGroup>
|
||||
@@ -194,15 +199,25 @@ function ProfileArchiveAgentRow({
|
||||
}
|
||||
|
||||
function ProfileDeleteAgentRow({
|
||||
channelCount,
|
||||
isPending,
|
||||
managedAgent,
|
||||
onDelete,
|
||||
onExport,
|
||||
}: {
|
||||
channelCount: number;
|
||||
isPending: boolean;
|
||||
managedAgent?: ManagedAgent;
|
||||
onDelete: () => void;
|
||||
onExport?: () => void;
|
||||
}) {
|
||||
const [confirmOpen, setConfirmOpen] = React.useState(false);
|
||||
const memoryQuery = useAgentMemoryQuery(managedAgent?.pubkey, {
|
||||
enabled: confirmOpen && managedAgent !== undefined,
|
||||
});
|
||||
const memoryCount =
|
||||
memoryQuery.data &&
|
||||
(memoryQuery.data.core ? 1 : 0) + memoryQuery.data.memories.length;
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -223,11 +238,15 @@ function ProfileDeleteAgentRow({
|
||||
{managedAgent ? (
|
||||
<AgentDeleteConfirmDialog
|
||||
agent={managedAgent}
|
||||
channelCount={channelCount}
|
||||
isPending={isPending}
|
||||
memoryCount={memoryCount}
|
||||
memoriesLoading={memoryQuery.isLoading}
|
||||
onConfirm={() => {
|
||||
setConfirmOpen(false);
|
||||
onDelete();
|
||||
}}
|
||||
onExport={onExport}
|
||||
onOpenChange={setConfirmOpen}
|
||||
open={confirmOpen}
|
||||
/>
|
||||
@@ -238,44 +257,82 @@ function ProfileDeleteAgentRow({
|
||||
|
||||
function AgentDeleteConfirmDialog({
|
||||
agent,
|
||||
channelCount,
|
||||
isPending,
|
||||
memoryCount,
|
||||
memoriesLoading,
|
||||
onConfirm,
|
||||
onExport,
|
||||
onOpenChange,
|
||||
open,
|
||||
}: {
|
||||
agent: ManagedAgent;
|
||||
channelCount: number;
|
||||
isPending: boolean;
|
||||
memoryCount?: number;
|
||||
memoriesLoading: boolean;
|
||||
onConfirm: () => void;
|
||||
onExport?: () => void;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
open: boolean;
|
||||
}) {
|
||||
const isProviderAgent = agent.backend.type === "provider";
|
||||
const showExportRecommendation =
|
||||
onExport !== undefined && memoryCount !== undefined && memoryCount > 0;
|
||||
|
||||
return (
|
||||
<AlertDialog onOpenChange={onOpenChange} open={open}>
|
||||
<AlertDialogContent data-testid="agent-delete-confirm-dialog">
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>Delete this agent?</AlertDialogTitle>
|
||||
<AlertDialogTitle>Delete {agent.name}?</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
Deleting this agent stops and removes the agent from this community.
|
||||
This permanently removes the agent, its saved key, and its channel
|
||||
access.
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<ul className="list-disc space-y-1.5 pl-5 text-sm text-muted-foreground">
|
||||
<li>Removes the local management record and saved agent key</li>
|
||||
<li>Removes the agent from every channel it belongs to</li>
|
||||
<li>
|
||||
Archives the agent's identity on the relay so it no longer
|
||||
appears in member lists or mention suggestions
|
||||
</li>
|
||||
<li>
|
||||
{isProviderAgent
|
||||
? "Requests remote deletion; if it is online, Buzz first sends a shutdown command when possible. If the deployment cannot be reached through a channel, the remote process may keep running without local management."
|
||||
: "Stops any local agent process before deleting the record"}
|
||||
</li>
|
||||
</ul>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Archive this agent if you want to hide it instead of removing it.
|
||||
</p>
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<div className="rounded-lg border bg-muted/30 px-4 py-3">
|
||||
<p className="text-xs text-muted-foreground">Memories</p>
|
||||
<p
|
||||
className="mt-1 text-2xl font-semibold tabular-nums"
|
||||
data-testid="agent-delete-memory-count"
|
||||
>
|
||||
{memoriesLoading ? "…" : (memoryCount ?? "—")}
|
||||
</p>
|
||||
</div>
|
||||
<div className="rounded-lg border bg-muted/30 px-4 py-3">
|
||||
<p className="text-xs text-muted-foreground">Channels</p>
|
||||
<p
|
||||
className="mt-1 text-2xl font-semibold tabular-nums"
|
||||
data-testid="agent-delete-channel-count"
|
||||
>
|
||||
{channelCount}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
{showExportRecommendation ? (
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Want a backup?{" "}
|
||||
<Button
|
||||
className="h-auto p-0 align-baseline font-medium underline underline-offset-2"
|
||||
data-testid="agent-delete-export-action"
|
||||
onClick={() => {
|
||||
onOpenChange(false);
|
||||
onExport();
|
||||
}}
|
||||
type="button"
|
||||
variant="link"
|
||||
>
|
||||
Export this agent
|
||||
</Button>
|
||||
.
|
||||
</p>
|
||||
) : null}
|
||||
{isProviderAgent ? (
|
||||
<p className="text-sm text-muted-foreground">
|
||||
The remote process may keep running if Buzz can't reach it.
|
||||
</p>
|
||||
) : null}
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel asChild>
|
||||
<Button type="button" variant="outline">
|
||||
|
||||
@@ -511,6 +511,7 @@ export function ProfileSummaryView({
|
||||
archiveActions={archiveActions}
|
||||
canArchiveAgent={isBot && archiveActions.canArchive}
|
||||
canDeleteAgent={canDeleteAgent}
|
||||
channelCount={channelCount}
|
||||
channelIdToName={channelIdToName}
|
||||
isArchived={isArchived}
|
||||
isDeleteAgentPending={isAgentActionPending}
|
||||
|
||||
@@ -203,6 +203,7 @@ export function ProfileInfoTabContent({
|
||||
archiveActions,
|
||||
canArchiveAgent,
|
||||
canDeleteAgent,
|
||||
channelCount,
|
||||
channelIdToName,
|
||||
isArchived,
|
||||
isDeleteAgentPending,
|
||||
@@ -223,6 +224,7 @@ export function ProfileInfoTabContent({
|
||||
archiveActions: IdentityArchiveActions;
|
||||
canArchiveAgent: boolean;
|
||||
canDeleteAgent: boolean;
|
||||
channelCount: number;
|
||||
channelIdToName: Record<string, string>;
|
||||
isArchived: boolean;
|
||||
isDeleteAgentPending: boolean;
|
||||
@@ -311,6 +313,7 @@ export function ProfileInfoTabContent({
|
||||
canDeleteAgent={canDeleteAgent}
|
||||
isDeletePending={isDeleteAgentPending}
|
||||
managedAgent={managedAgent}
|
||||
channelCount={channelCount}
|
||||
onCreateCard={onCreateCard}
|
||||
onDeleteAgent={onDeleteAgent}
|
||||
onDuplicateAgent={onDuplicateAgent}
|
||||
|
||||
@@ -7,7 +7,11 @@ import type { RelayEvent } from "@/shared/api/types";
|
||||
import { emojiAvatarDataUrl } from "@/features/profile/ui/ProfileAvatarEditor.utils";
|
||||
|
||||
import { waitForAnimations } from "../helpers/animations";
|
||||
import { installMockBridge, TEST_IDENTITIES } from "../helpers/bridge";
|
||||
import {
|
||||
createMockAgentMemoryListing,
|
||||
installMockBridge,
|
||||
TEST_IDENTITIES,
|
||||
} from "../helpers/bridge";
|
||||
import { seedActiveIdentity } from "../helpers/onboarding";
|
||||
|
||||
function createCatalogEvent(input: {
|
||||
@@ -2695,8 +2699,9 @@ test("duplicate instances stay visible in the gallery and agent profile", async
|
||||
}) => {
|
||||
const personaId = "custom:duplicate-auditor";
|
||||
const primaryPubkey = TEST_IDENTITIES.alice.pubkey;
|
||||
const additionalPubkey = TEST_IDENTITIES.charlie.pubkey;
|
||||
const additionalPubkey = TEST_IDENTITIES.outsider.pubkey;
|
||||
await installMockBridge(page, {
|
||||
agentMemory: createMockAgentMemoryListing(),
|
||||
personas: [
|
||||
{
|
||||
id: personaId,
|
||||
@@ -2716,6 +2721,14 @@ test("duplicate instances stay visible in the gallery and agent profile", async
|
||||
name: "Duplicate Auditor",
|
||||
personaId,
|
||||
status: "stopped",
|
||||
channelNames: ["general", "random"],
|
||||
},
|
||||
],
|
||||
relayAgents: [
|
||||
{
|
||||
pubkey: additionalPubkey,
|
||||
name: "Duplicate Auditor",
|
||||
channelNames: ["general", "random"],
|
||||
},
|
||||
],
|
||||
});
|
||||
@@ -2728,8 +2741,10 @@ test("duplicate instances stay visible in the gallery and agent profile", async
|
||||
).toBeVisible();
|
||||
|
||||
await page.getByTestId(`managed-agent-${primaryPubkey}`).click();
|
||||
await page.getByTestId("user-profile-tab-runtime").click();
|
||||
await page.getByTestId("user-profile-instances").click();
|
||||
await page.getByTestId(`user-profile-instance-${additionalPubkey}`).click();
|
||||
await page.getByTestId("user-profile-tab-info").click();
|
||||
|
||||
await expect(page.getByTestId("user-profile-panel")).toBeVisible();
|
||||
await expect(page.getByTestId("user-profile-agent-status")).toContainText(
|
||||
@@ -2739,6 +2754,20 @@ test("duplicate instances stay visible in the gallery and agent profile", async
|
||||
page.getByTestId("user-profile-settings-menu-trigger"),
|
||||
).toHaveCount(0);
|
||||
await expect(
|
||||
page.getByTestId(`user-profile-agent-delete-${additionalPubkey}`),
|
||||
).toHaveCount(0);
|
||||
page.getByTestId("user-profile-duplicate-agent-row"),
|
||||
).toBeVisible();
|
||||
await expect(page.getByTestId("user-profile-export-agent-row")).toBeVisible();
|
||||
await page.getByTestId("user-profile-delete-agent-row").click();
|
||||
|
||||
const deleteDialog = page.getByTestId("agent-delete-confirm-dialog");
|
||||
await expect(deleteDialog).toBeVisible();
|
||||
await expect(deleteDialog.getByRole("heading")).toHaveText(
|
||||
"Delete Duplicate Auditor?",
|
||||
);
|
||||
await expect(page.getByTestId("agent-delete-memory-count")).toHaveText("9");
|
||||
await expect(page.getByTestId("agent-delete-channel-count")).toHaveText("2");
|
||||
|
||||
await page.getByTestId("agent-delete-export-action").click();
|
||||
await expect(deleteDialog).toHaveCount(0);
|
||||
await expect(page.getByTestId("agent-snapshot-export-dialog")).toBeVisible();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user