"use client"; import { useState, useEffect } from "react"; import { Timer, Play, Pause, RefreshCw, ChevronRight } from "lucide-react"; import Link from "next/link"; import { timeAgo } from "@/lib/utils"; interface CronJob { id: string; name?: string; prompt: string; schedule: string; status: string; deliver: string; skill?: string; skills?: string[]; model?: string; repeat?: number; run_count?: number; last_run?: string; next_run?: string; created_at?: string; } export default function CronPage() { const [jobs, setJobs] = useState([]); const [loading, setLoading] = useState(true); const load = () => { fetch("/cron/jobs") .then((r) => r.json()) .then((d) => setJobs(d.jobs || [])) .catch(() => {}) .finally(() => setLoading(false)); }; useEffect(() => { load(); }, []); const trigger = async (id: string) => { await fetch(`/cron/jobs/${id}/run`, { method: "POST" }); load(); }; const active = jobs.filter((j) => j.status === "active" || j.status === "enabled"); const paused = jobs.filter((j) => j.status === "paused"); const other = jobs.filter((j) => !["active", "enabled", "paused"].includes(j.status)); return (

Cron Jobs

{loading ? (
{[...Array(3)].map((_, i) => (
))}
) : jobs.length === 0 ? (
No cron jobs configured
) : (
{active.length > 0 && (
)} {paused.length > 0 && (
)} {other.length > 0 && (
)}
)}
); } function Section({ label, jobs, onTrigger }: { label: string; jobs: CronJob[]; onTrigger: (id: string) => void }) { return (

{label} ({jobs.length})

{jobs.map((j) => ( onTrigger(j.id)} /> ))}
); } function CronCard({ job, onTrigger }: { job: CronJob; onTrigger: () => void }) { const isActive = job.status === "active" || job.status === "enabled"; return (
{job.name || job.prompt.slice(0, 60)}
{job.schedule}
→ {job.deliver} {job.skills && job.skills.length > 0 && ( {job.skills.join(", ")} )} {job.last_run && last: {timeAgo(new Date(job.last_run).getTime() / 1000)}}
); }