import React, { useMemo } from 'react'; import { Input } from '@renderer/components/ui/input'; import { Label } from '@renderer/components/ui/label'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@renderer/components/ui/select'; import { Cron } from 'croner'; import cronstrue from 'cronstrue/i18n'; import { AlertCircle, Calendar, Clock, Globe } from 'lucide-react'; // ============================================================================= // Common Timezone Presets // ============================================================================= const TIMEZONE_PRESETS = [ { value: 'UTC', label: 'UTC' }, { value: 'America/New_York', label: 'New York (ET)' }, { value: 'America/Chicago', label: 'Chicago (CT)' }, { value: 'America/Denver', label: 'Denver (MT)' }, { value: 'America/Los_Angeles', label: 'Los Angeles (PT)' }, { value: 'Europe/London', label: 'London (GMT/BST)' }, { value: 'Europe/Berlin', label: 'Berlin (CET)' }, { value: 'Europe/Moscow', label: 'Moscow (MSK)' }, { value: 'Asia/Tokyo', label: 'Tokyo (JST)' }, { value: 'Asia/Shanghai', label: 'Shanghai (CST)' }, { value: 'Asia/Kolkata', label: 'Kolkata (IST)' }, { value: 'Australia/Sydney', label: 'Sydney (AEST)' }, ] as const; const WARMUP_OPTIONS = [ { value: 0, label: 'No warm-up' }, { value: 5, label: '5 min' }, { value: 10, label: '10 min' }, { value: 15, label: '15 min' }, { value: 30, label: '30 min' }, ] as const; // ============================================================================= // Cron Presets // ============================================================================= const CRON_PRESETS = [ { label: 'Every hour', cron: '0 * * * *' }, { label: 'Every 6 hours', cron: '0 */6 * * *' }, { label: 'Daily at 9am', cron: '0 9 * * *' }, { label: 'Weekdays at 9am', cron: '0 9 * * 1-5' }, { label: 'Monday at 9am', cron: '0 9 * * 1' }, { label: 'Every 30 min', cron: '*/30 * * * *' }, ] as const; // ============================================================================= // Props // ============================================================================= interface CronScheduleInputProps { cronExpression: string; onCronExpressionChange: (value: string) => void; timezone: string; onTimezoneChange: (value: string) => void; warmUpMinutes: number; onWarmUpMinutesChange: (value: number) => void; } // ============================================================================= // Component // ============================================================================= export const CronScheduleInput = ({ cronExpression, onCronExpressionChange, timezone, onTimezoneChange, warmUpMinutes, onWarmUpMinutesChange, }: CronScheduleInputProps): React.JSX.Element => { // Parse and validate cron expression const cronInfo = useMemo(() => { if (!cronExpression.trim()) { return { valid: false, description: null, nextRuns: [], error: 'Enter a cron expression' }; } try { const job = new Cron(cronExpression.trim(), { timezone, paused: true }); const runs = job.nextRuns(3); job.stop(); let description: string; try { description = cronstrue.toString(cronExpression.trim(), { locale: 'en', use24HourTimeFormat: true, }); } catch { description = ''; } // Warn if interval is less than 5 minutes const isHighFrequency = runs.length >= 2 && runs[1].getTime() - runs[0].getTime() < 5 * 60 * 1000; return { valid: true, description, nextRuns: runs, error: null, highFrequencyWarning: isHighFrequency, }; } catch (err) { return { valid: false, description: null, nextRuns: [], error: err instanceof Error ? err.message : 'Invalid cron expression', }; } }, [cronExpression, timezone]); const formatNextRun = (date: Date): string => { return date.toLocaleString('en-US', { weekday: 'short', month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit', hour12: false, timeZone: timezone, }); }; return (
{cronInfo.description}
) : null} {cronInfo.error && cronExpression.trim() ? (Next runs:
Pre-warms CLI environment before scheduled execution