Desenvolvedores
src_
components_
LoginForm.jsx

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 Form gerencia o estado do formulário usando o hook useForm.
  • 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