You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

64 lines
1.9 KiB

"use client";
import { useCallback, useEffect, useRef, useState } from "react";
import { Clock } from "@phosphor-icons/react";
import { ConfirmDialog } from "@/components/ui/confirm-dialog";
interface CountdownTimerProps {
validUntil: string;
onExpire?: () => void;
}
export function CountdownTimer({ validUntil, onExpire }: CountdownTimerProps) {
const [remainMs, setRemainMs] = useState(
() => new Date(validUntil).getTime() - Date.now(),
);
const [showExpiredDialog, setShowExpiredDialog] = useState(false);
const expiredCalled = useRef(false);
useEffect(() => {
expiredCalled.current = false;
setShowExpiredDialog(false);
const id = setInterval(() => {
const left = new Date(validUntil).getTime() - Date.now();
setRemainMs(left);
if (left <= 0 && !expiredCalled.current) {
expiredCalled.current = true;
clearInterval(id);
setShowExpiredDialog(true);
onExpire?.();
}
}, 500);
return () => clearInterval(id);
}, [validUntil, onExpire]);
const safe = Math.max(0, remainMs);
const mm = String(Math.floor(safe / 60_000)).padStart(2, "0");
const ss = String(Math.floor((safe % 60_000) / 1_000)).padStart(2, "0");
const isWarning = safe < 30_000 && safe > 0;
return (
<>
<div
className={`inline-flex items-center gap-1.5 text-sm font-medium ${
isWarning ? "text-warning" : "text-text-secondary"
}`}
>
<Clock size={16} weight={isWarning ? "fill" : "regular"} />
<span className="num">
{mm}:{ss}
</span>
</div>
<ConfirmDialog
open={showExpiredDialog}
title="报价已过期"
message="当前报价已超过 3 分钟有效期,请重新询价获取最新价格。"
confirmLabel="重新询价"
cancelLabel="关闭"
onConfirm={() => setShowExpiredDialog(false)}
onCancel={() => setShowExpiredDialog(false)}
/>
</>
);
}