I use a combination of several libraries to create a form, namely react hook form, zod, shadcn ui and the nextjs framework. My form has two fields for input that retrieve values from a database call, the first field is called it_service
and the second is called technology_version
. What I wish to have is to have the technology_version
disabled at first, it will only be enabled after the user has selected a value for it_service
. Furthermore the database call to retrieve the possible values for technology_version
should be based on the input value of it_service
by the user. So for example if the user selects ABC
as the value of it_service
then the server action should be something of that nature in SQL select distinct technology_version from table where it_service = ABC
. With that the user can only select technology_version
for which the it_service
is ABC
. Here is my code so far.
'use client';
import { zodResolver } from '@hookform/resolvers/zod';
import { CaretSortIcon } from '@radix-ui/react-icons';
import { useForm } from 'react-hook-form';
import { z } from 'zod';
import { cn } from '@/app/lib/utils';
import { Button } from '@/components/ui/button';
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@/components/ui/form';
import {
Popover,
PopoverContent,
PopoverTrigger,
} from '@/components/ui/popover';
import { toast } from '@/components/ui/use-toast';
import * as React from 'react';
import { VirtualizedCommand } from '@/components/ui/virtualized-combobox';
const FormSchema = z.object({
service: z.string({
required_error: 'Please select an IT Service.',
}),
tech_version: z.string({
required_error: 'Please select a technology version',
}),
});
export function ComboboxForm({
data,
width,
}: {
data: string[];
width: string;
}) {
const options = data.map((item) => ({
value: item,
label: item,
}));
const form = useForm<z.infer<typeof FormSchema>>({
resolver: zodResolver(FormSchema),
});
const [open, setOpen] = React.useState<boolean>(false);
function onSubmit(data: z.infer<typeof FormSchema>) {
toast({
title: 'You submitted the following values:',
description: (
<pre className="mt-2 w-[340px] rounded-md bg-slate-950 p-4">
<code className="text-white">{JSON.stringify(data, null, 2)}</code>
</pre>
),
});
}
return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-6">
<div className="rounded-md bg-gray-50 p-4 md:p-6">
<FormField
control={form.control}
name="service"
render={({ field }) => (
<FormItem className="flex flex-col">
<FormLabel className="mb-2 block text-sm font-medium">
IT Service
</FormLabel>
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<FormControl>
<Button
variant="outline"
role="combobox"
className={cn(
'w-[384px] justify-between',
!field.value && 'text-muted-foreground',
)}
style={{
width: width,
}}
>
{field.value
? options.find(
(option) => option.value === field.value,
)?.label
: 'Select IT Service'}
<CaretSortIcon className="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
</FormControl>
</PopoverTrigger>
<PopoverContent
className="w-[384px] p-0"
style={{ width: width }}
>
<VirtualizedCommand
height="208px"
options={options}
placeholder="Search IT Service ..."
selectedOption={field.value}
onSelectOption={(currentValue) => {
field.onChange(currentValue);
setOpen(false);
}}
/>
</PopoverContent>
</Popover>
<FormDescription>
This is the IT service for which the remediation date should
be updated.
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="tech_version"
render={({ field }) => (
<FormItem className="flex flex-col">
<FormLabel className="mt-2 block text-sm font-medium">
Technology Version
</FormLabel>
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<FormControl>
<Button
variant="outline"
role="combobox"
className={cn(
'justify-between',
!field.value && 'text-muted-foreground',
)}
style={{
width: width,
}}
>
{field.value
? options.find(
(option) => option.value === field.value,
)?.label
: 'Select Technology Version'}
<CaretSortIcon className="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
</FormControl>
</PopoverTrigger>
<PopoverContent className="p-0" style={{ width: width }}>
<VirtualizedCommand
height="208px"
options={options}
placeholder="Search Technology Version ..."
selectedOption={field.value}
onSelectOption={(currentValue) => {
field.onChange(currentValue);
setOpen(false);
}}
/>
</PopoverContent>
</Popover>
<FormDescription>
This is the technology version for which the remediation date
should be updated.
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<Button type="submit" className="mt-6">
Submit
</Button>
</div>
</form>
</Form>
);
}