2025-12-22 14:53:19 +07:00
|
|
|
import { createFileRoute, redirect, useNavigate } from '@tanstack/react-router'
|
2025-08-11 23:21:36 +07:00
|
|
|
import {
|
|
|
|
|
Card,
|
|
|
|
|
CardContent,
|
|
|
|
|
CardDescription,
|
|
|
|
|
CardFooter,
|
|
|
|
|
CardHeader,
|
|
|
|
|
CardTitle,
|
|
|
|
|
} from '@/components/ui/card'
|
|
|
|
|
import { Input } from '@/components/ui/input'
|
|
|
|
|
import { Label } from '@/components/ui/label'
|
|
|
|
|
import { Button } from '@/components/ui/button'
|
|
|
|
|
import {
|
|
|
|
|
formOptions,
|
|
|
|
|
useForm,
|
|
|
|
|
} from '@tanstack/react-form'
|
2025-12-22 14:53:19 +07:00
|
|
|
import { useLogin } from '@/hooks/queries'
|
|
|
|
|
import { toast } from 'sonner'
|
2025-08-11 23:21:36 +07:00
|
|
|
|
|
|
|
|
interface LoginFormProps {
|
|
|
|
|
username: string
|
|
|
|
|
password: string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const defaultInput: LoginFormProps = {
|
|
|
|
|
username: '',
|
|
|
|
|
password: '',
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const formOpts = formOptions({
|
|
|
|
|
defaultValues: defaultInput,
|
|
|
|
|
})
|
|
|
|
|
|
2025-12-22 14:53:19 +07:00
|
|
|
export const Route = createFileRoute('/(auth)/login/')({
|
2025-08-11 23:21:36 +07:00
|
|
|
beforeLoad: async ({ context }) => {
|
2025-12-22 14:53:19 +07:00
|
|
|
const { token } = context.auth
|
|
|
|
|
if (token) throw redirect({ to: '/' })
|
2025-08-11 23:21:36 +07:00
|
|
|
},
|
|
|
|
|
component: LoginForm,
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
function LoginForm() {
|
2025-12-22 14:53:19 +07:00
|
|
|
const navigate = useNavigate()
|
|
|
|
|
const loginMutation = useLogin()
|
|
|
|
|
|
2025-08-11 23:21:36 +07:00
|
|
|
const form = useForm({
|
|
|
|
|
...formOpts,
|
|
|
|
|
onSubmit: async ({ value }) => {
|
2025-12-22 14:53:19 +07:00
|
|
|
try {
|
|
|
|
|
await loginMutation.mutateAsync({
|
|
|
|
|
username: value.username,
|
|
|
|
|
password: value.password,
|
|
|
|
|
})
|
2025-08-11 23:21:36 +07:00
|
|
|
|
2025-12-22 14:53:19 +07:00
|
|
|
toast.success('Đăng nhập thành công!')
|
|
|
|
|
navigate({ to: '/' })
|
|
|
|
|
} catch (error: any) {
|
|
|
|
|
console.error('Login error:', error)
|
|
|
|
|
toast.error(
|
|
|
|
|
error.response?.data?.message || 'Tài khoản hoặc mật khẩu không đúng.'
|
|
|
|
|
)
|
2025-08-11 23:21:36 +07:00
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<Card className="max-w-md mx-auto mt-20 p-6">
|
|
|
|
|
<CardHeader>
|
|
|
|
|
<CardTitle>Đăng nhập</CardTitle>
|
|
|
|
|
<CardDescription>
|
|
|
|
|
Vui lòng nhập thông tin đăng nhập của bạn.
|
|
|
|
|
</CardDescription>
|
|
|
|
|
</CardHeader>
|
|
|
|
|
<CardContent>
|
|
|
|
|
<form
|
|
|
|
|
onSubmit={(e) => {
|
|
|
|
|
e.preventDefault()
|
|
|
|
|
form.handleSubmit()
|
|
|
|
|
}}
|
|
|
|
|
className="space-y-4"
|
|
|
|
|
>
|
|
|
|
|
{/* Username */}
|
|
|
|
|
<form.Field name="username">
|
|
|
|
|
{(field) => (
|
|
|
|
|
<div>
|
|
|
|
|
<Label htmlFor="username">Tên đăng nhập</Label>
|
|
|
|
|
<Input
|
|
|
|
|
id="username"
|
|
|
|
|
value={field.state.value}
|
|
|
|
|
onChange={(e) => field.handleChange(e.target.value)}
|
|
|
|
|
placeholder="Tên đăng nhập"
|
|
|
|
|
/>
|
|
|
|
|
{field.state.meta.isTouched && field.state.meta.errors && (
|
|
|
|
|
<p className="text-sm text-red-500 mt-1">
|
|
|
|
|
{field.state.meta.errors}
|
|
|
|
|
</p>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</form.Field>
|
|
|
|
|
|
|
|
|
|
{/* Password */}
|
|
|
|
|
<form.Field name="password">
|
|
|
|
|
{(field) => (
|
|
|
|
|
<div>
|
|
|
|
|
<Label htmlFor="password">Mật khẩu</Label>
|
|
|
|
|
<Input
|
|
|
|
|
id="password"
|
|
|
|
|
type="password"
|
|
|
|
|
value={field.state.value}
|
|
|
|
|
onChange={(e) => field.handleChange(e.target.value)}
|
|
|
|
|
placeholder="Mật khẩu"
|
|
|
|
|
/>
|
|
|
|
|
{field.state.meta.isTouched && field.state.meta.errors && (
|
|
|
|
|
<p className="text-sm text-red-500 mt-1">
|
|
|
|
|
{field.state.meta.errors}
|
|
|
|
|
</p>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</form.Field>
|
|
|
|
|
|
2025-12-22 14:53:19 +07:00
|
|
|
<Button type="submit" className="w-full" disabled={loginMutation.isPending}>
|
|
|
|
|
{loginMutation.isPending ? 'Đang đăng nhập...' : 'Đăng nhập'}
|
2025-08-11 23:21:36 +07:00
|
|
|
</Button>
|
|
|
|
|
</form>
|
|
|
|
|
</CardContent>
|
|
|
|
|
<CardFooter>
|
|
|
|
|
<p className="text-sm text-muted-foreground">
|
|
|
|
|
Chưa có tài khoản? <span className="underline cursor-pointer">Đăng ký</span>
|
|
|
|
|
</p>
|
|
|
|
|
</CardFooter>
|
|
|
|
|
</Card>
|
|
|
|
|
)
|
|
|
|
|
}
|