({ isOpen, onClose, onSave, editingJob }: CronJobModalProps)
| 77 | } |
| 78 | |
| 79 | export function CronJobModal({ isOpen, onClose, onSave, editingJob }: CronJobModalProps) { |
| 80 | const [name, setName] = useState(""); |
| 81 | const [description, setDescription] = useState(""); |
| 82 | const [schedule, setSchedule] = useState("0 9 * * *"); |
| 83 | const [timezone, setTimezone] = useState(DEFAULT_TIMEZONE); |
| 84 | const [frequencyMode, setFrequencyMode] = useState<FrequencyMode>("daily"); |
| 85 | const [showPresets, setShowPresets] = useState(false); |
| 86 | const [showTemplates, setShowTemplates] = useState(false); |
| 87 | const [errors, setErrors] = useState<Record<string, string>>({}); |
| 88 | const [isSaving, setIsSaving] = useState(false); |
| 89 | |
| 90 | // Visual builder state |
| 91 | const [everyMinutes, setEveryMinutes] = useState(15); |
| 92 | const [selectedHour, setSelectedHour] = useState(9); |
| 93 | const [selectedMinute, setSelectedMinute] = useState(0); |
| 94 | const [selectedDays, setSelectedDays] = useState<number[]>([1]); // Monday |
| 95 | const [selectedDayOfMonth, setSelectedDayOfMonth] = useState(1); |
| 96 | |
| 97 | useEffect(() => { |
| 98 | if (isOpen) { |
| 99 | if (editingJob) { |
| 100 | setName(editingJob.name); |
| 101 | setDescription(editingJob.description); |
| 102 | setSchedule(typeof editingJob.schedule === "string" ? editingJob.schedule : String(editingJob.schedule)); |
| 103 | setTimezone(editingJob.timezone); |
| 104 | setFrequencyMode("custom"); |
| 105 | } else { |
| 106 | setName(""); |
| 107 | setDescription(""); |
| 108 | setSchedule("0 9 * * *"); |
| 109 | setTimezone(DEFAULT_TIMEZONE); |
| 110 | setFrequencyMode("daily"); |
| 111 | } |
| 112 | setErrors({}); |
| 113 | } |
| 114 | }, [isOpen, editingJob]); |
| 115 | |
| 116 | // Update schedule when visual builder changes |
| 117 | useEffect(() => { |
| 118 | if (frequencyMode === "custom") return; |
| 119 | const newSchedule = buildCron(frequencyMode, { |
| 120 | minutes: everyMinutes, |
| 121 | minute: selectedMinute, |
| 122 | hour: selectedHour, |
| 123 | days: selectedDays, |
| 124 | day: selectedDayOfMonth, |
| 125 | }); |
| 126 | setSchedule(newSchedule); |
| 127 | }, [frequencyMode, everyMinutes, selectedHour, selectedMinute, selectedDays, selectedDayOfMonth]); |
| 128 | |
| 129 | const validate = (): boolean => { |
| 130 | const newErrors: Record<string, string> = {}; |
| 131 | if (!name.trim()) newErrors.name = "Name is required"; |
| 132 | if (!schedule.trim()) newErrors.schedule = "Schedule is required"; |
| 133 | else if (!isValidCron(schedule)) newErrors.schedule = "Invalid cron expression"; |
| 134 | setErrors(newErrors); |
| 135 | return Object.keys(newErrors).length === 0; |
| 136 | }; |
nothing calls this directly
no test coverage detected