Skip to content

Commit b08f92a

Browse files
committed
Merge branch 'feature/tasks' into development
2 parents 4b9311b + 20b5111 commit b08f92a

12 files changed

Lines changed: 941 additions & 899 deletions

File tree

src/client/app/tasks/page.js

Lines changed: 138 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,7 @@ import Script from "next/script"
1818
// New component imports
1919
import TasksHeader from "@components/tasks/TasksHeader"
2020
import 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

2623
const 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-

src/client/components/LayoutWrapper.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,13 @@ export default function LayoutWrapper({ children }) {
8282
),
8383
{ duration: 10000 }
8484
)
85+
} else if (data.type === "task_progress_update") {
86+
// Dispatch a custom event that the tasks page can listen for
87+
window.dispatchEvent(
88+
new CustomEvent("taskProgressUpdate", {
89+
detail: data.payload
90+
})
91+
)
8592
}
8693
}
8794
ws.onclose = () => {

0 commit comments

Comments
 (0)