fix(ui): подтверждать копирование community после записи
This commit is contained in:
1 parent
adfb2fc4c3
commit
f515818254
6 files changed
+92
-8
No files matched your search
@@ -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>
|
||||
|
||||
Reference in new issue
Block a user