From a6d46ad9ae664e68620b0f9afb9f09a0c4f6919d Mon Sep 17 00:00:00 2001 From: Jaret Burkett Date: Sat, 22 Feb 2025 13:54:06 -0700 Subject: [PATCH] Cleanup of job page --- ui/src/app/jobs/new/page.tsx | 4 +- ui/src/components/GPUWidget.tsx | 1 - ui/src/components/JobOverview.tsx | 100 ++++++++++++++++++++++++++---- ui/src/hooks/useGPUInfo.tsx | 13 ++-- 4 files changed, 99 insertions(+), 19 deletions(-) diff --git a/ui/src/app/jobs/new/page.tsx b/ui/src/app/jobs/new/page.tsx index 0633f31e..527334ed 100644 --- a/ui/src/app/jobs/new/page.tsx +++ b/ui/src/app/jobs/new/page.tsx @@ -64,7 +64,7 @@ export default function TrainingForm() { useEffect(() => { if (isGPUInfoLoaded) { if (gpuIDs === null && gpuList.length > 0) { - setGpuIDs(`${gpuList[0]}`); + setGpuIDs(`${gpuList[0].index}`); } } }, [gpuList, isGPUInfoLoaded]); @@ -154,7 +154,7 @@ export default function TrainingForm() { value={`${gpuIDs}`} className="pt-2" onChange={value => setGpuIDs(value)} - options={gpuList.map(gpu => ({ value: `${gpu}`, label: `GPU #${gpu}` }))} + options={gpuList.map(gpu => ({ value: `${gpu.index}`, label: `GPU #${gpu.index}` }))} /> diff --git a/ui/src/components/GPUWidget.tsx b/ui/src/components/GPUWidget.tsx index e9f219b1..adb71ea7 100644 --- a/ui/src/components/GPUWidget.tsx +++ b/ui/src/components/GPUWidget.tsx @@ -28,7 +28,6 @@ export default function GPUWidget({ gpu }: GPUWidgetProps) { #{gpu.index} -
diff --git a/ui/src/components/JobOverview.tsx b/ui/src/components/JobOverview.tsx index 3df5152c..61c48b84 100644 --- a/ui/src/components/JobOverview.tsx +++ b/ui/src/components/JobOverview.tsx @@ -1,23 +1,99 @@ import { Job } from '@prisma/client'; +import useGPUInfo from '@/hooks/useGPUInfo'; +import GPUWidget from '@/components/GPUWidget'; +import { getJobConfig, getTotalSteps } from '@/utils/jobs'; +import { Cpu, HardDrive, Info } from 'lucide-react'; interface JobOverviewProps { job: Job; } export default function JobOverview({ job }: JobOverviewProps) { + const gpuIds = job.gpu_ids.split(',').map(id => parseInt(id)); + const { gpuList, isGPUInfoLoaded } = useGPUInfo(gpuIds); + const totalSteps = getTotalSteps(job); + const progress = (job.step / totalSteps) * 100; + const isStopping = job.stop && job.status === 'running'; + + const getStatusColor = (status: string) => { + switch (status.toLowerCase()) { + case 'running': return 'bg-emerald-500/10 text-emerald-500'; + case 'stopping': return 'bg-amber-500/10 text-amber-500'; + case 'stopped': return 'bg-gray-500/10 text-gray-400'; + case 'completed': return 'bg-blue-500/10 text-blue-500'; + case 'error': return 'bg-rose-500/10 text-rose-500'; + default: return 'bg-gray-500/10 text-gray-400'; + } + }; + + let status = job.status; + if (isStopping) { + status = 'stopping'; + } + return ( - <> -
-
-

Job Details

-

ID: {job.id}

-

Name: {job.name}

-

GPUs: {job.gpu_ids}

-

Status: {job.status}

-

Info: {job.info}

-

Step: {job.step}

+
+ {/* Job Information Panel */} +
+
+

Job Details

+ + {job.status} + +
+ +
+ {/* Progress Bar */} +
+
+ Progress + + Step {job.step} of {totalSteps} + +
+
+
+
+
+ + {/* Job Info Grid */} +
+
+ +
+

{job.name}

+

Job Name

+
+
+ +
+ +
+

GPUs: {job.gpu_ids}

+

Assigned GPUs

+
+
+ +
+ +
+

{job.info}

+

Additional Information

+
+
+
- + + {/* GPU Widget Panel */} +
+ {isGPUInfoLoaded && gpuList.length > 0 && ( + + )} +
+
); -} +} \ No newline at end of file diff --git a/ui/src/hooks/useGPUInfo.tsx b/ui/src/hooks/useGPUInfo.tsx index dd7e32fe..f8474701 100644 --- a/ui/src/hooks/useGPUInfo.tsx +++ b/ui/src/hooks/useGPUInfo.tsx @@ -1,10 +1,10 @@ 'use client'; -import { GPUApiResponse } from '@/types'; +import { GPUApiResponse, GpuInfo } from '@/types'; import { useEffect, useState } from 'react'; -export default function useGPUInfo() { - const [gpuList, setGpuList] = useState([]); +export default function useGPUInfo(gpuIds: null | number[] = null) { + const [gpuList, setGpuList] = useState([]); const [isGPUInfoLoaded, setIsLoaded] = useState(false); useEffect(() => { const fetchGpuInfo = async () => { @@ -16,7 +16,12 @@ export default function useGPUInfo() { } const data: GPUApiResponse = await response.json(); - setGpuList(data.gpus.map(gpu => gpu.index).sort()); + let gpus = data.gpus.sort((a, b) => a.index - b.index); + if (gpuIds) { + gpus = gpus.filter(gpu => gpuIds.includes(gpu.index)); + } + + setGpuList(gpus); } catch (err) { console.log(`Failed to fetch GPU data: ${err instanceof Error ? err.message : String(err)}`); } finally {