first commit
Made-with: Cursor
This commit is contained in:
50
frontend/src/components/Countdown.tsx
Normal file
50
frontend/src/components/Countdown.tsx
Normal file
@@ -0,0 +1,50 @@
|
||||
import { useState, useEffect } from "react";
|
||||
|
||||
export type CountdownFormat = "clock" | "duration";
|
||||
|
||||
interface CountdownProps {
|
||||
targetUnixSeconds: number;
|
||||
format?: CountdownFormat;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
function formatClock(secondsLeft: number): string {
|
||||
const m = Math.floor(secondsLeft / 60);
|
||||
const s = secondsLeft % 60;
|
||||
return `${m}:${s.toString().padStart(2, "0")}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format seconds into "Xd Xh Xm" for "next eligible" style countdown.
|
||||
* Exported for reuse in denial panel and success modal.
|
||||
*/
|
||||
export function formatDuration(secondsLeft: number): string {
|
||||
if (secondsLeft <= 0) return "0d 0h 0m";
|
||||
const d = Math.floor(secondsLeft / 86400);
|
||||
const h = Math.floor((secondsLeft % 86400) / 3600);
|
||||
const m = Math.floor((secondsLeft % 3600) / 60);
|
||||
const parts: string[] = [];
|
||||
if (d > 0) parts.push(`${d}d`);
|
||||
parts.push(`${h}h`);
|
||||
parts.push(`${m}m`);
|
||||
return parts.join(" ");
|
||||
}
|
||||
|
||||
function getSecondsLeft(targetUnixSeconds: number): number {
|
||||
return Math.max(0, targetUnixSeconds - Math.floor(Date.now() / 1000));
|
||||
}
|
||||
|
||||
export function Countdown({ targetUnixSeconds, format = "clock", className }: CountdownProps) {
|
||||
const [secondsLeft, setSecondsLeft] = useState(() => getSecondsLeft(targetUnixSeconds));
|
||||
|
||||
useEffect(() => {
|
||||
setSecondsLeft(getSecondsLeft(targetUnixSeconds));
|
||||
const t = setInterval(() => {
|
||||
setSecondsLeft(getSecondsLeft(targetUnixSeconds));
|
||||
}, 1000);
|
||||
return () => clearInterval(t);
|
||||
}, [targetUnixSeconds]);
|
||||
|
||||
const text = format === "duration" ? formatDuration(secondsLeft) : formatClock(secondsLeft);
|
||||
return <span className={className}>{text}</span>;
|
||||
}
|
||||
Reference in New Issue
Block a user