diff --git a/ui/src/components/JobOverview.tsx b/ui/src/components/JobOverview.tsx
index 61c48b84..92502491 100644
--- a/ui/src/components/JobOverview.tsx
+++ b/ui/src/components/JobOverview.tsx
@@ -3,26 +3,34 @@ import useGPUInfo from '@/hooks/useGPUInfo';
import GPUWidget from '@/components/GPUWidget';
import { getJobConfig, getTotalSteps } from '@/utils/jobs';
import { Cpu, HardDrive, Info } from 'lucide-react';
+import { useMemo } from '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 gpuIds = useMemo(() => job.gpu_ids.split(',').map(id => parseInt(id)), [job.gpu_ids]);
+
+ const { gpuList, isGPUInfoLoaded } = useGPUInfo(gpuIds, 5000);
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';
+ 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';
}
};
@@ -37,9 +45,7 @@ export default function JobOverview({ job }: JobOverviewProps) {
Job Details
-
- {job.status}
-
+ {job.status}
@@ -52,10 +58,7 @@ export default function JobOverview({ job }: JobOverviewProps) {
@@ -89,11 +92,7 @@ export default function JobOverview({ job }: JobOverviewProps) {
{/* GPU Widget Panel */}
-
- {isGPUInfoLoaded && gpuList.length > 0 && (
-
- )}
-
+ {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 f8474701..5f2eda38 100644
--- a/ui/src/hooks/useGPUInfo.tsx
+++ b/ui/src/hooks/useGPUInfo.tsx
@@ -3,35 +3,52 @@
import { GPUApiResponse, GpuInfo } from '@/types';
import { useEffect, useState } from 'react';
-export default function useGPUInfo(gpuIds: null | number[] = null) {
+export default function useGPUInfo(gpuIds: null | number[] = null, reloadInterval: null | number = null) {
const [gpuList, setGpuList] = useState([]);
const [isGPUInfoLoaded, setIsLoaded] = useState(false);
- useEffect(() => {
- const fetchGpuInfo = async () => {
- try {
- const response = await fetch('/api/gpu');
+ const [status, setStatus] = useState<'idle' | 'loading' | 'success' | 'error'>('idle');
- if (!response.ok) {
- throw new Error(`HTTP error! Status: ${response.status}`);
- }
+ const fetchGpuInfo = async () => {
+ setStatus('loading');
+ try {
+ const response = await fetch('/api/gpu');
- const data: GPUApiResponse = await response.json();
- 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 {
- setIsLoaded(true);
+ if (!response.ok) {
+ throw new Error(`HTTP error! Status: ${response.status}`);
}
- };
+ const data: GPUApiResponse = await response.json();
+ let gpus = data.gpus.sort((a, b) => a.index - b.index);
+ if (gpuIds) {
+ gpus = gpus.filter(gpu => gpuIds.includes(gpu.index));
+ }
+
+ setGpuList(gpus);
+ setStatus('success');
+ } catch (err) {
+ console.error(`Failed to fetch GPU data: ${err instanceof Error ? err.message : String(err)}`);
+ setStatus('error');
+ } finally {
+ setIsLoaded(true);
+ }
+ };
+
+ useEffect(() => {
// Fetch immediately on component mount
fetchGpuInfo();
- }, []);
- return { gpuList, setGpuList, isGPUInfoLoaded };
-}
+ // Set up interval if specified
+ if (reloadInterval) {
+ const interval = setInterval(() => {
+ fetchGpuInfo();
+ }, reloadInterval);
+
+ // Cleanup interval on unmount
+ return () => {
+ clearInterval(interval);
+ };
+ }
+ }, [gpuIds, reloadInterval]); // Added dependencies
+
+ return { gpuList, setGpuList, isGPUInfoLoaded, status, refreshGpuInfo: fetchGpuInfo };
+}
\ No newline at end of file