Files
edr-platform/apps/edr-freight-web/backoffice/src/components/bookings/detail/BookingPaymentCountdownCard.tsx
2026-06-16 09:20:31 +00:00

93 lines
2.7 KiB
TypeScript

import { useEffect, useState } from "react";
import { Group, Stack, Text } from "@mantine/core";
import { Timer } from "lucide-react";
import { SectionCard } from "./SectionCard";
export interface BookingPaymentCountdownCardProps {
/** ISO timestamp marking the end of the pay window. */
paymentDeadline: string;
}
interface Remaining {
days: number;
hours: number;
minutes: number;
seconds: number;
expired: boolean;
}
function getRemaining(deadlineMs: number): Remaining {
const diff = deadlineMs - Date.now();
if (diff <= 0) {
return { days: 0, hours: 0, minutes: 0, seconds: 0, expired: true };
}
const totalSeconds = Math.floor(diff / 1000);
return {
days: Math.floor(totalSeconds / 86400),
hours: Math.floor((totalSeconds % 86400) / 3600),
minutes: Math.floor((totalSeconds % 3600) / 60),
seconds: totalSeconds % 60,
expired: false,
};
}
function Segment({ value, label }: { value: number; label: string }) {
return (
<Stack gap={2} align="center" style={{ minWidth: 56 }}>
<Text fw={700} size="2rem" style={{ lineHeight: 1, fontVariantNumeric: "tabular-nums" }}>
{String(value).padStart(2, "0")}
</Text>
<Text size="xs" c="dimmed" tt="uppercase" lts="0.06em">
{label}
</Text>
</Stack>
);
}
/** Live countdown to the payment deadline. Ticks every second; shows an expired state past the deadline. */
export function BookingPaymentCountdownCard({ paymentDeadline }: BookingPaymentCountdownCardProps) {
const deadlineMs = new Date(paymentDeadline).getTime();
const [remaining, setRemaining] = useState<Remaining>(() => getRemaining(deadlineMs));
useEffect(() => {
setRemaining(getRemaining(deadlineMs));
const interval = setInterval(() => {
const next = getRemaining(deadlineMs);
setRemaining(next);
if (next.expired) {
clearInterval(interval);
}
}, 1000);
return () => clearInterval(interval);
}, [deadlineMs]);
const accent = remaining.expired ? "red" : "orange";
return (
<SectionCard
icon={Timer}
title="Payment Deadline"
subtitle={
remaining.expired
? "The pay window has closed"
: "Time remaining to complete payment"
}
accent={accent}
>
{remaining.expired ? (
<Text fw={600} c="red.7">
Expired
</Text>
) : (
<Group justify="center" gap="lg" wrap="nowrap">
<Segment value={remaining.days} label="Days" />
<Segment value={remaining.hours} label="Hours" />
<Segment value={remaining.minutes} label="Mins" />
<Segment value={remaining.seconds} label="Secs" />
</Group>
)}
</SectionCard>
);
}