@@ -18,10 +18,7 @@ import Script from "next/script"
1818// New component imports
1919import TasksHeader from "@components/tasks/TasksHeader"
2020import AllTasksView from "@components/tasks/AllTasksView"
21-
22- // Existing component imports (modals remain)
23- import TaskDetailsModal from "@components/tasks/TaskDetailsModal"
24- import EditTaskModal from "@components/tasks/EditTaskModal"
21+ import TaskDetailsPanel from "@components/tasks/TaskDetailsPanel"
2522
2623const StorylaneDemoModal = ( { onClose } ) => {
2724 return (
@@ -98,9 +95,8 @@ function TasksPageContent() {
9895 const [ allTools , setAllTools ] = useState ( [ ] )
9996 const [ isLoading , setIsLoading ] = useState ( true )
10097
101- // Modal States
102- const [ selectedTask , setSelectedTask ] = useState ( null ) // For viewing details
103- const [ editingTask , setEditingTask ] = useState ( null ) // For editing
98+ // Panel State
99+ const [ selectedTask , setSelectedTask ] = useState ( null )
104100 const [ isDemoModalOpen , setIsDemoModalOpen ] = useState ( false )
105101
106102 // View control states
@@ -120,7 +116,7 @@ function TasksPageContent() {
120116
121117 useEffect ( ( ) => {
122118 const taskIdParam = searchParams . get ( "taskId" )
123- if ( taskIdParam && ! selectedTask && ! editingTask ) {
119+ if ( taskIdParam && ! selectedTask ) {
124120 const taskInList = tasks . find ( ( t ) => t . task_id === taskIdParam )
125121 if ( taskInList ) {
126122 setSelectedTask ( taskInList )
@@ -138,7 +134,7 @@ function TasksPageContent() {
138134 }
139135 router . replace ( "/tasks" , { scroll : false } )
140136 }
141- } , [ searchParams , tasks , isLoading , router , selectedTask , editingTask ] )
137+ } , [ searchParams , tasks , isLoading , router , selectedTask ] )
142138
143139 const fetchTasks = useCallback ( async ( ) => {
144140 setIsLoading ( true )
@@ -170,6 +166,56 @@ function TasksPageContent() {
170166 // eslint-disable-next-line react-hooks/exhaustive-deps
171167 } , [ ] )
172168
169+ // Listen for real-time progress updates from WebSocket
170+ useEffect ( ( ) => {
171+ const handleProgressUpdate = ( event ) => {
172+ const { task_id, run_id, update } = event . detail
173+
174+ const updateTaskState = ( task ) => {
175+ if ( ! task ) return null
176+ // Ensure runs is an array
177+ const runs = Array . isArray ( task . runs ) ? task . runs : [ ]
178+ const newRuns = runs . map ( ( run ) => {
179+ if ( run . run_id === run_id ) {
180+ // Ensure progress_updates is an array
181+ const progressUpdates = Array . isArray (
182+ run . progress_updates
183+ )
184+ ? run . progress_updates
185+ : [ ]
186+ return {
187+ ...run ,
188+ progress_updates : [ ...progressUpdates , update ]
189+ }
190+ }
191+ return run
192+ } )
193+ return { ...task , runs : newRuns }
194+ }
195+
196+ setTasks ( ( prevTasks ) =>
197+ prevTasks . map ( ( task ) =>
198+ task . task_id === task_id ? updateTaskState ( task ) : task
199+ )
200+ )
201+
202+ setSelectedTask ( ( prevSelectedTask ) =>
203+ prevSelectedTask ?. task_id === task_id
204+ ? updateTaskState ( prevSelectedTask )
205+ : prevSelectedTask
206+ )
207+ }
208+
209+ window . addEventListener ( "taskProgressUpdate" , handleProgressUpdate )
210+
211+ return ( ) => {
212+ window . removeEventListener (
213+ "taskProgressUpdate" ,
214+ handleProgressUpdate
215+ )
216+ }
217+ } , [ ] ) // Empty dependency array means this runs once on mount
218+
173219 const handleAddTask = useCallback ( ( ) => {
174220 if ( groupBy !== "status" ) {
175221 toast . error ( "Please group by status to add a new task." , {
@@ -180,12 +226,15 @@ function TasksPageContent() {
180226 setIsAddingNewTask ( true )
181227 } , [ groupBy ] )
182228
183- const handleTaskAdded = useCallback ( ( isSuccess ) => {
184- setIsAddingNewTask ( false )
185- if ( isSuccess ) {
186- fetchTasks ( )
187- }
188- } , [ fetchTasks ] )
229+ const handleTaskAdded = useCallback (
230+ ( isSuccess ) => {
231+ setIsAddingNewTask ( false )
232+ if ( isSuccess ) {
233+ fetchTasks ( )
234+ }
235+ } ,
236+ [ fetchTasks ]
237+ )
189238
190239 const handleAction = useCallback (
191240 async ( actionFn , successMessage , ...args ) => {
@@ -306,7 +355,7 @@ function TasksPageContent() {
306355 }
307356
308357 const handleUpdateTask = async ( updatedTask ) => {
309- handleAction (
358+ await handleAction (
310359 ( ) =>
311360 fetch ( "/api/tasks/update" , {
312361 method : "POST" ,
@@ -318,6 +367,16 @@ function TasksPageContent() {
318367 } ) ,
319368 "Task updated!"
320369 )
370+ // After saving, update the selected task to show the new data
371+ if ( selectedTask && selectedTask . task_id === updatedTask . task_id ) {
372+ const refreshedTasks = await fetch ( "/api/tasks" ) . then ( ( res ) =>
373+ res . json ( )
374+ )
375+ const newlyUpdatedTask = refreshedTasks . tasks . find (
376+ ( t ) => t . task_id === updatedTask . task_id
377+ )
378+ setSelectedTask ( newlyUpdatedTask || null )
379+ }
321380 }
322381
323382 const workflowTasks = useMemo (
@@ -340,75 +399,77 @@ function TasksPageContent() {
340399 style = { { zIndex : 9999 } }
341400 />
342401
343- < main className = "flex-1 flex flex-col overflow-hidden" >
344- < TasksHeader
345- onOpenDemo = { ( ) => setIsDemoModalOpen ( true ) }
346- activeTab = { activeTab }
347- onTabChange = { setActiveTab }
348- groupBy = { groupBy }
349- onGroupChange = { setGroupBy }
402+ < div className = "flex flex-1 overflow-hidden" >
403+ < main className = "flex-1 flex flex-col overflow-hidden" >
404+ < TasksHeader
405+ onOpenDemo = { ( ) => setIsDemoModalOpen ( true ) }
406+ activeTab = { activeTab }
407+ onTabChange = { setActiveTab }
408+ groupBy = { groupBy }
409+ onGroupChange = { setGroupBy }
410+ />
411+ { isLoading ? (
412+ < div className = "flex justify-center items-center flex-1" >
413+ < IconLoader className = "w-8 h-8 animate-spin text-[var(--color-accent-blue)]" />
414+ </ div >
415+ ) : (
416+ < div className = "flex-1 overflow-hidden" >
417+ < AllTasksView
418+ tasks = { [ ...oneOffTasks , ...workflowTasks ] }
419+ onViewDetails = { setSelectedTask }
420+ onMarkComplete = { handleMarkComplete }
421+ onAssigneeChange = { handleAssigneeChange }
422+ activeTab = { activeTab }
423+ groupBy = { groupBy }
424+ onTabChange = { setActiveTab }
425+ onGroupChange = { setGroupBy }
426+ isAddingNewTask = { isAddingNewTask }
427+ onAddTask = { handleAddTask }
428+ onTaskAdded = { handleTaskAdded }
429+ />
430+ </ div >
431+ ) }
432+ </ main >
433+
434+ < TaskDetailsPanel
435+ task = { selectedTask }
436+ allTools = { allTools }
437+ integrations = { integrations }
438+ onClose = { ( ) => setSelectedTask ( null ) }
439+ onSave = { handleUpdateTask }
440+ onApprove = { ( taskId ) => {
441+ handleApproveTask ( taskId )
442+ setSelectedTask ( null )
443+ } }
444+ onDelete = { ( taskId ) => {
445+ handleDeleteTask ( taskId )
446+ setSelectedTask ( null )
447+ } }
448+ onRerun = { ( taskId ) => {
449+ handleRerunTask ( taskId )
450+ setSelectedTask ( null )
451+ } }
452+ onAnswerClarifications = { ( taskId , answers ) => {
453+ handleAnswerClarifications ( taskId , answers )
454+ setSelectedTask ( null )
455+ } }
456+ onArchiveTask = { ( taskId ) => {
457+ handleArchiveTask ( taskId )
458+ setSelectedTask ( null )
459+ } }
460+ onMarkComplete = { ( taskId ) => {
461+ handleMarkComplete ( taskId )
462+ setSelectedTask ( null )
463+ } }
350464 />
351- { isLoading ? (
352- < div className = "flex justify-center items-center flex-1" >
353- < IconLoader className = "w-8 h-8 animate-spin text-[var(--color-accent-blue)]" />
354- </ div >
355- ) : (
356- < div className = "flex-1 overflow-hidden" >
357- < AllTasksView
358- tasks = { [ ...oneOffTasks , ...workflowTasks ] }
359- onViewDetails = { setSelectedTask }
360- onEditTask = { setEditingTask }
361- onDeleteTask = { handleDeleteTask }
362- onRerunTask = { handleRerunTask }
363- onMarkComplete = { handleMarkComplete }
364- onAssigneeChange = { handleAssigneeChange }
365- activeTab = { activeTab }
366- groupBy = { groupBy }
367- onTabChange = { setActiveTab }
368- onGroupChange = { setGroupBy }
369- isAddingNewTask = { isAddingNewTask }
370- onAddTask = { handleAddTask }
371- onTaskAdded = { handleTaskAdded }
372- />
373- </ div >
374- ) }
375- </ main >
465+ </ div >
376466
377467 < AnimatePresence >
378468 { isDemoModalOpen && (
379469 < StorylaneDemoModal
380470 onClose = { ( ) => setIsDemoModalOpen ( false ) }
381471 />
382472 ) }
383- { selectedTask && (
384- < TaskDetailsModal
385- task = { selectedTask }
386- onClose = { ( ) => setSelectedTask ( null ) }
387- onEdit = { ( taskToEdit ) => {
388- setEditingTask ( taskToEdit )
389- setSelectedTask ( null )
390- } }
391- onApprove = { handleApproveTask }
392- onDelete = { ( taskId ) => handleDeleteTask ( taskId ) }
393- integrations = { integrations }
394- onAnswerClarifications = { handleAnswerClarifications }
395- onArchiveTask = { handleArchiveTask }
396- onMarkComplete = { handleMarkComplete }
397- onUpdateTask = { handleUpdateTask }
398- />
399- ) }
400- { editingTask && (
401- < EditTaskModal
402- task = { editingTask }
403- onClose = { ( ) => setEditingTask ( null ) }
404- onSave = { ( updatedTask ) => {
405- handleUpdateTask ( updatedTask )
406- setEditingTask ( null )
407- } }
408- integrations = { integrations }
409- allTools = { allTools }
410- />
411- ) }
412473 </ AnimatePresence >
413474 </ div >
414475 )
@@ -427,4 +488,3 @@ export default function TasksPage() {
427488 </ Suspense >
428489 )
429490}
430-
0 commit comments