diff --git a/frontend/components/gmm-home/gmm-jobs-list/hooks/__tests__/use-job-card.test.ts b/frontend/components/gmm-home/gmm-jobs-list/hooks/__tests__/use-job-card.test.ts index 7f977d8b..a829b039 100644 --- a/frontend/components/gmm-home/gmm-jobs-list/hooks/__tests__/use-job-card.test.ts +++ b/frontend/components/gmm-home/gmm-jobs-list/hooks/__tests__/use-job-card.test.ts @@ -9,7 +9,7 @@ jest.mock("next/router", () => ({ describe("useJobCard", () => { const mockUuid = "test-uuid"; - const mockDuration = 3600; // 1 hour in seconds + const mockDuration = 3_600_000; // 1 hour in milliseconds const mockRouter = { query: { experiment: "current-uuid", @@ -23,17 +23,13 @@ describe("useJobCard", () => { }); it("should return the current UUID from router query", () => { - const { result } = renderHook(() => - useJobCard({ uuid: mockUuid, duration: mockDuration }) - ); + const { result } = renderHook(() => useJobCard({ uuid: mockUuid })); expect(result.current.currentUUID).toBe("current-uuid"); }); it("should navigate to the job details when handleClick is called", () => { - const { result } = renderHook(() => - useJobCard({ uuid: mockUuid, duration: mockDuration }) - ); + const { result } = renderHook(() => useJobCard({ uuid: mockUuid })); act(() => { result.current.handleClick(); @@ -49,9 +45,7 @@ describe("useJobCard", () => { }); it("should return different styles based on selection state", () => { - const { result } = renderHook(() => - useJobCard({ uuid: mockUuid, duration: mockDuration }) - ); + const { result } = renderHook(() => useJobCard({ uuid: mockUuid })); const selectedStyle = result.current.getCardStyle(true); const unselectedStyle = result.current.getCardStyle(false); @@ -61,22 +55,21 @@ describe("useJobCard", () => { }); it("should format job duration correctly", () => { - const { result } = renderHook(() => - useJobCard({ uuid: mockUuid, duration: mockDuration }) - ); + const { result } = renderHook(() => useJobCard({ uuid: mockUuid })); - const formattedDuration = result.current.formatJobDuration(mockDuration); + const formattedDuration = result.current.formatDurationText( + "progress", + mockDuration + ); // The exact format might vary based on date-fns version and locale, // so we'll just check that it contains the expected parts expect(formattedDuration).toContain("Running for"); - expect(formattedDuration).toContain("hour"); + expect(formattedDuration).toContain("1h"); }); it("should correctly identify progress and suspend statuses", () => { - const { result } = renderHook(() => - useJobCard({ uuid: mockUuid, duration: mockDuration }) - ); + const { result } = renderHook(() => useJobCard({ uuid: mockUuid })); expect(result.current.isProgressOrSuspend("progress")).toBe(true); expect(result.current.isProgressOrSuspend("suspend")).toBe(true); @@ -88,23 +81,32 @@ describe("useJobCard", () => { it("should handle different durations correctly", () => { // Test with 30 minutes const { result: result1 } = renderHook(() => - useJobCard({ uuid: mockUuid, duration: 1800 }) + useJobCard({ uuid: mockUuid }) + ); + const formattedDuration1 = result1.current.formatDurationText( + "progress", + 1800 ); - const formattedDuration1 = result1.current.formatJobDuration(1800); expect(formattedDuration1).toContain("Running for"); // Test with 2 days const { result: result2 } = renderHook(() => - useJobCard({ uuid: mockUuid, duration: 172800 }) + useJobCard({ uuid: mockUuid }) + ); + const formattedDuration2 = result2.current.formatDurationText( + "progress", + 172800 ); - const formattedDuration2 = result2.current.formatJobDuration(172800); expect(formattedDuration2).toContain("Running for"); // Test with 0 seconds const { result: result3 } = renderHook(() => - useJobCard({ uuid: mockUuid, duration: 0 }) + useJobCard({ uuid: mockUuid }) + ); + const formattedDuration3 = result3.current.formatDurationText( + "progress", + 0 ); - const formattedDuration3 = result3.current.formatJobDuration(0); - expect(formattedDuration3).toContain("Running for"); + expect(formattedDuration3).toBe(""); }); }); diff --git a/frontend/components/gmm-home/gmm-jobs-list/hooks/use-job-card.ts b/frontend/components/gmm-home/gmm-jobs-list/hooks/use-job-card.ts index 54c78e2a..f51dddbb 100644 --- a/frontend/components/gmm-home/gmm-jobs-list/hooks/use-job-card.ts +++ b/frontend/components/gmm-home/gmm-jobs-list/hooks/use-job-card.ts @@ -10,24 +10,20 @@ export type JobStatus = export interface UseJobCardProps { uuid: string; - duration: number; } export interface UseJobCardReturn { currentUUID: string | string[] | undefined; handleClick: () => void; getCardStyle: (isSelected: boolean) => React.CSSProperties; - formatJobDuration: (duration: number) => string; + formatDurationText: (status: JobStatus, duration?: number) => string; isProgressOrSuspend: (status: JobStatus) => boolean; } /** * Custom hook for managing JobCard state and interactions */ -export function useJobCard({ - uuid, - duration, -}: UseJobCardProps): UseJobCardReturn { +export function useJobCard({ uuid }: UseJobCardProps): UseJobCardReturn { const router = useRouter(); const currentUUID = router.query.experiment; @@ -60,11 +56,37 @@ export function useJobCard({ /** * Format duration text */ - const formatJobDuration = (duration: number): string => { - return ( - "Running for " + - formatDuration(intervalToDuration({ start: 0, end: duration * 1000 })) - ); + const formatDurationText = (status: JobStatus, duration?: number): string => { + if (status !== "progress" || !duration) { + return ""; + } + + const durationObj = intervalToDuration({ + start: 0, + end: duration, + }); + + // Convert days to hours and add to existing hours + const totalHours = (durationObj.days || 0) * 24 + (durationObj.hours || 0); + + // Create abbreviated format: "xxh xxm xxs" + const parts: string[] = []; + + if (totalHours > 0) { + parts.push(`${totalHours}h`); + } + + if (durationObj.minutes) { + parts.push(`${durationObj.minutes}m`); + } + + if (durationObj.seconds) { + parts.push(`${durationObj.seconds}s`); + } + + const str = parts.join(" "); + + return `Running for ${str}`; }; /** @@ -78,7 +100,7 @@ export function useJobCard({ currentUUID, handleClick, getCardStyle, - formatJobDuration, + formatDurationText, isProgressOrSuspend, }; } diff --git a/frontend/components/gmm-home/gmm-jobs-list/job-card.tsx b/frontend/components/gmm-home/gmm-jobs-list/job-card.tsx index 42a2ea38..c5f81a2c 100644 --- a/frontend/components/gmm-home/gmm-jobs-list/job-card.tsx +++ b/frontend/components/gmm-home/gmm-jobs-list/job-card.tsx @@ -21,11 +21,10 @@ const JobCard: React.FC = (props) => { currentUUID, handleClick, getCardStyle, - formatJobDuration, + formatDurationText, isProgressOrSuspend, } = useJobCard({ uuid, - duration, }); /** @@ -37,7 +36,9 @@ const JobCard: React.FC = (props) => { {name}
{status === "progress" && ( - {formatJobDuration(duration)} + + {formatDurationText(status, duration)} + )} {status === "success" && ( diff --git a/frontend/components/trainer-home/vae-jobs-list/child-job-card.tsx b/frontend/components/trainer-home/vae-jobs-list/child-job-card.tsx index e4b7030d..bb87ebdd 100644 --- a/frontend/components/trainer-home/vae-jobs-list/child-job-card.tsx +++ b/frontend/components/trainer-home/vae-jobs-list/child-job-card.tsx @@ -32,9 +32,6 @@ const ChildJobCard: React.FC = (props) => { : undefined; const { formatDurationText, handleClick, getCardStyle } = useChildJobCard({ - status, - duration, - isSelected, onClick: props.onClick, }); diff --git a/frontend/components/trainer-home/vae-jobs-list/hooks/use-child-job-card.ts b/frontend/components/trainer-home/vae-jobs-list/hooks/use-child-job-card.ts index af769fad..4275b0c7 100644 --- a/frontend/components/trainer-home/vae-jobs-list/hooks/use-child-job-card.ts +++ b/frontend/components/trainer-home/vae-jobs-list/hooks/use-child-job-card.ts @@ -1,10 +1,7 @@ -import { formatDuration, intervalToDuration } from "date-fns"; +import { intervalToDuration } from "date-fns"; import { JobStatus } from "./use-job-card"; export interface UseChildJobCardProps { - status: JobStatus; - duration?: number; - isSelected?: boolean; onClick?: (event: React.MouseEvent) => void; } @@ -18,9 +15,6 @@ export interface UseChildJobCardReturn { * Custom hook for managing ChildJobCard state and interactions */ export function useChildJobCard({ - status, - duration, - isSelected, onClick, }: UseChildJobCardProps): UseChildJobCardReturn { /** @@ -36,7 +30,27 @@ export function useChildJobCard({ end: duration, }); - return `Running for ${formatDuration(durationObj)}`; + // Convert days to hours and add to existing hours + const totalHours = (durationObj.days || 0) * 24 + (durationObj.hours || 0); + + // Create abbreviated format: "xxh xxm xxs" + const parts: string[] = []; + + if (totalHours > 0) { + parts.push(`${totalHours}h`); + } + + if (durationObj.minutes) { + parts.push(`${durationObj.minutes}m`); + } + + if (durationObj.seconds) { + parts.push(`${durationObj.seconds}s`); + } + + const str = parts.join(" "); + + return `Running for ${str}`; }; /** diff --git a/frontend/components/viewer/model-picker/vae-picker.tsx b/frontend/components/viewer/model-picker/vae-picker.tsx index 97701051..5955b599 100644 --- a/frontend/components/viewer/model-picker/vae-picker.tsx +++ b/frontend/components/viewer/model-picker/vae-picker.tsx @@ -127,7 +127,7 @@ const VAEPicker: React.FC<{ { return (
-

Experiment: {jobItem.name}

+

{jobItem.name}