/** * @ai-summary Form component for creating maintenance schedules * @ai-context Mobile-first responsive design with proper validation */ import React, { useState, useEffect } from 'react'; import { useForm, Controller } from 'react-hook-form'; import { z } from 'zod'; import { zodResolver } from '@hookform/resolvers/zod'; import { Card, CardHeader, CardContent, TextField, Select, MenuItem, Button, Box, Grid, FormControl, InputLabel, FormHelperText, CircularProgress, Typography, RadioGroup, FormControlLabel, Radio, FormLabel, } from '@mui/material'; import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; import { DatePicker } from '@mui/x-date-pickers/DatePicker'; import dayjs from 'dayjs'; import { useMaintenanceRecords } from '../hooks/useMaintenanceRecords'; import { useVehicles } from '../../vehicles/hooks/useVehicles'; import { SubtypeCheckboxGroup } from './SubtypeCheckboxGroup'; import { EmailNotificationToggle } from '../../notifications/components/EmailNotificationToggle'; import { MaintenanceCategory, ScheduleType, CreateScheduleRequest, getCategoryDisplayName, } from '../types/maintenance.types'; import toast from 'react-hot-toast'; const schema = z .object({ vehicle_id: z.string().uuid({ message: 'Please select a vehicle' }), category: z.enum(['routine_maintenance', 'repair', 'performance_upgrade'], { errorMap: () => ({ message: 'Please select a category' }), }), subtypes: z.array(z.string()).min(1, { message: 'Please select at least one subtype' }), schedule_type: z.enum(['interval', 'fixed_date', 'time_since_last'], { errorMap: () => ({ message: 'Please select a schedule type' }), }), interval_months: z.coerce.number().positive().optional().or(z.literal('')), interval_miles: z.coerce.number().positive().optional().or(z.literal('')), fixed_due_date: z.string().optional(), email_notifications: z.boolean().optional(), reminder_days_1: z.coerce.number().optional().or(z.literal('')), reminder_days_2: z.coerce.number().optional().or(z.literal('')), reminder_days_3: z.coerce.number().optional().or(z.literal('')), }) .refine( (data) => { if (data.schedule_type === 'fixed_date') { return !!data.fixed_due_date; } return true; }, { message: 'Fixed due date is required for fixed date schedules', path: ['fixed_due_date'], } ) .refine( (data) => { if (data.schedule_type === 'interval' || data.schedule_type === 'time_since_last') { return !!data.interval_months || !!data.interval_miles; } return true; }, { message: 'At least one of interval months or interval miles is required', path: ['interval_months'], } ); type FormData = z.infer; const REMINDER_OPTIONS = [ { value: '', label: 'None' }, { value: '1', label: '1 day' }, { value: '7', label: '7 days' }, { value: '14', label: '14 days' }, { value: '30', label: '30 days' }, { value: '60', label: '60 days' }, ]; export const MaintenanceScheduleForm: React.FC = () => { const { data: vehicles, isLoading: isLoadingVehicles } = useVehicles(); const { createSchedule, isScheduleMutating } = useMaintenanceRecords(); const [selectedCategory, setSelectedCategory] = useState(null); const { control, handleSubmit, watch, setValue, reset, formState: { errors, isValid }, } = useForm({ resolver: zodResolver(schema), mode: 'onChange', defaultValues: { vehicle_id: '', category: undefined as any, subtypes: [], schedule_type: 'interval' as ScheduleType, interval_months: '' as any, interval_miles: '' as any, fixed_due_date: '', email_notifications: false, reminder_days_1: '' as any, reminder_days_2: '' as any, reminder_days_3: '' as any, }, }); // Watch category and schedule type changes const watchedCategory = watch('category'); const watchedScheduleType = watch('schedule_type'); useEffect(() => { if (watchedCategory) { setSelectedCategory(watchedCategory as MaintenanceCategory); setValue('subtypes', []); } }, [watchedCategory, setValue]); const onSubmit = async (data: FormData) => { try { const payload: CreateScheduleRequest = { vehicleId: data.vehicle_id, category: data.category as MaintenanceCategory, subtypes: data.subtypes, scheduleType: data.schedule_type as ScheduleType, intervalMonths: data.interval_months ? Number(data.interval_months) : undefined, intervalMiles: data.interval_miles ? Number(data.interval_miles) : undefined, fixedDueDate: data.fixed_due_date || undefined, emailNotifications: data.email_notifications, reminderDays1: data.reminder_days_1 ? Number(data.reminder_days_1) : undefined, reminderDays2: data.reminder_days_2 ? Number(data.reminder_days_2) : undefined, reminderDays3: data.reminder_days_3 ? Number(data.reminder_days_3) : undefined, }; await createSchedule(payload); toast.success('Maintenance schedule created successfully'); // Reset form reset({ vehicle_id: '', category: undefined as any, subtypes: [], schedule_type: 'interval' as ScheduleType, interval_months: '' as any, interval_miles: '' as any, fixed_due_date: '', email_notifications: false, reminder_days_1: '' as any, reminder_days_2: '' as any, reminder_days_3: '' as any, }); setSelectedCategory(null); } catch (error) { console.error('Failed to create maintenance schedule:', error); toast.error('Failed to create maintenance schedule'); } }; if (isLoadingVehicles) { return ( ); } return (
{/* Vehicle Selection */} ( Vehicle * {errors.vehicle_id && ( {errors.vehicle_id.message} )} )} /> {/* Category Selection */} ( Category * {errors.category && ( {errors.category.message} )} )} /> {/* Subtypes */} {selectedCategory && ( Subtypes * ( {errors.subtypes && ( {errors.subtypes.message} )} )} /> )} {/* Schedule Type */} ( Schedule Type * } label="Interval-based (every X months/miles)" sx={{ mb: 1, '& .MuiFormControlLabel-label': { fontSize: { xs: 14, sm: 16 }, }, }} /> } label="Fixed date" sx={{ mb: 1, '& .MuiFormControlLabel-label': { fontSize: { xs: 14, sm: 16 }, }, }} /> } label="Time since last service" sx={{ '& .MuiFormControlLabel-label': { fontSize: { xs: 14, sm: 16 }, }, }} /> {errors.schedule_type && ( {errors.schedule_type.message} )} )} /> {/* Conditional fields based on schedule type */} {(watchedScheduleType === 'interval' || watchedScheduleType === 'time_since_last') && ( <> ( )} /> ( )} /> )} {watchedScheduleType === 'fixed_date' && ( ( field.onChange(newValue?.toISOString().split('T')[0] || '') } format="MM/DD/YYYY" slotProps={{ textField: { fullWidth: true, error: !!errors.fixed_due_date, helperText: errors.fixed_due_date?.message, sx: { '& .MuiOutlinedInput-root': { minHeight: 56, }, }, }, }} /> )} /> )} {/* Reminder Dropdowns */} Reminders ( Reminder 1 )} /> ( Reminder 2 )} /> ( Reminder 3 )} /> {/* Email Notifications Toggle */} ( )} /> {/* Submit Button */}
); };