import { motion } from 'framer-motion' import { useEffect, useState } from 'react' import { UseFormReturn } from 'react-hook-form' import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, Button, cn, FormControl, FormField, FormItem, FormLabel, FormMessage, Input, SheetSection, Switch, } from 'ui' import { useDebounce } from 'use-debounce' import { formatScheduleString, getScheduleMessage } from '../CronJobs.utils' import CronSyntaxChart from '../CronSyntaxChart' import { type CreateCronJobForm } from './CreateCronJobSheet.constants' import { useSqlCronGenerateMutation } from '@/data/ai/sql-cron-mutation' import { useCronTimezoneQuery } from '@/data/database-cron-jobs/database-cron-timezone-query' import { useSelectedProjectQuery } from '@/hooks/misc/useSelectedProject' interface CronJobScheduleSectionProps { form: UseFormReturn supportsSeconds: boolean } export const CronJobScheduleSection = ({ form, supportsSeconds }: CronJobScheduleSectionProps) => { const { data: project } = useSelectedProjectQuery() const [inputValue, setInputValue] = useState('') const [debouncedValue] = useDebounce(inputValue, 750) const [useNaturalLanguage, setUseNaturalLanguage] = useState(false) const PRESETS = [ ...(supportsSeconds ? [{ name: 'Every 30 seconds', expression: '30 seconds' }] : []), { name: 'Every minute', expression: '* * * * *' }, { name: 'Every 5 minutes', expression: '*/5 * * * *' }, { name: 'Every first of the month, at 00:00', expression: '0 0 1 * *' }, { name: 'Every night at midnight', expression: '0 0 * * *' }, { name: 'Every Monday at 2 AM', expression: '0 2 * * 1' }, ] as const const { mutate: generateCronSyntax, isPending: isGeneratingCron } = useSqlCronGenerateMutation({ onSuccess: (expression) => { form.setValue('schedule', expression, { shouldValidate: true, shouldDirty: true, shouldTouch: true, }) }, }) const { data: timezone } = useCronTimezoneQuery({ projectRef: project?.ref, connectionString: project?.connectionString, }) useEffect(() => { if (useNaturalLanguage && debouncedValue) { generateCronSyntax({ prompt: debouncedValue }) } // eslint-disable-next-line react-hooks/exhaustive-deps }, [debouncedValue, useNaturalLanguage]) const schedule = form.watch('schedule') const scheduleString = formatScheduleString(schedule) return ( { return (
Schedule {useNaturalLanguage ? 'Describe your schedule in words' : 'Enter a cron expression'}
{useNaturalLanguage ? ( { if (e.key === 'Enter') { e.preventDefault() } }} onChange={(e) => setInputValue(e.target.value)} /> ) : ( { if (e.key === 'Enter') { e.preventDefault() } }} /> )}
{ setUseNaturalLanguage(!useNaturalLanguage) setInputValue('') }} />

Use natural language

    {PRESETS.map((preset) => (
  • ))}
View syntax chart

Schedule {timezone ? `(${timezone})` : ''}

{isGeneratingCron ? : schedule || '* * * * * *'} {!inputValue && !isGeneratingCron && !scheduleString ? ( Describe your schedule above ) : ( {isGeneratingCron ? : getScheduleMessage(scheduleString)} )}
) }} />
) } const CronSyntaxLoader = () => { return (
{['*', '*', '*', '*', '*'].map((char, i) => ( {char} ))}
) } const LoadingDots = () => { return ( {[0, 1, 2].map((i) => ( . ))} ) }