import { Group, Text } from "@mantine/core"; import { Clock } from "lucide-react"; import { useEffect, useState } from "react"; export interface CountdownTimerProps { /** ISO timestamp the countdown targets. */ deadline: string | null | undefined; /** Optional label shown before the time (e.g. "Window closes in"). */ label?: string; /** Text shown once the deadline has passed. */ expiredText?: string; /** Visual size of the time text. */ size?: "xs" | "sm" | "md" | "lg"; /** Colour once under this many seconds remain (urgency). Default 300 (5 min). */ urgentUnderSeconds?: number; } function pad(n: number): string { return String(n).padStart(2, "0"); } /** Break a remaining-milliseconds figure into a human string. */ function formatRemaining(ms: number): string { const total = Math.floor(ms / 1000); const days = Math.floor(total / 86400); const hours = Math.floor((total % 86400) / 3600); const minutes = Math.floor((total % 3600) / 60); const seconds = total % 60; if (days > 0) return `${days}d ${pad(hours)}h ${pad(minutes)}m`; if (hours > 0) return `${hours}h ${pad(minutes)}m ${pad(seconds)}s`; return `${pad(minutes)}m ${pad(seconds)}s`; } /** * Live countdown to an ISO deadline. Ticks once a second, shows the remaining * time (d/h/m/s), turns red when under `urgentUnderSeconds`, and shows * `expiredText` once the deadline is in the past. Display only — enforcement * lives server-side. */ export function CountdownTimer({ deadline, label, expiredText = "Expired", size = "sm", urgentUnderSeconds = 300, }: CountdownTimerProps) { const [remaining, setRemaining] = useState(() => deadline ? new Date(deadline).getTime() - Date.now() : null, ); useEffect(() => { if (!deadline) { setRemaining(null); return; } const target = new Date(deadline).getTime(); const tick = () => setRemaining(target - Date.now()); tick(); const id = setInterval(tick, 1000); return () => clearInterval(id); }, [deadline]); if (!deadline || remaining == null || Number.isNaN(remaining)) { return null; } const expired = remaining <= 0; const urgent = !expired && remaining <= urgentUnderSeconds * 1000; const color = expired ? "red.7" : urgent ? "orange.7" : "dimmed"; return ( {label && ( {label} )} {expired ? expiredText : formatRemaining(remaining)} ); } export default CountdownTimer;