1+ "use client"
2+
3+ import { useState , useEffect } from "react"
14import { Button } from "@/components/ui/button"
25import { Clock , Play } from "lucide-react"
36
4- const upcomingTasks = [
5- {
6- id : "1" ,
7- name : "Weekly Backup" ,
8- agent : "server-main" ,
9- nextRun : "Tomorrow at 6:00 PM" ,
10- timeUntil : "in 22 hours" ,
11- } ,
12- {
13- id : "2" ,
14- name : "System Update" ,
15- agent : "backup-server" ,
16- nextRun : "Monday at 2:00 AM" ,
17- timeUntil : "in 3 days" ,
18- } ,
19- {
20- id : "3" ,
21- name : "Database Backup" ,
22- agent : "server-main" ,
23- nextRun : "Sunday at 11:00 PM" ,
24- timeUntil : "in 2 days" ,
25- } ,
26- ]
7+ interface Task {
8+ id : string
9+ name : string
10+ description : string
11+ agent_hostname : string
12+ schedule : string
13+ active : boolean
14+ }
15+
16+ interface UpcomingTask {
17+ id : string
18+ name : string
19+ agent : string
20+ nextRun : string
21+ timeUntil : string
22+ schedule : string
23+ }
2724
2825export function UpcomingTasks ( ) {
26+ const [ upcomingTasks , setUpcomingTasks ] = useState < UpcomingTask [ ] > ( [ ] )
27+ const [ loading , setLoading ] = useState ( true )
28+
29+ const parseNextRun = ( schedule : string ) : Date | null => {
30+ if ( ! schedule ) return null
31+
32+ // Implementação básica para interpretar cron expressions
33+ // Para uma implementação completa, seria recomendado usar uma biblioteca como node-cron
34+ const now = new Date ( )
35+
36+ // Exemplos de schedules simples que podemos interpretar
37+ if ( schedule . includes ( "daily" ) || schedule === "0 0 * * *" ) {
38+ const tomorrow = new Date ( now )
39+ tomorrow . setDate ( tomorrow . getDate ( ) + 1 )
40+ tomorrow . setHours ( 0 , 0 , 0 , 0 )
41+ return tomorrow
42+ }
43+
44+ if ( schedule . includes ( "weekly" ) || schedule === "0 0 * * 0" ) {
45+ const nextSunday = new Date ( now )
46+ const daysUntilSunday = ( 7 - now . getDay ( ) ) % 7 || 7
47+ nextSunday . setDate ( now . getDate ( ) + daysUntilSunday )
48+ nextSunday . setHours ( 0 , 0 , 0 , 0 )
49+ return nextSunday
50+ }
51+
52+ // Para schedules mais complexos, retorna uma data estimada
53+ const nextHour = new Date ( now )
54+ nextHour . setHours ( nextHour . getHours ( ) + 1 )
55+ return nextHour
56+ }
57+
58+ const formatNextRun = ( date : Date ) : string => {
59+ const now = new Date ( )
60+ const tomorrow = new Date ( now )
61+ tomorrow . setDate ( tomorrow . getDate ( ) + 1 )
62+
63+ if ( date . toDateString ( ) === now . toDateString ( ) ) {
64+ return `Today at ${ date . toLocaleTimeString ( [ ] , { hour : '2-digit' , minute : '2-digit' } ) } `
65+ } else if ( date . toDateString ( ) === tomorrow . toDateString ( ) ) {
66+ return `Tomorrow at ${ date . toLocaleTimeString ( [ ] , { hour : '2-digit' , minute : '2-digit' } ) } `
67+ } else {
68+ const dayName = date . toLocaleDateString ( [ ] , { weekday : 'long' } )
69+ const time = date . toLocaleTimeString ( [ ] , { hour : '2-digit' , minute : '2-digit' } )
70+ return `${ dayName } at ${ time } `
71+ }
72+ }
73+
74+ const formatTimeUntil = ( date : Date ) : string => {
75+ const now = new Date ( )
76+ const diffMs = date . getTime ( ) - now . getTime ( )
77+ const diffHours = Math . floor ( diffMs / ( 1000 * 60 * 60 ) )
78+ const diffDays = Math . floor ( diffHours / 24 )
79+
80+ if ( diffHours < 1 ) {
81+ const diffMins = Math . floor ( diffMs / ( 1000 * 60 ) )
82+ return `in ${ diffMins } minutes`
83+ } else if ( diffHours < 24 ) {
84+ return `in ${ diffHours } hours`
85+ } else {
86+ return `in ${ diffDays } days`
87+ }
88+ }
89+
90+ const fetchUpcomingTasks = async ( ) => {
91+ try {
92+ const response = await fetch ( "/api/tasks" )
93+ if ( response . ok ) {
94+ const tasks : Task [ ] = await response . json ( )
95+
96+ // Filtrar apenas tarefas ativas com schedule
97+ const activeTasks = tasks . filter ( task => task . active && task . schedule )
98+
99+ // Processar e ordenar por próxima execução
100+ const processed = activeTasks
101+ . map ( task => {
102+ const nextRunDate = parseNextRun ( task . schedule )
103+ if ( ! nextRunDate ) return null
104+
105+ return {
106+ id : task . id ,
107+ name : task . name ,
108+ agent : task . agent_hostname ,
109+ nextRun : formatNextRun ( nextRunDate ) ,
110+ timeUntil : formatTimeUntil ( nextRunDate ) ,
111+ schedule : task . schedule ,
112+ nextRunDate
113+ }
114+ } )
115+ . filter ( Boolean )
116+ . sort ( ( a , b ) => a ! . nextRunDate . getTime ( ) - b ! . nextRunDate . getTime ( ) )
117+ . slice ( 0 , 5 ) // Mostrar apenas as próximas 5 tarefas
118+ . map ( ( { nextRunDate, ...task } ) => task ) as UpcomingTask [ ]
119+
120+ setUpcomingTasks ( processed )
121+ }
122+ } catch ( error ) {
123+ console . error ( "Failed to fetch upcoming tasks:" , error )
124+ } finally {
125+ setLoading ( false )
126+ }
127+ }
128+
129+ const executeTask = async ( taskId : string ) => {
130+ try {
131+ const response = await fetch ( `/api/tasks/${ taskId } /execute` , {
132+ method : "POST" ,
133+ } )
134+
135+ if ( response . ok ) {
136+ // Opcional: mostrar feedback de sucesso
137+ console . log ( "Task executed successfully" )
138+ } else {
139+ console . error ( "Failed to execute task" )
140+ }
141+ } catch ( error ) {
142+ console . error ( "Failed to execute task:" , error )
143+ }
144+ }
145+
146+ useEffect ( ( ) => {
147+ fetchUpcomingTasks ( )
148+ // Atualizar a cada 5 minutos
149+ const interval = setInterval ( fetchUpcomingTasks , 5 * 60 * 1000 )
150+ return ( ) => clearInterval ( interval )
151+ } , [ ] )
152+
153+ if ( loading ) {
154+ return < div className = "text-center py-4" > Loading upcoming tasks...</ div >
155+ }
156+
157+ if ( upcomingTasks . length === 0 ) {
158+ return (
159+ < div className = "text-center py-8 text-muted-foreground" >
160+ < Clock className = "h-8 w-8 mx-auto mb-2 opacity-50" />
161+ < p > No upcoming scheduled tasks</ p >
162+ < p className = "text-sm" > Create tasks with schedules to see them here</ p >
163+ </ div >
164+ )
165+ }
166+
29167 return (
30168 < div className = "space-y-3" >
31169 { upcomingTasks . map ( ( task ) => (
@@ -34,15 +172,20 @@ export function UpcomingTasks() {
34172 < Clock className = "h-4 w-4 text-blue-600" />
35173 < div >
36174 < p className = "font-medium" > { task . name } </ p >
37- < p className = "text-sm text-muted-foreground" > { task . agent } </ p >
175+ < p className = "text-sm text-muted-foreground" > on { task . agent } </ p >
38176 </ div >
39177 </ div >
40178 < div className = "flex items-center space-x-3" >
41179 < div className = "text-right" >
42- < p className = "text-sm" > { task . nextRun } </ p >
180+ < p className = "text-sm font-medium " > { task . nextRun } </ p >
43181 < p className = "text-xs text-muted-foreground" > { task . timeUntil } </ p >
44182 </ div >
45- < Button variant = "ghost" size = "sm" >
183+ < Button
184+ variant = "ghost"
185+ size = "sm"
186+ onClick = { ( ) => executeTask ( task . id ) }
187+ title = "Execute now"
188+ >
46189 < Play className = "h-4 w-4" />
47190 </ Button >
48191 </ div >
0 commit comments