79 lines
2.2 KiB
TypeScript
79 lines
2.2 KiB
TypeScript
'use client'
|
|
|
|
import { useEffect, useState } from 'react'
|
|
import { Button } from '@/components/ui/button'
|
|
import { RefreshCw } from 'lucide-react'
|
|
import { cn } from '@/lib/utils'
|
|
|
|
interface RefreshCountdownProps {
|
|
onRefresh: () => void
|
|
intervalMs?: number
|
|
className?: string
|
|
}
|
|
|
|
export function RefreshCountdown({
|
|
onRefresh,
|
|
intervalMs = 5 * 60 * 1000, // 5 minutes default
|
|
className,
|
|
}: RefreshCountdownProps) {
|
|
const [secondsRemaining, setSecondsRemaining] = useState(intervalMs / 1000)
|
|
const [isRefreshing, setIsRefreshing] = useState(false)
|
|
|
|
useEffect(() => {
|
|
const interval = setInterval(() => {
|
|
setSecondsRemaining((prev) => {
|
|
if (prev <= 1) {
|
|
// Auto refresh
|
|
handleRefresh()
|
|
return intervalMs / 1000
|
|
}
|
|
return prev - 1
|
|
})
|
|
}, 1000)
|
|
|
|
return () => clearInterval(interval)
|
|
}, [intervalMs])
|
|
|
|
const handleRefresh = async () => {
|
|
setIsRefreshing(true)
|
|
await onRefresh()
|
|
setSecondsRemaining(intervalMs / 1000)
|
|
setIsRefreshing(false)
|
|
}
|
|
|
|
const formatTime = (seconds: number): string => {
|
|
const mins = Math.floor(seconds / 60)
|
|
const secs = Math.floor(seconds % 60)
|
|
return `${mins}:${secs.toString().padStart(2, '0')}`
|
|
}
|
|
|
|
const progressPercentage = ((intervalMs / 1000 - secondsRemaining) / (intervalMs / 1000)) * 100
|
|
|
|
return (
|
|
<div className={cn('flex items-center gap-4', className)}>
|
|
<div className="flex-1">
|
|
<div className="flex items-center justify-between mb-2">
|
|
<span className="text-sm font-medium">Next refresh</span>
|
|
<span className="text-sm text-muted-foreground">{formatTime(secondsRemaining)}</span>
|
|
</div>
|
|
<div className="h-2 w-full bg-secondary rounded-full overflow-hidden">
|
|
<div
|
|
className="h-full bg-primary transition-all duration-1000 ease-linear"
|
|
style={{ width: `${progressPercentage}%` }}
|
|
/>
|
|
</div>
|
|
</div>
|
|
<Button
|
|
onClick={handleRefresh}
|
|
disabled={isRefreshing}
|
|
size="icon"
|
|
variant="outline"
|
|
className="min-h-[44px] min-w-[44px]"
|
|
aria-label="Refresh now"
|
|
>
|
|
<RefreshCw className={cn('h-4 w-4', isRefreshing && 'animate-spin')} />
|
|
</Button>
|
|
</div>
|
|
)
|
|
}
|