web/app/components/elapsed-time.tsxblame
View source
a011f1e1"use client";
a011f1e2
a011f1e3import { useState, useEffect } from "react";
a011f1e4
a011f1e5function format(ms: number): string {
a011f1e6 if (ms < 1000) return "<1s";
a011f1e7 const s = Math.floor(ms / 1000);
a011f1e8 if (s < 60) return `${s}s`;
a011f1e9 const m = Math.floor(s / 60);
a011f1e10 const rem = s % 60;
a011f1e11 return rem > 0 ? `${m}m ${rem}s` : `${m}m`;
a011f1e12}
a011f1e13
a011f1e14function parseDate(dateStr: string): number {
a011f1e15 const normalized = dateStr.endsWith("Z") || dateStr.includes("+") || dateStr.includes("T")
a011f1e16 ? dateStr
a011f1e17 : dateStr + "Z";
a011f1e18 return new Date(normalized).getTime();
a011f1e19}
a011f1e20
a011f1e21export function ElapsedTime({ since }: { since: string }) {
9d879c022 const [elapsed, setElapsed] = useState<number | null>(null);
a011f1e23
a011f1e24 useEffect(() => {
9d879c025 const update = () => {
a011f1e26 setElapsed(Math.max(0, Date.now() - parseDate(since)));
9d879c027 };
9d879c028 update();
9d879c029 const interval = setInterval(() => {
9d879c030 update();
a011f1e31 }, 1000);
a011f1e32 return () => clearInterval(interval);
a011f1e33 }, [since]);
a011f1e34
9d879c035 if (elapsed === null) return <>--</>;
a011f1e36 return <>{format(elapsed)}</>;
a011f1e37}