You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
192 lines
5.4 KiB
192 lines
5.4 KiB
'use client'
|
|
|
|
import { useState } from 'react'
|
|
import { Button } from '@/components/ui/button'
|
|
import { Input } from '@/components/ui/input'
|
|
import { Label } from '@/components/ui/label'
|
|
import { Badge } from '@/components/ui/badge'
|
|
import { Shield, Loader2, Mail, Check, UserCircle } from 'lucide-react'
|
|
import { useForm } from 'react-hook-form'
|
|
import { z } from 'zod'
|
|
import { zodResolver } from '@hookform/resolvers/zod'
|
|
import { useToast } from '@/hooks/use-toast'
|
|
import httpBrowserClient from '@/lib/httpBrowserClient'
|
|
import { ApiEndpoints } from '@/config/api'
|
|
import { useMutation, useQuery } from '@tanstack/react-query'
|
|
import { Spinner } from '@/components/ui/spinner'
|
|
import { useSession } from 'next-auth/react'
|
|
|
|
const updateProfileSchema = z.object({
|
|
name: z.string().min(1, 'Name is required'),
|
|
email: z.string().email().optional(),
|
|
phone: z
|
|
.string()
|
|
.regex(/^\+?\d{0,14}$/, 'Invalid phone number')
|
|
.optional(),
|
|
})
|
|
|
|
type UpdateProfileFormData = z.infer<typeof updateProfileSchema>
|
|
|
|
export default function EditProfileForm() {
|
|
const { toast } = useToast()
|
|
const { update: updateSession } = useSession()
|
|
|
|
const {
|
|
data: currentUser,
|
|
isLoading: isLoadingUser,
|
|
refetch: refetchCurrentUser,
|
|
} = useQuery({
|
|
queryKey: ['currentUser'],
|
|
queryFn: () =>
|
|
httpBrowserClient
|
|
.get(ApiEndpoints.auth.whoAmI())
|
|
.then((res) => res.data?.data),
|
|
})
|
|
|
|
const updateProfileForm = useForm<UpdateProfileFormData>({
|
|
resolver: zodResolver(updateProfileSchema),
|
|
defaultValues: {
|
|
name: currentUser?.name,
|
|
email: currentUser?.email,
|
|
phone: currentUser?.phone,
|
|
},
|
|
})
|
|
|
|
const handleVerifyEmail = () => {
|
|
// TODO: Implement email verification
|
|
}
|
|
|
|
const {
|
|
mutate: updateProfile,
|
|
isPending: isUpdatingProfile,
|
|
error: updateProfileError,
|
|
isSuccess: isUpdateProfileSuccess,
|
|
} = useMutation({
|
|
mutationFn: (data: UpdateProfileFormData) =>
|
|
httpBrowserClient.patch(ApiEndpoints.auth.updateProfile(), data),
|
|
onSuccess: () => {
|
|
refetchCurrentUser()
|
|
toast({
|
|
title: 'Profile updated successfully!',
|
|
})
|
|
updateSession({
|
|
name: updateProfileForm.getValues().name,
|
|
phone: updateProfileForm.getValues().phone,
|
|
})
|
|
},
|
|
onError: () => {
|
|
toast({
|
|
title: 'Failed to update profile',
|
|
})
|
|
},
|
|
})
|
|
|
|
if (isLoadingUser)
|
|
return (
|
|
<div className='flex justify-center items-center h-full min-h-[200px]'>
|
|
<Spinner size='sm' />
|
|
</div>
|
|
)
|
|
|
|
return (
|
|
<form
|
|
onSubmit={updateProfileForm.handleSubmit((data) => updateProfile(data))}
|
|
className='space-y-4'
|
|
>
|
|
<div className='space-y-2'>
|
|
<Label htmlFor='name'>Full Name</Label>
|
|
<Input
|
|
id='name'
|
|
{...updateProfileForm.register('name')}
|
|
placeholder='Enter your full name'
|
|
defaultValue={currentUser?.name}
|
|
/>
|
|
{updateProfileForm.formState.errors.name && (
|
|
<p className='text-sm text-destructive'>
|
|
{updateProfileForm.formState.errors.name.message}
|
|
</p>
|
|
)}
|
|
</div>
|
|
|
|
<div className='space-y-2'>
|
|
<Label htmlFor='email' className='flex items-center gap-2'>
|
|
Email Address
|
|
{currentUser?.emailVerifiedAt && (
|
|
<Badge variant='secondary' className='ml-2'>
|
|
<Shield className='h-3 w-3 mr-1' />
|
|
Verified
|
|
</Badge>
|
|
)}
|
|
</Label>
|
|
<div className='flex gap-2'>
|
|
<Input
|
|
id='email'
|
|
type='email'
|
|
{...updateProfileForm.register('email')}
|
|
placeholder='Enter your email'
|
|
defaultValue={currentUser?.email}
|
|
disabled
|
|
/>
|
|
{!currentUser?.emailVerifiedAt ? (
|
|
<Button
|
|
type='button'
|
|
variant='outline'
|
|
onClick={handleVerifyEmail}
|
|
disabled={true}
|
|
>
|
|
{isUpdatingProfile ? (
|
|
<Loader2 className='h-4 w-4 animate-spin' />
|
|
) : (
|
|
<Mail className='h-4 w-4 mr-2' />
|
|
)}
|
|
Verify
|
|
</Button>
|
|
) : (
|
|
<Button variant='outline' disabled>
|
|
<Check className='h-4 w-4 mr-2' />
|
|
Verified
|
|
</Button>
|
|
)}
|
|
</div>
|
|
{updateProfileForm.formState.errors.email && (
|
|
<p className='text-sm text-destructive'>
|
|
{updateProfileForm.formState.errors.email.message}
|
|
</p>
|
|
)}
|
|
</div>
|
|
|
|
<div className='space-y-2'>
|
|
<Label htmlFor='phone'>Phone Number</Label>
|
|
<Input
|
|
id='phone'
|
|
type='tel'
|
|
{...updateProfileForm.register('phone')}
|
|
placeholder='Enter your phone number'
|
|
defaultValue={currentUser?.phone}
|
|
/>
|
|
{updateProfileForm.formState.errors.phone && (
|
|
<p className='text-sm text-destructive'>
|
|
{updateProfileForm.formState.errors.phone.message}
|
|
</p>
|
|
)}
|
|
</div>
|
|
|
|
{isUpdateProfileSuccess && (
|
|
<p className='text-sm text-green-500'>
|
|
Profile updated successfully!
|
|
</p>
|
|
)}
|
|
|
|
<Button
|
|
type='submit'
|
|
className='w-full mt-6'
|
|
disabled={isUpdatingProfile}
|
|
>
|
|
{isUpdatingProfile ? (
|
|
<Loader2 className='h-4 w-4 animate-spin mr-2' />
|
|
) : null}
|
|
Save Changes
|
|
</Button>
|
|
</form>
|
|
)
|
|
}
|