Explicação Inicial 📚
O código define um componente de LoginForm em React que usa o React Hook Form para controle do formulário e o Zod para validação de dados. Além disso, faz uma requisição para o backend, armazena o token no localStorage, e realiza um redirecionamento após login bem-sucedido.
Definindo o Schema de Validação 🔐
Primeiro, vamos olhar a definição do schema de validação com Zod:
Código do Schema de Validação:
const formSchema = z.object({
email: z.string().email({ message: 'Email inválido' }),
password: z.string().min(6, { message: 'Senha deve ter pelo menos 6 caracteres.' }),
})O Zod é utilizado aqui para garantir que o email tenha o formato correto e que a senha tenha pelo menos 6 caracteres. Se algum dado não cumprir esses requisitos, uma mensagem de erro será exibida.
Controle do Formulário com useForm 📝
Em seguida, o React Hook Form é usado para gerenciar o estado do formulário.
Código de Controle do Formulário:
const form = useForm({
resolver: zodResolver(formSchema), // Conecta o Zod com o React Hook Form
defaultValues: {
email: '',
password: '',
},
})- resolver: Conecta o schema do Zod com o React Hook Form.
- defaultValues: Define os valores iniciais dos campos como vazios.
Função de Envio do Formulário 📨
Agora, vamos examinar a função que é chamada quando o formulário é enviado.
Código da Função de Submissão:
const onSubmit = async (values) => {
try {
const apiUrl = process.env.NEXT_PUBLIC_SERVER_URL
const response = await fetch(`${apiUrl}/api/alunos/login`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
email: values.email,
password: values.password,
}),
})
if (!response.ok) {
throw new Error('Credenciais inválidas')
}
const data = await response.json()
localStorage.setItem('token', data.token) // Armazena o token no localStorage
await fetchUser() // Atualiza o estado do usuário
const redirectUrl = localStorage.getItem('redirectAfterLogin') || '/cursos'
localStorage.removeItem('redirectAfterLogin') // Limpa o redirecionamento após login
router.push(redirectUrl) // Redireciona para a página desejada
} catch (error) {
console.error('Erro no login:', error)
alert('Erro no login: ' + error.message)
}
}A função onSubmit:
- Envia uma requisição POST para o backend com o email e a senha.
- Se as credenciais forem válidas, o token é armazenado no localStorage.
- Após o login bem-sucedido, o usuário é redirecionado para a página desejada.
Layout e Estilização do Formulário 🖌️
Agora, vamos analisar o código responsável pela renderização do formulário.
Código do Formulário:
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem>
<FormLabel>
Email <span className="text-red-500">*</span>
</FormLabel>
<FormControl>
<Input type="email" placeholder="email@exemplo.com" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="password"
render={({ field }) => (
<FormItem>
<FormLabel className="flex justify-between items-center">
Senha <span className="text-red-500">*</span>
<Link href="#" className="ml-auto inline-block text-sm underline">
Esqueceu sua senha?
</Link>
</FormLabel>
<FormControl>
<Input type="password" placeholder="Digite sua senha" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<div className="flex justify-center">
<Button type="submit">Login</Button>
</div>
<div className="mt-4 text-center text-sm">
Não tem uma conta ainda?{' '}
<Link href="/cadastro" className="underline">
Criar Conta
</Link>
</div>
</form>
</Form>- Form: O componente
Formgerencia o estado do formulário usando o hookuseForm. - FormField: Cada campo de entrada, como email e senha, é envolvido por um FormField que gerencia a validação e o erro.
- Input: O campo de entrada renderiza o valor do email e da senha com base nos dados do formulário.
Código Completo 🖥️
'use client'
import React from 'react'
import { useForm } from 'react-hook-form'
import { useRouter } from 'next/navigation'
import { zodResolver } from '@hookform/resolvers/zod'
import { z } from 'zod'
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@/components/ui/form'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import BackgroundVideo from '@/components/BackgroundVideo'
import { useUser } from '@/components/context/UserContext'
import Link from 'next/link'
const formSchema = z.object({
email: z.string().email({ message: 'Email inválido' }),
password: z.string().min(6, { message: 'Senha deve ter pelo menos 6 caracteres.' }),
})
const LoginForm = () => {
const { fetchUser } = useUser()
const router = useRouter()
const form = useForm({
resolver: zodResolver(formSchema),
defaultValues: {
email: '',
password: '',
},
})
const onSubmit = async (values) => {
try {
const apiUrl = process.env.NEXT_PUBLIC_SERVER_URL
const response = await fetch(`${apiUrl}/api/alunos/login`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
email: values.email,
password: values.password,
}),
})
if (!response.ok) {
throw new Error('Credenciais inválidas')
}
const data = await response.json()
localStorage.setItem('token', data.token)
await fetchUser()
const redirectUrl = localStorage.getItem('redirectAfterLogin') || '/cursos'
localStorage.removeItem('redirectAfterLogin')
router.push(redirectUrl)
} catch (error) {
console.error('Erro no login:', error)
alert('Erro no login: ' + error.message)
}
}
return (
<div className="relative w-full h-screen overflow-y-auto bg-background">
<BackgroundVideo />
<div className="relative z-10 flex items-center justify-center min-h-screen py-8">
<Card className="p-8 rounded-lg shadow-lg max-w-md w-full mx-4">
<CardHeader>
<CardTitle>Login</CardTitle>
<CardDescription>Preencha os campos abaixo para efetuar o login</CardDescription>
</CardHeader>
<CardContent>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem>
<FormLabel>
Email <span className="text-red-500">*</span>
</FormLabel>
<FormControl>
<Input type="email" placeholder="email@exemplo.com" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="password"
render={({ field }) => (
<FormItem>
<FormLabel className="flex justify-between items-center">
Senha <span className="text-red-500">*</span>
<Link href="#" className="ml-auto inline-block text-sm underline">
Esqueceu sua senha?
</Link>
</FormLabel>
<FormControl>
<Input type="password" placeholder="Digite sua senha" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<div className="flex
justify-center">
<Button type="submit">Login</Button>
</div>
<div className="mt-4 text-center text-sm">
Não tem uma conta ainda?{' '}
<Link href="/cadastro" className="underline">
Criar Conta
</Link>
</div>
</form>
</Form>
</CardContent>
</Card>
</div>
</div>
)
}
export default LoginForm