Skip to content

Commit c8d1910

Browse files
committed
fix upcoming tasks
1 parent a4c5727 commit c8d1910

1 file changed

Lines changed: 169 additions & 26 deletions

File tree

components/upcoming-tasks.tsx

Lines changed: 169 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,169 @@
1+
"use client"
2+
3+
import { useState, useEffect } from "react"
14
import { Button } from "@/components/ui/button"
25
import { 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

2825
export 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

Comments
 (0)