profile-form.tsx 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. import { z } from 'zod'
  2. import { Link } from 'react-router-dom'
  3. import { useFieldArray, useForm } from 'react-hook-form'
  4. import { Button } from '@/components/custom/button'
  5. import {
  6. Form,
  7. FormControl,
  8. FormDescription,
  9. FormField,
  10. FormItem,
  11. FormLabel,
  12. FormMessage,
  13. } from '@/components/ui/form'
  14. import { Input } from '@/components/ui/input'
  15. import {
  16. Select,
  17. SelectContent,
  18. SelectItem,
  19. SelectTrigger,
  20. SelectValue,
  21. } from '@/components/ui/select'
  22. import { Textarea } from '@/components/ui/textarea'
  23. import { toast } from '@/components/ui/use-toast'
  24. import { cn } from '@/lib/utils'
  25. import { zodResolver } from '@hookform/resolvers/zod'
  26. const profileFormSchema = z.object({
  27. username: z
  28. .string()
  29. .min(2, {
  30. message: 'Username must be at least 2 characters.',
  31. })
  32. .max(30, {
  33. message: 'Username must not be longer than 30 characters.',
  34. }),
  35. email: z
  36. .string({
  37. required_error: 'Please select an email to display.',
  38. })
  39. .email(),
  40. bio: z.string().max(160).min(4),
  41. urls: z
  42. .array(
  43. z.object({
  44. value: z.string().url({ message: 'Please enter a valid URL.' }),
  45. })
  46. )
  47. .optional(),
  48. })
  49. type ProfileFormValues = z.infer<typeof profileFormSchema>
  50. // This can come from your database or API.
  51. const defaultValues: Partial<ProfileFormValues> = {
  52. bio: 'I own a computer.',
  53. urls: [
  54. { value: 'https://shadcn.com' },
  55. { value: 'http://twitter.com/shadcn' },
  56. ],
  57. }
  58. export default function ProfileForm() {
  59. const form = useForm<ProfileFormValues>({
  60. resolver: zodResolver(profileFormSchema),
  61. defaultValues,
  62. mode: 'onChange',
  63. })
  64. const { fields, append } = useFieldArray({
  65. name: 'urls',
  66. control: form.control,
  67. })
  68. function onSubmit(data: ProfileFormValues) {
  69. toast({
  70. title: 'You submitted the following values:',
  71. description: (
  72. <pre className='mt-2 w-[340px] rounded-md bg-slate-950 p-4'>
  73. <code className='text-white'>{JSON.stringify(data, null, 2)}</code>
  74. </pre>
  75. ),
  76. })
  77. }
  78. return (
  79. <Form {...form}>
  80. <form onSubmit={form.handleSubmit(onSubmit)} className='space-y-8'>
  81. <FormField
  82. control={form.control}
  83. name='username'
  84. render={({ field }) => (
  85. <FormItem>
  86. <FormLabel>Username</FormLabel>
  87. <FormControl>
  88. <Input placeholder='shadcn' {...field} />
  89. </FormControl>
  90. <FormDescription>
  91. This is your public display name. It can be your real name or a
  92. pseudonym. You can only change this once every 30 days.
  93. </FormDescription>
  94. <FormMessage />
  95. </FormItem>
  96. )}
  97. />
  98. <FormField
  99. control={form.control}
  100. name='email'
  101. render={({ field }) => (
  102. <FormItem>
  103. <FormLabel>Email</FormLabel>
  104. <Select onValueChange={field.onChange} defaultValue={field.value}>
  105. <FormControl>
  106. <SelectTrigger>
  107. <SelectValue placeholder='Select a verified email to display' />
  108. </SelectTrigger>
  109. </FormControl>
  110. <SelectContent>
  111. <SelectItem value='m@example.com'>m@example.com</SelectItem>
  112. <SelectItem value='m@google.com'>m@google.com</SelectItem>
  113. <SelectItem value='m@support.com'>m@support.com</SelectItem>
  114. </SelectContent>
  115. </Select>
  116. <FormDescription>
  117. You can manage verified email addresses in your{' '}
  118. <Link to='/examples/forms'>email settings</Link>.
  119. </FormDescription>
  120. <FormMessage />
  121. </FormItem>
  122. )}
  123. />
  124. <FormField
  125. control={form.control}
  126. name='bio'
  127. render={({ field }) => (
  128. <FormItem>
  129. <FormLabel>Bio</FormLabel>
  130. <FormControl>
  131. <Textarea
  132. placeholder='Tell us a little bit about yourself'
  133. className='resize-none'
  134. {...field}
  135. />
  136. </FormControl>
  137. <FormDescription>
  138. You can <span>@mention</span> other users and organizations to
  139. link to them.
  140. </FormDescription>
  141. <FormMessage />
  142. </FormItem>
  143. )}
  144. />
  145. <div>
  146. {fields.map((field, index) => (
  147. <FormField
  148. control={form.control}
  149. key={field.id}
  150. name={`urls.${index}.value`}
  151. render={({ field }) => (
  152. <FormItem>
  153. <FormLabel className={cn(index !== 0 && 'sr-only')}>
  154. URLs
  155. </FormLabel>
  156. <FormDescription className={cn(index !== 0 && 'sr-only')}>
  157. Add links to your website, blog, or social media profiles.
  158. </FormDescription>
  159. <FormControl>
  160. <Input {...field} />
  161. </FormControl>
  162. <FormMessage />
  163. </FormItem>
  164. )}
  165. />
  166. ))}
  167. <Button
  168. type='button'
  169. variant='outline'
  170. size='sm'
  171. className='mt-2'
  172. onClick={() => append({ value: '' })}
  173. >
  174. Add URL
  175. </Button>
  176. </div>
  177. <Button type='submit'>Update profile</Button>
  178. </form>
  179. </Form>
  180. )
  181. }