200 lines
7.0 KiB
TypeScript
200 lines
7.0 KiB
TypeScript
"use client"
|
|
|
|
import { useTransition } from "react"
|
|
import { zodResolver } from "@hookform/resolvers/zod"
|
|
import { useForm } from "react-hook-form"
|
|
import { z } from "zod"
|
|
import { Loader2, Save } from "lucide-react"
|
|
import { toast } from "sonner"
|
|
|
|
import { Button } from "@/shared/components/ui/button"
|
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle, CardFooter } from "@/shared/components/ui/card"
|
|
import { Input } from "@/shared/components/ui/input"
|
|
import { Label } from "@/shared/components/ui/label"
|
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/shared/components/ui/select"
|
|
import { Form, FormControl, FormDescription, FormField, FormItem, FormLabel, FormMessage } from "@/shared/components/ui/form"
|
|
import { UserProfile } from "@/modules/users/data-access"
|
|
import { updateUserProfile } from "@/modules/users/actions"
|
|
|
|
const profileFormSchema = z.object({
|
|
name: z.string().min(2, "Name must be at least 2 characters."),
|
|
email: z.string().email().optional(), // Read only
|
|
role: z.string().optional(), // Read only
|
|
phone: z.string().optional(),
|
|
address: z.string().optional(),
|
|
gender: z.string().optional(),
|
|
age: z.coerce.number().min(0).optional(),
|
|
})
|
|
|
|
type ProfileFormValues = z.infer<typeof profileFormSchema>
|
|
|
|
export function ProfileSettingsForm({ user }: { user: UserProfile }) {
|
|
const [isPending, startTransition] = useTransition()
|
|
|
|
const form = useForm<ProfileFormValues>({
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
resolver: zodResolver(profileFormSchema) as any,
|
|
defaultValues: {
|
|
name: user.name ?? "",
|
|
email: user.email ?? "",
|
|
role: user.role ?? "",
|
|
phone: user.phone ?? "",
|
|
address: user.address ?? "",
|
|
gender: user.gender ?? "",
|
|
age: user.age ?? undefined,
|
|
},
|
|
})
|
|
|
|
function onSubmit(data: ProfileFormValues) {
|
|
startTransition(async () => {
|
|
try {
|
|
await updateUserProfile({
|
|
name: data.name,
|
|
phone: data.phone || undefined,
|
|
address: data.address || undefined,
|
|
gender: data.gender || undefined,
|
|
age: data.age || undefined,
|
|
})
|
|
toast.success("Profile updated successfully")
|
|
} catch (error) {
|
|
toast.error("Failed to update profile")
|
|
console.error(error)
|
|
}
|
|
})
|
|
}
|
|
|
|
return (
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle>Profile Information</CardTitle>
|
|
<CardDescription>Update your personal information.</CardDescription>
|
|
</CardHeader>
|
|
<Form {...form}>
|
|
<form onSubmit={form.handleSubmit(onSubmit)}>
|
|
<CardContent className="space-y-4">
|
|
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
|
|
<FormField
|
|
control={form.control}
|
|
name="name"
|
|
render={({ field }) => (
|
|
<FormItem>
|
|
<FormLabel>Full Name</FormLabel>
|
|
<FormControl>
|
|
<Input placeholder="Your name" {...field} />
|
|
</FormControl>
|
|
<FormMessage />
|
|
</FormItem>
|
|
)}
|
|
/>
|
|
<FormField
|
|
control={form.control}
|
|
name="email"
|
|
render={({ field }) => (
|
|
<FormItem>
|
|
<FormLabel>Email</FormLabel>
|
|
<FormControl>
|
|
<Input {...field} disabled />
|
|
</FormControl>
|
|
<FormDescription>Email cannot be changed.</FormDescription>
|
|
<FormMessage />
|
|
</FormItem>
|
|
)}
|
|
/>
|
|
<FormField
|
|
control={form.control}
|
|
name="phone"
|
|
render={({ field }) => (
|
|
<FormItem>
|
|
<FormLabel>Phone</FormLabel>
|
|
<FormControl>
|
|
<Input placeholder="+1 234 567 890" {...field} />
|
|
</FormControl>
|
|
<FormMessage />
|
|
</FormItem>
|
|
)}
|
|
/>
|
|
<FormField
|
|
control={form.control}
|
|
name="gender"
|
|
render={({ field }) => (
|
|
<FormItem>
|
|
<FormLabel>Gender</FormLabel>
|
|
<Select onValueChange={field.onChange} defaultValue={field.value}>
|
|
<FormControl>
|
|
<SelectTrigger>
|
|
<SelectValue placeholder="Select gender" />
|
|
</SelectTrigger>
|
|
</FormControl>
|
|
<SelectContent>
|
|
<SelectItem value="male">Male</SelectItem>
|
|
<SelectItem value="female">Female</SelectItem>
|
|
<SelectItem value="other">Other</SelectItem>
|
|
<SelectItem value="prefer_not_to_say">Prefer not to say</SelectItem>
|
|
</SelectContent>
|
|
</Select>
|
|
<FormMessage />
|
|
</FormItem>
|
|
)}
|
|
/>
|
|
<FormField
|
|
control={form.control}
|
|
name="age"
|
|
render={({ field }) => (
|
|
<FormItem>
|
|
<FormLabel>Age</FormLabel>
|
|
<FormControl>
|
|
<Input type="number" placeholder="Age" {...field} value={field.value ?? ""} />
|
|
</FormControl>
|
|
<FormMessage />
|
|
</FormItem>
|
|
)}
|
|
/>
|
|
<FormField
|
|
control={form.control}
|
|
name="role"
|
|
render={({ field }) => (
|
|
<FormItem>
|
|
<FormLabel>Role</FormLabel>
|
|
<FormControl>
|
|
<Input {...field} disabled className="capitalize" />
|
|
</FormControl>
|
|
<FormMessage />
|
|
</FormItem>
|
|
)}
|
|
/>
|
|
<FormField
|
|
control={form.control}
|
|
name="address"
|
|
render={({ field }) => (
|
|
<FormItem className="col-span-1 sm:col-span-2">
|
|
<FormLabel>Address</FormLabel>
|
|
<FormControl>
|
|
<Input placeholder="123 Main St, City, Country" {...field} />
|
|
</FormControl>
|
|
<FormMessage />
|
|
</FormItem>
|
|
)}
|
|
/>
|
|
</div>
|
|
</CardContent>
|
|
<CardFooter className="flex justify-end border-t px-6 py-4">
|
|
<Button type="submit" disabled={isPending}>
|
|
{isPending ? (
|
|
<>
|
|
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
|
Saving...
|
|
</>
|
|
) : (
|
|
<>
|
|
<Save className="mr-2 h-4 w-4" />
|
|
Save Changes
|
|
</>
|
|
)}
|
|
</Button>
|
|
</CardFooter>
|
|
</form>
|
|
</Form>
|
|
</Card>
|
|
)
|
|
}
|