Compare commits

...
1 Commits
Author SHA1 Message Date
DenozordecandCursor 153be28799 fix(health): показывать пинг при наведении на график uptime
quality / commitlint (push) Skipped
CD / update-wiki (push) Successful in 6s
quality / changes (push) Successful in 9s
quality / api (push) Skipped
quality / docker-check (push) Skipped
quality / web (push) Successful in 1m11s
CD / quality (push) Successful in 1m23s
CD / publish (push) Successful in 1m57s
Co-authored-by: Cursor <[email protected]>
2026-08-20 12:44:17 +07:00
@@ -1,6 +1,6 @@
import { useId, useMemo, useState } from 'react'
import { ActivityIcon, InfoIcon, TrendingDownIcon, TrendingUpIcon } from 'lucide-react'
import { Area, ComposedChart, Line, XAxis } from 'recharts'
import { Area, ComposedChart, Line, XAxis, YAxis } from 'recharts'
import { EmptyState } from '@/components/empty-state'
import { Badge } from '@/components/reui/badge'
@@ -226,14 +226,14 @@ export function UptimeChart({
}: UptimeChartProps) {
const gradientId = useId().replace(/:/g, '')
const [internalPeriod, setInternalPeriod] = useState<UptimePeriodKey>('5D')
const [pinnedIndex, setPinnedIndex] = useState<number | undefined>()
const [hovered, setHovered] = useState<AlignedChartPoint | null>(null)
const period = periodProp ?? internalPeriod
const days = UPTIME_PERIODS.find((entry) => entry.key === period)?.days ?? 5
function handlePeriodChange(next: UptimePeriodKey) {
onPeriodChange?.(next)
if (periodProp == null) setInternalPeriod(next)
setPinnedIndex(undefined)
setHovered(null)
}
const { points, keys } = useMemo(
@@ -246,9 +246,25 @@ export function UptimeChart({
const tileClass = lastOk ? 'text-success' : 'text-destructive'
const single = keys.length <= 1
const areaKey = keys[0] ?? 'local'
const hoverPings = hovered
? keys.map((key) => {
const ok = hovered[`${key}Ok`]
const label = chartConfig[key].label
return `${label} ${formatPing(hovered[key], ok)}`
})
: []
function syncHover(state: {
activeTooltipIndex?: unknown
activeIndex?: unknown
}) {
const index = Number(state.activeTooltipIndex ?? state.activeIndex)
if (!Number.isFinite(index)) return
setHovered(points[index] ?? null)
}
const panel = (
<FramePanel className="flex flex-col gap-6">
<FramePanel className="flex flex-col gap-6 overflow-visible">
{hideHeader ? null : (
<div className="border-border flex items-center justify-between gap-2 border-b border-dashed pb-4">
<div className="flex items-center gap-2.5">
@@ -316,22 +332,33 @@ export function UptimeChart({
</div>
</div>
{hoverPings.length > 0 ? (
<p className="text-muted-foreground min-h-4 min-w-0 text-xs tabular-nums">
<span className="text-foreground font-medium">Пинг</span>
{' · '}
{formatDate(hovered?.at)}
{' · '}
{hoverPings.join(' · ')}
</p>
) : (
<p className="text-muted-foreground min-h-4 text-xs">
Наведите на точку графика
</p>
)}
<div className="h-40 w-full overflow-visible">
<ChartContainer
config={chartConfig}
className="h-full w-full overflow-visible rounded-b-xl"
className="[&_.recharts-tooltip-wrapper]:z-50 [&_.recharts-wrapper]:overflow-visible h-full w-full overflow-visible rounded-b-xl"
initialDimension={{ width: 320, height: 160 }}
>
<ComposedChart
data={points}
margin={{ top: 16, left: 8, right: 8, bottom: 4 }}
margin={{ top: 24, left: 8, right: 8, bottom: 8 }}
accessibilityLayer
onClick={(state) => {
const index = state.activeTooltipIndex ?? state.activeIndex
if (index == null) return
const next = Number(index)
if (Number.isFinite(next)) setPinnedIndex(next)
}}
onMouseMove={syncHover}
onMouseLeave={() => setHovered(null)}
onClick={syncHover}
>
<defs>
<linearGradient id={gradientId} x1="0" y1="0" x2="0" y2="1">
@@ -347,13 +374,14 @@ export function UptimeChart({
/>
</linearGradient>
</defs>
<XAxis dataKey="period" hide />
<XAxis dataKey="at" hide />
<YAxis hide domain={['auto', 'auto']} />
<ChartTooltip
key={pinnedIndex ?? 'hover'}
cursor={{ stroke: 'var(--border)', strokeDasharray: '4 4' }}
filterNull={false}
defaultIndex={pinnedIndex}
active={pinnedIndex != null ? true : undefined}
shared
isAnimationActive={false}
allowEscapeViewBox={{ x: true, y: true }}
wrapperStyle={{ zIndex: 50, pointerEvents: 'none' }}
content={
<ChartTooltipContent
@@ -393,6 +421,7 @@ export function UptimeChart({
strokeWidth={2}
connectNulls={false}
isAnimationActive={false}
dot={{ r: 3, strokeWidth: 1, stroke: 'var(--background)' }}
activeDot={{
r: 6,
stroke: 'var(--background)',