studio: humanize ETA display for long training runs (#4608)
* studio: humanize ETA display for long training runs When training takes hours or days, the ETA displayed raw minutes (e.g. '560m 50s'). This changes the format to: - Under 1 hour: Xm Ys (unchanged) - 1-24 hours: Xh Ym Zs - Over 24 hours: Xd Xh Xm * Fix formatDuration edge cases and consolidate duplicate for PR #4608 - Guard NaN/Infinity inputs with Number.isFinite() (matches formatNumber in same file) - Add sub-minute branch so 30s displays as "30s" instead of "0m 30s" - Accept undefined in type signature to match formatNumber pattern - Remove duplicate formatDuration from history-card-grid.tsx and import the shared one --------- Co-authored-by: Daniel Han <danielhanchen@gmail.com>
This commit is contained in:
parent
937da02f6c
commit
a6fe743ebe
2 changed files with 11 additions and 15 deletions
|
|
@ -14,6 +14,7 @@ import {
|
|||
import { Button } from "@/components/ui/button";
|
||||
import type { TrainingRunSummary } from "@/features/training";
|
||||
import { deleteTrainingRun, listTrainingRuns } from "@/features/training";
|
||||
import { formatDuration } from "@/features/studio/sections/progress-section-lib";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { Delete02Icon } from "@hugeicons/core-free-icons";
|
||||
import { HugeiconsIcon } from "@hugeicons/react";
|
||||
|
|
@ -129,16 +130,6 @@ function formatRelativeTime(isoDate: string): string {
|
|||
return `${days}d ago`;
|
||||
}
|
||||
|
||||
function formatDuration(seconds: number | null): string {
|
||||
if (seconds == null) return "--";
|
||||
const total = Math.floor(seconds);
|
||||
if (total < 60) return `${total}s`;
|
||||
const min = Math.floor(total / 60);
|
||||
const sec = total % 60;
|
||||
if (min < 60) return `${min}m ${sec}s`;
|
||||
const hrs = Math.floor(min / 60);
|
||||
return `${hrs}h ${min % 60}m`;
|
||||
}
|
||||
|
||||
interface HistoryCardGridProps {
|
||||
onSelectRun: (runId: string) => void;
|
||||
|
|
|
|||
|
|
@ -35,12 +35,17 @@ export const phaseColors: Record<TrainingPhase, string> = {
|
|||
stopped: "bg-muted text-muted-foreground",
|
||||
};
|
||||
|
||||
export function formatDuration(seconds: number | null): string {
|
||||
if (seconds == null || seconds < 0) return "--";
|
||||
export function formatDuration(seconds: number | null | undefined): string {
|
||||
if (seconds == null || !Number.isFinite(seconds) || seconds < 0) return "--";
|
||||
const total = Math.floor(seconds);
|
||||
const min = Math.floor(total / 60);
|
||||
const sec = total % 60;
|
||||
return `${min}m ${sec}s`;
|
||||
const d = Math.floor(total / 86400);
|
||||
const h = Math.floor((total % 86400) / 3600);
|
||||
const m = Math.floor((total % 3600) / 60);
|
||||
const s = total % 60;
|
||||
if (d > 0) return `${d}d ${h}h ${m}m`;
|
||||
if (h > 0) return `${h}h ${m}m ${s}s`;
|
||||
if (m > 0) return `${m}m ${s}s`;
|
||||
return `${s}s`;
|
||||
}
|
||||
|
||||
export function formatNumber(value: number | null | undefined, digits: number): string {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue