fix(uptime): enhance monitor status handling and display logic in MonitorCard

This commit is contained in:
GitHub Actions
2026-03-01 16:33:09 +00:00
parent f20e789a16
commit 0241de69f4

View File

@@ -6,6 +6,18 @@ import { Activity, ArrowUp, ArrowDown, Settings, X, Pause, RefreshCw, Plus, Load
import { toast } from 'react-hot-toast'
import { formatDistanceToNow } from 'date-fns';
type BaseMonitorStatus = 'up' | 'down' | 'pending';
type EffectiveMonitorStatus = BaseMonitorStatus | 'paused';
const normalizeMonitorStatus = (status: string | undefined): BaseMonitorStatus => {
const normalized = status?.toLowerCase();
if (normalized === 'up' || normalized === 'down' || normalized === 'pending') {
return normalized;
}
return 'down';
};
const MonitorCard: FC<{ monitor: UptimeMonitor; onEdit: (monitor: UptimeMonitor) => void; t: (key: string, options?: Record<string, unknown>) => string }> = ({ monitor, onEdit, t }) => {
const { data: history } = useQuery({
queryKey: ['uptimeHistory', monitor.id],
@@ -64,27 +76,33 @@ const MonitorCard: FC<{ monitor: UptimeMonitor; onEdit: (monitor: UptimeMonitor)
? history.reduce((a, b) => new Date(a.created_at) > new Date(b.created_at) ? a : b)
: null
const isPending = monitor.status === 'pending' && (!history || history.length === 0);
const isUp = latestBeat ? latestBeat.status === 'up' : monitor.status === 'up';
const hasHistory = Boolean(history && history.length > 0);
const isPaused = monitor.enabled === false;
const effectiveStatus: EffectiveMonitorStatus = isPaused
? 'paused'
: latestBeat
? (latestBeat.status === 'up' ? 'up' : 'down')
: monitor.status === 'pending' && !hasHistory
? 'pending'
: normalizeMonitorStatus(monitor.status);
return (
<div className={`bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 p-4 border-l-4 ${isPaused ? 'border-l-yellow-400' : isPending ? 'border-l-amber-500' : isUp ? 'border-l-green-500' : 'border-l-red-500'}`} data-testid="monitor-card">
<div className={`bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 p-4 border-l-4 ${effectiveStatus === 'paused' ? 'border-l-yellow-400' : effectiveStatus === 'pending' ? 'border-l-amber-500' : effectiveStatus === 'up' ? 'border-l-green-500' : 'border-l-red-500'}`} data-testid="monitor-card">
{/* Top Row: Name (left), Badge (center-right), Settings (right) */}
<div className="flex items-center justify-between mb-4">
<h3 className="font-semibold text-lg text-gray-900 dark:text-white flex-1 min-w-0 truncate">{monitor.name}</h3>
<div className="flex items-center gap-2 shrink-0">
<div className={`flex items-center justify-center px-3 py-1 rounded-full text-sm font-medium min-w-22.5 ${
isPaused
effectiveStatus === 'paused'
? 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200'
: isPending
: effectiveStatus === 'pending'
? 'bg-amber-100 text-amber-800 dark:bg-amber-900 dark:text-amber-200 animate-pulse motion-reduce:animate-none'
: isUp
: effectiveStatus === 'up'
? 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200'
: 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200'
}`} data-testid="status-badge" data-status={isPaused ? 'paused' : monitor.status} role="status" aria-label={isPaused ? t('uptime.paused') : isPending ? t('uptime.pending') : isUp ? 'UP' : 'DOWN'}>
{isPaused ? <Pause className="w-4 h-4 mr-1" /> : isPending ? <Loader className="w-4 h-4 mr-1 animate-spin motion-reduce:animate-none" aria-hidden="true" /> : isUp ? <ArrowUp className="w-4 h-4 mr-1" /> : <ArrowDown className="w-4 h-4 mr-1" />}
{isPaused ? t('uptime.paused') : isPending ? t('uptime.pending') : monitor.status.toUpperCase()}
}`} data-testid="status-badge" data-status={effectiveStatus} role="status" aria-label={effectiveStatus === 'paused' ? t('uptime.paused') : effectiveStatus === 'pending' ? t('uptime.pending') : effectiveStatus === 'up' ? 'UP' : 'DOWN'}>
{effectiveStatus === 'paused' ? <Pause className="w-4 h-4 mr-1" /> : effectiveStatus === 'pending' ? <Loader className="w-4 h-4 mr-1 animate-spin motion-reduce:animate-none" aria-hidden="true" /> : effectiveStatus === 'up' ? <ArrowUp className="w-4 h-4 mr-1" /> : <ArrowDown className="w-4 h-4 mr-1" />}
{effectiveStatus === 'paused' ? t('uptime.paused') : effectiveStatus === 'pending' ? t('uptime.pending') : effectiveStatus.toUpperCase()}
</div>
<button
onClick={async () => {
@@ -203,7 +221,7 @@ Message: ${beat.message}`}
/>
))}
{(!history || history.length === 0) && (
<div className="absolute w-full text-center text-xs text-gray-400">{isPending ? t('uptime.pendingFirstCheck') : t('uptime.noHistoryAvailable')}</div>
<div className="absolute w-full text-center text-xs text-gray-400">{effectiveStatus === 'pending' ? t('uptime.pendingFirstCheck') : t('uptime.noHistoryAvailable')}</div>
)}
</div>
</div>