diff options
| author | joonhoekim <26rote@gmail.com> | 2025-03-25 15:55:45 +0900 |
|---|---|---|
| committer | joonhoekim <26rote@gmail.com> | 2025-03-25 15:55:45 +0900 |
| commit | 1a2241c40e10193c5ff7008a7b7b36cc1d855d96 (patch) | |
| tree | 8a5587f10ca55b162d7e3254cb088b323a34c41b /components/settings/appearance-form.tsx | |
initial commit
Diffstat (limited to 'components/settings/appearance-form.tsx')
| -rw-r--r-- | components/settings/appearance-form.tsx | 244 |
1 files changed, 244 insertions, 0 deletions
diff --git a/components/settings/appearance-form.tsx b/components/settings/appearance-form.tsx new file mode 100644 index 00000000..8f843fd6 --- /dev/null +++ b/components/settings/appearance-form.tsx @@ -0,0 +1,244 @@ +"use client" + +import { zodResolver } from "@hookform/resolvers/zod" +import { useForm } from "react-hook-form" +import { z } from "zod" +import { useTheme } from "next-themes" +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 { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group" +import { useMetaColor } from "@/hooks/use-meta-color" +import { META_THEME_COLORS } from "@/config/site" +import { + Command, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, + CommandList, +} from "@/components/ui/command" +import { + Popover, + PopoverContent, + PopoverTrigger, +} from "@/components/ui/popover" +import { Check, ChevronsUpDown } from "lucide-react" +import { cn } from "@/lib/utils" +import { useTranslation } from '@/i18n/client' +import { useRouter, useParams, usePathname } from 'next/navigation'; + +const appearanceFormSchema = z.object({ + theme: z.enum(["light", "dark"], { + required_error: "Please select a theme.", + }), + language: z.string({ + required_error: "Please select a language.", + }), + +}) + +type AppearanceFormValues = z.infer<typeof appearanceFormSchema> + +// This can come from your database or API. +const defaultValues: Partial<AppearanceFormValues> = { + theme: "light", + language:'ko' +} +const languages = [ + { label: "English", value: "en" }, + { label: "한국어", value: "ko" }, +] as const + +export function AppearanceForm() { + const { setTheme, resolvedTheme } = useTheme() + const { setMetaColor } = useMetaColor() + + const pathname = usePathname(); + const router = useRouter(); + + const params = useParams(); + const lng = params.lng as string; + const { t, i18n } = useTranslation(lng, 'translation'); + + const form = useForm<AppearanceFormValues>({ + resolver: zodResolver(appearanceFormSchema), + defaultValues, + }) + + function onSubmit(data: AppearanceFormValues) { + setTheme(data.theme) + setMetaColor( + resolvedTheme === "dark" + ? META_THEME_COLORS.light + : META_THEME_COLORS.dark + ) + + const segments = pathname.split('/'); + segments[1] = data.language; + router.push(segments.join('/')); + + toast({ + title: "Updated Successfully", + // description: ( + // <div className="mt-2 w-[340px] rounded-md bg-slate-950 p-4"> + // <div className="text-white">{JSON.stringify(data, null, 2)}</div> + // </div> + // ), + }) + } + + return ( + <Form {...form}> + <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-8"> + + <FormField + control={form.control} + name="theme" + render={({ field }) => ( + <FormItem className="space-y-1"> + <FormLabel>Theme</FormLabel> + <FormDescription> + Customize the appearance of the app. Automatically switch between day + and night themes. + </FormDescription> + <FormMessage /> + <RadioGroup + onValueChange={field.onChange} + defaultValue={field.value} + className="grid max-w-md grid-cols-2 gap-8 pt-2" + > + <FormItem> + <FormLabel className="[&:has([data-state=checked])>div]:border-primary"> + <FormControl> + <RadioGroupItem value="light" className="sr-only" /> + </FormControl> + <div className="items-center rounded-md border-2 border-muted p-1 hover:border-accent"> + <div className="space-y-2 rounded-sm bg-[#ecedef] p-2"> + <div className="space-y-2 rounded-md bg-white p-2 shadow-sm"> + <div className="h-2 w-[80px] rounded-lg bg-[#ecedef]" /> + <div className="h-2 w-[100px] rounded-lg bg-[#ecedef]" /> + </div> + <div className="flex items-center space-x-2 rounded-md bg-white p-2 shadow-sm"> + <div className="h-4 w-4 rounded-full bg-[#ecedef]" /> + <div className="h-2 w-[100px] rounded-lg bg-[#ecedef]" /> + </div> + <div className="flex items-center space-x-2 rounded-md bg-white p-2 shadow-sm"> + <div className="h-4 w-4 rounded-full bg-[#ecedef]" /> + <div className="h-2 w-[100px] rounded-lg bg-[#ecedef]" /> + </div> + </div> + </div> + <span className="block w-full p-2 text-center font-normal"> + Light + </span> + </FormLabel> + </FormItem> + <FormItem> + <FormLabel className="[&:has([data-state=checked])>div]:border-primary"> + <FormControl> + <RadioGroupItem value="dark" className="sr-only" /> + </FormControl> + <div className="items-center rounded-md border-2 border-muted bg-popover p-1 hover:bg-accent hover:text-accent-foreground"> + <div className="space-y-2 rounded-sm bg-slate-950 p-2"> + <div className="space-y-2 rounded-md bg-slate-800 p-2 shadow-sm"> + <div className="h-2 w-[80px] rounded-lg bg-slate-400" /> + <div className="h-2 w-[100px] rounded-lg bg-slate-400" /> + </div> + <div className="flex items-center space-x-2 rounded-md bg-slate-800 p-2 shadow-sm"> + <div className="h-4 w-4 rounded-full bg-slate-400" /> + <div className="h-2 w-[100px] rounded-lg bg-slate-400" /> + </div> + <div className="flex items-center space-x-2 rounded-md bg-slate-800 p-2 shadow-sm"> + <div className="h-4 w-4 rounded-full bg-slate-400" /> + <div className="h-2 w-[100px] rounded-lg bg-slate-400" /> + </div> + </div> + </div> + <span className="block w-full p-2 text-center font-normal"> + Dark + </span> + </FormLabel> + </FormItem> + </RadioGroup> + </FormItem> + )} + /> + + <FormField + control={form.control} + name="language" + render={({ field }) => ( + <FormItem className="flex flex-col"> + <FormLabel>Language</FormLabel> + <Popover> + <PopoverTrigger asChild> + <FormControl> + <Button + variant="outline" + role="combobox" + className={cn( + "w-[200px] justify-between", + !field.value && "text-muted-foreground" + )} + > + {field.value + ? languages.find( + (language) => language.value === field.value + )?.label + : "Select language"} + <ChevronsUpDown className="opacity-50" /> + </Button> + </FormControl> + </PopoverTrigger> + <PopoverContent className="w-[200px] p-0"> + <Command> + <CommandInput placeholder="Search language..." /> + <CommandList> + <CommandEmpty>No language found.</CommandEmpty> + <CommandGroup> + {languages.map((language) => ( + <CommandItem + value={language.label} + key={language.value} + onSelect={() => { + form.setValue("language", language.value) + }} + > + <Check + className={cn( + "mr-2", + language.value === field.value + ? "opacity-100" + : "opacity-0" + )} + /> + {language.label} + </CommandItem> + ))} + </CommandGroup> + </CommandList> + </Command> + </PopoverContent> + </Popover> + <FormDescription> + This is the language that will be used in the system. + </FormDescription> + <FormMessage /> + </FormItem> + )} + /> + + <Button type="submit">Update preferences</Button> + </form> + </Form> + ) +} |
