From 4634aac2d10f45b37532ec28033fed87833718e5 Mon Sep 17 00:00:00 2001 From: Denozordec Date: Fri, 7 Aug 2026 16:16:38 +0700 Subject: [PATCH] feat(network): add delete functionality for BGP peers with confirmation dialog Implemented a delete action for BGP peers in the NetworkPeersCard component. Added a confirmation dialog to ensure user intent before deletion, enhancing the user experience and preventing accidental removals. Updated the component's state management to handle the delete target and integrated the delete mutation for backend interaction. --- .../components/network/network-peers-card.tsx | 70 ++++++++++++++++++- 1 file changed, 67 insertions(+), 3 deletions(-) diff --git a/apps/web/src/components/network/network-peers-card.tsx b/apps/web/src/components/network/network-peers-card.tsx index 99b8234..e2f7d96 100644 --- a/apps/web/src/components/network/network-peers-card.tsx +++ b/apps/web/src/components/network/network-peers-card.tsx @@ -1,8 +1,10 @@ import { useMemo, useState } from 'react' -import { Plus, SearchIcon, ActivityIcon } from 'lucide-react' +import type { ColumnDef } from '@tanstack/react-table' +import { Plus, SearchIcon, ActivityIcon, Trash2 } from 'lucide-react' import { Button } from '@evobgp/ui/components/button' +import { ConfirmDialog } from '@/components/confirm-dialog' import { PeerFormDialog } from '@/components/network/peer-form-dialog' import { getPeerFilterFieldValue, @@ -15,13 +17,19 @@ import { type FilterFieldConfig, } from '@/components/reui/filters' import { ResourcePage, renderSingleSelectedLabel } from '@/components/reui-kit' +import { useDeletePeerMutation } from '@/queries/network' import type { PeerRow, SpeakerRow } from '@/types/api' /** * BGP peers list — ResourcePage (Frame + tabs + Filters + DataGrid). * @see https://reui.io/preview/base/data-grid-filtering-2 + * @see https://reui.io/docs/components/base/data-grid */ +function peerDeleteLabel(p: PeerRow): string { + return p.name?.trim() || p.neighbor || p.id +} + const PEER_TABS = [ { id: 'all', label: 'Все' }, { id: 'established', label: 'Established' }, @@ -89,7 +97,9 @@ export function NetworkPeersCard({ error, onRetry, }: NetworkPeersCardProps) { + const deleteMutation = useDeletePeerMutation() const [dialogOpen, setDialogOpen] = useState(false) + const [deleteTarget, setDeleteTarget] = useState(null) const [filters, setFilters] = useState(createDefaultPeerFilters) const addButton = useMemo( @@ -102,6 +112,33 @@ export function NetworkPeersCard({ [], ) + const columns = useMemo[]>( + () => [ + ...peerColumns, + { + id: 'actions', + enableSorting: false, + header: () => Действия, + cell: ({ row }) => ( +
+ +
+ ), + }, + ], + [], + ) + return ( <> setFilters(createDefaultPeerFilters())} getFilterFieldValue={getPeerFilterFieldValue} - columns={peerColumns} + columns={columns} data={items} getRowId={(row) => row.id} isLoading={isLoading} @@ -129,7 +166,34 @@ export function NetworkPeersCard({ }} /> - + + + { + if (!open) setDeleteTarget(null) + }} + title="Удалить пира?" + description={ + deleteTarget + ? `${peerDeleteLabel(deleteTarget)} будет удалён. Запустится reconcile пиров и apply на спикеры.` + : undefined + } + confirmLabel="Удалить" + confirmLoadingLabel="Удаление…" + destructive + confirmLoading={deleteMutation.isPending} + onConfirm={() => { + if (!deleteTarget) return + void deleteMutation + .mutateAsync(deleteTarget.id) + .then(() => setDeleteTarget(null)) + }} + /> ) }