Spaces:
Sleeping
Sleeping
File size: 4,805 Bytes
f234112 32b6488 f234112 32b6488 f234112 32b6488 f234112 32b6488 f234112 32b6488 f234112 32b6488 f234112 32b6488 f234112 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 | // components/ui.jsx — shared reusable UI primitives
export function StatusBadge({ status }) {
const map = {
SAFE: 'bg-emerald-500/20 text-emerald-400 border-emerald-500/30',
LOW: 'bg-emerald-500/20 text-emerald-400 border-emerald-500/30',
MEDIUM: 'bg-yellow-500/20 text-yellow-400 border-yellow-500/30',
HIGH: 'bg-orange-500/20 text-orange-400 border-orange-500/30',
CRITICAL: 'bg-red-500/20 text-red-400 border-red-500/30',
CONNECTED: 'bg-emerald-500/20 text-emerald-400 border-emerald-500/30',
DISCONNECTED: 'bg-red-500/20 text-red-400 border-red-500/30',
CONNECTING: 'bg-yellow-500/20 text-yellow-400 border-yellow-500/30',
ACTIVE: 'bg-blue-500/20 text-blue-400 border-blue-500/30',
INACTIVE: 'bg-slate-500/20 text-slate-400 border-slate-500/30',
EXECUTED: 'bg-emerald-500/20 text-emerald-400 border-emerald-500/30',
REJECTED: 'bg-red-500/20 text-red-400 border-red-500/30',
UNAVAILABLE: 'bg-slate-500/20 text-slate-400 border-slate-500/30',
APPROVED: 'bg-emerald-500/20 text-emerald-400 border-emerald-500/30',
running: 'bg-emerald-500/20 text-emerald-400 border-emerald-500/30',
stopped: 'bg-slate-500/20 text-slate-400 border-slate-500/30',
error: 'bg-red-500/20 text-red-400 border-red-500/30',
}
const cls = map[status] ?? 'bg-slate-500/20 text-slate-400 border-slate-500/30'
return (
<span className={`inline-flex items-center rounded-md border px-2 py-0.5 text-xs font-medium ${cls}`}>
{status}
</span>
)
}
export function MetricCard({ label, value, sub, icon: Icon, accent = 'blue' }) {
const accents = {
blue: 'border-cyan-400/25 bg-cyan-400/8 text-cyan-300',
green: 'border-emerald-400/25 bg-emerald-400/8 text-emerald-300',
yellow: 'border-amber-400/25 bg-amber-400/8 text-amber-300',
red: 'border-rose-400/25 bg-rose-400/8 text-rose-300',
purple: 'border-violet-400/25 bg-violet-400/8 text-violet-300',
}
return (
<div className={`group rounded-lg border p-4 shadow-lg shadow-slate-950/20 backdrop-blur ${accents[accent] ?? accents.blue}`}>
<div className="flex items-start justify-between gap-3">
<div className="min-w-0">
<p className="mb-1 text-[11px] font-semibold uppercase text-slate-400">{label}</p>
<p className="break-words text-2xl font-bold leading-tight text-white">{value ?? '—'}</p>
{sub && <p className="mt-1 text-xs text-slate-500">{sub}</p>}
</div>
{Icon && (
<div className="rounded-md border border-white/10 bg-white/5 p-2 text-current">
<Icon className="h-4 w-4" />
</div>
)}
</div>
</div>
)
}
export function Card({ title, children, className = '' }) {
return (
<section className={`rounded-lg border border-slate-700/60 bg-slate-900/70 shadow-xl shadow-slate-950/20 backdrop-blur ${className}`}>
{title && (
<div className="border-b border-slate-700/50 px-4 py-3">
<h3 className="text-sm font-semibold text-slate-100">{title}</h3>
</div>
)}
<div className="p-4">{children}</div>
</section>
)
}
export function LoadingState({ message = 'Loading...' }) {
return (
<div className="flex items-center justify-center h-40 text-slate-400 text-sm">
<div className="flex items-center gap-2">
<div className="w-4 h-4 border-2 border-slate-600 border-t-blue-400 rounded-full animate-spin" />
{message}
</div>
</div>
)
}
export function EmptyState({ message = 'No data available' }) {
return (
<div className="flex items-center justify-center h-32 text-slate-500 text-sm">
{message}
</div>
)
}
export function ErrorState({ message = 'Backend unavailable. Trying to reconnect...' }) {
return (
<div className="flex items-center justify-center h-32 text-red-400 text-sm">
<div className="text-center">
<p className="font-medium">Connection Error</p>
<p className="text-slate-500 mt-1">{message}</p>
</div>
</div>
)
}
export function CheckRow({ label, value }) {
const ok = value === true
const na = value === undefined || value === null
return (
<div className="flex items-center justify-between py-1.5 border-b border-slate-700/30 last:border-0">
<span className="text-sm text-slate-300">{label}</span>
{na ? (
<span className="text-xs text-slate-500">—</span>
) : ok ? (
<span className="text-xs text-emerald-400 font-medium">✓ PASS</span>
) : (
<span className="text-xs text-red-400 font-medium">✗ FAIL</span>
)}
</div>
)
}
export function DataSourceTag({ source }) {
return (
<span className="text-xs text-slate-500 bg-slate-700/50 px-2 py-0.5 rounded">
Source: {source}
</span>
)
}
|