account-form.tsx 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. import { z } from 'zod'
  2. import { format } from 'date-fns'
  3. import { useForm } from 'react-hook-form'
  4. import { CalendarIcon, CaretSortIcon, CheckIcon } from '@radix-ui/react-icons'
  5. import { zodResolver } from '@hookform/resolvers/zod'
  6. import { cn } from '@/lib/utils'
  7. import { toast } from '@/hooks/use-toast'
  8. import { Calendar } from '@/components/ui/calendar'
  9. import {
  10. Command,
  11. CommandEmpty,
  12. CommandGroup,
  13. CommandInput,
  14. CommandItem,
  15. CommandList,
  16. } from '@/components/ui/command'
  17. import {
  18. Form,
  19. FormControl,
  20. FormDescription,
  21. FormField,
  22. FormItem,
  23. FormLabel,
  24. FormMessage,
  25. } from '@/components/ui/form'
  26. import { Input } from '@/components/ui/input'
  27. import {
  28. Popover,
  29. PopoverContent,
  30. PopoverTrigger,
  31. } from '@/components/ui/popover'
  32. import { Button } from '@/components/button'
  33. const languages = [
  34. { label: 'English', value: 'en' },
  35. { label: 'French', value: 'fr' },
  36. { label: 'German', value: 'de' },
  37. { label: 'Spanish', value: 'es' },
  38. { label: 'Portuguese', value: 'pt' },
  39. { label: 'Russian', value: 'ru' },
  40. { label: 'Japanese', value: 'ja' },
  41. { label: 'Korean', value: 'ko' },
  42. { label: 'Chinese', value: 'zh' },
  43. ] as const
  44. const accountFormSchema = z.object({
  45. name: z
  46. .string()
  47. .min(2, {
  48. message: 'Name must be at least 2 characters.',
  49. })
  50. .max(30, {
  51. message: 'Name must not be longer than 30 characters.',
  52. }),
  53. dob: z.date({
  54. required_error: 'A date of birth is required.',
  55. }),
  56. language: z.string({
  57. required_error: 'Please select a language.',
  58. }),
  59. })
  60. type AccountFormValues = z.infer<typeof accountFormSchema>
  61. // This can come from your database or API.
  62. const defaultValues: Partial<AccountFormValues> = {
  63. // name: "Your name",
  64. // dob: new Date("2023-01-23"),
  65. }
  66. export function AccountForm() {
  67. const form = useForm<AccountFormValues>({
  68. resolver: zodResolver(accountFormSchema),
  69. defaultValues,
  70. })
  71. function onSubmit(data: AccountFormValues) {
  72. toast({
  73. title: 'You submitted the following values:',
  74. description: (
  75. <pre className='mt-2 w-[340px] rounded-md bg-slate-950 p-4'>
  76. <code className='text-white'>{JSON.stringify(data, null, 2)}</code>
  77. </pre>
  78. ),
  79. })
  80. }
  81. return (
  82. <Form {...form}>
  83. <form onSubmit={form.handleSubmit(onSubmit)} className='space-y-8'>
  84. <FormField
  85. control={form.control}
  86. name='name'
  87. render={({ field }) => (
  88. <FormItem>
  89. <FormLabel>Name</FormLabel>
  90. <FormControl>
  91. <Input placeholder='Your name' {...field} />
  92. </FormControl>
  93. <FormDescription>
  94. This is the name that will be displayed on your profile and in
  95. emails.
  96. </FormDescription>
  97. <FormMessage />
  98. </FormItem>
  99. )}
  100. />
  101. <FormField
  102. control={form.control}
  103. name='dob'
  104. render={({ field }) => (
  105. <FormItem className='flex flex-col'>
  106. <FormLabel>Date of birth</FormLabel>
  107. <Popover>
  108. <PopoverTrigger asChild>
  109. <FormControl>
  110. <Button
  111. variant={'outline'}
  112. className={cn(
  113. 'w-[240px] pl-3 text-left font-normal',
  114. !field.value && 'text-muted-foreground'
  115. )}
  116. >
  117. {field.value ? (
  118. format(field.value, 'MMM d, yyyy')
  119. ) : (
  120. <span>Pick a date</span>
  121. )}
  122. <CalendarIcon className='ml-auto h-4 w-4 opacity-50' />
  123. </Button>
  124. </FormControl>
  125. </PopoverTrigger>
  126. <PopoverContent className='w-auto p-0' align='start'>
  127. <Calendar
  128. mode='single'
  129. selected={field.value}
  130. onSelect={field.onChange}
  131. disabled={(date: Date) =>
  132. date > new Date() || date < new Date('1900-01-01')
  133. }
  134. initialFocus
  135. />
  136. </PopoverContent>
  137. </Popover>
  138. <FormDescription>
  139. Your date of birth is used to calculate your age.
  140. </FormDescription>
  141. <FormMessage />
  142. </FormItem>
  143. )}
  144. />
  145. <FormField
  146. control={form.control}
  147. name='language'
  148. render={({ field }) => (
  149. <FormItem className='flex flex-col'>
  150. <FormLabel>Language</FormLabel>
  151. <Popover>
  152. <PopoverTrigger asChild>
  153. <FormControl>
  154. <Button
  155. variant='outline'
  156. role='combobox'
  157. className={cn(
  158. 'w-[200px] justify-between',
  159. !field.value && 'text-muted-foreground'
  160. )}
  161. >
  162. {field.value
  163. ? languages.find(
  164. (language) => language.value === field.value
  165. )?.label
  166. : 'Select language'}
  167. <CaretSortIcon className='ml-2 h-4 w-4 shrink-0 opacity-50' />
  168. </Button>
  169. </FormControl>
  170. </PopoverTrigger>
  171. <PopoverContent className='w-[200px] p-0'>
  172. <Command>
  173. <CommandInput placeholder='Search language...' />
  174. <CommandEmpty>No language found.</CommandEmpty>
  175. <CommandGroup>
  176. <CommandList>
  177. {languages.map((language) => (
  178. <CommandItem
  179. value={language.label}
  180. key={language.value}
  181. onSelect={() => {
  182. form.setValue('language', language.value)
  183. }}
  184. >
  185. <CheckIcon
  186. className={cn(
  187. 'mr-2 h-4 w-4',
  188. language.value === field.value
  189. ? 'opacity-100'
  190. : 'opacity-0'
  191. )}
  192. />
  193. {language.label}
  194. </CommandItem>
  195. ))}
  196. </CommandList>
  197. </CommandGroup>
  198. </Command>
  199. </PopoverContent>
  200. </Popover>
  201. <FormDescription>
  202. This is the language that will be used in the dashboard.
  203. </FormDescription>
  204. <FormMessage />
  205. </FormItem>
  206. )}
  207. />
  208. <Button type='submit'>Update account</Button>
  209. </form>
  210. </Form>
  211. )
  212. }