Documentação do LoginGen
Introdução
LoginGen é um site de modelos para páginas de login. O motivo para o desenvolvimento deste projeto é que, embora existam muitos sites de modelos no mercado, a maioria carece de suporte para páginas de login ou oferece apenas um ou dois modelos simples, além de não possuir lógica frontend completa. Embora, em termos de peso de interação, a página de login possa não ser tão importante quanto uma Landing Page, uma página de login bonita também pode melhorar a primeira impressão do usuário sobre o site.
A stack de tecnologia é baseada principalmente em Next.js e Shadcn/ui. Além de fornecer designs de modelos, também oferece lógica frontend completa e utiliza a capacidade de Server Actions do Next.js para processamento unificado de autenticação no servidor (exceto para login social, que, por conveniência, foi colocado em eventos do cliente).
Todo o código é aberto, assim como o Shadcn, permitindo copiar e colar diretamente para uso. Portanto, você pode modificar o código de acordo com as necessidades do seu projeto, e isso é recomendado. Claro, para maior conveniência, os componentes de formulário também incluem algumas propriedades comuns, permitindo uso imediato.
No futuro, continuaremos a adicionar, aprimorar e otimizar os modelos. Fiquem atentos.
Instalação
Os modelos são desenvolvidos com base no Next.js e no Shadcn, portanto, primeiro você precisa ter o ambiente do projeto configurado:
- "next": "^14.2+"
- "tailwindcss": "^3.4+"
- "react-hook-form: 7.52.2" - Não use versões mais altas por enquanto, pois versões superiores têm bugs de renderização (issues: https://github.com/react-hook-form/react-hook-form/issues/12518)
Componentes do shadcn
Se você estiver usando outras ferramentas de comando de pacotes ou quiser instalar manualmente, consulte a documentação do shadcn.
pnpm dlx shadcn@latest add card tabs form button input input-otp separator sonner
Adicione as seguintes animações CSS ao seu arquivo tailwind.config.js
:
// for input-otp
/** @type {import('tailwindcss').Config} */
module.exports = {
theme: {
extend: {
keyframes: {
'caret-blink': {
'0%,70%,100%': { opacity: '1' },
'20%,50%': { opacity: '0' }
}
},
animation: {
'caret-blink': 'caret-blink 1.25s ease-out infinite'
}
}
}
}
Para o sonner, você precisa adicionar o seguinte componente:
import { Toaster } from '@/components/ui/sonner'
export default function RootLayout({ children }) {
return (
<html lang='en'>
<head />
<body>
<main>{children}</main>
<Toaster />
</body>
</html>
)
}
Bibliotecas de dependências de terceiros
pnpm add react-hook-form@7.52.2 zod motion @zxcvbn-ts/core @zxcvbn-ts/language-common @zxcvbn-ts/language-en embla-carousel-react embla-carousel-autoplay embla-carousel-fade lucide-react
pnpm add -D embla-carousel
react-hook-form
+zod
-- Validação de formulário no clientemotion
-- Biblioteca de animação@zxcvbn-*
-- Biblioteca de verificação de força de senhaembla-carousel-*
-- Biblioteca de carrossel (ondeembla-carousel
precisa ser instalado emdevDependencies
para obter os tipos TypeScript corretamente)lucide-react
-- Biblioteca de ícones
Copiar e colar código
Cada página de visualização do modelo possui uma exibição completa do código. Basta copiar e colar o código necessário na posição correspondente do seu projeto. As imagens incluídas nos modelos podem ser baixadas diretamente, e os vídeos são fornecidos via CDN.
Atualmente, não há suporte para instalação automática semelhante ao npx
, mas isso pode ser considerado no
futuro.
Tema
Cada modelo possui um arquivo de tema styles/theme.module.css
, que contém todas as variáveis de tema,
estilos e animações necessárias para o modelo. Você pode modificar os estilos neste arquivo para ajustar o
tema ou adequá-lo à estética da UI do seu site.
Modo escuro
Para o modo escuro, siga a documentação do shadcn. Nos arquivos
de tema theme.module.css
de cada modelo, o tema escuro utiliza o prefixo de seletor CSS :global(.dark)
para responder ao modo escuro global do site.
I18n (TODO)
Atualmente, não há suporte para configuração de múltiplos idiomas, mas você ainda pode adicioná-lo manualmente, embora possa ser um pouco trabalhoso. No futuro, consideraremos adicionar essa funcionalidade.
Propriedades do formulário
Existem cinco formulários: login, registro, recuperação de senha, redefinição de senha e verificação OTP (one-time password). Cada formulário possui alguns parâmetros para facilitar o uso imediato, e a maioria dos parâmetros são comuns.
Embora, no código do modelo, cada página de formulário seja uma rota separada, com redirecionamento no servidor para navegação entre as páginas de formulário, todos os componentes são projetados para serem usados com propriedades. Isso significa que você pode perfeitamente usar rotas no cliente.
Login
A definição da interface é a seguinte:
const _auths = ['social', 'email', 'password'] as const
type Auth = (typeof _auths)[number]
export interface SignInFormProps {
auths?: Readonly<Auth[]>
socials?: SocialKey[]
email?: string
emailStrategy?: 'magic-link' | 'one-time-pwd'
socialHandler?: (event: MouseEvent<HTMLButtonElement>, key: SocialKey) => void | Promise<void>
forgetHandler?: string | ((event: React.MouseEvent<HTMLAnchorElement>) => void | Promise<void>) // for more convenient use
serverAction?: (
formType: 'email' | 'password',
currentState: SignInActionState,
formData: FormData
) => Promise<SignInActionState>
serverStateSuccessHandler?: (formType: 'email' | 'password', email: string) => void | Promise<void>
}
Parâmetro | Valor padrão | Explicação |
---|---|---|
auths | ['social', 'password', 'email'] | Métodos de autenticação: login social, login por e-mail, login por senha |
socials | ['google', 'github'] | Tipos de login social (o layout é ajustado automaticamente com base no número selecionado e na largura do formulário) |
E-mail inicial, também pode ser usado para pré-preencher o e-mail após o redirecionamento do formulário de verificação OTP | ||
emailStrategy | 'magic-link' | Tipo de e-mail enviado. Se for 'magic-link', não redireciona a página, mas aciona um pop-up sonner; caso contrário, deve redirecionar para o formulário de verificação OTP |
socialHandler | Função de retorno ao clicar no botão de login social. Você pode usar bibliotecas como Auth.js para implementar | |
forgetHandler | '#' | Função de retorno ao clicar no botão "Esqueci a senha". Este parâmetro é fornecido principalmente para conveniência |
serverAction | Todos os formulários, exceto login social, são enviados ao servidor via Server Actions | |
serverStateSuccessHandler | Geralmente, após a autenticação no servidor, o redirecionamento é feito diretamente no servidor. Mas, se você quiser implementar alguma lógica de redirecionamento no cliente, pode fazê-lo neste parâmetro de retorno |
Observações:
socials
-- Cada modelo possui um arquivo de configuraçãoconfig/social.tsx
. Você pode editar este arquivo para adicionar os métodos de login social que desejar. Você pode modificar livremente akey
para se adequar à estrutura de autenticação que estiver usando ou alterar o ícone SVG para o de sua preferência.email
-- Na UI do formulárioverify-otp
, há um botão para editar o e-mail. Quando o usuário clica nele, deve retornar à página anterior para permitir que o usuário corrija o e-mail. O parâmetroemail
é projetado para pré-preencher o e-mail exibido após esse retorno.
Registro
A definição da interface é a seguinte:
export interface SignUpFormProps {
email?: string
serverAction: (currentState: SignUpActionState, formData: FormData) => Promise<SignUpActionState>
serverStateSuccessHandler?: (email: string) => void | Promise<void>
}
Parâmetro | Valor padrão | Explicação |
---|---|---|
E-mail inicial, também pode ser usado para pré-preencher o e-mail após o redirecionamento do formulário de verificação OTP | ||
serverAction | Envia os dados do formulário para o servidor via Server Actions | |
serverStateSuccessHandler | Geralmente, após a autenticação no servidor, o redirecionamento é feito diretamente no servidor. Mas, se você quiser implementar alguma lógica de redirecionamento no cliente, pode fazê-lo neste parâmetro de retorno |
Esqueci a senha
A definição da interface é a seguinte:
export interface ForgetPasswordProps {
email?: string
emailStrategy?: 'magic-link' | 'one-time-pwd'
serverAction: (
currentState: ForgetPasswordActionState,
formData: FormData
) => Promise<ForgetPasswordActionState>
serverStateSuccessHandler?: (email: string) => void | Promise<void>
}
Parâmetro | Valor padrão | Explicação |
---|---|---|
E-mail inicial, também pode ser usado para pré-preencher o e-mail após o redirecionamento do formulário de verificação OTP | ||
emailStrategy | 'magic-link' | Tipo de e-mail enviado. Se for 'magic-link', não redireciona a página, mas aciona um pop-up sonner; caso contrário, deve redirecionar para o formulário de verificação OTP |
serverAction | Envia os dados do formulário para o servidor via Server Actions | |
serverStateSuccessHandler | Geralmente, após a autenticação no servidor, o redirecionamento é feito diretamente no servidor. Mas, se você quiser implementar alguma lógica de redirecionamento no cliente, pode fazê-lo neste parâmetro de retorno |
Redefinir senha
A definição da interface é a seguinte:
export interface ResetPasswordFormProps {
email?: string // Only used to test password strength
resetToken?: string
serverAction: (
resetToken: string,
currentState: ResetPasswordActionState,
formData: FormData
) => Promise<ResetPasswordActionState>
serverStateSuccessHandler?: () => void | Promise<void>
}
Parâmetro | Valor padrão | Explicação |
---|---|---|
Este e-mail é usado apenas para testar a força da senha (recomenda-se configurá-lo para melhorar a força da senha inserida pelo usuário) | ||
resetToken | Token gerado pelo servidor para rastrear o fluxo do formulário | |
serverAction | Envia os dados do formulário para o servidor via Server Actions | |
serverStateSuccessHandler | Geralmente, após a autenticação no servidor, o redirecionamento é feito diretamente no servidor. Mas, se você quiser implementar alguma lógica de redirecionamento no cliente, pode fazê-lo neste parâmetro de retorno |
resetToken No fluxo de redefinição de senha, geralmente o servidor gera um
token
para rastrear e confirmar com segurança o remetente e o estado atual do formulário.O servidor geralmente passa o
token
de duas maneiras: uma é colocá-lo nos parâmetros de consulta da URL, e a outra é colocá-lo em umcookie
, que o navegador enviará automaticamente quando o formulário for enviado.No primeiro caso, você pode usar
searchParams
ouuseSearchParams()
para obter oresetToken
nos parâmetros de consulta e passá-lo para o formulário, que o enviará automaticamente para a Server Action do servidor quando for submetido.No segundo caso, você pode ignorar este parâmetro.
Verificação OTP
A definição da interface é a seguinte:
export interface VerifyOTPFormProps {
email: string
verifyToken?: string
editEmailHandler?: (event: MouseEvent<HTMLButtonElement>) => void
serverAction: (
email: string,
verifyToken: string,
currentState: VerifyOTPActionState,
formData: FormData
) => Promise<VerifyOTPActionState>
codeResendAction: (email: string, verifyToken: string) => Promise<VerifyOTPActionState>
serverStateSuccessHandler?: () => void | Promise<void>
}
Parâmetro | Valor padrão | Explicação |
---|---|---|
E-mail inicial, que será exibido na UI para permitir que o usuário o edite facilmente | ||
verifyToken | Token gerado pelo servidor para rastrear o fluxo do formulário | |
editEmailHandler | Função de evento quando o botão de edição de e-mail é clicado | |
codeResendAction | Função de evento para reenviar o código (o intervalo em segundos pode ser definido pela constante code_resend_max_seconds em verify-otp-form.tsx ) | |
serverAction | Envia os dados do formulário para o servidor via Server Actions | |
serverStateSuccessHandler | Geralmente, após a autenticação no servidor, o redirecionamento é feito diretamente no servidor. Mas, se você quiser implementar alguma lógica de redirecionamento no cliente, pode fazê-lo neste parâmetro de retorno |
verifyToken No fluxo de verificação de senha única (one-time password), geralmente o servidor gera um
token
para rastrear e confirmar com segurança o remetente e o estado atual do formulário.O servidor geralmente passa o
token
de duas maneiras: uma é colocá-lo nos parâmetros de consulta da URL, e a outra é colocá-lo em umcookie
, que o navegador enviará automaticamente quando o formulário for enviado.No primeiro caso, você pode usar
searchParams
ouuseSearchParams()
para obter overifyToken
nos parâmetros de consulta e passá-lo para o formulário, que o enviará automaticamente para a Server Action do servidor quando for submetido.No segundo caso, você pode ignorar este parâmetro.
Servidor
Exceto para login social, todas as outras opções de login, após a validação no cliente, são enviadas para
Server Actions para processamento lógico no servidor. O envio do formulário é realizado usando useFormState
e useFormStatus
do React.
No arquivo types/action-state.ts
do código do modelo, há uma definição simples do formato dos dados enviados
para a Server Action, que você pode expandir conforme necessário.
Aqui está um exemplo de código para operações em server actions:
sign-in-action.ts
'use server'
import type { FormType, SignInActionState } from '@/types/server'
import { redirect } from 'next/navigation'
import { revalidatePath } from 'next/cache'
export async function signInAction(
formType: FormType,
currentState: SignInActionState,
formData: FormData
): Promise<SignInActionState> {
switch (formType) {
case 'email':
return await emailAction(formData)
case 'password':
return await passwordAction(formData)
}
}
async function emailAction(formData: FormData): Promise<SignInActionState> {
const data = Object.fromEntries(formData)
const parsed = signInSchemas.email.safeParse(data)
// A validação do formulário também é necessária no lado do servidor
if (!parsed.success) {
const issues: Record<string, string> = {}
parsed.error.issues.map((issue) => (issues[issue.path[0]] = issue.message))
return {
success: false,
formIssues: issues
}
}
// Realize algumas operações no servidor, como enviar e-mails, operar bancos de dados, etc.
// O redirecionamento pode ser feito no servidor
revalidatePath('/auth/sign-in')
redirect(`/auth/verify-otp`)
// Ou retorne diretamente para o cliente
// return { success: true }
}
async function passwordAction(formData: FormData): Promise<SignInActionState> {
const data = Object.fromEntries(formData)
// simular atraso
await new Promise((resolve) => setTimeout(resolve, 2000))
console.log('Sign in password server action form data : ' + JSON.stringify(data, null, 2))
return { success: true }
}