@@ -17,16 +17,13 @@ import {
1717 IconAlertCircle ,
1818 IconFilter ,
1919 IconChevronUp ,
20- IconPlus ,
2120 IconGripVertical ,
2221 IconBrain ,
2322 IconBook ,
2423 IconMail ,
2524 IconCalendarEvent ,
2625 IconMessage ,
27- IconArrowRight ,
2826 IconPlugConnected ,
29- IconChevronDown ,
3027 IconChecklist ,
3128 IconHelpCircle as HelpIcon
3229} from "@tabler/icons-react"
@@ -112,32 +109,14 @@ const Tasks = () => {
112109 const [ error , setError ] = useState ( null )
113110 const [ editingTask , setEditingTask ] = useState ( null )
114111 const [ filterStatus , setFilterStatus ] = useState ( "all" )
115- const [ isGeneratingPlan , setIsGeneratingPlan ] = useState ( false )
116112 const [ searchTerm , setSearchTerm ] = useState ( "" )
117113 const [ viewingTask , setViewingTask ] = useState ( null )
118- const [ isCreatePlanOpen , setCreatePlanOpen ] = useState ( false )
119- const [ createStep , setCreateStep ] = useState ( "generate" ) // 'generate' or 'review'
120114 const [ openSections , setOpenSections ] = useState ( {
121- // By default, only show sections that are likely to need action
122- // The user can expand the others if needed.
123- // This improves the initial view by reducing clutter.
124115 active : false , // Recurring tasks are less frequently managed
125116 approval_pending : true ,
126117 processing : true ,
127118 completed : true
128119 } )
129- const [ isAdding , setIsAdding ] = useState ( false )
130- const [ generationPrompt , setGenerationPrompt ] = useState ( "" )
131- const [ newTaskDescription , setNewTaskDescription ] = useState ( "" )
132- const [ newTaskPriority , setNewTaskPriority ] = useState ( 1 )
133- const [ newTaskPlan , setNewTaskPlan ] = useState ( [ ] )
134- const [ newSchedule , setNewSchedule ] = useState ( {
135- type : "once" ,
136- run_at : null ,
137- frequency : "daily" ,
138- time : "09:00" ,
139- days : [ ]
140- } )
141120 const [ allTools , setAllTools ] = useState ( [ ] )
142121 const [ integrations , setIntegrations ] = useState ( [ ] )
143122 const [ loadingIntegrations , setLoadingIntegrations ] = useState ( true )
@@ -218,50 +197,9 @@ const Tasks = () => {
218197 fetchAllToolsAndIntegrations ( )
219198 const intervalId = setInterval ( fetchTasksData , 60000 )
220199 return ( ) => clearInterval ( intervalId )
221- } , [ fetchTasksData ] )
222-
223- const handleShortcuts = useCallback ( ( e ) => {
224- if ( e . ctrlKey && e . key === "Enter" ) {
225- e . preventDefault ( )
226- setCreatePlanOpen ( ( prev ) => ! prev )
227- }
228200 } , [ ] )
229- useEffect ( ( ) => {
230- window . addEventListener ( "keydown" , handleShortcuts )
231- return ( ) => window . removeEventListener ( "keydown" , handleShortcuts )
232- } , [ fetchTasksData ] )
233-
234- const handleGeneratePlan = async ( ) => {
235- if ( ! generationPrompt . trim ( ) ) {
236- toast . error ( "Please enter a goal for your plan." )
237- return
238- }
239- setIsGeneratingPlan ( true )
240- try {
241- const response = await fetch ( "/api/tasks/generate-plan" , {
242- method : "POST" ,
243- headers : { "Content-Type" : "application/json" } ,
244- body : JSON . stringify ( { prompt : generationPrompt } )
245- } )
246- const data = await response . json ( )
247- if ( ! response . ok ) throw new Error ( data . detail )
248- if ( ! data . plan || data . plan . length === 0 ) {
249- toast . error (
250- "The agent could not generate a plan for this goal. Please try rephrasing."
251- )
252- } else {
253- setNewTaskDescription ( data . description || generationPrompt )
254- setNewTaskPlan ( data . plan )
255- setCreateStep ( "review" )
256- toast . success ( "Plan generated! Review and save it below." )
257- }
258- } catch ( error ) {
259- toast . error ( `Plan Generation Failed: ${ error . message } ` )
260- } finally {
261- setIsGeneratingPlan ( false )
262- }
263- }
264201
202+ const handleEditTask = ( task ) => setEditingTask ( { ...task } )
265203 const handleUpdateTaskSchedule = async ( taskId , schedule ) => {
266204 if ( ! taskId ) return false
267205 try {
@@ -279,66 +217,6 @@ const Tasks = () => {
279217 return false
280218 }
281219 }
282-
283- const handleAddTask = async ( ) => {
284- if ( ! newTaskDescription . trim ( ) )
285- return toast . error ( "Please enter a task description." )
286- if ( newTaskPlan . some ( ( step ) => ! step . tool || ! step . description . trim ( ) ) )
287- return toast . error (
288- "All plan steps must have a tool and description."
289- )
290- setIsAdding ( true )
291- try {
292- // Clean up schedule object before sending
293- const schedulePayload =
294- newSchedule . type === "once"
295- ? { type : "once" , run_at : newSchedule . run_at || null }
296- : newSchedule
297-
298- const taskData = {
299- description : newTaskDescription ,
300- priority : newTaskPriority ,
301- plan : newTaskPlan ,
302- schedule : schedulePayload
303- }
304-
305- const response = await fetch ( "/api/tasks/add" , {
306- method : "POST" ,
307- headers : { "Content-Type" : "application/json" } ,
308- body : JSON . stringify ( taskData )
309- } )
310- if ( ! response . ok ) {
311- const data = await response . json ( )
312- throw new Error ( data . error || "Failed to add task" )
313- }
314- toast . success ( "New plan created successfully!" )
315- posthog ?. capture ( "workflow_created" , {
316- plan_steps : taskData . plan . length ,
317- is_recurring : taskData . schedule . type === "recurring" ,
318- priority : taskData . priority
319- } )
320- setGenerationPrompt ( "" )
321- setNewTaskDescription ( "" )
322- setNewTaskPriority ( 1 )
323- setNewTaskPlan ( [ ] )
324- setNewSchedule ( {
325- type : "once" ,
326- run_at : null ,
327- frequency : "daily" ,
328- time : "09:00" ,
329- days : [ ]
330- } )
331- setCreateStep ( "generate" )
332- setCreatePlanOpen ( false )
333- await fetchTasksData ( )
334- } catch ( error ) {
335- toast . error ( `Failed to add task: ${ error . message } ` )
336- } finally {
337- setIsAdding ( false )
338- }
339- }
340-
341- const handleEditTask = ( task ) => setEditingTask ( { ...task } )
342220 const handleUpdateTask = async ( ) => {
343221 if (
344222 ! editingTask ||
@@ -476,7 +354,7 @@ const Tasks = () => {
476354 < Tooltip id = "tasks-tooltip" />
477355 < Tooltip id = "page-help-tooltip" />
478356 < div className = "flex-1 flex flex-col overflow-hidden relative" >
479- < HelpTooltip content = "This is the Tasks page. Here you can view all your tasks, approve new ones, and create custom multi-step plans (workflows). Press Ctrl + Enter to toggle the plan creator ." />
357+ < HelpTooltip content = "This is the Tasks page. Here you can view all your tasks and approve new ones." />
480358 < motion . header
481359 initial = { { y : - 20 , opacity : 0 } }
482360 animate = { { y : 0 , opacity : 1 } }
@@ -533,7 +411,7 @@ const Tasks = () => {
533411 </ motion . header >
534412
535413 < main className = "flex-1 w-full max-w-3xl mx-auto flex flex-col overflow-y-auto custom-scrollbar px-4" >
536- < div className = "space-y-2 pt-6 pb-36 " >
414+ < div className = "space-y-2 pt-6 pb-6 " >
537415 { loading || loadingIntegrations ? (
538416 < div className = "flex justify-center items-center h-full" >
539417 < IconLoader className = "w-12 h-12 animate-spin text-[var(--color-accent-blue)]" />
@@ -544,7 +422,7 @@ const Tasks = () => {
544422 </ div >
545423 ) : filteredTasks . length === 0 ? (
546424 < p className = "text-gray-500 text-center py-20 mt-5" >
547- No tasks found. Create a new plan below!
425+ No tasks found.
548426 </ p >
549427 ) : (
550428 < >
@@ -596,111 +474,6 @@ const Tasks = () => {
596474 ) }
597475 </ div >
598476 </ main >
599-
600- < div className = "absolute bottom-0 left-0 right-0 p-4 bg-gradient-to-t from-black/50 via-neutral-900/80 to-transparent backdrop-blur-sm border-t border-neutral-700/50 z-20" >
601- < div className = "max-w-3xl mx-auto" >
602- < div
603- onClick = { ( ) => setCreatePlanOpen ( ! isCreatePlanOpen ) }
604- className = "flex justify-between items-center cursor-pointer"
605- >
606- < h3 className = "text-lg font-semibold text-white" >
607- Create a New Plan
608- </ h3 >
609- < IconChevronDown
610- className = { cn (
611- "transform transition-transform duration-200" ,
612- ! isCreatePlanOpen && "rotate-180"
613- ) }
614- />
615- </ div >
616- < AnimatePresence >
617- { isCreatePlanOpen && (
618- < motion . div
619- key = "create-plan-panel"
620- initial = { { height : 0 , opacity : 0 } }
621- animate = { { height : "auto" , opacity : 1 } }
622- exit = { { height : 0 , opacity : 0 } }
623- className = "overflow-hidden"
624- >
625- { createStep === "generate" ? (
626- < div className = "space-y-4 pt-4" >
627- < label className = "text-sm font-medium text-gray-300 mb-1 block" >
628- What is your goal?
629- </ label >
630- < textarea
631- placeholder = "e.g., Send a daily summary of my calendar to my boss"
632- value = { generationPrompt }
633- onChange = { ( e ) =>
634- setGenerationPrompt (
635- e . target . value
636- )
637- }
638- rows = { 3 }
639- className = "w-full p-3 bg-neutral-800/50 border border-neutral-700 rounded-lg text-white placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-blue-500 backdrop-blur-sm transition-colors"
640- />
641- < div className = "flex justify-end" >
642- < button
643- onClick = { handleGeneratePlan }
644- disabled = { isGeneratingPlan }
645- className = "p-3 px-6 bg-[var(--color-accent-blue)] hover:bg-[var(--color-accent-blue-hover)] rounded-lg text-white font-semibold transition-colors disabled:opacity-50 flex items-center gap-2"
646- >
647- { isGeneratingPlan ? (
648- < IconLoader className = "w-5 h-5 animate-spin" />
649- ) : (
650- < >
651- Next: Review Plan{ " " }
652- < IconArrowRight className = "w-4 h-4" />
653- </ >
654- ) }
655- </ button >
656- </ div >
657- </ div >
658- ) : (
659- // REVIEW STEP
660- < div className = "space-y-6 pt-4" >
661- < PlanEditor
662- description = { newTaskDescription }
663- setDescription = {
664- setNewTaskDescription
665- }
666- priority = { newTaskPriority }
667- setPriority = { setNewTaskPriority }
668- plan = { newTaskPlan }
669- setPlan = { setNewTaskPlan }
670- schedule = { newSchedule }
671- setSchedule = { setNewSchedule }
672- allTools = { allTools }
673- integrations = { integrations }
674- />
675- < div className = "flex justify-between items-center" >
676- < button
677- onClick = { ( ) =>
678- setCreateStep (
679- "generate"
680- )
681- }
682- className = "py-2.5 px-6 rounded-lg bg-[var(--color-primary-surface-elevated)] hover:bg-[var(--color-primary-surface)] text-white text-sm font-semibold"
683- >
684- Back
685- </ button >
686- < button
687- onClick = { handleAddTask }
688- disabled = { isAdding }
689- className = "py-2.5 px-6 rounded-lg bg-[var(--color-accent-blue)] hover:bg-[var(--color-accent-blue-hover)] text-white text-sm font-semibold transition-colors disabled:opacity-50 flex items-center gap-2"
690- >
691- { isAdding && (
692- < IconLoader className = "h-5 h-5 animate-spin" />
693- ) }
694- Save New Plan
695- </ button >
696- </ div >
697- </ div >
698- ) }
699- </ motion . div >
700- ) }
701- </ AnimatePresence >
702- </ div >
703- </ div >
704477 </ div >
705478 { viewingTask && (
706479 < TaskDetailsModal
@@ -861,7 +634,7 @@ const PlanEditor = ({
861634 onClick = { handleAddStep }
862635 className = "flex items-center gap-1.5 py-1.5 px-3 rounded-full bg-[var(--color-primary-surface-elevated)] hover:bg-[var(--color-primary-surface)] text-xs"
863636 >
864- < IconPlus className = "h-4 w-4" /> Add Step
637+ < IconGripVertical className = "h-4 w-4" /> Add Step
865638 </ button >
866639 </ div >
867640 < div >
0 commit comments