Добрый день! У меня возникли небольшие проблемы после рефакторинга моего кода. Я создал условное поле, в котором, если пользователь выберет Yes
, ниже появится дополнительное поле, в котором пользователь должен на него ответить. А если пользователь выбрал No
, это будет просто пустая строка.
Проблема, с которой я сталкиваюсь сейчас, заключается в том, что выбранное Yes
дополнительное поле не установлено как обязательное.
Шаги.ts
const steps = [
{
id: 'Step 1',
name: 'General Information',
fields: ['title', 'email', 'fullname', 'contact_person', 'department']
},
{
id: 'Step 2',
name: 'Date, Time and Purpose',
fields: ['event_date, start_time', 'end_time', 'purpose']
},
{
id: "Step 3",
name: "Dry Run",
fields: ['does_have_dry_run', 'dry_run_date', 'dry_run_start_time', 'dry_run_end_time', 'does_have_assistance', 'name_of_assistance']
},
{
id: "Step 4",
name: "Type of Service",
fields: ['meeting_type_option', 'meeting_type_service', 'meeting_type_link']
},
{
id: "Steps 5",
name: "Type of Service",
}
]
Форма.tsx
const CreateScheduleDialog = ({ open, setOpen, pickedDate }: Props) => {
const [optionalDate, setOptionalDate] = useState<Date | undefined>(
new Date()
);
const [step, setStep] = useState(0);
const currentStep = steps[step];
const form = useForm<CreateAppointmentSchemaType>({
resolver: zodResolver(CreateAppointmentSchema),
defaultValues: {
does_have_dry_run: false,
dry_run_date: new Date(),
dry_run_start_time: '',
dry_run_end_time: '',
}
})
type FieldName = keyof CreateAppointmentSchemaType;
const nextStep = async () => {
const fields = steps[step].fields as FieldName[];
const isValid = await form.trigger(fields, { shouldFocus: true });
if (!isValid) return;
setStep((prevStep) => prevStep + 1);
};
const prevStep = () => {
if (step > 0) {
setStep(step - 1);
}
};
const handleClick = () => {
form.resetField("dry_run_date");
form.resetField("dry_run_start_time");
form.resetField("dry_run_end_time");
};
return (
<Dialog>
<DialogTrigger asChild>
<Button disabled = {open !== false ? false : true} className='w-full' color = "primary-foreground">Add Schedule</Button>
</DialogTrigger>
<DialogContent className = {cn(`max-w-[400px] md:max-w-[800px]`)}>
<DialogHeader>
<DialogTitle>{currentStep.name}</DialogTitle>
<DialogDescription>
Step {step + 1} of {steps.length}
</DialogDescription>
</DialogHeader>
<Form {...form}>
{step == 2 && (
<div className='flex flex-col gap-y-2'>
<FormField
control = {form.control}
name = "does_have_dry_run"
render = {({ field }) => (
<FormItem className = "space-y-3">
<FormLabel>
(Optional) Preferred Meeting Date / Dry Run
</FormLabel>
<FormControl>
<RadioGroup
onValueChange = {(value) =>
field.onChange(value === "true")
}
defaultValue = {String(field.value)}
className = "flex flex-col space-y-1"
>
<FormItem className = "flex items-center space-x-3 space-y-0">
<FormControl>
<RadioGroupItem
onClick = {handleClick}
value = "false" />
</FormControl>
<FormLabel className = "font-normal">
None / No
</FormLabel>
</FormItem>
<FormItem className = "flex items-center space-x-3 space-y-0">
<FormControl>
<RadioGroupItem value = "true" />
</FormControl>
<FormLabel className = "font-normal">
Yes
</FormLabel>
</FormItem>
</RadioGroup>
</FormControl>
{field.value === true && (
<FormItem>
<div className = "flex flex-col gap-2 pt-2">
<Label>(Dry Run) Time of Event</Label>
<div className = "flex flex-col gap-2">
<FormField
control = {form.control}
name = "dry_run_date"
render = {({ field }) => (
<FormItem className = "flex flex-col">
<FormLabel>Date</FormLabel>
<Popover>
<PopoverTrigger asChild>
<FormControl>
<Button
variant = {"outline"}
className = {cn(
"w-[240px] pl-3 text-left font-normal",
!field.value &&
"text-muted-foreground"
)}
>
{field.value ? (
format(field.value, "PPP")
) : (
<span>Pick a date</span>
)}
<CalendarIcon className = "ml-auto h-4 w-4 opacity-50" />
</Button>
</FormControl>
</PopoverTrigger>
<PopoverContent
className = "w-auto p-0"
align = "start"
>
<Calendar
mode = "single"
disabled = {(date) =>
new Date(date) <= new Date()
} // Disable past dates and today's date
selected = {optionalDate}
onSelect = {field.onChange}
initialFocus
/>
</PopoverContent>
</Popover>
<FormMessage />
</FormItem>
)}
/>
<FormField
control = {form.control}
name = "dry_run_start_time"
render = {({ field }) => (
<FormItem>
<FormLabel>Start</FormLabel>
<FormControl>
<Input type = "time" placeholder = "Start" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control = {form.control}
name = "dry_run_end_time"
render = {({ field }) => (
<FormItem>
<FormLabel>End</FormLabel>
<FormControl>
<Input type = "time" placeholder = "End" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
</div>
</FormItem>
)}
<FormMessage />
</FormItem>
)}
/>
</div>
)}
</Form>
<DialogFooter>
<Button onClick = {prevStep} disabled = {step === 0}>Back</Button>
{step === steps.length - 1 ? (
<Button
disabled = {!form.formState.isValid}>
Submit
</Button>
) : (
<Button
onClick = {nextStep}
>
Next
</Button>
)}
</DialogFooter>
</DialogContent>
</Dialog>
)
}
Теперь я создал зод, в котором использую .superRefine
, чтобы добавить проверку, но он не работает должным образом.
зод.ц
import { z } from "zod";
export const CreateAppointmentSchema = z.object({
// //SET 3
does_have_dry_run: z.boolean({
required_error: "Please select if yes or no"
}),
dry_run_date: z.coerce.date().optional(),
dry_run_start_time: z.string().optional(),
dry_run_end_time: z.string().optional(),
})
.superRefine(({
does_have_dry_run,
dry_run_date,
dry_run_start_time,
dry_run_end_time,
}, ctx) => {
if (does_have_dry_run === true) {
if (!dry_run_date) {
ctx.addIssue({
code: 'custom',
message: 'Please provide information in the missing field.',
path: ['dry_run_date']
})
}
if (!dry_run_start_time) {
ctx.addIssue({
code: 'custom',
message: 'Please provide information in the missing field.',
path: ['dry_run_start_time']
})
}
if (!dry_run_end_time) {
ctx.addIssue({
code: 'custom',
message: 'Please provide information in the missing field.',
path: ['dry_run_end_time']
})
}
}
});
export type CreateAppointmentSchemaType = z.infer<typeof CreateAppointmentSchema>
если пользователь выберет «да» в раскрывающемся списке, следующие поля будут обязательными. Вы можете изменить свою схему zod таким образом, чтобы получить желаемые результаты.
const CreateAppointmentSchema = z
.object({
dryrun: z.string(),
dry_run_start_time: z.string(),
dry_run_end_time: z.string(),
})
.partial()
.superRefine((v, ctx) => {
if (v.dryrun == "no" || (v.dry_run_start_time && v.dry_run_end_time))
return true;
if (!v.dry_run_start_time) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "dryrun start time is required",
path: ["dry_run_start_time"],
});
}
if (!v.dry_run_end_time) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "dryrun end time is required",
path: ["dry_run_end_time"],
});
}
});
вот также рабочий пример решения: https://replit.com/@MubashirWaheed/zodValidation#app/page.tsx