Files
app/shadcn-admin/src/features/auth/sign-in/components/user-auth-form.tsx
2026-02-09 21:54:32 +08:00

162 lines
4.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { useState } from 'react'
import { z } from 'zod'
import { useForm } from 'react-hook-form'
import { zodResolver } from '@hookform/resolvers/zod'
import { Link, useNavigate } from '@tanstack/react-router'
import { Loader2, LogIn } from 'lucide-react'
import { toast } from 'sonner'
import { useAuthStore } from '@/stores/auth-store'
import { cn } from '@/lib/utils'
import { Button } from '@/components/ui/button'
import { supabase } from '@/lib/supabase'
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@/components/ui/form'
import { Input } from '@/components/ui/input'
import { PasswordInput } from '@/components/password-input'
const formSchema = z.object({
email: z.email({
error: (iss) => (iss.input === '' ? 'Please enter your email' : undefined),
}),
password: z
.string()
.min(1, 'Please enter your password')
.min(7, 'Password must be at least 7 characters long'),
})
interface UserAuthFormProps extends React.HTMLAttributes<HTMLFormElement> {
redirectTo?: string
}
export function UserAuthForm({
className,
redirectTo,
...props
}: UserAuthFormProps) {
const [isLoading, setIsLoading] = useState(false)
const navigate = useNavigate()
const { auth } = useAuthStore()
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: {
email: '',
password: '',
},
})
async function onSubmit(data: z.infer<typeof formSchema>) {
setIsLoading(true)
try {
const { data: authData, error } = await supabase.auth.signInWithPassword({
email: data.email,
password: data.password,
})
if (error) throw error
if (authData.session && authData.user) {
setIsLoading(false)
const user = {
accountNo: authData.user.id,
email: authData.user.email || '',
role: ['admin'],
exp: authData.session.expires_at ? authData.session.expires_at * 1000 : Date.now() + 24 * 60 * 60 * 1000,
}
auth.setUser(user)
auth.setAccessToken(authData.session.access_token)
const targetPath = redirectTo || '/'
navigate({ to: targetPath, replace: true })
toast.success(`Welcome back, ${user.email}!`)
}
} catch (error: any) {
setIsLoading(false)
toast.error(error.message || 'Login failed')
}
}
return (
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className={cn('grid gap-3', className)}
{...props}
>
<FormField
control={form.control}
name='email'
render={({ field }) => (
<FormItem>
<FormLabel></FormLabel>
<FormControl>
<Input placeholder='name@example.com' {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name='password'
render={({ field }) => (
<FormItem className='relative'>
<FormLabel></FormLabel>
<FormControl>
<PasswordInput placeholder='********' {...field} />
</FormControl>
<FormMessage />
<Link
to='/forgot-password'
className='absolute end-0 -top-0.5 text-sm font-medium text-muted-foreground hover:opacity-75'
>
</Link>
</FormItem>
)}
/>
<Button className='mt-2' disabled={isLoading}>
{isLoading ? <Loader2 className='animate-spin' /> : <LogIn />}
</Button>
<div className='relative my-2'>
<div className='absolute inset-0 flex items-center'>
<span className='w-full border-t' />
</div>
<div className='relative flex justify-center text-xs uppercase'>
<span className='bg-background px-2 text-muted-foreground'>
使
</span>
</div>
</div>
{/* <div className='grid grid-cols-3 gap-2'>
<Button
variant='outline'
type='button'
disabled={isLoading}
onClick={handleGoogleLogin}
>
<IconGoogle className='h-4 w-4' /> Google
</Button>
<Button variant='outline' type='button' disabled={isLoading}>
<IconGithub className='h-4 w-4' /> GitHub
</Button>
<Button variant='outline' type='button' disabled={isLoading}>
<IconFacebook className='h-4 w-4' /> Facebook
</Button>
</div> */}
</form>
</Form>
)
}