mission-control/components/EventCard.tsx
GeorgeWebberley 1f43a7c29e
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
Added next rocket data
2026-01-28 20:39:18 +01:00

152 lines
5.3 KiB
TypeScript

"use client";
import { useState, useEffect, useRef } from 'react';
import { EventCardProps } from '@/types/space';
import { useRouter } from 'next/navigation';
import { Sparkles } from 'lucide-react';
export default function EventCard({ id, title, targetDate, endDate, icon }: EventCardProps) {
const [isLive, setIsLive] = useState(false);
const router = useRouter();
const [timeLeft, setTimeLeft] = useState({ d: "00", h: "00", m: "00", s: "00" });
const lastRefreshedRef = useRef<string | null>(null);
useEffect(() => {
const updateTimer = () => {
if (!targetDate || !endDate) return;
const now = new Date();
const timeToStart = targetDate.getTime() - now.getTime();
const timeToEnd = endDate.getTime() - now.getTime();
if (timeToStart <= 0 && timeToEnd > 0) {
if (!isLive) setIsLive(true);
return;
}
if (timeToEnd <= 0) {
if (isLive) setIsLive(false);
const passId = endDate.toISOString();
if (lastRefreshedRef.current !== passId) {
const isStaleDataFromServer = endDate.getTime() < (new Date().getTime() - 5000);
if (!isStaleDataFromServer) {
console.log("🛰️ Pass complete. Syncing with Mission Control...");
lastRefreshedRef.current = passId;
router.refresh();
} else {
console.warn("⚠️ Server returned stale ISS data. Standing by for worker update...");
lastRefreshedRef.current = passId;
}
}
return;
}
const distance = timeToStart;
const d = Math.floor(distance / (1000 * 60 * 60 * 24));
const h = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
const m = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
const s = Math.floor((distance % (1000 * 60)) / 1000);
setTimeLeft({
d: d.toString().padStart(2, '0'),
h: h.toString().padStart(2, '0'),
m: m.toString().padStart(2, '0'),
s: s.toString().padStart(2, '0')
});
};
const interval = setInterval(updateTimer, 1000);
updateTimer();
return () => clearInterval(interval);
}, [targetDate, endDate, isLive, router]);
if (!targetDate) {
return (
<div className="relative p-6 rounded-xl border border-slate-800 bg-slate-900/20 backdrop-blur-md overflow-hidden opacity-60">
<div className="flex items-center gap-4 mb-4">
<div className="p-2 bg-slate-700/30 rounded-lg text-slate-500">
{icon}
</div>
<h3 className="text-slate-500 font-mono tracking-widest uppercase text-xs">
{title}
</h3>
</div>
<div className="py-2">
<div className="h-8 w-48 bg-slate-800 animate-pulse rounded" />
<p className="text-[10px] text-slate-600 mt-4 font-mono uppercase tracking-widest italic">
Waiting for orbital telemetry...
</p>
</div>
</div>
);
}
return (
<div className={`relative p-6 rounded-xl border transition-all duration-500 ${isLive ? 'border-blue-500 bg-blue-900/20 shadow-[0_0_20px_rgba(59,130,246,0.3)]' : 'border-slate-800 bg-slate-900/40'}`}>
{isLive ? (
<div className="flex flex-col items-center justify-center py-4 animate-pulse">
<Sparkles className="text-yellow-400 mb-2" size={32} />
<h3 className="text-xl font-bold text-white text-center">
{id === 'iss' ? 'Look Up!' : 'Event in Progress'}
</h3>
<p className="text-blue-300 text-sm text-center mt-1">
{id === 'iss'
? 'The ISS is directly above. Give them a wave! 👋'
: `The ${title} is occurring right now.`}
</p>
</div>
) : (
<>
<div className="flex items-center gap-4 mb-4">
<div className="p-2 bg-blue-500/10 rounded-lg text-blue-400">
{icon}
</div>
<h3 className="text-slate-300 font-mono tracking-widest uppercase text-xs">
{title}
</h3>
</div>
<div className="text-4xl font-mono text-white flex items-baseline gap-2">
{parseInt(timeLeft.d) >= 2 ? (
<>
<span>{timeLeft.d}</span>
<span className="text-slate-500 text-xl lowercase">Days</span>
</>
) : parseInt(timeLeft.d) === 1 ? (
<>
<span>01</span><span className="text-slate-500 text-sm">d</span>
<span className="text-slate-500">:</span>
<span>{timeLeft.h}</span><span className="text-slate-500 text-sm">h</span>
</>
) : (
<>
<span>{timeLeft.h}</span>
<span className="text-slate-500">:</span>
<span>{timeLeft.m}</span>
<span className="text-slate-500">:</span>
<span>{timeLeft.s}</span>
</>
)}
</div>
<p className="text-[10px] text-slate-500 mt-2 font-mono uppercase tracking-widest leading-none">
{isLive ? (
"Mission in Progress"
) : id === 'iss' ? (
"T-Minus to Horizon"
) : id === 'launch' ? (
"T-Minus to Ignition"
) : id === 'moon' ? (
"Until Lunar Phase"
) : (
"Days to Peak Phase"
)}
</p>
</>
)}
</div>
);
}