import { useState, useEffect, useCallback } from "react"; import { Plus, Clock } from "lucide-react"; import type { ScheduledTask, ScheduledTaskCreateInput } from "@fusion/core"; import { fetchAutomations, createAutomation, updateAutomation, deleteAutomation, runAutomation, toggleAutomation, } from "../api"; import { ScheduleForm } from "./ScheduleForm"; import { ScheduleCard } from "./ScheduleCard"; import type { ToastType } from "../hooks/useToast"; /** Polling interval for auto-refreshing the schedule list (30 seconds). */ const POLL_INTERVAL_MS = 30_000; interface ScheduledTasksModalProps { onClose: () => void; addToast: (message: string, type?: ToastType) => void; } type ModalView = "list" | "create" | "edit"; export function ScheduledTasksModal({ onClose, addToast }: ScheduledTasksModalProps) { const [schedules, setSchedules] = useState([]); const [loading, setLoading] = useState(true); const [view, setView] = useState("list"); const [editingSchedule, setEditingSchedule] = useState(); /** Track which schedule is currently running a manual execution. */ const [runningId, setRunningId] = useState(null); // Load schedules const loadSchedules = useCallback(async () => { try { const data = await fetchAutomations(); setSchedules(data); } catch (err: any) { addToast(err.message || "Failed to load schedules", "error"); } finally { setLoading(false); } }, [addToast]); useEffect(() => { loadSchedules(); }, [loadSchedules]); // Poll for updates while modal is open useEffect(() => { const interval = setInterval(loadSchedules, POLL_INTERVAL_MS); return () => clearInterval(interval); }, [loadSchedules]); // Close on Escape (only when not in a sub-form) useEffect(() => { const handleKey = (e: KeyboardEvent) => { if (e.key === "Escape") { if (view !== "list") { setView("list"); setEditingSchedule(undefined); } else { onClose(); } } }; document.addEventListener("keydown", handleKey); return () => document.removeEventListener("keydown", handleKey); }, [onClose, view]); const handleOverlayClick = useCallback( (e: React.MouseEvent) => { if (e.target === e.currentTarget) onClose(); }, [onClose], ); // CRUD handlers const handleCreate = useCallback( async (input: ScheduledTaskCreateInput) => { try { await createAutomation(input); addToast("Schedule created", "success"); setView("list"); await loadSchedules(); } catch (err: any) { addToast(err.message || "Failed to create schedule", "error"); } }, [addToast, loadSchedules], ); const handleEdit = useCallback((schedule: ScheduledTask) => { setEditingSchedule(schedule); setView("edit"); }, []); const handleUpdate = useCallback( async (input: ScheduledTaskCreateInput) => { if (!editingSchedule) return; try { await updateAutomation(editingSchedule.id, input); addToast("Schedule updated", "success"); setView("list"); setEditingSchedule(undefined); await loadSchedules(); } catch (err: any) { addToast(err.message || "Failed to update schedule", "error"); } }, [editingSchedule, addToast, loadSchedules], ); const handleDelete = useCallback( async (schedule: ScheduledTask) => { try { await deleteAutomation(schedule.id); addToast(`Deleted "${schedule.name}"`, "success"); await loadSchedules(); } catch (err: any) { addToast(err.message || "Failed to delete schedule", "error"); } }, [addToast, loadSchedules], ); const handleRun = useCallback( async (schedule: ScheduledTask) => { setRunningId(schedule.id); try { const { result } = await runAutomation(schedule.id); if (result.success) { addToast(`"${schedule.name}" completed successfully`, "success"); } else { addToast(`"${schedule.name}" failed: ${result.error || "Unknown error"}`, "error"); } await loadSchedules(); } catch (err: any) { addToast(err.message || "Failed to run schedule", "error"); } finally { setRunningId(null); } }, [addToast, loadSchedules], ); const handleToggle = useCallback( async (schedule: ScheduledTask) => { try { await toggleAutomation(schedule.id); addToast( `"${schedule.name}" ${schedule.enabled ? "disabled" : "enabled"}`, "success", ); await loadSchedules(); } catch (err: any) { addToast(err.message || "Failed to toggle schedule", "error"); } }, [addToast, loadSchedules], ); const handleFormCancel = useCallback(() => { setView("list"); setEditingSchedule(undefined); }, []); const renderContent = () => { if (view === "create") { return ; } if (view === "edit" && editingSchedule) { return ( ); } // List view if (loading) { return
Loading schedules…
; } if (schedules.length === 0) { return (

No scheduled tasks yet

Create a schedule to automate recurring tasks.

); } return (
{schedules.map((s) => ( ))}
); }; return (

Scheduled Tasks

{view === "list" && schedules.length > 0 && ( )}
{renderContent()}
); }