import React, { useState } from 'react'; import { Plus, RefreshCw, CheckCircle2, Pause, Edit2, Trash2, X, Play, Clock } from 'lucide-react'; import { motion, AnimatePresence } from 'motion/react'; import { useTranslation } from 'react-i18next'; import { useAppContext } from '../context/AppContext'; import { CronJob } from '../types'; const initialCronForm = { name: '', expr: '*/10 * * * *', message: '', deliver: false, channel: 'telegram', to: '', enabled: true, }; const Cron: React.FC = () => { const { t } = useTranslation(); const { cron, refreshCron, q } = useAppContext(); const [isCronModalOpen, setIsCronModalOpen] = useState(false); const [editingCron, setEditingCron] = useState(null); const [cronForm, setCronForm] = useState(initialCronForm); async function cronAction(action: 'delete' | 'enable' | 'disable', id: string) { try { await fetch(`/webui/api/cron${q}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action, id }), }); await refreshCron(); } catch (e) { console.error(e); } } async function openCronModal(job?: CronJob) { if (job) { try { const r = await fetch(`/webui/api/cron${q}&id=${job.id}`); if (r.ok) { const details = await r.json(); setEditingCron(details.job); setCronForm({ name: details.job.name || '', expr: details.job.expr || '', message: details.job.message || '', deliver: details.job.deliver || false, channel: details.job.channel || 'telegram', to: details.job.to || '', enabled: details.job.enabled ?? true, }); } } catch (e) { console.error('Failed to fetch job details', e); } } else { setEditingCron(null); setCronForm(initialCronForm); } setIsCronModalOpen(true); } async function handleCronSubmit() { try { const action = editingCron ? 'update' : 'create'; const payload = { action, ...(editingCron && { id: editingCron.id }), ...cronForm, }; const r = await fetch(`/webui/api/cron${q}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload), }); if (r.ok) { setIsCronModalOpen(false); await refreshCron(); } else { const err = await r.text(); alert('Action failed: ' + err); } } catch (e) { alert('Action failed: ' + e); } } return (

{t('cronJobs')}

{cron.map((j) => (

{j.name || j.id}

ID: {j.id.slice(-6)}
{j.enabled ? ( {t('active')} ) : ( {t('paused')} )}
"{j.message}"
{t('cronExpression')}
{j.expr || '-'}
))} {cron.length === 0 && (

{t('noCronJobs')}

)}
{isCronModalOpen && (
setIsCronModalOpen(false)} className="absolute inset-0 bg-black/60 backdrop-blur-sm" />

{editingCron ? t('editJob') : t('addJob')}