diff --git a/frontend/src/components/bounty/CountdownTimer.tsx b/frontend/src/components/bounty/CountdownTimer.tsx new file mode 100644 index 000000000..ae6bf44e9 --- /dev/null +++ b/frontend/src/components/bounty/CountdownTimer.tsx @@ -0,0 +1,95 @@ +import React, { useState, useEffect } from 'react'; +import { motion } from 'framer-motion'; + +interface CountdownTimerProps { + deadline: string | Date; + onComplete?: () => void; + className?: string; +} + +interface TimeLeft { + days: number; + hours: number; + minutes: number; + seconds: number; + total: number; +} + +function getTimeLeft(deadline: string | Date): TimeLeft { + const target = new Date(deadline).getTime(); + const now = Date.now(); + const total = target - now; + + if (total <= 0) { + return { days: 0, hours: 0, minutes: 0, seconds: 0, total: 0 }; + } + + return { + days: Math.floor(total / (1000 * 60 * 60 * 24)), + hours: Math.floor((total / (1000 * 60 * 60)) % 24), + minutes: Math.floor((total / (1000 * 60)) % 60), + seconds: Math.floor((total / 1000) % 60), + total, + }; +} + +function TimeUnit({ value, label, urgent }: { value: number; label: string; urgent: boolean }) { + return ( +
+ + {String(value).padStart(2, '0')} + + + {label} + +
+ ); +} + +export function CountdownTimer({ deadline, onComplete, className = '' }: CountdownTimerProps) { + const [timeLeft, setTimeLeft] = useState(getTimeLeft(deadline)); + + useEffect(() => { + const interval = setInterval(() => { + const newTime = getTimeLeft(deadline); + setTimeLeft(newTime); + + if (newTime.total <= 0) { + clearInterval(interval); + onComplete?.(); + } + }, 1000); + + return () => clearInterval(interval); + }, [deadline, onComplete]); + + const isUrgent = timeLeft.total > 0 && timeLeft.total < 24 * 60 * 60 * 1000; + const isExpired = timeLeft.total <= 0; + + if (isExpired) { + return ( +
+ + Expired +
+ ); + } + + return ( +
+ {timeLeft.days > 0 && } + + : + + : + +
+ ); +}