feat: Pulse offline status indicator
Some checks are pending
Test / backend-test (push) Waiting to run
Test / frontend-test (push) Waiting to run

This commit is contained in:
fullsizemalt 2026-01-05 22:52:33 -08:00
parent 1abb972d37
commit 22d0668ba1

View file

@ -24,6 +24,12 @@ interface PulseSensorCardProps {
export function PulseSensorCard({ reading, history, onClick }: PulseSensorCardProps) {
const navigate = useNavigate();
// Determine offline status (older than 15 mins)
const now = new Date();
const readingTime = new Date(reading.timestamp);
const diffMinutes = (now.getTime() - readingTime.getTime()) / 1000 / 60;
const isOffline = diffMinutes > 15;
// Determine status color based on VPD (gold standard for crop health)
const getStatusColor = (vpd: number) => {
if (vpd < 0.8 || vpd > 1.2) return 'text-amber-500'; // Warning
@ -31,7 +37,11 @@ export function PulseSensorCard({ reading, history, onClick }: PulseSensorCardPr
return 'text-emerald-500'; // Good
};
const statusColor = getStatusColor(reading.vpd);
// If offline, use neutral/error color for metrics
const statusColor = isOffline ? 'text-slate-400 dark:text-slate-500' : getStatusColor(reading.vpd);
const badgeColor = isOffline
? 'text-rose-500 bg-rose-500/10 border-rose-500/20'
: cn("bg-slate-100 dark:bg-slate-800 border border-slate-200 dark:border-slate-700", statusColor);
// Simple Sparkline
const Sparkline = ({ data, color = "#10b981", width = 120, height = 40 }: { data: number[], color?: string, width?: number, height?: number }) => {
@ -74,7 +84,12 @@ export function PulseSensorCard({ reading, history, onClick }: PulseSensorCardPr
>
<div className="flex justify-between items-start mb-4">
<div className="flex gap-3 items-center">
<div className="p-2.5 rounded-xl bg-emerald-500/10 text-emerald-500 ring-1 ring-emerald-500/20">
<div className={cn(
"p-2.5 rounded-xl ring-1",
isOffline
? "bg-slate-100 text-slate-400 ring-slate-200 dark:bg-slate-800 dark:text-slate-500 dark:ring-slate-700"
: "bg-emerald-500/10 text-emerald-500 ring-emerald-500/20"
)}>
<Activity size={18} />
</div>
<div>
@ -85,8 +100,8 @@ export function PulseSensorCard({ reading, history, onClick }: PulseSensorCardPr
</div>
</div>
<div className={cn("text-[10px] font-bold px-2 py-0.5 rounded-full bg-slate-100 dark:bg-slate-800 border border-slate-200 dark:border-slate-700", statusColor)}>
{reading.timestamp ? 'LIVE' : 'OFFLINE'}
<div className={cn("text-[10px] font-bold px-2 py-0.5 rounded-full border", badgeColor)}>
{isOffline ? 'OFFLINE' : 'LIVE'}
</div>
</div>
@ -96,7 +111,7 @@ export function PulseSensorCard({ reading, history, onClick }: PulseSensorCardPr
<Thermometer size={12} />
<span className="text-[10px] font-bold uppercase tracking-wider">Temp</span>
</div>
<p className="text-2xl font-bold text-[var(--color-text-primary)]">
<p className={cn("text-2xl font-bold transition-colors", isOffline ? "text-slate-400" : "text-[var(--color-text-primary)]")}>
{reading.temperature.toFixed(1)}°
</p>
</div>
@ -106,7 +121,7 @@ export function PulseSensorCard({ reading, history, onClick }: PulseSensorCardPr
<Droplets size={12} />
<span className="text-[10px] font-bold uppercase tracking-wider">RH</span>
</div>
<p className="text-2xl font-bold text-[var(--color-text-primary)]">
<p className={cn("text-2xl font-bold transition-colors", isOffline ? "text-slate-400" : "text-[var(--color-text-primary)]")}>
{reading.humidity.toFixed(0)}%
</p>
</div>
@ -116,17 +131,26 @@ export function PulseSensorCard({ reading, history, onClick }: PulseSensorCardPr
<CloudFog size={12} />
<span className="text-[10px] font-bold uppercase tracking-wider">VPD</span>
</div>
<p className={cn("text-2xl font-bold", statusColor)}>
<p className={cn("text-2xl font-bold transition-colors", statusColor)}>
{reading.vpd.toFixed(2)}
</p>
</div>
<div className="flex items-end justify-end">
{history && history.temperature && (
<div className="flex flex-col items-end justify-end">
{history && history.temperature && !isOffline && (
<div className="mb-1">
<Sparkline data={history.temperature} color={reading.temperature > 80 ? '#f43f5e' : '#10b981'} />
</div>
)}
{isOffline && (
<div className="text-right">
<p className="text-[10px] font-medium text-rose-500">Last Updated</p>
<p className="text-[10px] text-[var(--color-text-tertiary)]">
{readingTime.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
</p>
</div>
)}
</div>
</div>
</motion.div>