feat(network): add delete functionality for BGP peers with confirmation dialog
CI / changes (push) Successful in 6s
CI / commitlint (push) Skipped
CI / openapi (push) Successful in 44s
CI / web (push) Successful in 1m7s
CI / go (push) Successful in 2m36s
CI / bird2 (push) Successful in 17s
CI / release (push) Successful in 4m4s
CI / changes (push) Successful in 6s
CI / commitlint (push) Skipped
CI / openapi (push) Successful in 44s
CI / web (push) Successful in 1m7s
CI / go (push) Successful in 2m36s
CI / bird2 (push) Successful in 17s
CI / release (push) Successful in 4m4s
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.
This commit is contained in:
@@ -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<PeerRow | null>(null)
|
||||
const [filters, setFilters] = useState<Filter[]>(createDefaultPeerFilters)
|
||||
|
||||
const addButton = useMemo(
|
||||
@@ -102,6 +112,33 @@ export function NetworkPeersCard({
|
||||
[],
|
||||
)
|
||||
|
||||
const columns = useMemo<ColumnDef<PeerRow, unknown>[]>(
|
||||
() => [
|
||||
...peerColumns,
|
||||
{
|
||||
id: 'actions',
|
||||
enableSorting: false,
|
||||
header: () => <span className="sr-only">Действия</span>,
|
||||
cell: ({ row }) => (
|
||||
<div className="flex items-center justify-end">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon-sm"
|
||||
type="button"
|
||||
className="text-destructive"
|
||||
aria-label="Удалить пира"
|
||||
title="Удалить"
|
||||
onClick={() => setDeleteTarget(row.original)}
|
||||
>
|
||||
<Trash2 className="size-3.5" />
|
||||
</Button>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
],
|
||||
[],
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
<ResourcePage
|
||||
@@ -114,7 +151,7 @@ export function NetworkPeersCard({
|
||||
onFiltersChange={setFilters}
|
||||
onClearFilters={() => setFilters(createDefaultPeerFilters())}
|
||||
getFilterFieldValue={getPeerFilterFieldValue}
|
||||
columns={peerColumns}
|
||||
columns={columns}
|
||||
data={items}
|
||||
getRowId={(row) => row.id}
|
||||
isLoading={isLoading}
|
||||
@@ -129,7 +166,34 @@ export function NetworkPeersCard({
|
||||
}}
|
||||
/>
|
||||
|
||||
<PeerFormDialog open={dialogOpen} onOpenChange={setDialogOpen} speakers={speakers} />
|
||||
<PeerFormDialog
|
||||
open={dialogOpen}
|
||||
onOpenChange={setDialogOpen}
|
||||
speakers={speakers}
|
||||
/>
|
||||
|
||||
<ConfirmDialog
|
||||
open={deleteTarget !== null}
|
||||
onOpenChange={(open) => {
|
||||
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))
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user