fix(ui): адаптировать строки Ping-проб для телефона
This commit is contained in:
1 parent
dfb993e1a3
commit
e9e5030047
5 files changed
+133
-38
No files matched your search
+35
-32
@@ -1,5 +1,7 @@
|
||||
"use client"
|
||||
|
||||
import probeStyles from "./probe-rows.module.css"
|
||||
|
||||
import { Suspense, useState, useMemo, useEffect, useRef, useCallback } from "react"
|
||||
import Link from "next/link"
|
||||
import { useSearchParams } from "next/navigation"
|
||||
@@ -2422,7 +2424,7 @@ function UptimePageContent({ selectedProbeId, allProbesHref }: { selectedProbeId
|
||||
</div>
|
||||
|
||||
{/* ── probe groups ── */}
|
||||
<div className="p-6 flex flex-col gap-4">
|
||||
<div className="p-4 md:p-6 flex flex-col gap-4">
|
||||
|
||||
{grouped.length === 0 && (
|
||||
<div className="flex flex-col items-center justify-center py-20 text-muted-foreground gap-2">
|
||||
@@ -2439,18 +2441,18 @@ function UptimePageContent({ selectedProbeId, allProbesHref }: { selectedProbeId
|
||||
const warnCount = allServerProbes.filter(p => p.status === "warn").length
|
||||
|
||||
return (
|
||||
<Frame key={srv.id} dense className="w-full overflow-hidden">
|
||||
<Frame key={srv.id} dense className={cn("w-full overflow-hidden", probeStyles.container)}>
|
||||
<FramePanel className="p-0 overflow-hidden">
|
||||
|
||||
{/* server header */}
|
||||
<button onClick={() => toggleCollapse(srv.id)}
|
||||
className="w-full flex items-center gap-2.5 px-4 py-3 hover:bg-muted/30 transition-colors text-left border-b">
|
||||
<button type="button" aria-expanded={!isCollapsed} onClick={() => toggleCollapse(srv.id)}
|
||||
className="w-full flex flex-wrap items-center gap-2.5 px-4 py-3 hover:bg-muted/30 transition-colors text-left border-b">
|
||||
{isCollapsed
|
||||
? <ChevronRightIcon className="size-4 text-muted-foreground shrink-0" />
|
||||
: <ChevronDownIcon className="size-4 text-muted-foreground shrink-0" />}
|
||||
<StatusDot status={srv.status} pulse={srv.status === "online"} />
|
||||
<Flag code={srv.country} size={16} />
|
||||
<span className="font-mono text-sm font-semibold">{srv.name}</span>
|
||||
<span className={cn(probeStyles.serverName, "font-mono text-sm font-semibold min-w-0 wrap-anywhere")}>{srv.name}</span>
|
||||
<TypeChip type={srv.type} />
|
||||
<span className="text-xs text-muted-foreground">{srv.site}</span>
|
||||
<div className="ml-auto flex items-center gap-3 text-xs">
|
||||
@@ -2475,10 +2477,10 @@ function UptimePageContent({ selectedProbeId, allProbesHref }: { selectedProbeId
|
||||
const groupBusy = !!probeGroupPingBusy[groupBusyKey]
|
||||
return (
|
||||
<div key={`${group.name}|${group.target}`}>
|
||||
<div className="px-4 py-2 border-b bg-muted/20 flex items-center gap-2 text-xs">
|
||||
<div className="px-4 py-2 border-b bg-muted/20 flex flex-wrap items-center gap-2 text-xs">
|
||||
<ArrowRightIcon className="size-3.5 text-muted-foreground/60 shrink-0" />
|
||||
<span className="font-medium truncate min-w-0">{group.name}</span>
|
||||
<span className="font-mono text-muted-foreground shrink-0">{group.target}</span>
|
||||
<span className="font-medium min-w-0 wrap-anywhere">{group.name}</span>
|
||||
<span className="font-mono text-muted-foreground min-w-0 wrap-anywhere">{group.target}</span>
|
||||
<div className="ml-auto flex items-center gap-2 shrink-0">
|
||||
<span className="text-muted-foreground whitespace-nowrap">{group.probes.length} интерф.</span>
|
||||
<Button
|
||||
@@ -2496,8 +2498,7 @@ function UptimePageContent({ selectedProbeId, allProbesHref }: { selectedProbeId
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid items-center gap-3 px-4 py-1.5 bg-muted/30 border-b text-[10px] font-semibold uppercase tracking-widest text-muted-foreground"
|
||||
style={{ gridTemplateColumns: "36px 16px 130px 120px 140px 70px 44px minmax(132px,1fr) 96px 36px 72px" }}>
|
||||
<div className={cn(probeStyles.header, "items-center gap-3 px-4 py-1.5 bg-muted/30 border-b text-[10px] font-semibold uppercase tracking-widest text-muted-foreground")}>
|
||||
<span />
|
||||
<span />
|
||||
<span>Интерфейс</span>
|
||||
@@ -2517,33 +2518,33 @@ function UptimePageContent({ selectedProbeId, allProbesHref }: { selectedProbeId
|
||||
<div key={p.id} data-probe-id={p.id} className="border-b border-border/40 last:border-b-0">
|
||||
<div
|
||||
className={cn(
|
||||
"grid items-center gap-3 px-4 py-2.5 hover:bg-muted/20 transition-colors",
|
||||
probeStyles.row, "items-center gap-3 px-4 py-2.5 hover:bg-muted/20 transition-colors",
|
||||
!p.enabled && "opacity-40",
|
||||
)}
|
||||
style={{ gridTemplateColumns: "36px 16px 130px 120px 140px 70px 44px minmax(132px,1fr) 96px 36px 72px" }}>
|
||||
<FormToggle checked={p.enabled} onChange={v => toggleProbe(p.id, v)} />
|
||||
<StatusDot
|
||||
>
|
||||
<label className={probeStyles.toggle}><span className="sr-only">Включить пробу {p.name}</span><span className={probeStyles.label} aria-hidden="true">Включена</span><FormToggle checked={p.enabled} onChange={v => toggleProbe(p.id, v)} /></label>
|
||||
<div className={probeStyles.status}><StatusDot
|
||||
status={p.status === "up" ? "online" : p.status === "warn" ? "degraded" : "offline"}
|
||||
pulse={p.status === "up" && p.enabled}
|
||||
/>
|
||||
<span className="font-mono text-xs text-muted-foreground truncate">{p.srcInterface || "auto"}</span>
|
||||
<span className="text-sm font-medium truncate">{p.name}</span>
|
||||
{p.filter === "—"
|
||||
/><span className={probeStyles.mobile}>{!p.enabled ? "Отключена" : p.status === "up" ? "Доступна" : p.status === "warn" ? "Предупреждение" : "Недоступна"}</span></div>
|
||||
<div className={probeStyles.iface}><span className={probeStyles.label}>Интерфейс</span><span className="font-mono text-xs text-muted-foreground">{p.srcInterface || "auto"}</span></div>
|
||||
<div className={probeStyles.name}><span className="text-sm font-medium">{p.name}</span></div>
|
||||
<div className={probeStyles.filter}><span className={probeStyles.label}>Фильтр</span>{p.filter === "—"
|
||||
? <span className="text-muted-foreground/40 text-xs">—</span>
|
||||
: <span className="text-[11px] font-medium text-sky-600 dark:text-sky-400 truncate">{p.filter}</span>
|
||||
}
|
||||
<span className={cn("font-mono text-sm font-semibold tabular-nums", rttColor(p.rtt, p.loss))}>
|
||||
: <span className="text-[11px] font-medium text-sky-600 dark:text-sky-400">{p.filter}</span>
|
||||
}</div>
|
||||
<div className={probeStyles.rtt}><span className={probeStyles.label}>RTT</span><span className={cn("font-mono text-sm font-semibold tabular-nums", rttColor(p.rtt, p.loss))}>
|
||||
{p.rtt === null ? "—" : `${p.rtt}мс`}
|
||||
</span>
|
||||
<span className={cn("font-mono text-xs tabular-nums",
|
||||
</span></div>
|
||||
<div className={probeStyles.loss}><span className={probeStyles.label}>Потери</span><span className={cn("font-mono text-xs tabular-nums",
|
||||
p.loss === 0 ? "text-muted-foreground" :
|
||||
p.loss >= 100 ? "text-red-600 dark:text-red-400" :
|
||||
p.loss > 5 ? "text-red-600 dark:text-red-400" :
|
||||
"text-amber-600 dark:text-amber-400",
|
||||
)}>
|
||||
{p.loss}%
|
||||
</span>
|
||||
<div className="min-w-0 flex flex-col gap-0.5 justify-center">
|
||||
</span></div>
|
||||
<div className={cn(probeStyles.bt, "min-w-0 flex flex-col gap-0.5 justify-center")}><span className={probeStyles.label}>BT · ping</span>
|
||||
{!linkedSp?.lastRunAt ? (
|
||||
<span className="text-muted-foreground/35 text-[10px]">—</span>
|
||||
) : linkedSp.lastStatus === "error" && linkedSp.lastError ? (
|
||||
@@ -2577,8 +2578,8 @@ function UptimePageContent({ selectedProbeId, allProbesHref }: { selectedProbeId
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<Sparkline data={p.series} width={96} height={24} color={probeSparkColor(p.status)} filled />
|
||||
<div className="flex justify-center">
|
||||
<div className={probeStyles.graph}><span className={probeStyles.label}>График RTT</span><Sparkline data={p.series} width={96} height={24} color={probeSparkColor(p.status)} filled /></div>
|
||||
<div className={probeStyles.star}><span className={probeStyles.label}>На дашборде</span>
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
@@ -2586,6 +2587,7 @@ function UptimePageContent({ selectedProbeId, allProbesHref }: { selectedProbeId
|
||||
className="size-7 p-0 text-muted-foreground hover:text-amber-500"
|
||||
onClick={() => toggleDashboardStar(p.id)}
|
||||
title={p.showOnDashboard ? "Убрать с дашборда" : "Показать на дашборде"}
|
||||
aria-label={p.showOnDashboard ? "Убрать с дашборда" : "Показать на дашборде"}
|
||||
aria-pressed={p.showOnDashboard === true}
|
||||
>
|
||||
<StarIcon
|
||||
@@ -2598,15 +2600,15 @@ function UptimePageContent({ selectedProbeId, allProbesHref }: { selectedProbeId
|
||||
/>
|
||||
</Button>
|
||||
</div>
|
||||
<div className="flex justify-end gap-0.5">
|
||||
<div className={cn(probeStyles.actions, "flex justify-end gap-1")}>
|
||||
<Button size="sm" variant="ghost"
|
||||
className="size-7 p-0 text-muted-foreground hover:text-foreground"
|
||||
onClick={() => openEditProbe(p)} title="Редактировать пробу">
|
||||
type="button" aria-label="Редактировать пробу" onClick={() => openEditProbe(p)} title="Редактировать пробу">
|
||||
<PencilIcon className="size-3.5" />
|
||||
</Button>
|
||||
<Button size="sm" variant="ghost"
|
||||
className="size-7 p-0 text-muted-foreground hover:text-destructive"
|
||||
onClick={() => deleteProbe(p.id)} title="Удалить пробу">
|
||||
type="button" aria-label="Удалить пробу" onClick={() => deleteProbe(p.id)} title="Удалить пробу">
|
||||
<TrashIcon className="size-3.5" />
|
||||
</Button>
|
||||
</div>
|
||||
@@ -2615,7 +2617,7 @@ function UptimePageContent({ selectedProbeId, allProbesHref }: { selectedProbeId
|
||||
open={probeRttChartOpen[p.id] ?? false}
|
||||
onOpenChange={(open) => setProbeRttChartOpen((m) => ({ ...m, [p.id]: open }))}
|
||||
>
|
||||
<CollapsibleTrigger
|
||||
<CollapsibleTrigger type="button"
|
||||
className={cn(
|
||||
"flex w-full items-center gap-2 border-t border-border/50 bg-muted/15 px-4 py-1.5 text-left text-xs text-muted-foreground",
|
||||
"outline-none hover:bg-muted/30 focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
||||
@@ -2657,7 +2659,8 @@ function UptimePageContent({ selectedProbeId, allProbesHref }: { selectedProbeId
|
||||
setNewName(group.name); setNewTarget(group.target); setNewFilter("—")
|
||||
setSheetOpen(true)
|
||||
}}
|
||||
className="w-full flex items-center gap-2 px-4 py-2 text-xs text-muted-foreground hover:text-foreground hover:bg-muted/20 transition-colors border-t">
|
||||
type="button"
|
||||
className="w-full flex items-center gap-2 px-4 py-2 text-left wrap-anywhere text-xs text-muted-foreground hover:text-foreground hover:bg-muted/20 transition-colors border-t">
|
||||
<PlusIcon className="size-3.5" />
|
||||
Добавить интерфейс для {group.name} ({group.target})
|
||||
</button>
|
||||
|
||||
Reference in new issue
Block a user