'use client'; import React, { useEffect, useState } from 'react'; import { Clock, Plus, Trash2, Play, RefreshCw, Loader2, AlertCircle, X, } from 'lucide-react'; import { listCronJobs, addCronJob, removeCronJob, toggleCronJob, runCronJob, } from '@/lib/api'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { TaskManagementTabs } from '@/components/task-management/TaskManagementTabs'; import { Badge } from '@/components/ui/badge'; import { Switch } from '@/components/ui/switch'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@/components/ui/table'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select'; import { pickAppText } from '@/lib/i18n/core'; import { useAppI18n } from '@/lib/i18n/provider'; import { useChatStore } from '@/lib/store'; import type { CronJob } from '@/types'; export default function CronPage() { const { locale } = useAppI18n(); const sessionId = useChatStore((s) => s.sessionId); const [jobs, setJobs] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [showAdd, setShowAdd] = useState(false); const targetSessionKey = sessionId.startsWith('web:') ? sessionId : 'web:default'; const loadJobs = async () => { setLoading(true); setError(null); try { const data = await listCronJobs(true); setJobs(data); } catch (err: any) { setError(err.message || pickAppText(locale, '加载任务失败', 'Failed to load jobs')); } finally { setLoading(false); } }; useEffect(() => { loadJobs(); }, []); const handleToggle = async (jobId: string, enabled: boolean) => { try { await toggleCronJob(jobId, enabled); loadJobs(); } catch { // ignore } }; const handleDelete = async (jobId: string) => { try { await removeCronJob(jobId); loadJobs(); } catch { // ignore } }; const handleRun = async (jobId: string) => { try { await runCronJob(jobId); loadJobs(); } catch { // ignore } }; const handleAdd = async (params: { name: string; message: string; every_seconds?: number; cron_expr?: string; }) => { try { await addCronJob({ ...params, session_key: targetSessionKey, }); setShowAdd(false); loadJobs(); } catch (err: any) { setError(err.message); } }; const formatTime = (ms: number | null) => { if (!ms) return '-'; return new Date(ms).toLocaleString(undefined, { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit', }); }; if (loading) { return (
); } return (

{pickAppText(locale, '定时任务', 'Scheduled tasks')}

{error && (
{error}
)} {/* Add Job Form */} {showAdd && ( setShowAdd(false)} /> )} {/* Jobs Table */} {jobs.length === 0 ? (

{pickAppText(locale, '暂无定时任务', 'No scheduled tasks yet')}

{pickAppText(locale, '新建一个任务,让智能体按计划自动执行。', 'Create a job to let the agent run on a schedule.')}

) : ( {pickAppText(locale, '启用', 'Enabled')} {pickAppText(locale, '名称', 'Name')} {pickAppText(locale, '计划', 'Schedule')} {pickAppText(locale, '消息', 'Message')} {pickAppText(locale, '上次运行', 'Last run')} {pickAppText(locale, '下次运行', 'Next run')} {pickAppText(locale, '状态', 'Status')} {pickAppText(locale, '操作', 'Actions')} {jobs.map((job) => ( handleToggle(job.id, checked) } />
{job.name} {job.id}
{job.schedule_display} {job.message} {formatTime(job.last_run_at_ms)} {formatTime(job.next_run_at_ms)} {job.last_status === 'ok' && ( OK )} {job.last_status === 'error' && ( {pickAppText(locale, '错误', 'Error')} )} {!job.last_status && ( - )}
))}
)}
); } function AddJobForm({ targetSessionKey, onAdd, onCancel, }: { targetSessionKey: string; onAdd: (params: { name: string; message: string; every_seconds?: number; cron_expr?: string; }) => void; onCancel: () => void; }) { const { locale } = useAppI18n(); const [name, setName] = useState(''); const [message, setMessage] = useState(''); const [scheduleType, setScheduleType] = useState<'every' | 'cron'>('every'); const [everySeconds, setEverySeconds] = useState('3600'); const [cronExpr, setCronExpr] = useState('0 9 * * *'); const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); if (!name.trim() || !message.trim()) return; const params: any = { name: name.trim(), message: message.trim() }; if (scheduleType === 'every') { params.every_seconds = parseInt(everySeconds, 10) || 3600; } else { params.cron_expr = cronExpr.trim(); } onAdd(params); }; return (
{pickAppText(locale, '新建定时任务', 'New scheduled task')}
setName(e.target.value)} placeholder={pickAppText(locale, '例如:日报汇总', 'Example: daily summary')} />
{scheduleType === 'every' ? (
setEverySeconds(e.target.value)} min="10" placeholder="3600" />

{parseInt(everySeconds, 10) >= 3600 ? pickAppText(locale, `约 ${Math.floor(parseInt(everySeconds, 10) / 3600)} 小时 ${Math.floor((parseInt(everySeconds, 10) % 3600) / 60)} 分`, `About ${Math.floor(parseInt(everySeconds, 10) / 3600)}h ${Math.floor((parseInt(everySeconds, 10) % 3600) / 60)}m`) : parseInt(everySeconds, 10) >= 60 ? pickAppText(locale, `约 ${Math.floor(parseInt(everySeconds, 10) / 60)} 分 ${parseInt(everySeconds, 10) % 60} 秒`, `About ${Math.floor(parseInt(everySeconds, 10) / 60)}m ${parseInt(everySeconds, 10) % 60}s`) : ''}

) : (
setCronExpr(e.target.value)} placeholder="0 9 * * *" />

{pickAppText(locale, '格式:分钟 小时 日 月 周', 'Format: minute hour day month weekday')}

)}
setMessage(e.target.value)} placeholder={pickAppText(locale, '例如:检查我的邮件并生成摘要', 'Example: check my email and generate a summary')} />

{pickAppText(locale, '任务结果会自动回写到当前 Web 会话:', 'Results are written back to the current web session:')} {targetSessionKey}

); }