diff --git a/site/src/pages/AgentsPage/components/ChatModelAdminPanel/ModelForm.tsx b/site/src/pages/AgentsPage/components/ChatModelAdminPanel/ModelForm.tsx index 88af0fa346..5295b9963e 100644 --- a/site/src/pages/AgentsPage/components/ChatModelAdminPanel/ModelForm.tsx +++ b/site/src/pages/AgentsPage/components/ChatModelAdminPanel/ModelForm.tsx @@ -9,14 +9,6 @@ import { type FC, useState } from "react"; import * as Yup from "yup"; import type * as TypesGen from "#/api/typesGenerated"; import { Button } from "#/components/Button/Button"; -import { - Dialog, - DialogContent, - DialogDescription, - DialogFooter, - DialogHeader, - DialogTitle, -} from "#/components/Dialog/Dialog"; import { Input } from "#/components/Input/Input"; import { InputGroup, @@ -41,6 +33,7 @@ import { import { cn } from "#/utils/cn"; import { getFormHelpers } from "#/utils/formUtils"; import { BackButton } from "../BackButton"; +import { ConfirmDeleteDialog } from "../ConfirmDeleteDialog"; import type { ProviderState } from "./ChatModelAdminPanel"; import { GeneralModelConfigFields, @@ -646,37 +639,13 @@ export const ModelForm: FC = ({ {editingModel && onDeleteModel && ( - void onDeleteModel(editingModel.id)} + isPending={isDeleting} open={confirmingDelete} onOpenChange={(open) => !open && setConfirmingDelete(false)} - > - - - Delete model - - Are you sure you want to delete this model? This action is - irreversible. - - - - - - - - + /> )}{" "} ); diff --git a/site/src/pages/AgentsPage/components/ChatModelAdminPanel/ProviderForm.tsx b/site/src/pages/AgentsPage/components/ChatModelAdminPanel/ProviderForm.tsx index 07e0c9645e..ccc4635575 100644 --- a/site/src/pages/AgentsPage/components/ChatModelAdminPanel/ProviderForm.tsx +++ b/site/src/pages/AgentsPage/components/ChatModelAdminPanel/ProviderForm.tsx @@ -10,14 +10,6 @@ import { import type * as TypesGen from "#/api/typesGenerated"; import { Alert, AlertDescription, AlertTitle } from "#/components/Alert/Alert"; import { Button } from "#/components/Button/Button"; -import { - Dialog, - DialogContent, - DialogDescription, - DialogFooter, - DialogHeader, - DialogTitle, -} from "#/components/Dialog/Dialog"; import { Input } from "#/components/Input/Input"; import { Spinner } from "#/components/Spinner/Spinner"; import { Switch } from "#/components/Switch/Switch"; @@ -28,6 +20,7 @@ import { } from "#/components/Tooltip/Tooltip"; import { formatProviderLabel } from "../../utils/modelOptions"; import { BackButton } from "../BackButton"; +import { ConfirmDeleteDialog } from "../ConfirmDeleteDialog"; import type { ProviderState } from "./ChatModelAdminPanel"; import { readOptionalString } from "./helpers"; import { ProviderIcon } from "./ProviderIcon"; @@ -266,7 +259,6 @@ export const ProviderForm: FC = ({
- {isAPIKeyEnvManaged ? ( API key managed by environment variable @@ -401,38 +393,15 @@ export const ProviderForm: FC = ({ )} - {providerConfig && ( - void onDeleteProvider(providerConfig.id)} + isPending={isProviderMutationPending} open={confirmingDelete} onOpenChange={(open) => !open && setConfirmingDelete(false)} - > - - - Delete provider - {deleteProviderDescription} - - - - - - - + /> )} ); diff --git a/site/src/pages/AgentsPage/components/ConfirmDeleteDialog.tsx b/site/src/pages/AgentsPage/components/ConfirmDeleteDialog.tsx new file mode 100644 index 0000000000..fe591a3bd3 --- /dev/null +++ b/site/src/pages/AgentsPage/components/ConfirmDeleteDialog.tsx @@ -0,0 +1,59 @@ +import type { FC, ReactNode } from "react"; +import { Button } from "#/components/Button/Button"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from "#/components/Dialog/Dialog"; +import { Spinner } from "#/components/Spinner/Spinner"; + +interface ConfirmDeleteDialogProps { + open: boolean; + onOpenChange: (open: boolean) => void; + /** The entity type being deleted, shown in the title and button. */ + entity: string; + /** + * Optional description. Defaults to "Are you sure you want to + * delete this {entity}? This action is irreversible." + */ + description?: ReactNode; + onConfirm: () => void; + isPending?: boolean; +} + +export const ConfirmDeleteDialog: FC = ({ + open, + onOpenChange, + entity, + description, + onConfirm, + isPending = false, +}) => ( + + + + Delete {entity} + + {description ?? + `Are you sure you want to delete this ${entity}? This action is irreversible.`} + + + + + + + + +); diff --git a/site/src/pages/AgentsPage/components/LimitsTab/GroupLimitsSection.stories.tsx b/site/src/pages/AgentsPage/components/LimitsTab/GroupLimitsSection.stories.tsx index fe4b7b6252..c76839ea80 100644 --- a/site/src/pages/AgentsPage/components/LimitsTab/GroupLimitsSection.stories.tsx +++ b/site/src/pages/AgentsPage/components/LimitsTab/GroupLimitsSection.stories.tsx @@ -146,9 +146,7 @@ export const DeleteGroupOverride: Story = { const dialog = await body.findByRole("dialog"); await expect(dialog).toBeInTheDocument(); await expect( - body.getByText( - /Are you sure you want to delete this group limit override/i, - ), + body.getByText(/Are you sure you want to delete this group override/i), ).toBeInTheDocument(); }, }; diff --git a/site/src/pages/AgentsPage/components/LimitsTab/GroupLimitsSection.tsx b/site/src/pages/AgentsPage/components/LimitsTab/GroupLimitsSection.tsx index 6cd9bb8a57..ef351560ee 100644 --- a/site/src/pages/AgentsPage/components/LimitsTab/GroupLimitsSection.tsx +++ b/site/src/pages/AgentsPage/components/LimitsTab/GroupLimitsSection.tsx @@ -5,14 +5,6 @@ import type { Group } from "#/api/typesGenerated"; import { Autocomplete } from "#/components/Autocomplete/Autocomplete"; import { AvatarData } from "#/components/Avatar/AvatarData"; import { Button } from "#/components/Button/Button"; -import { - Dialog, - DialogContent, - DialogDescription, - DialogFooter, - DialogHeader, - DialogTitle, -} from "#/components/Dialog/Dialog"; import { Input } from "#/components/Input/Input"; import { Label } from "#/components/Label/Label"; import { Spinner } from "#/components/Spinner/Spinner"; @@ -29,6 +21,7 @@ import { formatCostMicros, isPositiveFiniteDollarAmount, } from "#/utils/currency"; +import { ConfirmDeleteDialog } from "../ConfirmDeleteDialog"; import { SectionHeader } from "../SectionHeader"; interface GroupLimitsSectionProps { @@ -298,40 +291,16 @@ export const GroupLimitsSection: FC = ({ )} {pendingDeleteGroupId && ( - { + void onDeleteGroupOverride(pendingDeleteGroupId); + setPendingDeleteGroupId(null); + }} + isPending={deletePending} + open={true} onOpenChange={(open) => !open && setPendingDeleteGroupId(null)} - > - - - Delete group override - - Are you sure you want to delete this group limit override? This - action is irreversible. - - - - - - - - + /> )}{" "} ); diff --git a/site/src/pages/AgentsPage/components/LimitsTab/UserOverridesSection.stories.tsx b/site/src/pages/AgentsPage/components/LimitsTab/UserOverridesSection.stories.tsx index 953460c988..c12185a558 100644 --- a/site/src/pages/AgentsPage/components/LimitsTab/UserOverridesSection.stories.tsx +++ b/site/src/pages/AgentsPage/components/LimitsTab/UserOverridesSection.stories.tsx @@ -123,9 +123,7 @@ export const DeleteUserOverride: Story = { const dialog = await body.findByRole("dialog"); await expect(dialog).toBeInTheDocument(); await expect( - body.getByText( - /Are you sure you want to delete this user limit override/i, - ), + body.getByText(/Are you sure you want to delete this user override/i), ).toBeInTheDocument(); }, }; diff --git a/site/src/pages/AgentsPage/components/LimitsTab/UserOverridesSection.tsx b/site/src/pages/AgentsPage/components/LimitsTab/UserOverridesSection.tsx index f02050090f..0b58d64095 100644 --- a/site/src/pages/AgentsPage/components/LimitsTab/UserOverridesSection.tsx +++ b/site/src/pages/AgentsPage/components/LimitsTab/UserOverridesSection.tsx @@ -3,14 +3,6 @@ import { getErrorMessage } from "#/api/errors"; import type { User } from "#/api/typesGenerated"; import { AvatarData } from "#/components/Avatar/AvatarData"; import { Button } from "#/components/Button/Button"; -import { - Dialog, - DialogContent, - DialogDescription, - DialogFooter, - DialogHeader, - DialogTitle, -} from "#/components/Dialog/Dialog"; import { Input } from "#/components/Input/Input"; import { Label } from "#/components/Label/Label"; import { Spinner } from "#/components/Spinner/Spinner"; @@ -27,6 +19,7 @@ import { formatCostMicros, isPositiveFiniteDollarAmount, } from "#/utils/currency"; +import { ConfirmDeleteDialog } from "../ConfirmDeleteDialog"; import { SectionHeader } from "../SectionHeader"; interface UserOverridesSectionProps { @@ -257,40 +250,16 @@ export const UserOverridesSection: FC = ({ )} {pendingDeleteUserId && ( - { + void onDeleteOverride(pendingDeleteUserId); + setPendingDeleteUserId(null); + }} + isPending={deletePending} + open={true} onOpenChange={(open) => !open && setPendingDeleteUserId(null)} - > - - - Delete user override - - Are you sure you want to delete this user limit override? This - action is irreversible. - - - - - - - - + /> )}{" "} ); diff --git a/site/src/pages/AgentsPage/components/MCPServerAdminPanel.stories.tsx b/site/src/pages/AgentsPage/components/MCPServerAdminPanel.stories.tsx index c15ef78716..b225145b8a 100644 --- a/site/src/pages/AgentsPage/components/MCPServerAdminPanel.stories.tsx +++ b/site/src/pages/AgentsPage/components/MCPServerAdminPanel.stories.tsx @@ -492,7 +492,9 @@ export const DeleteServerConfirmed: Story = { await userEvent.click(await body.findByRole("button", { name: /Sentry/ })); await userEvent.click(await body.findByRole("button", { name: "Delete" })); await body.findByText(/Are you sure you want to delete this MCP server/i); - await userEvent.click(body.getByRole("button", { name: /Delete server/i })); + await userEvent.click( + body.getByRole("button", { name: /Delete MCP server/i }), + ); await waitFor(() => { expect(args.onDeleteServer).toHaveBeenCalledTimes(1); diff --git a/site/src/pages/AgentsPage/components/MCPServerAdminPanel.tsx b/site/src/pages/AgentsPage/components/MCPServerAdminPanel.tsx index 35e2fdefba..2ec01ad833 100644 --- a/site/src/pages/AgentsPage/components/MCPServerAdminPanel.tsx +++ b/site/src/pages/AgentsPage/components/MCPServerAdminPanel.tsx @@ -13,14 +13,6 @@ import { useSearchParams } from "react-router"; import type * as TypesGen from "#/api/typesGenerated"; import { ErrorAlert } from "#/components/Alert/ErrorAlert"; import { Button } from "#/components/Button/Button"; -import { - Dialog, - DialogContent, - DialogDescription, - DialogFooter, - DialogHeader, - DialogTitle, -} from "#/components/Dialog/Dialog"; import { ExternalImage } from "#/components/ExternalImage/ExternalImage"; import { IconField } from "#/components/IconField/IconField"; import { Input } from "#/components/Input/Input"; @@ -42,6 +34,7 @@ import { import { cn } from "#/utils/cn"; import { BackButton } from "./BackButton"; import { ProviderField as Field } from "./ChatModelAdminPanel/ProviderForm"; +import { ConfirmDeleteDialog } from "./ConfirmDeleteDialog"; import { SectionHeader } from "./SectionHeader"; // ── Constants ────────────────────────────────────────────────── @@ -842,37 +835,13 @@ const ServerForm: FC = ({ {server && ( - void onDelete(server.id)} + isPending={isDeleting} open={confirmingDelete} onOpenChange={(open) => !open && setConfirmingDelete(false)} - > - - - Delete server - - Are you sure you want to delete this MCP server? This action is - irreversible. - - - - - - - - + /> )}{" "} );