/** * Reusable Schedule Input * * Shows the active scheduling mode with a subtle text link to switch. * Interval mode is the default; a small "Use cron schedule" link beneath * toggles to cron mode, and vice-versa. * * For M3U / EPG (default interval NumberInput): * form.setFieldValue('refresh_interval', v)} * cronValue={form.getValues().cron_expression} * onCronChange={(v) => form.setFieldValue('cron_expression', v)} * /> * * For Backups (custom simple-mode UI via children): * handleScheduleChange('cron_expression', v)} * switchToCronLabel="Use custom cron schedule" * switchToIntervalLabel="Use simple schedule" * > * ...frequency / time / day selectors... * */ import React, { useState, useEffect } from 'react'; import { TextInput, NumberInput, Anchor, Stack, Text, Code, Popover, ActionIcon, Group, PopoverTarget, PopoverDropdown, } from '@mantine/core'; import { Info } from 'lucide-react'; import { validateCronExpression } from '../../utils/cronUtils'; import CronBuilder from './CronBuilder'; export default function ScheduleInput({ // Schedule type scheduleType = 'interval', onScheduleTypeChange, // Cron cronValue = '', onCronChange, // Default interval input (used when children not provided) intervalValue = 0, onIntervalChange, intervalLabel = 'Refresh Interval (hours)', intervalDescription = 'How often to refresh (0 to disable)', min = 0, // Custom simple-mode content (replaces the default NumberInput) children, // Link text for toggling switchToCronLabel = 'Use cron schedule', switchToIntervalLabel = 'Use interval schedule', disabled = false, }) { const [cronError, setCronError] = useState(null); const [builderOpened, setBuilderOpened] = useState(false); // Validate cron whenever it changes useEffect(() => { if (scheduleType === 'cron' && cronValue) { const v = validateCronExpression(cronValue); setCronError(v.valid ? null : v.error); } else { setCronError(null); } }, [scheduleType, cronValue]); const switchToCron = (e) => { e.preventDefault(); onScheduleTypeChange('cron'); }; const switchToInterval = (e) => { e.preventDefault(); onScheduleTypeChange('interval'); onCronChange(''); setCronError(null); }; const handleCronChange = (val) => { onCronChange(val); if (val) { const v = validateCronExpression(val); setCronError(v.valid ? null : v.error); } else { setCronError(null); } }; const handleBuilderApply = (cron) => { handleCronChange(cron); }; return ( {scheduleType === 'cron' ? ( Cron Expression COMMON EXAMPLES Every day at 3 AM: 0 3 * * * At 4 AM, 10 AM, 4 PM: 0 4,10,16 * * * Sundays at 2 AM: 0 2 * * 0 1st of month at 2:30 PM: 30 14 1 * * } placeholder="0 3 * * *" description="minute hour day month weekday" value={cronValue} onChange={(e) => handleCronChange(e.currentTarget.value)} error={cronError} disabled={disabled} /> {!disabled && ( {switchToIntervalLabel} setBuilderOpened(true)}> Open Cron Builder )} setBuilderOpened(false)} onApply={handleBuilderApply} currentValue={cronValue} /> ) : children ? ( {children} {!disabled && ( {switchToCronLabel} )} ) : ( {!disabled && ( {switchToCronLabel} )} )} ); }