1- import  React ,  {  useState ,  useEffect ,   useCallback  }  from  'react' ; 
1+ import  React ,  {  useState ,  useCallback  }  from  'react' ; 
22import  Sheet  from  '@mui/joy/Sheet' ; 
33
44import  {  Button ,  LinearProgress ,  Stack ,  Typography  }  from  '@mui/joy' ; 
55
66import  dayjs  from  'dayjs' ; 
77import  relativeTime  from  'dayjs/plugin/relativeTime' ; 
88import  {  PlusIcon  }  from  'lucide-react' ; 
9+ import  useSWR  from  'swr' ; 
910
1011import  *  as  chatAPI  from  'renderer/lib/transformerlab-api-sdk' ; 
1112import  {  useExperimentInfo  }  from  'renderer/lib/ExperimentInfoContext' ; 
13+ import  {  fetcher  }  from  'renderer/lib/transformerlab-api-sdk' ; 
1214import  TaskTemplateList  from  './TaskTemplateList' ; 
1315import  JobsList  from  './JobsList' ; 
1416import  NewTaskModal  from  './NewTaskModal' ; 
17+ import  ViewOutputModalStreaming  from  './ViewOutputModalStreaming' ; 
1518
1619const  duration  =  require ( 'dayjs/plugin/duration' ) ; 
1720
@@ -21,67 +24,54 @@ dayjs.extend(relativeTime);
2124export  default  function  Tasks ( )  { 
2225  const  [ modalOpen ,  setModalOpen ]  =  useState ( false ) ; 
2326  const  [ isSubmitting ,  setIsSubmitting ]  =  useState ( false ) ; 
24-   const  [ tasks ,  setTasks ]  =  useState ( [ ] ) ; 
25-   const  [ jobs ,  setJobs ]  =  useState ( [ ] ) ; 
26-   const  [ loading ,  setLoading ]  =  useState ( false ) ; 
27+   const  [ viewOutputFromJob ,  setViewOutputFromJob ]  =  useState ( - 1 ) ; 
2728  const  [ currentTensorboardForModal ,  setCurrentTensorboardForModal ]  = 
2829    useState ( - 1 ) ; 
29-   const  [ viewOutputFromJob ,  setViewOutputFromJob ]  =  useState ( - 1 ) ; 
30-   const  [ viewOutputFromSweepJob ,  setViewOutputFromSweepJob ]  =  useState ( false ) ; 
31-   const  [ viewEvalImagesFromJob ,  setViewEvalImagesFromJob ]  =  useState ( - 1 ) ; 
3230  const  [ viewCheckpointsFromJob ,  setViewCheckpointsFromJob ]  =  useState ( - 1 ) ; 
31+   const  [ viewEvalImagesFromJob ,  setViewEvalImagesFromJob ]  =  useState ( - 1 ) ; 
32+   const  [ viewOutputFromSweepJob ,  setViewOutputFromSweepJob ]  =  useState ( false ) ; 
3333  const  {  experimentInfo }  =  useExperimentInfo ( ) ; 
3434
3535  const  handleOpen  =  ( )  =>  setModalOpen ( true ) ; 
3636  const  handleClose  =  ( )  =>  setModalOpen ( false ) ; 
3737
38-   const  fetchTasks  =  async  ( )  =>  { 
39-     if  ( ! experimentInfo ?. id )  return ; 
40- 
41-     try  { 
42-       const  response  =  await  chatAPI . authenticatedFetch ( 
43-         chatAPI . Endpoints . Tasks . List ( ) , 
44-       ) ; 
45-       const  data  =  await  response . json ( ) ; 
46- 
47-       // Filter for remote tasks in this experiment only 
48-       const  remoteTasks  =  data . filter ( 
49-         ( task : any )  => 
50-           task . remote_task  ===  true  &&  task . experiment_id  ===  experimentInfo . id , 
51-       ) ; 
52-       setTasks ( remoteTasks ) ; 
53-     }  catch  ( error )  { 
54-       // eslint-disable-next-line no-console 
55-       console . error ( 'Error fetching tasks:' ,  error ) ; 
56-     } 
57-   } ; 
58- 
59-   const  fetchJobs  =  async  ( )  =>  { 
60-     if  ( ! experimentInfo ?. id )  return ; 
38+   // Fetch tasks with useSWR 
39+   const  { 
40+     data : allTasks , 
41+     error : tasksError , 
42+     isLoading : tasksIsLoading , 
43+     mutate : tasksMutate , 
44+   }  =  useSWR ( 
45+     experimentInfo ?. id  ? chatAPI . Endpoints . Tasks . List ( )  : null , 
46+     fetcher , 
47+   ) ; 
6148
62-     try  { 
63-       const  response  =  await  chatAPI . authenticatedFetch ( 
64-         chatAPI . Endpoints . Jobs . GetJobsOfType ( experimentInfo . id ,  'REMOTE' ,  '' ) , 
65-       ) ; 
66-       const  data  =  await  response . json ( ) ; 
67-       setJobs ( data ) ; 
68-     }  catch  ( error )  { 
69-       // eslint-disable-next-line no-console 
70-       console . error ( 'Error fetching jobs:' ,  error ) ; 
71-     } 
72-   } ; 
49+   // Filter tasks for remote tasks in this experiment only 
50+   const  tasks  = 
51+     allTasks ?. filter ( 
52+       ( task : any )  => 
53+         task . remote_task  ===  true  &&  task . experiment_id  ===  experimentInfo ?. id , 
54+     )  ||  [ ] ; 
7355
74-   const  fetchData  =  useCallback ( async  ( )  =>  { 
75-     setLoading ( true ) ; 
76-     await  Promise . all ( [ fetchTasks ( ) ,  fetchJobs ( ) ] ) ; 
77-     setLoading ( false ) ; 
78-   } ,  [ experimentInfo ?. id ] ) ; 
56+   // Fetch jobs with automatic polling 
57+   const  { 
58+     data : jobs , 
59+     error : jobsError , 
60+     isLoading : jobsIsLoading , 
61+     mutate : jobsMutate , 
62+   }  =  useSWR ( 
63+     experimentInfo ?. id 
64+       ? chatAPI . Endpoints . Jobs . GetJobsOfType ( experimentInfo . id ,  'REMOTE' ,  '' ) 
65+       : null , 
66+     fetcher , 
67+     { 
68+       refreshInterval : 3000 ,  // Poll every 3 seconds for job status updates 
69+       revalidateOnFocus : false ,  // Don't refetch when window regains focus 
70+       revalidateOnReconnect : true ,  // Refetch when network reconnects 
71+     } , 
72+   ) ; 
7973
80-   useEffect ( ( )  =>  { 
81-     if  ( experimentInfo ?. id )  { 
82-       fetchData ( ) ; 
83-     } 
84-   } ,  [ experimentInfo ?. id ,  fetchData ] ) ; 
74+   const  loading  =  tasksIsLoading  ||  jobsIsLoading ; 
8575
8676  const  handleDeleteTask  =  async  ( taskId : string )  =>  { 
8777    if  ( ! experimentInfo ?. id )  return ; 
@@ -103,7 +93,7 @@ export default function Tasks() {
10393        // eslint-disable-next-line no-alert 
10494        alert ( 'Task deleted successfully!' ) ; 
10595        // Refresh the data to remove the deleted task 
106-         await  fetchData ( ) ; 
96+         await  tasksMutate ( ) ; 
10797      }  else  { 
10898        // eslint-disable-next-line no-alert 
10999        alert ( 'Failed to delete task. Please try again.' ) ; 
@@ -136,7 +126,7 @@ export default function Tasks() {
136126        // eslint-disable-next-line no-alert 
137127        alert ( 'Job deleted successfully!' ) ; 
138128        // Refresh the data to remove the deleted job 
139-         await  fetchData ( ) ; 
129+         await  jobsMutate ( ) ; 
140130      }  else  { 
141131        // eslint-disable-next-line no-alert 
142132        alert ( 'Failed to delete job. Please try again.' ) ; 
@@ -189,7 +179,7 @@ export default function Tasks() {
189179        alert ( 'Task launched successfully!' ) ; 
190180        setModalOpen ( false ) ; 
191181        // Refresh the data to show the new task and job 
192-         await  fetchData ( ) ; 
182+         await  Promise . all ( [ tasksMutate ( ) ,   jobsMutate ( ) ] ) ; 
193183      }  else  { 
194184        // eslint-disable-next-line no-alert 
195185        alert ( `Error: ${ result . message }  ) ; 
@@ -272,6 +262,10 @@ export default function Tasks() {
272262          /> 
273263        ) } 
274264      </ Sheet > 
265+       < ViewOutputModalStreaming 
266+         jobId = { viewOutputFromJob } 
267+         setJobId = { ( jobId : number )  =>  setViewOutputFromJob ( jobId ) } 
268+       /> 
275269    </ Sheet > 
276270  ) ; 
277271} 
0 commit comments