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.
This commit is contained in:
@@ -1,8 +1,10 @@
|
|||||||
import { useMemo, useState } from 'react'
|
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 { Button } from '@evobgp/ui/components/button'
|
||||||
|
|
||||||
|
import { ConfirmDialog } from '@/components/confirm-dialog'
|
||||||
import { SpeakerFormDialog } from '@/components/network/speaker-form-dialog'
|
import { SpeakerFormDialog } from '@/components/network/speaker-form-dialog'
|
||||||
import {
|
import {
|
||||||
getSpeakerFilterFieldValue,
|
getSpeakerFilterFieldValue,
|
||||||
@@ -15,6 +17,7 @@ import {
|
|||||||
type FilterFieldConfig,
|
type FilterFieldConfig,
|
||||||
} from '@/components/reui/filters'
|
} from '@/components/reui/filters'
|
||||||
import { ResourcePage, renderSingleSelectedLabel } from '@/components/reui-kit'
|
import { ResourcePage, renderSingleSelectedLabel } from '@/components/reui-kit'
|
||||||
|
import { useDeleteSpeakerMutation } from '@/queries/network'
|
||||||
import type { SpeakerRow } from '@/types/api'
|
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
|
* @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 = [
|
const SPEAKER_TABS = [
|
||||||
{ id: 'all', label: 'Все' },
|
{ id: 'all', label: 'Все' },
|
||||||
{ id: 'online', label: 'Online' },
|
{ id: 'online', label: 'Online' },
|
||||||
@@ -75,7 +82,9 @@ export function NetworkSpeakersCard({
|
|||||||
error,
|
error,
|
||||||
onRetry,
|
onRetry,
|
||||||
}: NetworkSpeakersCardProps) {
|
}: NetworkSpeakersCardProps) {
|
||||||
|
const deleteMutation = useDeleteSpeakerMutation()
|
||||||
const [dialogOpen, setDialogOpen] = useState(false)
|
const [dialogOpen, setDialogOpen] = useState(false)
|
||||||
|
const [deleteTarget, setDeleteTarget] = useState<SpeakerRow | null>(null)
|
||||||
const [filters, setFilters] = useState<Filter[]>(createDefaultSpeakerFilters)
|
const [filters, setFilters] = useState<Filter[]>(createDefaultSpeakerFilters)
|
||||||
|
|
||||||
const addButton = useMemo(
|
const addButton = useMemo(
|
||||||
@@ -88,6 +97,33 @@ export function NetworkSpeakersCard({
|
|||||||
[],
|
[],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const columns = useMemo<ColumnDef<SpeakerRow, unknown>[]>(
|
||||||
|
() => [
|
||||||
|
...speakerColumns,
|
||||||
|
{
|
||||||
|
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 (
|
return (
|
||||||
<>
|
<>
|
||||||
<ResourcePage
|
<ResourcePage
|
||||||
@@ -100,7 +136,7 @@ export function NetworkSpeakersCard({
|
|||||||
onFiltersChange={setFilters}
|
onFiltersChange={setFilters}
|
||||||
onClearFilters={() => setFilters(createDefaultSpeakerFilters())}
|
onClearFilters={() => setFilters(createDefaultSpeakerFilters())}
|
||||||
getFilterFieldValue={getSpeakerFilterFieldValue}
|
getFilterFieldValue={getSpeakerFilterFieldValue}
|
||||||
columns={speakerColumns}
|
columns={columns}
|
||||||
data={items}
|
data={items}
|
||||||
getRowId={(row) => row.id}
|
getRowId={(row) => row.id}
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
@@ -116,6 +152,27 @@ export function NetworkSpeakersCard({
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<SpeakerFormDialog open={dialogOpen} onOpenChange={setDialogOpen} />
|
<SpeakerFormDialog open={dialogOpen} onOpenChange={setDialogOpen} />
|
||||||
|
|
||||||
|
<ConfirmDialog
|
||||||
|
open={deleteTarget !== null}
|
||||||
|
onOpenChange={(open) => {
|
||||||
|
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))
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user