import { Button } from '@/components/ui/button' import { Dialog, DialogContent, DialogHeader, DialogTitle, } from '@/components/ui/dialog' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select' import { Textarea } from '@/components/ui/textarea' import { type CreateScheduledTaskRequest, type RecurrenceType, type TaskType, getRecurrenceTypeLabel, getTaskTypeLabel, } from '@/lib/api/services/schedulers' import { getSupportedTimezones } from '@/utils/locale' import { useLocale } from '@/hooks/use-locale' import { CalendarPlus, Loader2 } from 'lucide-react' import { useState } from 'react' interface CreateTaskDialogProps { open: boolean onOpenChange: (open: boolean) => void loading: boolean onSubmit: (data: CreateScheduledTaskRequest) => void onCancel: () => void } const TASK_TYPES: TaskType[] = ['credit_recharge', 'play_sound', 'play_playlist'] const RECURRENCE_TYPES: RecurrenceType[] = ['none', 'hourly', 'daily', 'weekly', 'monthly', 'yearly', 'cron'] export function CreateTaskDialog({ open, onOpenChange, loading, onSubmit, onCancel, }: CreateTaskDialogProps) { const { timezone } = useLocale() const [formData, setFormData] = useState({ name: '', task_type: 'play_sound', scheduled_at: '', timezone: timezone, parameters: {}, recurrence_type: 'none', cron_expression: null, recurrence_count: null, expires_at: null, }) const [parametersJson, setParametersJson] = useState('{}') const [parametersError, setParametersError] = useState(null) const handleSubmit = (e: React.FormEvent) => { e.preventDefault() // Validate parameters JSON try { const parameters = JSON.parse(parametersJson) // Send the datetime as UTC to prevent backend timezone conversion // The user's selected time should be stored exactly as entered let scheduledAt = formData.scheduled_at if (scheduledAt.length === 16) { scheduledAt += ':00' // Add seconds if missing } // Add Z to indicate this is already UTC time, preventing backend conversion const scheduledAtUTC = scheduledAt + 'Z' onSubmit({ ...formData, parameters, scheduled_at: scheduledAtUTC, }) } catch { setParametersError('Invalid JSON format') } } const handleParametersChange = (value: string) => { setParametersJson(value) setParametersError(null) // Try to parse JSON to validate try { JSON.parse(value) } catch { if (value.trim()) { setParametersError('Invalid JSON format') } } } const handleCancel = () => { // Reset form setFormData({ name: '', task_type: 'play_sound', scheduled_at: '', timezone: timezone, parameters: {}, recurrence_type: 'none', cron_expression: null, recurrence_count: null, expires_at: null, }) setParametersJson('{}') setParametersError(null) onCancel() } const getDefaultScheduledTime = () => { const now = new Date() now.setMinutes(now.getMinutes() + 10) // Default to 10 minutes from now // Format the time in the user's timezone const formatter = new Intl.DateTimeFormat('en-US', { timeZone: timezone, year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', hour12: false, }) const parts = formatter.formatToParts(now) const partsObj = parts.reduce((acc, part) => { acc[part.type] = part.value return acc }, {} as Record) // Return in datetime-local format (YYYY-MM-DDTHH:MM) return `${partsObj.year}-${partsObj.month}-${partsObj.day}T${partsObj.hour}:${partsObj.minute}` } return ( Create Scheduled Task
setFormData(prev => ({ ...prev, name: e.target.value }))} placeholder="Enter task name" required />
setFormData(prev => ({ ...prev, scheduled_at: e.target.value }))} required />
{formData.recurrence_type === 'cron' && (
setFormData(prev => ({ ...prev, cron_expression: e.target.value }))} placeholder="0 0 * * *" />
)} {formData.recurrence_type !== 'none' && formData.recurrence_type !== 'cron' && (
setFormData(prev => ({ ...prev, recurrence_count: e.target.value ? parseInt(e.target.value) : null }))} placeholder="Leave empty for infinite" />
)}
setFormData(prev => ({ ...prev, expires_at: e.target.value || null }))} />