feat: static login & register pages
This commit is contained in:
@@ -0,0 +1,77 @@
|
|||||||
|
'use client'
|
||||||
|
import Button from '@/components/ui/Button'
|
||||||
|
import Typography from '@/components/ui/Typography'
|
||||||
|
import FormGroup from '@/components/ui/form/FormGroup'
|
||||||
|
import Input from '@/components/ui/form/Input'
|
||||||
|
import PasswordInput from '@/components/ui/form/PasswordInput'
|
||||||
|
import { useFormik } from 'formik'
|
||||||
|
import { type FC } from 'react'
|
||||||
|
import { object, string } from 'yup'
|
||||||
|
|
||||||
|
interface LoginData {
|
||||||
|
email: string
|
||||||
|
password: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const loginSchema = object({
|
||||||
|
email: string().email('El correo electrónico no es válido').required('El correo electrónico es requerido'),
|
||||||
|
password: string().required('La contraseña es requerida')
|
||||||
|
})
|
||||||
|
|
||||||
|
const LoginForm: FC = () => {
|
||||||
|
const formik = useFormik<LoginData>({
|
||||||
|
initialValues: {
|
||||||
|
email: '',
|
||||||
|
password: ''
|
||||||
|
},
|
||||||
|
onSubmit: (values) => {
|
||||||
|
console.log(values)
|
||||||
|
},
|
||||||
|
validationSchema: loginSchema
|
||||||
|
})
|
||||||
|
return (
|
||||||
|
<form
|
||||||
|
onSubmit={formik.handleSubmit}
|
||||||
|
>
|
||||||
|
<FormGroup>
|
||||||
|
<label htmlFor="email">Correo electrónico</label>
|
||||||
|
<Input
|
||||||
|
id="email"
|
||||||
|
name="email"
|
||||||
|
type="email"
|
||||||
|
onChange={formik.handleChange}
|
||||||
|
value={formik.values.email}
|
||||||
|
status={formik.touched.email !== undefined && formik.errors.email !== undefined ? 'danger' : undefined}
|
||||||
|
onBlur={formik.handleBlur}
|
||||||
|
fullWidth
|
||||||
|
/>
|
||||||
|
{formik.touched.email !== undefined && formik.errors.email !== undefined && (
|
||||||
|
<Typography variant="caption" color="danger">{formik.errors.email}</Typography>
|
||||||
|
)}
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup>
|
||||||
|
<label htmlFor="password">Contraseña</label>
|
||||||
|
<PasswordInput
|
||||||
|
id="password"
|
||||||
|
name="password"
|
||||||
|
onChange={formik.handleChange}
|
||||||
|
value={formik.values.password}
|
||||||
|
status={formik.touched.password !== undefined && formik.errors.password !== undefined ? 'danger' : undefined}
|
||||||
|
onBlur={formik.handleBlur}
|
||||||
|
fullWidth
|
||||||
|
/>
|
||||||
|
{formik.touched.password !== undefined && formik.errors.password !== undefined && (
|
||||||
|
<Typography variant="caption" color="danger">{formik.errors.password}</Typography>
|
||||||
|
)}
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup>
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
>
|
||||||
|
Iniciar sesión
|
||||||
|
</Button>
|
||||||
|
</FormGroup>
|
||||||
|
</form>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
export default LoginForm
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
import LoginForm from '@/app/login/LoginForm'
|
||||||
|
import Typography from '@/components/ui/Typography'
|
||||||
|
import { css, cx } from '@/styled-system/css'
|
||||||
|
import { Center } from '@/styled-system/jsx'
|
||||||
|
import { container } from '@/styled-system/patterns'
|
||||||
|
import { card } from '@/styled-system/recipes'
|
||||||
|
import NextLink from 'next/link'
|
||||||
|
import { type FC } from 'react'
|
||||||
|
|
||||||
|
const LoginPage: FC = () => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Center
|
||||||
|
className={cx(
|
||||||
|
container(),
|
||||||
|
css({
|
||||||
|
minHeight: 'calc(100vh - 60px - 72px)'
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={cx(
|
||||||
|
card().body,
|
||||||
|
css({
|
||||||
|
width: '100%',
|
||||||
|
maxWidth: { sm: 'breakpoint-sm' },
|
||||||
|
overflow: 'visible'
|
||||||
|
})
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={
|
||||||
|
card().header
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Typography align="center" variant="h1">
|
||||||
|
Iniciar sesión
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className={card().content}
|
||||||
|
>
|
||||||
|
<LoginForm />
|
||||||
|
<Typography variant="caption" align="center" >
|
||||||
|
No tienes una cuenta? <NextLink href="/register">Regístrate</NextLink>
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Center>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
export default LoginPage
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
'use client'
|
||||||
|
import Button from '@/components/ui/Button'
|
||||||
|
import Typography from '@/components/ui/Typography'
|
||||||
|
import FormGroup from '@/components/ui/form/FormGroup'
|
||||||
|
import Input from '@/components/ui/form/Input'
|
||||||
|
import PasswordInput from '@/components/ui/form/PasswordInput'
|
||||||
|
import { useFormik } from 'formik'
|
||||||
|
import { type FC } from 'react'
|
||||||
|
import { object, ref, string } from 'yup'
|
||||||
|
|
||||||
|
interface RegisterData {
|
||||||
|
email: string
|
||||||
|
password: string
|
||||||
|
passwordConfirmation: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const RegisterSchema = object({
|
||||||
|
email: string().email('El correo electrónico no es válido').required('El correo electrónico es requerido'),
|
||||||
|
password: string()
|
||||||
|
.min(6, 'La contraseña debe tener al menos 6 caracteres')
|
||||||
|
.matches(/[a-z]/, 'La contraseña debe tener al menos una letra minúscula')
|
||||||
|
.matches(/[A-Z]/, 'La contraseña debe tener al menos una letra mayúscula')
|
||||||
|
.matches(/[0-9]/, 'La contraseña debe tener al menos un número')
|
||||||
|
.required('La contraseña es requerida'),
|
||||||
|
passwordConfirmation: string().oneOf([ref('password')], 'Las contraseñas no coinciden').required('La confirmación de la contraseña es requerida')
|
||||||
|
})
|
||||||
|
|
||||||
|
const RegisterForm: FC = () => {
|
||||||
|
const formik = useFormik<RegisterData>({
|
||||||
|
initialValues: {
|
||||||
|
email: '',
|
||||||
|
password: '',
|
||||||
|
passwordConfirmation: ''
|
||||||
|
},
|
||||||
|
onSubmit: (values) => {
|
||||||
|
console.log(values)
|
||||||
|
},
|
||||||
|
validationSchema: RegisterSchema
|
||||||
|
})
|
||||||
|
return (
|
||||||
|
<form
|
||||||
|
onSubmit={formik.handleSubmit}
|
||||||
|
>
|
||||||
|
<FormGroup>
|
||||||
|
<label htmlFor="email">Correo electrónico</label>
|
||||||
|
<Input
|
||||||
|
id="email"
|
||||||
|
name="email"
|
||||||
|
type="email"
|
||||||
|
onChange={formik.handleChange}
|
||||||
|
value={formik.values.email}
|
||||||
|
status={formik.touched.email !== undefined && formik.errors.email !== undefined ? 'danger' : undefined}
|
||||||
|
onBlur={formik.handleBlur}
|
||||||
|
fullWidth
|
||||||
|
/>
|
||||||
|
{formik.touched.email !== undefined && formik.errors.email !== undefined && (
|
||||||
|
<Typography variant="caption" color="danger">{formik.errors.email}</Typography>
|
||||||
|
)}
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup>
|
||||||
|
<label htmlFor="password">Contraseña</label>
|
||||||
|
<PasswordInput
|
||||||
|
id="password"
|
||||||
|
name="password"
|
||||||
|
onChange={formik.handleChange}
|
||||||
|
value={formik.values.password}
|
||||||
|
status={formik.touched.password !== undefined && formik.errors.password !== undefined ? 'danger' : undefined}
|
||||||
|
onBlur={formik.handleBlur}
|
||||||
|
fullWidth
|
||||||
|
/>
|
||||||
|
{formik.touched.password !== undefined && formik.errors.password !== undefined && (
|
||||||
|
<Typography variant="caption" color="danger">{formik.errors.password}</Typography>
|
||||||
|
)}
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup>
|
||||||
|
<label htmlFor="passwordConfirmation">Confirmación de la contraseña</label>
|
||||||
|
<PasswordInput
|
||||||
|
id="passwordConfirmation"
|
||||||
|
name="passwordConfirmation"
|
||||||
|
onChange={formik.handleChange}
|
||||||
|
value={formik.values.passwordConfirmation}
|
||||||
|
status={formik.touched.passwordConfirmation !== undefined && formik.errors.passwordConfirmation !== undefined ? 'danger' : undefined}
|
||||||
|
onBlur={formik.handleBlur}
|
||||||
|
fullWidth
|
||||||
|
/>
|
||||||
|
{formik.touched.passwordConfirmation !== undefined && formik.errors.passwordConfirmation !== undefined && (
|
||||||
|
<Typography variant="caption" color="danger">{formik.errors.passwordConfirmation}</Typography>
|
||||||
|
)}
|
||||||
|
</FormGroup>
|
||||||
|
<FormGroup>
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
>
|
||||||
|
Registrarse
|
||||||
|
</Button>
|
||||||
|
</FormGroup>
|
||||||
|
</form>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
export default RegisterForm
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
import RegisterForm from '@/app/register/RegisterForm'
|
||||||
|
import Typography from '@/components/ui/Typography'
|
||||||
|
import { css, cx } from '@/styled-system/css'
|
||||||
|
import { Center } from '@/styled-system/jsx'
|
||||||
|
import { container } from '@/styled-system/patterns'
|
||||||
|
import { card } from '@/styled-system/recipes'
|
||||||
|
import NextLink from 'next/link'
|
||||||
|
import { type FC } from 'react'
|
||||||
|
|
||||||
|
const RegisterPage: FC = () => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Center
|
||||||
|
className={cx(
|
||||||
|
container(),
|
||||||
|
css({
|
||||||
|
minHeight: 'calc(100vh - 60px - 72px)'
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
>
|
||||||
|
|
||||||
|
<div
|
||||||
|
className={cx(
|
||||||
|
card().body,
|
||||||
|
css({
|
||||||
|
width: '100%',
|
||||||
|
maxWidth: { sm: 'breakpoint-sm' },
|
||||||
|
overflow: 'visible'
|
||||||
|
})
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
className={
|
||||||
|
card().header
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Typography align="center" variant="h1">
|
||||||
|
Regístrate
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
className={card().content}
|
||||||
|
>
|
||||||
|
<RegisterForm />
|
||||||
|
<Typography variant="caption" align="center" >
|
||||||
|
Ya tienes una cuenta? <NextLink href="/login">Inicia sesión</NextLink>
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Center>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
export default RegisterPage
|
||||||
@@ -1,8 +1,12 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import EntGamers from '@/assets/logos/EntGamers'
|
import EntGamers from '@/assets/logos/EntGamers'
|
||||||
import Menu from '@/components/layout/Menu'
|
import Menu from '@/components/layout/Menu'
|
||||||
|
import Tooltip from '@/components/ui/Tooltip'
|
||||||
import { css } from '@/styled-system/css'
|
import { css } from '@/styled-system/css'
|
||||||
import { Container } from '@/styled-system/jsx'
|
import { Container } from '@/styled-system/jsx'
|
||||||
|
import { iconButton } from '@/styled-system/recipes'
|
||||||
|
import { faUser } from '@fortawesome/free-solid-svg-icons'
|
||||||
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||||
import NextLink from 'next/link'
|
import NextLink from 'next/link'
|
||||||
import { useCallback, useEffect, useState, type FC } from 'react'
|
import { useCallback, useEffect, useState, type FC } from 'react'
|
||||||
|
|
||||||
@@ -65,6 +69,19 @@ const Header: FC = () => {
|
|||||||
</NextLink>
|
</NextLink>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
<Tooltip
|
||||||
|
title="Próximamente"
|
||||||
|
position="bottom"
|
||||||
|
>
|
||||||
|
<NextLink
|
||||||
|
href="/login"
|
||||||
|
className={
|
||||||
|
iconButton()
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon={faUser} fixedWidth />
|
||||||
|
</NextLink>
|
||||||
|
</Tooltip>
|
||||||
<Menu />
|
<Menu />
|
||||||
</div>
|
</div>
|
||||||
</Container>
|
</Container>
|
||||||
|
|||||||
Reference in New Issue
Block a user