fix(ui): address PR-Agent round 2 on health redesign

PR-Agent flagged two more real regressions vs the original health page:

1. Stale timestamp (HealthStatusRibbon): formatRelativeTime reads
   Date.now() during render, but nothing forces a re-render after mount
   so the 'last scan' label froze and never advanced to '1 minute ago',
   '2 hours ago', etc. Restore the per-second tick (matching the
   original health.tsx behavior) so the label behaves like a real
   relative timestamp.

2. Hidden copy action (HealthPriorityCard): the fix-copy button was
   gated by group-hover, making it invisible to keyboard users and
   unreachable on mobile (no hover state). Make it always visible with
   an aria-label so screen-reader and touch users can copy the fix
   command.
This commit is contained in:
Tam Nhu Tran
2026-04-25 19:14:42 -04:00
parent 525c4f9812
commit e64060fe0d
2 changed files with 21 additions and 2 deletions
@@ -127,14 +127,20 @@ export function HealthPriorityCard({ check }: HealthPriorityCardProps) {
{(check.fix || check.fixable) && (
<div className="flex flex-col sm:flex-row items-stretch sm:items-center gap-3">
{check.fix && (
<div className="flex-1 flex items-center gap-2 h-10 px-3 rounded-full bg-background/50 border border-border/40 group/fix">
<div className="flex-1 flex items-center gap-2 h-10 px-3 rounded-full bg-background/50 border border-border/40">
<Terminal className="w-3.5 h-3.5 text-muted-foreground" />
<code className="text-xs font-mono flex-1 truncate">{check.fix}</code>
{/*
* Always-visible copy button. The previous design hid it behind
* group-hover, which excluded keyboard users and touch-only
* devices (no hover state on mobile = effectively unreachable).
*/}
<Button
variant="ghost"
size="icon"
onClick={copyFix}
className="h-6 w-6 rounded-full opacity-0 group-hover/fix:opacity-100 transition-opacity"
aria-label={t('health.copy') ?? 'Copy fix command'}
className="h-6 w-6 rounded-full text-muted-foreground hover:text-foreground"
>
<Copy className="w-3 h-3" />
</Button>
@@ -1,3 +1,4 @@
import { useEffect, useState } from 'react';
import { Cpu, RefreshCw, Terminal, Copy } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { cn } from '@/lib/utils';
@@ -41,6 +42,18 @@ export function HealthStatusRibbon({
}: HealthStatusRibbonProps) {
const { t } = useTranslation();
// formatRelativeTime reads Date.now() during render. Without a ticking
// re-render, the "last scan" label freezes at whatever it showed on mount
// and never advances to "1 minute ago", "2 hours ago", etc. — even though
// the underlying lastScan timestamp is stale. Force a re-render every
// second; the cost is negligible (one tiny component) and the UX win is
// a label that actually behaves like a relative timestamp.
const [, setTick] = useState(0);
useEffect(() => {
const interval = setInterval(() => setTick((n) => n + 1), 1000);
return () => clearInterval(interval);
}, []);
const copyDoctorCommand = () => {
navigator.clipboard.writeText('ccs doctor');
toast.success(t('health.copied'));