mirror of
https://github.com/tiennm99/ccs.git
synced 2026-07-16 12:15:57 +00:00
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:
@@ -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'));
|
||||
|
||||
Reference in New Issue
Block a user