From 0e705f5767fd1c79674f7e6eb4695163099dfcdc Mon Sep 17 00:00:00 2001 From: Owen Schwartz Date: Sun, 13 Oct 2024 16:18:54 -0400 Subject: [PATCH] Add form components --- package.json | 8 + src/app/configuration/page.tsx | 7 + .../sites/[siteId]/account/page.tsx | 18 ++ .../sites/[siteId]/appearance/page.tsx | 18 ++ .../sites/[siteId]/display/page.tsx | 17 ++ .../configuration/sites/[siteId]/layout.tsx | 76 ++++++ .../sites/[siteId]/notifications/page.tsx | 17 ++ src/app/configuration/sites/[siteId]/page.tsx | 17 ++ src/app/configuration/sites/page.tsx | 7 + src/app/profile/account/account-form.tsx | 176 ++++++++++++++ src/app/profile/account/page.tsx | 18 ++ .../profile/appearance/appearance-form.tsx | 164 +++++++++++++ src/app/profile/appearance/page.tsx | 18 ++ src/app/profile/components/sidebar-nav.tsx | 44 ++++ src/app/profile/display/display-form.tsx | 132 +++++++++++ src/app/profile/display/page.tsx | 17 ++ src/app/profile/layout.tsx | 75 ++++++ .../notifications/notifications-form.tsx | 222 ++++++++++++++++++ src/app/profile/notifications/page.tsx | 17 ++ src/app/profile/page.tsx | 17 ++ src/app/profile/profile-form.tsx | 192 +++++++++++++++ src/components/SiteForm.tsx | 0 src/components/account-form.tsx | 176 ++++++++++++++ src/components/appearance-form.tsx | 164 +++++++++++++ src/components/display-form.tsx | 132 +++++++++++ src/components/notifications-form.tsx | 222 ++++++++++++++++++ src/components/profile-form.tsx | 192 +++++++++++++++ src/components/sidebar-nav.tsx | 44 ++++ src/components/ui/checkbox.tsx | 30 +++ src/components/ui/command.tsx | 155 ++++++++++++ src/components/ui/dialog.tsx | 122 ++++++++++ src/components/ui/popover.tsx | 31 +++ src/components/ui/radio-group.tsx | 44 ++++ src/components/ui/select.tsx | 160 +++++++++++++ src/components/ui/separator.tsx | 31 +++ src/components/ui/switch.tsx | 29 +++ src/components/ui/textarea.tsx | 24 ++ 37 files changed, 2833 insertions(+) create mode 100644 src/app/configuration/page.tsx create mode 100644 src/app/configuration/sites/[siteId]/account/page.tsx create mode 100644 src/app/configuration/sites/[siteId]/appearance/page.tsx create mode 100644 src/app/configuration/sites/[siteId]/display/page.tsx create mode 100644 src/app/configuration/sites/[siteId]/layout.tsx create mode 100644 src/app/configuration/sites/[siteId]/notifications/page.tsx create mode 100644 src/app/configuration/sites/[siteId]/page.tsx create mode 100644 src/app/configuration/sites/page.tsx create mode 100644 src/app/profile/account/account-form.tsx create mode 100644 src/app/profile/account/page.tsx create mode 100644 src/app/profile/appearance/appearance-form.tsx create mode 100644 src/app/profile/appearance/page.tsx create mode 100644 src/app/profile/components/sidebar-nav.tsx create mode 100644 src/app/profile/display/display-form.tsx create mode 100644 src/app/profile/display/page.tsx create mode 100644 src/app/profile/layout.tsx create mode 100644 src/app/profile/notifications/notifications-form.tsx create mode 100644 src/app/profile/notifications/page.tsx create mode 100644 src/app/profile/page.tsx create mode 100644 src/app/profile/profile-form.tsx create mode 100644 src/components/SiteForm.tsx create mode 100644 src/components/account-form.tsx create mode 100644 src/components/appearance-form.tsx create mode 100644 src/components/display-form.tsx create mode 100644 src/components/notifications-form.tsx create mode 100644 src/components/profile-form.tsx create mode 100644 src/components/sidebar-nav.tsx create mode 100644 src/components/ui/checkbox.tsx create mode 100644 src/components/ui/command.tsx create mode 100644 src/components/ui/dialog.tsx create mode 100644 src/components/ui/popover.tsx create mode 100644 src/components/ui/radio-group.tsx create mode 100644 src/components/ui/select.tsx create mode 100644 src/components/ui/separator.tsx create mode 100644 src/components/ui/switch.tsx create mode 100644 src/components/ui/textarea.tsx diff --git a/package.json b/package.json index 9e2a208..7c12f5d 100644 --- a/package.json +++ b/package.json @@ -18,9 +18,16 @@ "@hookform/resolvers": "3.9.0", "@lucia-auth/adapter-drizzle": "1.1.0", "@node-rs/argon2": "1.8.3", + "@radix-ui/react-checkbox": "1.1.2", + "@radix-ui/react-dialog": "1.1.2", "@radix-ui/react-icons": "1.3.0", "@radix-ui/react-label": "2.1.0", + "@radix-ui/react-popover": "1.1.2", + "@radix-ui/react-radio-group": "1.2.1", + "@radix-ui/react-select": "2.1.2", + "@radix-ui/react-separator": "1.1.0", "@radix-ui/react-slot": "1.1.0", + "@radix-ui/react-switch": "1.1.1", "@radix-ui/react-toast": "1.2.2", "@react-email/components": "0.0.25", "@react-email/tailwind": "0.1.0", @@ -28,6 +35,7 @@ "better-sqlite3": "11.3.0", "class-variance-authority": "0.7.0", "clsx": "2.1.1", + "cmdk": "1.0.0", "cookie-parser": "1.4.6", "cors": "2.8.5", "drizzle-orm": "0.33.0", diff --git a/src/app/configuration/page.tsx b/src/app/configuration/page.tsx new file mode 100644 index 0000000..9af30d8 --- /dev/null +++ b/src/app/configuration/page.tsx @@ -0,0 +1,7 @@ +export default async function Page() { + return ( + <> +

IDK what this will show...

+ + ); +} diff --git a/src/app/configuration/sites/[siteId]/account/page.tsx b/src/app/configuration/sites/[siteId]/account/page.tsx new file mode 100644 index 0000000..03df0d8 --- /dev/null +++ b/src/app/configuration/sites/[siteId]/account/page.tsx @@ -0,0 +1,18 @@ +import { Separator } from "@/components/ui/separator" +import { AccountForm } from "@/components/account-form" + +export default function SettingsAccountPage() { + return ( +
+
+

Account

+

+ Update your account settings. Set your preferred language and + timezone. +

+
+ + +
+ ) +} diff --git a/src/app/configuration/sites/[siteId]/appearance/page.tsx b/src/app/configuration/sites/[siteId]/appearance/page.tsx new file mode 100644 index 0000000..ca038aa --- /dev/null +++ b/src/app/configuration/sites/[siteId]/appearance/page.tsx @@ -0,0 +1,18 @@ +import { Separator } from "@/components/ui/separator" +import { AppearanceForm } from "@/components/appearance-form" + +export default function SettingsAppearancePage() { + return ( +
+
+

Appearance

+

+ Customize the appearance of the app. Automatically switch between day + and night themes. +

+
+ + +
+ ) +} diff --git a/src/app/configuration/sites/[siteId]/display/page.tsx b/src/app/configuration/sites/[siteId]/display/page.tsx new file mode 100644 index 0000000..f934f6e --- /dev/null +++ b/src/app/configuration/sites/[siteId]/display/page.tsx @@ -0,0 +1,17 @@ +import { Separator } from "@/components/ui/separator" +import { DisplayForm } from "@/components/display-form" + +export default function SettingsDisplayPage() { + return ( +
+
+

Display

+

+ Turn items on or off to control what's displayed in the app. +

+
+ + +
+ ) +} diff --git a/src/app/configuration/sites/[siteId]/layout.tsx b/src/app/configuration/sites/[siteId]/layout.tsx new file mode 100644 index 0000000..24bc7e6 --- /dev/null +++ b/src/app/configuration/sites/[siteId]/layout.tsx @@ -0,0 +1,76 @@ +import { Metadata } from "next" +import Image from "next/image" + +import { Separator } from "@/components/ui/separator" +import { SidebarNav } from "@/components/sidebar-nav" + +export const metadata: Metadata = { + title: "Forms", + description: "Advanced form example using react-hook-form and Zod.", +} + +const sidebarNavItems = [ + { + title: "Profile", + href: "/configuration/sites/{siteId}/", + }, + { + title: "Account", + href: "/configuration/sites/{siteId}/account", + }, + { + title: "Appearance", + href: "/configuration/sites/{siteId}/appearance", + }, + { + title: "Notifications", + href: "/configuration/sites/{siteId}/notifications", + }, + { + title: "Display", + href: "/configuration/sites/{siteId}/display", + }, +] + +interface SettingsLayoutProps { + children: React.ReactNode, + params: { siteId: string } +} + +export default function SettingsLayout({ children, params }: SettingsLayoutProps) { + return ( + <> +
+ Forms + Forms +
+
+
+

Settings

+

+ Manage your account settings and set e-mail preferences. +

+
+ +
+ +
{children}
+
+
+ + ) +} diff --git a/src/app/configuration/sites/[siteId]/notifications/page.tsx b/src/app/configuration/sites/[siteId]/notifications/page.tsx new file mode 100644 index 0000000..7c5c5ec --- /dev/null +++ b/src/app/configuration/sites/[siteId]/notifications/page.tsx @@ -0,0 +1,17 @@ +import { Separator } from "@/components/ui/separator" +import { NotificationsForm } from "@/components/notifications-form" + +export default function SettingsNotificationsPage() { + return ( +
+
+

Notifications

+

+ Configure how you receive notifications. +

+
+ + +
+ ) +} diff --git a/src/app/configuration/sites/[siteId]/page.tsx b/src/app/configuration/sites/[siteId]/page.tsx new file mode 100644 index 0000000..6674463 --- /dev/null +++ b/src/app/configuration/sites/[siteId]/page.tsx @@ -0,0 +1,17 @@ +import { Separator } from "@/components/ui/separator" +import { ProfileForm } from "@app/components/profile-form" + +export default function SettingsProfilePage() { + return ( +
+
+

Profile

+

+ This is how others will see you on the site. +

+
+ + +
+ ) +} diff --git a/src/app/configuration/sites/page.tsx b/src/app/configuration/sites/page.tsx new file mode 100644 index 0000000..f720649 --- /dev/null +++ b/src/app/configuration/sites/page.tsx @@ -0,0 +1,7 @@ +export default async function Page() { + return ( + <> +

This is where the table goes...

+ + ); +} diff --git a/src/app/profile/account/account-form.tsx b/src/app/profile/account/account-form.tsx new file mode 100644 index 0000000..2cb9bf1 --- /dev/null +++ b/src/app/profile/account/account-form.tsx @@ -0,0 +1,176 @@ +"use client" + +import { zodResolver } from "@hookform/resolvers/zod" +import { CalendarIcon, CaretSortIcon, CheckIcon } from "@radix-ui/react-icons" +import { useForm } from "react-hook-form" +import { z } from "zod" + +import { cn } from "@/lib/utils" +import { toast } from "@/hooks/use-toast" +import { Button } from "@/components/ui/button" +import { + Command, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, + CommandList, +} from "@/components/ui/command" +import { + Form, + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form" +import { Input } from "@/components/ui/input" +import { + Popover, + PopoverContent, + PopoverTrigger, +} from "@/components/ui/popover" + +const languages = [ + { label: "English", value: "en" }, + { label: "French", value: "fr" }, + { label: "German", value: "de" }, + { label: "Spanish", value: "es" }, + { label: "Portuguese", value: "pt" }, + { label: "Russian", value: "ru" }, + { label: "Japanese", value: "ja" }, + { label: "Korean", value: "ko" }, + { label: "Chinese", value: "zh" }, +] as const + +const accountFormSchema = z.object({ + name: z + .string() + .min(2, { + message: "Name must be at least 2 characters.", + }) + .max(30, { + message: "Name must not be longer than 30 characters.", + }), + dob: z.date({ + required_error: "A date of birth is required.", + }), + language: z.string({ + required_error: "Please select a language.", + }), +}) + +type AccountFormValues = z.infer + +// This can come from your database or API. +const defaultValues: Partial = { + // name: "Your name", + // dob: new Date("2023-01-23"), +} + +export function AccountForm() { + const form = useForm({ + resolver: zodResolver(accountFormSchema), + defaultValues, + }) + + function onSubmit(data: AccountFormValues) { + toast({ + title: "You submitted the following values:", + description: ( +
+          {JSON.stringify(data, null, 2)}
+        
+ ), + }) + } + + return ( +
+ + ( + + Name + + + + + This is the name that will be displayed on your profile and in + emails. + + + + )} + /> + ( + + Language + + + + + + + + + + + No language found. + + {languages.map((language) => ( + { + form.setValue("language", language.value) + }} + > + + {language.label} + + ))} + + + + + + + This is the language that will be used in the dashboard. + + + + )} + /> + + + + ) +} diff --git a/src/app/profile/account/page.tsx b/src/app/profile/account/page.tsx new file mode 100644 index 0000000..a8c3fae --- /dev/null +++ b/src/app/profile/account/page.tsx @@ -0,0 +1,18 @@ +import { Separator } from "@/components/ui/separator" +import { AccountForm } from "@/app/configuration/account/account-form" + +export default function SettingsAccountPage() { + return ( +
+
+

Account

+

+ Update your account settings. Set your preferred language and + timezone. +

+
+ + +
+ ) +} diff --git a/src/app/profile/appearance/appearance-form.tsx b/src/app/profile/appearance/appearance-form.tsx new file mode 100644 index 0000000..9a81f37 --- /dev/null +++ b/src/app/profile/appearance/appearance-form.tsx @@ -0,0 +1,164 @@ +"use client" + +import { zodResolver } from "@hookform/resolvers/zod" +import { ChevronDownIcon } from "@radix-ui/react-icons" +import { useForm } from "react-hook-form" +import { z } from "zod" + +import { cn } from "@/lib/utils" +import { toast } from "@/hooks/use-toast" +import { Button, buttonVariants } from "@/components/ui/button" +import { + Form, + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form" +import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group" + +const appearanceFormSchema = z.object({ + theme: z.enum(["light", "dark"], { + required_error: "Please select a theme.", + }), + font: z.enum(["inter", "manrope", "system"], { + invalid_type_error: "Select a font", + required_error: "Please select a font.", + }), +}) + +type AppearanceFormValues = z.infer + +// This can come from your database or API. +const defaultValues: Partial = { + theme: "light", +} + +export function AppearanceForm() { + const form = useForm({ + resolver: zodResolver(appearanceFormSchema), + defaultValues, + }) + + function onSubmit(data: AppearanceFormValues) { + toast({ + title: "You submitted the following values:", + description: ( +
+          {JSON.stringify(data, null, 2)}
+        
+ ), + }) + } + + return ( +
+ + ( + + Font +
+ + + + +
+ + Set the font you want to use in the dashboard. + + +
+ )} + /> + ( + + Theme + + Select the theme for the dashboard. + + + + + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + Light + + + + + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + Dark + + + + + + )} + /> + + + + + ) +} diff --git a/src/app/profile/appearance/page.tsx b/src/app/profile/appearance/page.tsx new file mode 100644 index 0000000..a8d65df --- /dev/null +++ b/src/app/profile/appearance/page.tsx @@ -0,0 +1,18 @@ +import { Separator } from "@/components/ui/separator" +import { AppearanceForm } from "@/app/configuration/appearance/appearance-form" + +export default function SettingsAppearancePage() { + return ( +
+
+

Appearance

+

+ Customize the appearance of the app. Automatically switch between day + and night themes. +

+
+ + +
+ ) +} diff --git a/src/app/profile/components/sidebar-nav.tsx b/src/app/profile/components/sidebar-nav.tsx new file mode 100644 index 0000000..addcfef --- /dev/null +++ b/src/app/profile/components/sidebar-nav.tsx @@ -0,0 +1,44 @@ +"use client" + +import Link from "next/link" +import { usePathname } from "next/navigation" + +import { cn } from "@/lib/utils" +import { buttonVariants } from "@/components/ui/button" + +interface SidebarNavProps extends React.HTMLAttributes { + items: { + href: string + title: string + }[] +} + +export function SidebarNav({ className, items, ...props }: SidebarNavProps) { + const pathname = usePathname() + + return ( + + ) +} diff --git a/src/app/profile/display/display-form.tsx b/src/app/profile/display/display-form.tsx new file mode 100644 index 0000000..cf1c6c0 --- /dev/null +++ b/src/app/profile/display/display-form.tsx @@ -0,0 +1,132 @@ +"use client" + +import { zodResolver } from "@hookform/resolvers/zod" +import { useForm } from "react-hook-form" +import { z } from "zod" + +import { toast } from "@/hooks/use-toast" +import { Button } from "@/components/ui/button" +import { Checkbox } from "@/components/ui/checkbox" +import { + Form, + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form" + +const items = [ + { + id: "recents", + label: "Recents", + }, + { + id: "home", + label: "Home", + }, + { + id: "applications", + label: "Applications", + }, + { + id: "desktop", + label: "Desktop", + }, + { + id: "downloads", + label: "Downloads", + }, + { + id: "documents", + label: "Documents", + }, +] as const + +const displayFormSchema = z.object({ + items: z.array(z.string()).refine((value) => value.some((item) => item), { + message: "You have to select at least one item.", + }), +}) + +type DisplayFormValues = z.infer + +// This can come from your database or API. +const defaultValues: Partial = { + items: ["recents", "home"], +} + +export function DisplayForm() { + const form = useForm({ + resolver: zodResolver(displayFormSchema), + defaultValues, + }) + + function onSubmit(data: DisplayFormValues) { + toast({ + title: "You submitted the following values:", + description: ( +
+          {JSON.stringify(data, null, 2)}
+        
+ ), + }) + } + + return ( +
+ + ( + +
+ Sidebar + + Select the items you want to display in the sidebar. + +
+ {items.map((item) => ( + { + return ( + + + { + return checked + ? field.onChange([...field.value, item.id]) + : field.onChange( + field.value?.filter( + (value) => value !== item.id + ) + ) + }} + /> + + + {item.label} + + + ) + }} + /> + ))} + +
+ )} + /> + + + + ) +} diff --git a/src/app/profile/display/page.tsx b/src/app/profile/display/page.tsx new file mode 100644 index 0000000..a5579fb --- /dev/null +++ b/src/app/profile/display/page.tsx @@ -0,0 +1,17 @@ +import { Separator } from "@/components/ui/separator" +import { DisplayForm } from "@/app/configuration/display/display-form" + +export default function SettingsDisplayPage() { + return ( +
+
+

Display

+

+ Turn items on or off to control what's displayed in the app. +

+
+ + +
+ ) +} diff --git a/src/app/profile/layout.tsx b/src/app/profile/layout.tsx new file mode 100644 index 0000000..caa96a3 --- /dev/null +++ b/src/app/profile/layout.tsx @@ -0,0 +1,75 @@ +import { Metadata } from "next" +import Image from "next/image" + +import { Separator } from "@/components/ui/separator" +import { SidebarNav } from "@/app/configuration/components/sidebar-nav" + +export const metadata: Metadata = { + title: "Forms", + description: "Advanced form example using react-hook-form and Zod.", +} + +const sidebarNavItems = [ + { + title: "Profile", + href: "/configuration", + }, + { + title: "Account", + href: "/configuration/account", + }, + { + title: "Appearance", + href: "/configuration/appearance", + }, + { + title: "Notifications", + href: "/configuration/notifications", + }, + { + title: "Display", + href: "/configuration/display", + }, +] + +interface SettingsLayoutProps { + children: React.ReactNode +} + +export default function SettingsLayout({ children }: SettingsLayoutProps) { + return ( + <> +
+ Forms + Forms +
+
+
+

Settings

+

+ Manage your account settings and set e-mail preferences. +

+
+ +
+ +
{children}
+
+
+ + ) +} diff --git a/src/app/profile/notifications/notifications-form.tsx b/src/app/profile/notifications/notifications-form.tsx new file mode 100644 index 0000000..cc19125 --- /dev/null +++ b/src/app/profile/notifications/notifications-form.tsx @@ -0,0 +1,222 @@ +"use client" + +import Link from "next/link" +import { zodResolver } from "@hookform/resolvers/zod" +import { useForm } from "react-hook-form" +import { z } from "zod" + +import { toast } from "@/hooks/use-toast" +import { Button } from "@/components/ui/button" +import { Checkbox } from "@/components/ui/checkbox" +import { + Form, + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form" +import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group" +import { Switch } from "@/components/ui/switch" + +const notificationsFormSchema = z.object({ + type: z.enum(["all", "mentions", "none"], { + required_error: "You need to select a notification type.", + }), + mobile: z.boolean().default(false).optional(), + communication_emails: z.boolean().default(false).optional(), + social_emails: z.boolean().default(false).optional(), + marketing_emails: z.boolean().default(false).optional(), + security_emails: z.boolean(), +}) + +type NotificationsFormValues = z.infer + +// This can come from your database or API. +const defaultValues: Partial = { + communication_emails: false, + marketing_emails: false, + social_emails: true, + security_emails: true, +} + +export function NotificationsForm() { + const form = useForm({ + resolver: zodResolver(notificationsFormSchema), + defaultValues, + }) + + function onSubmit(data: NotificationsFormValues) { + toast({ + title: "You submitted the following values:", + description: ( +
+          {JSON.stringify(data, null, 2)}
+        
+ ), + }) + } + + return ( +
+ + ( + + Notify me about... + + + + + + + + All new messages + + + + + + + + Direct messages and mentions + + + + + + + Nothing + + + + + + )} + /> +
+

Email Notifications

+
+ ( + +
+ + Communication emails + + + Receive emails about your account activity. + +
+ + + +
+ )} + /> + ( + +
+ + Marketing emails + + + Receive emails about new products, features, and more. + +
+ + + +
+ )} + /> + ( + +
+ Social emails + + Receive emails for friend requests, follows, and more. + +
+ + + +
+ )} + /> + ( + +
+ Security emails + + Receive emails about your account activity and security. + +
+ + + +
+ )} + /> +
+
+ ( + + + + +
+ + Use different settings for my mobile devices + + + You can manage your mobile notifications in the{" "} + mobile settings page. + +
+
+ )} + /> + + + + ) +} diff --git a/src/app/profile/notifications/page.tsx b/src/app/profile/notifications/page.tsx new file mode 100644 index 0000000..efc2da6 --- /dev/null +++ b/src/app/profile/notifications/page.tsx @@ -0,0 +1,17 @@ +import { Separator } from "@/components/ui/separator" +import { NotificationsForm } from "@/app/configuration/notifications/notifications-form" + +export default function SettingsNotificationsPage() { + return ( +
+
+

Notifications

+

+ Configure how you receive notifications. +

+
+ + +
+ ) +} diff --git a/src/app/profile/page.tsx b/src/app/profile/page.tsx new file mode 100644 index 0000000..6674463 --- /dev/null +++ b/src/app/profile/page.tsx @@ -0,0 +1,17 @@ +import { Separator } from "@/components/ui/separator" +import { ProfileForm } from "@app/components/profile-form" + +export default function SettingsProfilePage() { + return ( +
+
+

Profile

+

+ This is how others will see you on the site. +

+
+ + +
+ ) +} diff --git a/src/app/profile/profile-form.tsx b/src/app/profile/profile-form.tsx new file mode 100644 index 0000000..0911ac0 --- /dev/null +++ b/src/app/profile/profile-form.tsx @@ -0,0 +1,192 @@ +"use client" + +import Link from "next/link" +import { zodResolver } from "@hookform/resolvers/zod" +import { useFieldArray, useForm } from "react-hook-form" +import { z } from "zod" + +import { cn } from "@/lib/utils" +import { toast } from "@/hooks/use-toast" + +import { Button } from "@/components/ui/button" +import { + Form, + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form" +import { Input } from "@/components/ui/input" +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select" +import { Textarea } from "@/components/ui/textarea" + +const profileFormSchema = z.object({ + username: z + .string() + .min(2, { + message: "Username must be at least 2 characters.", + }) + .max(30, { + message: "Username must not be longer than 30 characters.", + }), + email: z + .string({ + required_error: "Please select an email to display.", + }) + .email(), + bio: z.string().max(160).min(4), + urls: z + .array( + z.object({ + value: z.string().url({ message: "Please enter a valid URL." }), + }) + ) + .optional(), +}) + +type ProfileFormValues = z.infer + +// This can come from your database or API. +const defaultValues: Partial = { + bio: "I own a computer.", + urls: [ + { value: "https://shadcn.com" }, + { value: "http://twitter.com/shadcn" }, + ], +} + +export function ProfileForm() { + const form = useForm({ + resolver: zodResolver(profileFormSchema), + defaultValues, + mode: "onChange", + }) + + const { fields, append } = useFieldArray({ + name: "urls", + control: form.control, + }) + + function onSubmit(data: ProfileFormValues) { + toast({ + title: "You submitted the following values:", + description: ( +
+          {JSON.stringify(data, null, 2)}
+        
+ ), + }) + } + + return ( +
+ + ( + + Username + + + + + This is your public display name. It can be your real name or a + pseudonym. You can only change this once every 30 days. + + + + )} + /> + ( + + Email + + + You can manage verified email addresses in your{" "} + email settings. + + + + )} + /> + ( + + Bio + +