From 1e04e91dd8065a020bc3a1420003c065fbff2d1b Mon Sep 17 00:00:00 2001 From: Denozordec Date: Fri, 31 Jul 2026 03:19:54 +0700 Subject: [PATCH] feat(network): add delete functionality for speakers in network speakers card Implemented a delete feature for speakers within the NetworkSpeakersCard component. This includes a confirmation dialog for deletion and integration with the delete speaker mutation. Updated the table columns to include an action button for deleting speakers, enhancing the user experience for managing network speakers. --- .../network/network-speakers-card.tsx | 61 ++++++++++++++++++- 1 file changed, 59 insertions(+), 2 deletions(-) diff --git a/apps/web/src/components/network/network-speakers-card.tsx b/apps/web/src/components/network/network-speakers-card.tsx index 4bca06f..ac02e39 100644 --- a/apps/web/src/components/network/network-speakers-card.tsx +++ b/apps/web/src/components/network/network-speakers-card.tsx @@ -1,8 +1,10 @@ import { useMemo, useState } from 'react' -import { Plus, SearchIcon, TagIcon } from 'lucide-react' +import type { ColumnDef } from '@tanstack/react-table' +import { Plus, SearchIcon, TagIcon, Trash2 } from 'lucide-react' import { Button } from '@evobgp/ui/components/button' +import { ConfirmDialog } from '@/components/confirm-dialog' import { SpeakerFormDialog } from '@/components/network/speaker-form-dialog' import { getSpeakerFilterFieldValue, @@ -15,6 +17,7 @@ import { type FilterFieldConfig, } from '@/components/reui/filters' import { ResourcePage, renderSingleSelectedLabel } from '@/components/reui-kit' +import { useDeleteSpeakerMutation } from '@/queries/network' import type { SpeakerRow } from '@/types/api' /** @@ -22,6 +25,10 @@ import type { SpeakerRow } from '@/types/api' * @see https://reui.io/preview/base/data-grid-filtering-2 */ +function speakerDeleteLabel(s: SpeakerRow): string { + return s.agent_domain ?? s.endpoint ?? s.id +} + const SPEAKER_TABS = [ { id: 'all', label: 'Все' }, { id: 'online', label: 'Online' }, @@ -75,7 +82,9 @@ export function NetworkSpeakersCard({ error, onRetry, }: NetworkSpeakersCardProps) { + const deleteMutation = useDeleteSpeakerMutation() const [dialogOpen, setDialogOpen] = useState(false) + const [deleteTarget, setDeleteTarget] = useState(null) const [filters, setFilters] = useState(createDefaultSpeakerFilters) const addButton = useMemo( @@ -88,6 +97,33 @@ export function NetworkSpeakersCard({ [], ) + const columns = useMemo[]>( + () => [ + ...speakerColumns, + { + id: 'actions', + enableSorting: false, + header: () => Действия, + cell: ({ row }) => ( +
+ +
+ ), + }, + ], + [], + ) + return ( <> setFilters(createDefaultSpeakerFilters())} getFilterFieldValue={getSpeakerFilterFieldValue} - columns={speakerColumns} + columns={columns} data={items} getRowId={(row) => row.id} isLoading={isLoading} @@ -116,6 +152,27 @@ export function NetworkSpeakersCard({ /> + + { + if (!open) setDeleteTarget(null) + }} + title="Удалить спикера?" + description={ + deleteTarget + ? `${speakerDeleteLabel(deleteTarget)} будет удалён. Привязки пиров к этому спикеру сбросятся.` + : undefined + } + confirmLabel="Удалить" + confirmLoadingLabel="Удаление…" + destructive + confirmLoading={deleteMutation.isPending} + onConfirm={() => { + if (!deleteTarget) return + void deleteMutation.mutateAsync(deleteTarget.id).then(() => setDeleteTarget(null)) + }} + /> ) }