fix(ui): подтверждать копирование community после записи

This commit is contained in:
Denozordec committed 2026-10-04 15:45:56 +07:00
1 parent adfb2fc4c3
commit f515818254
6 files changed
+92 -8

No files matched your search

+32 -6
View File
@@ -1,6 +1,6 @@
"use client"
import { useState, useMemo } from "react"
import { useEffect, useRef, useState, useMemo } from "react"
import { PageHeader } from "@/components/page-header"
import { DataPageCard } from "@/components/data-page-card"
import {
@@ -12,6 +12,7 @@ import {
import { Frame, FramePanel } from "@/components/reui/frame"
import { KpiStatGrid } from "@/components/reui-kit/kpi-stat-grid"
import { OpsPanel } from "@/components/ops-panel"
import { Alert, AlertTitle, AlertDescription } from "@/components/reui/alert"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import {
@@ -105,6 +106,13 @@ function CommunitiesContent() {
const [search, setSearch] = useState("")
const [typeFilter, setTypeFilter] = useState<CommType | "all">("all")
const [copied, setCopied] = useState<string | null>(null)
const [copying, setCopying] = useState(false)
const [copyError, setCopyError] = useState<string | null>(null)
const copyRequest = useRef({ active: true, pending: false, revision: 0, timer: null as ReturnType<typeof setTimeout> | null })
useEffect(() => {
const state = copyRequest.current; state.active = true
return () => { state.active = false; state.revision++; if (state.timer) clearTimeout(state.timer) }
}, [])
const [selectedId, setSelectedId] = useState<string | null>(null)
const listData = useMemo((): Community[] => {
@@ -124,10 +132,24 @@ function CommunitiesContent() {
})
}, [search, typeFilter, listData])
const handleCopy = (value: string) => {
navigator.clipboard.writeText(value).catch(() => {})
setCopied(value)
setTimeout(() => setCopied(null), 1500)
const handleCopy = async (value: string) => {
const state = copyRequest.current
if (!state.active || state.pending) return
state.pending = true; const revision = ++state.revision
if (state.timer) clearTimeout(state.timer)
const current = () => state.active && state.revision === revision
setCopying(true); setCopied(null); setCopyError(null)
try {
await navigator.clipboard.writeText(value)
if (!current()) return
setCopied(value)
state.timer = setTimeout(() => { if (current()) setCopied(null) }, 1500)
} catch {
if (current()) setCopyError("Браузер не разрешил запись в буфер обмена. Повторите копирование или выделите значение вручную.")
} finally {
state.pending = false
if (current()) setCopying(false)
}
}
const filterName = (id: string) => filters.find(f => f.id === id)?.name ?? id
@@ -145,6 +167,9 @@ function CommunitiesContent() {
<div className="flex-1 overflow-y-auto p-6">
<div className="flex flex-col gap-5">
<EvoBgpCatalogStatus />
{copyError && <Alert variant="warning"><AlertTitle>Community не скопировано</AlertTitle><AlertDescription>{copyError}</AlertDescription></Alert>}
{copying && <p role="status" className="text-xs text-muted-foreground">Копирование community…</p>}
{copied && <p role="status" className="text-xs text-muted-foreground">Community {copied} скопировано</p>}
<KpiStatGrid
aria-label="Сводка communities"
@@ -205,6 +230,7 @@ function CommunitiesContent() {
communities={filtered}
selectedId={selected?.id}
copiedValue={copied}
copying={copying}
onSelect={row => setSelectedId(row.id)}
onCopy={handleCopy}
/> : <EvoBgpCatalogUnavailable />}
@@ -267,7 +293,7 @@ function CommunitiesContent() {
)}
<div className="pt-2 border-t flex flex-col gap-2">
<Button size="sm" variant="outline" className="w-full justify-start gap-2" onClick={() => handleCopy(selected.value)}>
<Button size="sm" variant="outline" className="w-full justify-start gap-2" disabled={copying} onClick={() => { void handleCopy(selected.value) }}>
{copied === selected.value ? <CheckIcon className="size-3.5" /> : <CopyIcon className="size-3.5" />}
Скопировать значение
</Button>