feat: start over and layout
This commit is contained in:
@@ -1,38 +0,0 @@
|
||||
import Head from 'next/head'
|
||||
import { type FC } from 'react'
|
||||
|
||||
export interface SeoProps {
|
||||
title?: string
|
||||
description?: string
|
||||
image?: string
|
||||
}
|
||||
|
||||
const SITE_NAME = process.env.SITE_NAME ?? 'EntGamers'
|
||||
|
||||
const Seo: FC<SeoProps> = ({ title, description, image }) => {
|
||||
return (
|
||||
<Head>
|
||||
{title !== undefined && (
|
||||
<>
|
||||
<title key="title">{`${title} - ${SITE_NAME}`}</title>
|
||||
<meta key="og_title" property="og:title" content={title} />
|
||||
<meta key="twitter_title" property="twitter:title" content={title} />
|
||||
</>
|
||||
)}
|
||||
{description !== undefined && (
|
||||
<>
|
||||
<meta key="description" name="description" content={description} />
|
||||
<meta key="og_description" property="og:description" content={description} />
|
||||
<meta key="twitter_description" property="twitter:description" content={description} />
|
||||
</>
|
||||
)}
|
||||
{image !== undefined && (
|
||||
<>
|
||||
<meta key="og_image" property="og:image" content={image} />
|
||||
<meta key="twitter_image" property="twitter:image" content={image} />
|
||||
</>
|
||||
)}
|
||||
</Head>
|
||||
)
|
||||
}
|
||||
export default Seo
|
||||
@@ -0,0 +1,50 @@
|
||||
import Typography from '@/components/ui/Typography'
|
||||
import { css } from '@/styled-system/css'
|
||||
import { Container } from '@/styled-system/jsx'
|
||||
import { faChevronRight, faHeart } from '@fortawesome/free-solid-svg-icons'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import NextLink from 'next/link'
|
||||
import { type FC } from 'react'
|
||||
|
||||
const Footer: FC = () => {
|
||||
return (
|
||||
<footer
|
||||
className={css({
|
||||
backgroundColor: 'surface',
|
||||
color: 'text',
|
||||
paddingY: 'medium'
|
||||
})}
|
||||
>
|
||||
<Container
|
||||
className={css({
|
||||
display: 'grid',
|
||||
gridTemplateColumns: { base: 'repeat(3, 1fr)', mdDown: '1fr' }
|
||||
})}
|
||||
>
|
||||
<div>
|
||||
<Typography variant="h3" component='div'> Acerca de </Typography>
|
||||
<ul className="fa-ul">
|
||||
<li><FontAwesomeIcon icon={faChevronRight} listItem fixedWidth /><NextLink href="/acerca-de"> EntGamers</NextLink></li>
|
||||
<li><FontAwesomeIcon icon={faChevronRight} listItem fixedWidth /><NextLink href="/clanes"> Clanes</NextLink></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div>
|
||||
<Typography variant="h3" component='div'> Contacto </Typography>
|
||||
</div>
|
||||
<div></div>
|
||||
</Container>
|
||||
<Container
|
||||
className={css({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
})}
|
||||
>
|
||||
<Typography variant="body2" component='div'>
|
||||
Hecho con <FontAwesomeIcon className={css({ color: 'red' })} icon={faHeart} /> por <a href="https://srjuggernaut.dev">SrJuggernaut</a>
|
||||
</Typography>
|
||||
</Container>
|
||||
</footer>
|
||||
)
|
||||
}
|
||||
export default Footer
|
||||
@@ -0,0 +1,80 @@
|
||||
'use client'
|
||||
import EntGamers from '@/assets/logos/EntGamers'
|
||||
import Menu from '@/components/layout/Menu'
|
||||
import { css } from '@/styled-system/css'
|
||||
import { Container } from '@/styled-system/jsx'
|
||||
import NextLink from 'next/link'
|
||||
import { useCallback, useEffect, useState, type FC } from 'react'
|
||||
|
||||
const Header: FC = () => {
|
||||
const [isScrolled, setIsScrolled] = useState(typeof window !== 'undefined' ? window.scrollY > 0 : false)
|
||||
const handleScroll = useCallback(() => {
|
||||
if (typeof window === 'undefined') return
|
||||
console.log(window.scrollY)
|
||||
setIsScrolled(window.scrollY > 0)
|
||||
}, [])
|
||||
useEffect(() => {
|
||||
if (typeof window === 'undefined') return
|
||||
window.addEventListener('scroll', handleScroll)
|
||||
return () => {
|
||||
window.removeEventListener('scroll', handleScroll)
|
||||
}
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<>
|
||||
<header
|
||||
className={css({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
backgroundColor: 'transparent',
|
||||
color: 'text',
|
||||
minHeight: '60px',
|
||||
position: 'fixed',
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: '100%',
|
||||
zIndex: 'sticky',
|
||||
boxShadow: 'none',
|
||||
transitionProperty: 'background-color, box-shadow',
|
||||
transitionDuration: '0.25s',
|
||||
transitionTimingFunction: 'easeInOut',
|
||||
willChange: 'background-color, box-shadow',
|
||||
'&[data-scrolled=true]': {
|
||||
backgroundColor: 'surface',
|
||||
boxShadow: '2px 2px 4px 0px rgba(0, 0, 0, 0.25)'
|
||||
}
|
||||
})}
|
||||
data-scrolled={isScrolled}
|
||||
>
|
||||
<Container
|
||||
className={css({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between'
|
||||
})}
|
||||
>
|
||||
<div>
|
||||
<NextLink
|
||||
href="/"
|
||||
>
|
||||
<EntGamers
|
||||
width="40px"
|
||||
/>
|
||||
</NextLink>
|
||||
</div>
|
||||
<div>
|
||||
<Menu />
|
||||
</div>
|
||||
</Container>
|
||||
</header>
|
||||
<div
|
||||
className={css({
|
||||
height: '60px'
|
||||
})}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
export default Header
|
||||
@@ -0,0 +1,121 @@
|
||||
import BackDrop from '@/components/ui/BackDrop'
|
||||
import { css } from '@/styled-system/css'
|
||||
import { iconButton } from '@/styled-system/recipes'
|
||||
import { type IconDefinition } from '@fortawesome/fontawesome-common-types'
|
||||
import { faBars, faHome, faTimes } from '@fortawesome/free-solid-svg-icons'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import { usePathname } from 'next/navigation'
|
||||
import { useCallback, useState, type FC } from 'react'
|
||||
|
||||
interface MenuLink {
|
||||
label: string
|
||||
href: string
|
||||
icon: IconDefinition
|
||||
}
|
||||
|
||||
const menuLinks: MenuLink[] = [
|
||||
{ label: 'Home', href: '/', icon: faHome },
|
||||
{ label: 'About', href: '/about', icon: faHome },
|
||||
{ label: 'Contact', href: '/contact', icon: faHome }
|
||||
]
|
||||
|
||||
const Menu: FC = () => {
|
||||
const [isMenuOpen, setIsMenuOpen] = useState(false)
|
||||
const pathName = usePathname()
|
||||
const handleClickAway = useCallback(() => {
|
||||
setIsMenuOpen(false)
|
||||
}, [])
|
||||
return (
|
||||
<>
|
||||
<button
|
||||
className={iconButton({
|
||||
color: 'primary'
|
||||
})}
|
||||
onClick={() => { setIsMenuOpen(!isMenuOpen) }}
|
||||
>
|
||||
<FontAwesomeIcon icon={faBars} />
|
||||
</button>
|
||||
<BackDrop
|
||||
onClickAway={handleClickAway}
|
||||
isOpen={isMenuOpen}
|
||||
>
|
||||
<div
|
||||
className={css({
|
||||
position: 'fixed',
|
||||
top: 0,
|
||||
right: 0,
|
||||
width: { base: '250px', smDown: '100%' },
|
||||
height: '100%',
|
||||
backgroundColor: 'surface',
|
||||
zIndex: 'modal'
|
||||
})}
|
||||
>
|
||||
<div
|
||||
className={css({
|
||||
height: '60px',
|
||||
borderBottom: '1px solid',
|
||||
borderColor: 'border',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'right',
|
||||
padding: '0 1rem'
|
||||
})}
|
||||
>
|
||||
<button
|
||||
className={iconButton({
|
||||
color: 'danger'
|
||||
})}
|
||||
onClick={() => { setIsMenuOpen(!isMenuOpen) }}
|
||||
>
|
||||
<FontAwesomeIcon icon={faTimes} />
|
||||
</button>
|
||||
</div>
|
||||
<nav>
|
||||
<ul
|
||||
className={css({
|
||||
listStyle: 'none',
|
||||
padding: 0,
|
||||
margin: 0
|
||||
})}
|
||||
>
|
||||
{menuLinks.map((menuLink, index) => (
|
||||
<li
|
||||
key={`menu-link-${index}`}
|
||||
>
|
||||
<a
|
||||
className={css({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'left',
|
||||
padding: '1rem',
|
||||
textDecoration: 'none',
|
||||
backgroundColor: 'transparent',
|
||||
color: 'text',
|
||||
transitionProperty: 'background-color',
|
||||
transitionDuration: 'normal',
|
||||
transitionTimingFunction: 'easeInOut',
|
||||
willChange: 'background-color color',
|
||||
'&:hover': {
|
||||
backgroundColor: 'primary',
|
||||
color: 'primary.contrast'
|
||||
},
|
||||
'&[data-active=true]': {
|
||||
backgroundColor: 'info',
|
||||
color: 'info.contrast'
|
||||
}
|
||||
})}
|
||||
href={menuLink.href}
|
||||
data-active={pathName === menuLink.href}
|
||||
>
|
||||
<FontAwesomeIcon icon={menuLink.icon} fixedWidth /> {menuLink.label}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</BackDrop>
|
||||
</>
|
||||
)
|
||||
}
|
||||
export default Menu
|
||||
@@ -1,27 +0,0 @@
|
||||
import { Container } from '@mui/material'
|
||||
import { type FC } from 'react'
|
||||
|
||||
import Header from '@components/layouts/Header'
|
||||
import Footer from '@components/layouts/Footer'
|
||||
|
||||
import { type ContainedProps } from '@interfaces'
|
||||
|
||||
const Contained: FC<ContainedProps> = ({ children }) => {
|
||||
return (
|
||||
<>
|
||||
<Header />
|
||||
<Container
|
||||
component="main"
|
||||
sx={{
|
||||
minHeight: 'calc(100vh - 92px)',
|
||||
marginTop: '60px',
|
||||
paddingBlock: 1
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</Container>
|
||||
<Footer />
|
||||
</>
|
||||
)
|
||||
}
|
||||
export default Contained
|
||||
@@ -1,76 +0,0 @@
|
||||
import { faAngleRight } from '@fortawesome/free-solid-svg-icons'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import { type FooterColumn } from '@interfaces'
|
||||
import { Container, Paper, Typography } from '@mui/material'
|
||||
import MuiLink from '@mui/material/Link'
|
||||
import NextLink from 'next/link'
|
||||
import { type FC } from 'react'
|
||||
|
||||
const Footer: FC = () => {
|
||||
const columns: FooterColumn[] = [
|
||||
{
|
||||
title: 'Acerca de',
|
||||
links: [
|
||||
{ label: 'EntGamers', url: '/about' },
|
||||
{ label: 'Clanes', url: '/clanes' }
|
||||
]
|
||||
},
|
||||
{
|
||||
title: 'Contacto',
|
||||
links: [
|
||||
{ label: 'Facebook', url: 'https://www.facebook.com/EntGamers/' },
|
||||
{ label: 'Twitter', url: 'https://twitter.com/EntGamers' },
|
||||
{ label: 'Email', url: 'mailto:contacto@entgamers.pro' }
|
||||
]
|
||||
}
|
||||
]
|
||||
return (
|
||||
<Paper
|
||||
sx={{
|
||||
paddingBlock: 1
|
||||
}}
|
||||
>
|
||||
<Container
|
||||
sx={(theme) => ({
|
||||
display: 'grid',
|
||||
gridTemplateColumns: '1fr',
|
||||
[theme.breakpoints.up('md')]: {
|
||||
gridTemplateColumns: '1fr 1fr'
|
||||
},
|
||||
[theme.breakpoints.up('lg')]: {
|
||||
gridTemplateColumns: '1fr 1fr 1fr'
|
||||
}
|
||||
})}
|
||||
>
|
||||
{columns.map((column, index) => (
|
||||
<div key={index}>
|
||||
<Typography variant="h3" component="div" align="center">{column.title}</Typography>
|
||||
{column.links.length > 0 && (
|
||||
<ul className="fa-ul">
|
||||
{column.links.map((link, index) => (
|
||||
<li key={index}>
|
||||
<FontAwesomeIcon icon={faAngleRight} listItem />
|
||||
<MuiLink
|
||||
component={NextLink}
|
||||
href={link.url}
|
||||
>
|
||||
{link.label}
|
||||
</MuiLink>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</Container>
|
||||
<Container>
|
||||
<Typography variant="body2" component="div" align="center">
|
||||
<p>
|
||||
Creado por <MuiLink href="https://srjuggernaut.dev" target="_blank">SrJuggernaut</MuiLink> con <3
|
||||
</p>
|
||||
</Typography>
|
||||
</Container>
|
||||
</Paper>
|
||||
)
|
||||
}
|
||||
export default Footer
|
||||
@@ -1 +0,0 @@
|
||||
export { default } from './Footer'
|
||||
@@ -1,135 +0,0 @@
|
||||
import EntGamers from '@assets/logos/EntGamers'
|
||||
import { faBars } from '@fortawesome/free-solid-svg-icons'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import { type Link } from '@interfaces'
|
||||
import { AppBar, Box, Container, Divider, IconButton, ListItemButton, NoSsr } from '@mui/material'
|
||||
import dynamic from 'next/dynamic'
|
||||
import NextLink from 'next/link'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useEffect, useState, type FC } from 'react'
|
||||
|
||||
const Drawer = dynamic(async () => await import('@mui/material/Drawer'), { ssr: false })
|
||||
const List = dynamic(async () => await import('@mui/material/List'), { ssr: false })
|
||||
const ListItemText = dynamic(async () => await import('@mui/material/ListItemText'), { ssr: false })
|
||||
|
||||
const MenuItems: Link[] = [
|
||||
{ label: 'Home', url: '/' },
|
||||
{ label: 'Clanes', url: '/clanes' }
|
||||
]
|
||||
|
||||
const Header: FC = () => {
|
||||
const [scrolled, setScrolled] = useState(false)
|
||||
const [openMenu, setOpenMenu] = useState(false)
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
const handleScroll = (): void => {
|
||||
if (window.scrollY > 15) {
|
||||
setScrolled(true)
|
||||
} else {
|
||||
setScrolled(false)
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof window !== 'undefined') {
|
||||
window.addEventListener('scroll', handleScroll)
|
||||
}
|
||||
return () => {
|
||||
if (typeof window !== 'undefined') {
|
||||
window.removeEventListener('scroll', handleScroll)
|
||||
}
|
||||
}
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<>
|
||||
<AppBar
|
||||
position="fixed"
|
||||
color={scrolled ? 'primary' : 'transparent'}
|
||||
elevation={scrolled ? 4 : 0}
|
||||
sx={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
minHeight: '60px',
|
||||
transition: 'background-color .3s cubic-bezier(0.4, 0, 0.2, 1) 0ms, box-shadow .3s cubic-bezier(0.4, 0, 0.2, 1) 0ms'
|
||||
}}
|
||||
>
|
||||
<Container
|
||||
sx={{
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between'
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
<NextLink href="/">
|
||||
<EntGamers
|
||||
width="40"
|
||||
height="40"
|
||||
/>
|
||||
</NextLink>
|
||||
</div>
|
||||
<div
|
||||
css={{
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center'
|
||||
}}
|
||||
>
|
||||
<IconButton
|
||||
aria-label="menu"
|
||||
sx={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
aspectRatio: '1'
|
||||
}}
|
||||
onClick={() => { setOpenMenu(true) }}
|
||||
>
|
||||
<FontAwesomeIcon icon={faBars} size="xs" />
|
||||
</IconButton>
|
||||
</div>
|
||||
</Container>
|
||||
</AppBar>
|
||||
<NoSsr>
|
||||
<Drawer
|
||||
open={openMenu}
|
||||
onClose={() => { setOpenMenu(false) }}
|
||||
anchor="right"
|
||||
>
|
||||
<Box
|
||||
sx={(theme) => ({
|
||||
width: '100vw',
|
||||
height: '100%',
|
||||
[theme.breakpoints.up('xs')]: {
|
||||
maxWidth: '300px'
|
||||
}
|
||||
})}
|
||||
>
|
||||
<div
|
||||
css={{
|
||||
minHeight: '60px'
|
||||
}}
|
||||
/>
|
||||
<Divider />
|
||||
<List
|
||||
sx={{ paddingTop: '0' }}
|
||||
>
|
||||
{MenuItems.map(({ label, url }) => (
|
||||
<ListItemButton
|
||||
key={`menu-item-${label}`}
|
||||
href={url}
|
||||
component={NextLink}
|
||||
selected={router.pathname === url}
|
||||
>
|
||||
<ListItemText primary={label} />
|
||||
</ListItemButton>
|
||||
))}
|
||||
</List>
|
||||
</Box>
|
||||
</Drawer>
|
||||
</NoSsr>
|
||||
</>
|
||||
)
|
||||
}
|
||||
export default Header
|
||||
@@ -1 +0,0 @@
|
||||
export { default } from './Header'
|
||||
@@ -1,89 +0,0 @@
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import { faChevronRight } from '@fortawesome/free-solid-svg-icons/faChevronRight'
|
||||
import { Box, NoSsr, Typography } from '@mui/material'
|
||||
import dynamic from 'next/dynamic'
|
||||
import { type FC } from 'react'
|
||||
|
||||
import { type PositionJoinTeamProps } from '@interfaces'
|
||||
|
||||
const UnirseForm = dynamic(async () => await import('@components/pages/equipo/unirse/UnirseForm'), {
|
||||
ssr: false,
|
||||
suspense: false
|
||||
})
|
||||
|
||||
const PositionJoinTeam: FC<PositionJoinTeamProps> = (
|
||||
{ benefits, description, requirements, title }
|
||||
) => {
|
||||
return (
|
||||
<Box>
|
||||
<Box
|
||||
sx={{
|
||||
marginBlock: '1rem'
|
||||
}}
|
||||
>
|
||||
<Typography variant="h2" align="center" gutterBottom>
|
||||
{title}
|
||||
</Typography>
|
||||
<Typography variant="body1" align="center" gutterBottom>
|
||||
{description}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box
|
||||
sx={theme => ({
|
||||
display: 'grid',
|
||||
gridTemplateColumns: '1fr',
|
||||
gap: 2,
|
||||
[theme.breakpoints.up('md')]: {
|
||||
gridTemplateColumns: '1fr 1fr'
|
||||
}
|
||||
})}
|
||||
>
|
||||
<Box>
|
||||
<Typography variant="h3" align="center" gutterBottom>
|
||||
Rellenar formulario
|
||||
</Typography>
|
||||
<NoSsr>
|
||||
<UnirseForm
|
||||
role={title}
|
||||
/>
|
||||
</NoSsr>
|
||||
</Box>
|
||||
<Box>
|
||||
<Typography variant="h3" gutterBottom>
|
||||
Requisitos
|
||||
</Typography>
|
||||
<ul className="fa-ul">
|
||||
{requirements.map(({ title, description }, index) => (
|
||||
<li key={index}>
|
||||
<FontAwesomeIcon icon={faChevronRight} className="fa-li" />
|
||||
<Typography variant="body1" gutterBottom>
|
||||
<strong>{title}</strong>
|
||||
</Typography>
|
||||
<Typography variant="body1" gutterBottom>
|
||||
{description}
|
||||
</Typography>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<Typography variant="h3" gutterBottom>
|
||||
Beneficios
|
||||
</Typography>
|
||||
<ul className="fa-ul">
|
||||
{benefits.map(({ title, description }, index) => (
|
||||
<li key={index}>
|
||||
<FontAwesomeIcon icon={faChevronRight} className="fa-li" />
|
||||
<Typography variant="body1" gutterBottom>
|
||||
<strong>{title}</strong>
|
||||
</Typography>
|
||||
<Typography variant="body1" gutterBottom>
|
||||
{description}
|
||||
</Typography>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
export default PositionJoinTeam
|
||||
@@ -1,133 +0,0 @@
|
||||
import { Box, Button, TextField, Typography } from '@mui/material'
|
||||
import { useFormik } from 'formik'
|
||||
import { useState, type FC } from 'react'
|
||||
import { object, string } from 'yup'
|
||||
|
||||
import { faSpinner } from '@fortawesome/free-solid-svg-icons'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import { type UnirseFormData, type UnirseFormProps } from '@interfaces'
|
||||
|
||||
const unirseFormSchema = object({
|
||||
name: string().required('El nombre es requerido'),
|
||||
email: string().email('El email no es válido').required('El email es requerido'),
|
||||
role: string().required('El rol es requerido'),
|
||||
discordUserName: string().matches(/^.{3,32}#[0-9]{4}$/, 'El formato correcto es userName#0000').required('El nombre de usuario de Discord es requerido'),
|
||||
experience: string().required('La experiencia es requerida')
|
||||
})
|
||||
|
||||
const UnirseForm: FC<UnirseFormProps> = ({ role }) => {
|
||||
const [isSended, setIsSended] = useState(false)
|
||||
const formik = useFormik<UnirseFormData>({
|
||||
initialValues: {
|
||||
name: '',
|
||||
email: '',
|
||||
role: role ?? '',
|
||||
discordUserName: '',
|
||||
experience: ''
|
||||
},
|
||||
onSubmit: async (values) => {
|
||||
try {
|
||||
const response = await fetch('/api/equipo/unirse/send-form', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(values)
|
||||
})
|
||||
if (response.ok) {
|
||||
setIsSended(true)
|
||||
formik.resetForm()
|
||||
} else {
|
||||
console.error('Error sending form')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
},
|
||||
validationSchema: unirseFormSchema
|
||||
})
|
||||
return isSended
|
||||
? (
|
||||
<Box
|
||||
sx={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
flexDirection: 'column',
|
||||
marginBlock: 1
|
||||
}}
|
||||
>
|
||||
<Typography variant="body1" align="center" color={(theme) => theme.palette.success.main} gutterBottom>
|
||||
¡Gracias por tu interés! Te contactaremos lo antes posible.
|
||||
</Typography>
|
||||
</Box>
|
||||
)
|
||||
: (
|
||||
<Box
|
||||
component="form"
|
||||
onSubmit={formik.handleSubmit}
|
||||
>
|
||||
<TextField
|
||||
name="name"
|
||||
label="Nombre"
|
||||
placeholder="Escribe tu nombre"
|
||||
value={formik.values.name}
|
||||
onChange={formik.handleChange}
|
||||
error={formik.touched.name !== undefined && formik.errors.name !== undefined }
|
||||
helperText={formik.touched.name !== undefined && formik.errors.name}
|
||||
fullWidth
|
||||
margin="normal"
|
||||
/>
|
||||
<TextField
|
||||
name="email"
|
||||
label="Correo electrónico"
|
||||
placeholder="Usaremos este correo para contactarte"
|
||||
value={formik.values.email}
|
||||
onChange={formik.handleChange}
|
||||
error={formik.touched.email !== undefined && formik.errors.email !== undefined }
|
||||
helperText={formik.touched.email !== undefined && formik.errors.email}
|
||||
fullWidth
|
||||
margin="normal"
|
||||
/>
|
||||
<TextField
|
||||
name="discordUserName"
|
||||
label="Nombre de usuario de Discord"
|
||||
placeholder="userName#0000"
|
||||
value={formik.values.discordUserName}
|
||||
onChange={formik.handleChange}
|
||||
error={formik.touched.discordUserName !== undefined && formik.errors.discordUserName !== undefined }
|
||||
helperText={formik.touched.discordUserName !== undefined && formik.errors.discordUserName}
|
||||
fullWidth
|
||||
margin="normal"
|
||||
/>
|
||||
<TextField
|
||||
name="experience"
|
||||
label="Experiencia"
|
||||
placeholder="¿Tienes experiencia en el área? ¿Qué conocimientos tienes?"
|
||||
value={formik.values.experience}
|
||||
onChange={formik.handleChange}
|
||||
error={formik.touched.experience !== undefined && formik.errors.experience !== undefined }
|
||||
helperText={formik.touched.experience !== undefined && formik.errors.experience}
|
||||
fullWidth
|
||||
margin="normal"
|
||||
multiline
|
||||
rows={4}
|
||||
/>
|
||||
<Box
|
||||
sx={{
|
||||
marginBlock: 1
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
variant="contained"
|
||||
fullWidth
|
||||
type="submit"
|
||||
endIcon={formik.isSubmitting ? <FontAwesomeIcon icon={faSpinner} spin /> : undefined}
|
||||
>
|
||||
Enviar
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
export default UnirseForm
|
||||
@@ -1,68 +0,0 @@
|
||||
import { Box, Button, Container, Paper, Typography } from '@mui/material'
|
||||
import Image from 'next/image'
|
||||
import Link from 'next/link'
|
||||
import { type FC } from 'react'
|
||||
|
||||
import ClanesImage from '@assets/images/Clanes.png'
|
||||
|
||||
export interface ClanesProps {
|
||||
title: string
|
||||
description: string
|
||||
}
|
||||
|
||||
const Clanes: FC<ClanesProps> = ({ description, title }) => {
|
||||
return (
|
||||
<div
|
||||
id="clanes"
|
||||
css={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
minHeight: '100vh',
|
||||
backgroundImage: 'url("/images/backgrounds/bricks.png")'
|
||||
}}
|
||||
>
|
||||
<Paper
|
||||
variant='glass'
|
||||
component={Container}
|
||||
>
|
||||
<Typography
|
||||
variant="h2"
|
||||
align="center"
|
||||
gutterBottom
|
||||
>
|
||||
{title}
|
||||
</Typography>
|
||||
<Box
|
||||
sx={(theme) => ({
|
||||
display: 'grid',
|
||||
gridTemplateColumns: '1fr',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
[theme.breakpoints.up('md')]: {
|
||||
gridTemplateColumns: '1fr 1fr'
|
||||
}
|
||||
})}
|
||||
>
|
||||
<div>
|
||||
<Image src={ClanesImage} alt="EntGamers" width={600} height={315} />
|
||||
</div>
|
||||
<div
|
||||
css={{
|
||||
textAlign: 'center'
|
||||
}}
|
||||
>
|
||||
<Typography variant="body1" gutterBottom >
|
||||
{description}
|
||||
</Typography>
|
||||
<Button variant="contained" color="primary" LinkComponent={Link} component="a" href="/clanes">
|
||||
Ver clanes
|
||||
</Button>
|
||||
</div>
|
||||
</Box>
|
||||
</Paper>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Clanes
|
||||
@@ -1,181 +0,0 @@
|
||||
import { Container, IconButton, Typography } from '@mui/material'
|
||||
import { faArrowDown } from '@fortawesome/free-solid-svg-icons'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import gsap, { Elastic, Linear } from 'gsap'
|
||||
import ScrollTrigger from 'gsap/dist/ScrollTrigger'
|
||||
import ScrollToPlugin from 'gsap/dist/ScrollToPlugin'
|
||||
import Image from 'next/image'
|
||||
import { type FC, useEffect, useRef } from 'react'
|
||||
|
||||
import EntGamers from '@assets/images/EntGamers.png'
|
||||
|
||||
gsap.registerPlugin(ScrollTrigger, ScrollToPlugin)
|
||||
|
||||
export interface HeroProps {
|
||||
title: string
|
||||
subtitle: string
|
||||
}
|
||||
|
||||
const Hero: FC<HeroProps> = ({ subtitle, title }) => {
|
||||
const layer01 = useRef<HTMLDivElement | null>(null)
|
||||
const layer02 = useRef<HTMLDivElement | null>(null)
|
||||
const layer03 = useRef<HTMLDivElement | null>(null)
|
||||
const layer04 = useRef<HTMLDivElement | null>(null)
|
||||
const layer05 = useRef<HTMLDivElement | null>(null)
|
||||
const layer06 = useRef<HTMLDivElement | null>(null)
|
||||
const verMasButton = useRef<HTMLButtonElement | null>(null)
|
||||
useEffect(() => {
|
||||
const scrollTrigger = {
|
||||
trigger: layer01.current,
|
||||
start: 'top bottom',
|
||||
end: 'bottom top',
|
||||
toggleActions: 'play pause resume pause'
|
||||
}
|
||||
const timeLine = gsap.timeline({ duration: 1, repeat: -1, repeatDelay: 4, scrollTrigger })
|
||||
const layer01Animation = gsap.to(layer01.current, { duration: 175, backgroundPositionX: '2048px', repeat: -1, ease: Linear.easeNone, scrollTrigger })
|
||||
const layer02Animation = gsap.to(layer02.current, { duration: 150, backgroundPositionX: '2048px', repeat: -1, ease: Linear.easeNone, scrollTrigger })
|
||||
const layer03Animation = gsap.to(layer03.current, { duration: 125, backgroundPositionX: '2048px', repeat: -1, ease: Linear.easeNone, scrollTrigger })
|
||||
const layer04Animation = gsap.to(layer04.current, { duration: 100, backgroundPositionX: '2048px', repeat: -1, ease: Linear.easeNone, scrollTrigger })
|
||||
const layer05Animation = gsap.to(layer05.current, { duration: 75, backgroundPositionX: '2048px', repeat: -1, ease: Linear.easeNone, scrollTrigger })
|
||||
const layer06Animation = gsap.to(layer06.current, { duration: 50, backgroundPositionX: '2048px', repeat: -1, ease: Linear.easeNone, scrollTrigger })
|
||||
timeLine
|
||||
.to(verMasButton.current, { y: '+=15', duration: 0.5 })
|
||||
.to(verMasButton.current, { y: '-=15', ease: Elastic.easeOut.config(2, 0.1), duration: 3 })
|
||||
return () => {
|
||||
layer01Animation.kill()
|
||||
layer02Animation.kill()
|
||||
layer03Animation.kill()
|
||||
layer04Animation.kill()
|
||||
layer05Animation.kill()
|
||||
layer06Animation.kill()
|
||||
timeLine.kill()
|
||||
}
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={layer01}
|
||||
css={{
|
||||
minHeight: '100vh',
|
||||
backgroundImage: 'url(/images/backgrounds/MysteriousForestNightLayer01.png)',
|
||||
backgroundPositionX: '0',
|
||||
backgroundPositionY: 'top',
|
||||
backgroundSize: 'auto',
|
||||
backgroundRepeat: 'repeat-x'
|
||||
}}
|
||||
>
|
||||
<div
|
||||
ref={layer02}
|
||||
css={{
|
||||
minHeight: '100vh',
|
||||
backgroundImage: 'url(/images/backgrounds/MysteriousForestNightLayer02.png)',
|
||||
backgroundPositionX: '0',
|
||||
backgroundPositionY: 'bottom',
|
||||
backgroundSize: 'auto',
|
||||
backgroundRepeat: 'repeat-x'
|
||||
}}
|
||||
>
|
||||
<div
|
||||
ref={layer03}
|
||||
css={{
|
||||
minHeight: '100vh',
|
||||
backgroundImage: 'url(/images/backgrounds/MysteriousForestNightLayer03.png)',
|
||||
backgroundPositionX: '0',
|
||||
backgroundPositionY: 'bottom',
|
||||
backgroundSize: 'auto',
|
||||
backgroundRepeat: 'repeat-x'
|
||||
}}
|
||||
>
|
||||
<div
|
||||
ref={layer04}
|
||||
css={{
|
||||
minHeight: '100vh',
|
||||
backgroundImage: 'url(/images/backgrounds/MysteriousForestNightLayer04.png)',
|
||||
backgroundPositionX: '0',
|
||||
backgroundPositionY: 'bottom',
|
||||
backgroundSize: 'auto',
|
||||
backgroundRepeat: 'repeat-x'
|
||||
}}
|
||||
>
|
||||
<div
|
||||
ref={layer05}
|
||||
css={{
|
||||
minHeight: '100vh',
|
||||
backgroundImage: 'url(/images/backgrounds/MysteriousForestNightLayer05.png)',
|
||||
backgroundPositionX: '0',
|
||||
backgroundPositionY: 'bottom',
|
||||
backgroundSize: 'auto',
|
||||
backgroundRepeat: 'repeat-x'
|
||||
}}
|
||||
>
|
||||
<div
|
||||
ref={layer06}
|
||||
css={{
|
||||
position: 'relative',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
minHeight: '100vh',
|
||||
backgroundImage: 'url(/images/backgrounds/MysteriousForestNightLayer06.png)',
|
||||
backgroundPositionX: '0',
|
||||
backgroundPositionY: 'bottom',
|
||||
backgroundSize: 'auto',
|
||||
backgroundRepeat: 'repeat-x'
|
||||
}}
|
||||
>
|
||||
<Container
|
||||
fixed
|
||||
sx={(theme) => ({
|
||||
display: 'grid',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
gridTemplateColumns: '1fr',
|
||||
[theme.breakpoints.up('md')]: {
|
||||
gridTemplateColumns: '1fr 1fr'
|
||||
}
|
||||
})}
|
||||
>
|
||||
<div>
|
||||
<Image src={EntGamers} alt="EntGamers" width={600} height={600} loading="eager" />
|
||||
</div>
|
||||
<div>
|
||||
<Typography variant="h1" gutterBottom align="center">
|
||||
{title}
|
||||
</Typography>
|
||||
<Typography variant="h2" gutterBottom align="center">
|
||||
{subtitle}
|
||||
</Typography>
|
||||
</div>
|
||||
</Container>
|
||||
<IconButton
|
||||
ref={verMasButton}
|
||||
sx={(theme) => ({
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
position: 'absolute',
|
||||
bottom: '2rem',
|
||||
right: '1rem',
|
||||
aspectRatio: '1',
|
||||
[theme.breakpoints.up('md')]: {
|
||||
bottom: '1rem',
|
||||
right: 'calc(50% - 1rem)'
|
||||
}
|
||||
})}
|
||||
onClick={() => {
|
||||
gsap.to(window, { duration: 0.3, scrollTo: '#clanes' })
|
||||
}}
|
||||
color='primary'
|
||||
>
|
||||
<FontAwesomeIcon icon={faArrowDown} fixedWidth />
|
||||
</IconButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Hero
|
||||
@@ -1,93 +0,0 @@
|
||||
// import { SocialSliderProps } from '@components/pages/home/socialNetworks/SocialSlider'
|
||||
import { Container, Paper } from '@mui/material'
|
||||
import gsap, { Linear } from 'gsap'
|
||||
import ScrollToPlugin from 'gsap/dist/ScrollToPlugin'
|
||||
import ScrollTrigger from 'gsap/dist/ScrollTrigger'
|
||||
import { type FC, useEffect, useRef } from 'react'
|
||||
|
||||
gsap.registerPlugin(ScrollTrigger, ScrollToPlugin)
|
||||
|
||||
const SocialNetworks: FC = () => {
|
||||
const layer01 = useRef<HTMLDivElement | null>(null)
|
||||
const layer02 = useRef<HTMLDivElement | null>(null)
|
||||
const layer03 = useRef<HTMLDivElement | null>(null)
|
||||
const layer04 = useRef<HTMLDivElement | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
const scrollTrigger = {
|
||||
trigger: layer01.current,
|
||||
start: 'top bottom',
|
||||
end: 'bottom top',
|
||||
toggleActions: 'play pause resume pause'
|
||||
}
|
||||
const layer02Animation = gsap.to(layer02.current, { duration: 150, backgroundPositionX: '2048px', repeat: -1, ease: Linear.easeNone, scrollTrigger })
|
||||
const layer03Animation = gsap.to(layer03.current, { duration: 60, backgroundPositionX: '2048px', repeat: -1, ease: Linear.easeNone, scrollTrigger })
|
||||
const layer04Animation = gsap.to(layer04.current, { duration: 125, backgroundPositionX: '2048px', repeat: -1, ease: Linear.easeNone, scrollTrigger })
|
||||
|
||||
return () => {
|
||||
layer02Animation.kill()
|
||||
layer03Animation.kill()
|
||||
layer04Animation.kill()
|
||||
}
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div
|
||||
ref={layer01}
|
||||
css={{
|
||||
minHeight: '100vh',
|
||||
backgroundImage: 'url(/images/backgrounds/SkyNightLayer01.png)',
|
||||
backgroundPositionX: 'center',
|
||||
backgroundPositionY: 'center',
|
||||
backgroundSize: 'auto'
|
||||
}}
|
||||
>
|
||||
<div
|
||||
ref={layer02}
|
||||
css={{
|
||||
minHeight: '100vh',
|
||||
backgroundImage: 'url(/images/backgrounds/SkyNightLayer02.png)',
|
||||
backgroundPositionX: '0',
|
||||
backgroundPositionY: 'center',
|
||||
backgroundSize: 'auto',
|
||||
backgroundRepeat: 'repeat-x'
|
||||
}}
|
||||
>
|
||||
<div
|
||||
ref={layer03}
|
||||
css={{
|
||||
minHeight: '100vh',
|
||||
backgroundImage: 'url(/images/backgrounds/SkyNightLayer03.png)',
|
||||
backgroundPositionX: '0',
|
||||
backgroundPositionY: 'center',
|
||||
backgroundSize: 'auto',
|
||||
backgroundRepeat: 'repeat-x'
|
||||
}}
|
||||
>
|
||||
<div
|
||||
ref={layer04}
|
||||
css={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
minHeight: '100vh',
|
||||
backgroundImage: 'url(/images/backgrounds/SkyNightLayer04.png)',
|
||||
backgroundPositionX: '0',
|
||||
backgroundPositionY: 'center',
|
||||
backgroundSize: 'auto',
|
||||
backgroundRepeat: 'repeat-x'
|
||||
}}
|
||||
>
|
||||
<Paper
|
||||
component={Container}
|
||||
variant='glass'
|
||||
>
|
||||
</Paper>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default SocialNetworks
|
||||
@@ -1,82 +0,0 @@
|
||||
import { Button, Container, Typography } from '@mui/material'
|
||||
import NextLink from 'next/link'
|
||||
import { type FC } from 'react'
|
||||
|
||||
import ProfileCard, { type ProfileCardProps } from '@components/profiles/ProfileCard'
|
||||
|
||||
export interface TeamProps {
|
||||
title: string
|
||||
teamMembers: ProfileCardProps[]
|
||||
viewTeamButtonText: string
|
||||
joinTeamButtonText: string
|
||||
}
|
||||
|
||||
const Team: FC<TeamProps> = ({ title, teamMembers, joinTeamButtonText, viewTeamButtonText }) => {
|
||||
return (
|
||||
<div
|
||||
css={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
minHeight: '100vh',
|
||||
backgroundImage: 'url(/images/backgrounds/MysteriousForest.jpg)',
|
||||
backgroundPositionX: 'bottom',
|
||||
backgroundPositionY: 'center',
|
||||
backgroundSize: 'cover',
|
||||
backgroundRepeat: 'repeat-x'
|
||||
}}
|
||||
>
|
||||
<Container>
|
||||
<Typography variant='h2' align="center" gutterBottom>{title}</Typography>
|
||||
<div
|
||||
css={{
|
||||
display: 'grid',
|
||||
gridTemplateColumns: 'repeat(auto-fit, minmax(300px, 1fr))',
|
||||
gridGap: '1rem',
|
||||
justifyContent: 'center',
|
||||
justifyItems: 'center',
|
||||
marginBlock: 2
|
||||
}}
|
||||
>
|
||||
{teamMembers.map(({ avatar, biography, socialNetworks, userName, role }) => (
|
||||
<ProfileCard
|
||||
key={`profile-card-${userName}` }
|
||||
avatar={avatar}
|
||||
biography={biography}
|
||||
socialNetworks={socialNetworks}
|
||||
userName={userName}
|
||||
role={role}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<div
|
||||
css={{
|
||||
display: 'flex',
|
||||
justifyContent: 'space-around',
|
||||
alignItems: 'center',
|
||||
marginBlock: '16px'
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
href="/equipo"
|
||||
variant="contained"
|
||||
color="info"
|
||||
component={NextLink}
|
||||
>
|
||||
{viewTeamButtonText}
|
||||
</Button>
|
||||
<Button
|
||||
href="/equipo/unirse"
|
||||
variant="contained"
|
||||
color="success"
|
||||
component={NextLink}
|
||||
>
|
||||
{joinTeamButtonText}
|
||||
</Button>
|
||||
</div>
|
||||
</Container>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Team
|
||||
@@ -1,163 +0,0 @@
|
||||
import { faFacebook, faInstagram, faTiktok, faTwitch, faTwitter, faYoutube } from '@fortawesome/free-brands-svg-icons'
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
|
||||
import { Avatar, Card, CardContent, IconButton, Tooltip, Typography } from '@mui/material'
|
||||
import NextImage from 'next/image'
|
||||
import { type FC } from 'react'
|
||||
|
||||
import BadgeBook from '@assets/images/gaming/BadgeBook.png'
|
||||
import BadgeShield from '@assets/images/gaming/BadgeShield.png'
|
||||
import BadgeSword from '@assets/images/gaming/BadgeSword.png'
|
||||
import ButtonA from '@assets/images/gaming/ButtonA.png'
|
||||
import { faGlobe } from '@fortawesome/free-solid-svg-icons'
|
||||
import { type SocialLink } from '@interfaces'
|
||||
|
||||
export interface ProfileCardProps {
|
||||
userName: string
|
||||
biography: string
|
||||
avatar: string
|
||||
socialNetworks: SocialLink[]
|
||||
role: 'user' | 'moderator' | 'collaborator' | 'admin'
|
||||
}
|
||||
|
||||
const ProfileCard: FC<ProfileCardProps> = ({ avatar, biography, socialNetworks, userName, role }) => {
|
||||
return (
|
||||
<Card
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'space-between',
|
||||
position: 'relative',
|
||||
maxWidth: '300px',
|
||||
overflow: 'visible'
|
||||
}}
|
||||
variant="gbaDialog"
|
||||
>
|
||||
{role !== 'user' && (
|
||||
<Tooltip
|
||||
title={role}
|
||||
placement="top"
|
||||
arrow
|
||||
>
|
||||
<div
|
||||
css={{
|
||||
position: 'absolute',
|
||||
top: '5px',
|
||||
right: '5px'
|
||||
}}
|
||||
>
|
||||
<NextImage
|
||||
src={role === 'moderator' ? BadgeShield : role === 'collaborator' ? BadgeBook : BadgeSword}
|
||||
alt={role}
|
||||
width={30}
|
||||
height={30}
|
||||
/>
|
||||
</div>
|
||||
</Tooltip>
|
||||
)}
|
||||
<div
|
||||
css={{
|
||||
position: 'absolute',
|
||||
bottom: '-15px',
|
||||
right: '0px'
|
||||
}}
|
||||
>
|
||||
<NextImage
|
||||
src={ButtonA}
|
||||
alt="Button A"
|
||||
width={30}
|
||||
height={30}
|
||||
/>
|
||||
</div>
|
||||
<CardContent
|
||||
sx={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
height: '100%'
|
||||
}}
|
||||
>
|
||||
<div
|
||||
css={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
flexGrow: 0
|
||||
}}
|
||||
>
|
||||
<Avatar
|
||||
sx={{
|
||||
width: '120px',
|
||||
height: '120px',
|
||||
backgroundColor: 'transparent'
|
||||
}}
|
||||
>
|
||||
<NextImage
|
||||
src={avatar}
|
||||
alt={userName}
|
||||
width={150}
|
||||
height={150}
|
||||
/>
|
||||
</Avatar>
|
||||
</div>
|
||||
<Typography variant='h4' component="h3" align="center" sx={{ marginBlock: 1, flexGrow: 0, textOverflow: 'ellipsis', whiteSpace: 'nowrap', overflow: 'hidden' }}>
|
||||
{userName}
|
||||
</Typography>
|
||||
<Typography variant='body1' align="center" sx={{ marginBlock: 1, flexGrow: 1 }}>
|
||||
{biography.replace(/^(.{120}[^\s]*).*/, '$1…')}
|
||||
</Typography>
|
||||
<div
|
||||
css={{
|
||||
display: 'flex',
|
||||
flexWrap: 'wrap',
|
||||
flexGrow: 0,
|
||||
justifyContent: 'space-around',
|
||||
alignItems: 'center',
|
||||
marginTop: 'auto'
|
||||
}}
|
||||
>
|
||||
{socialNetworks.map(({ socialNetwork, url, label }, i) => (
|
||||
<Tooltip
|
||||
key={`${userName}-${socialNetwork}-${i}`}
|
||||
title={label}
|
||||
placement="top"
|
||||
arrow
|
||||
>
|
||||
<IconButton
|
||||
|
||||
href={url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
color="inherit"
|
||||
sx={{
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
aspectRatio: '1'
|
||||
}}
|
||||
>
|
||||
{((socialNetwork) => {
|
||||
switch (socialNetwork) {
|
||||
case 'facebook':
|
||||
return <FontAwesomeIcon icon={faFacebook} size="xs" />
|
||||
case 'twitter':
|
||||
return <FontAwesomeIcon icon={faTwitter} size="xs" />
|
||||
case 'instagram':
|
||||
return <FontAwesomeIcon icon={faInstagram} size="xs" />
|
||||
case 'twitch':
|
||||
return <FontAwesomeIcon icon={faTwitch} size="xs" />
|
||||
case 'youtube':
|
||||
return <FontAwesomeIcon icon={faYoutube} size="xs" />
|
||||
case 'tiktok':
|
||||
return <FontAwesomeIcon icon={faTiktok} size="xs" />
|
||||
default:
|
||||
return <FontAwesomeIcon icon={faGlobe} size="xs" />
|
||||
}
|
||||
})(socialNetwork)}
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
))}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
export default ProfileCard
|
||||
@@ -0,0 +1,43 @@
|
||||
import { css } from '@/styled-system/css'
|
||||
import { AnimatePresence, motion } from 'framer-motion'
|
||||
import { type FC, type ReactNode } from 'react'
|
||||
import { createPortal } from 'react-dom'
|
||||
|
||||
export interface BackDropProps {
|
||||
children?: ReactNode
|
||||
isOpen: boolean
|
||||
onClickAway: () => void
|
||||
}
|
||||
|
||||
const BackDrop: FC<BackDropProps> = ({ isOpen, onClickAway, children }) => {
|
||||
if (typeof window === 'undefined') return null
|
||||
return createPortal((
|
||||
<AnimatePresence>
|
||||
{isOpen && (
|
||||
<motion.div
|
||||
className={css({
|
||||
position: 'fixed',
|
||||
top: 0,
|
||||
left: 0,
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
zIndex: 'modalBackdrop',
|
||||
backgroundColor: 'rgba(0, 0, 0, 0.5)'
|
||||
})}
|
||||
onClick={(event) => {
|
||||
if (event.target === event.currentTarget) {
|
||||
onClickAway()
|
||||
}
|
||||
}}
|
||||
transition={{ duration: 0.3, ease: 'easeInOut' }}
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
>
|
||||
{children}
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
), document.body)
|
||||
}
|
||||
export default BackDrop
|
||||
@@ -0,0 +1,59 @@
|
||||
import { cx } from '@/styled-system/css'
|
||||
import { typography, type TypographyVariantProps } from '@/styled-system/recipes'
|
||||
import { type MergeOmitting } from '@/types/utilities'
|
||||
import React, { type ElementType, type FC } from 'react'
|
||||
|
||||
type ComposedTypographyProps = MergeOmitting<React.HTMLAttributes<HTMLElement>, TypographyVariantProps>
|
||||
|
||||
export interface TypographyProps extends ComposedTypographyProps {
|
||||
component?: ElementType
|
||||
}
|
||||
|
||||
const variantToComponent = (variant: TypographyVariantProps['variant']): ElementType => {
|
||||
switch (variant) {
|
||||
case 'h1':
|
||||
case 'h2':
|
||||
case 'h3':
|
||||
case 'h4':
|
||||
case 'h5':
|
||||
case 'h6':
|
||||
return variant
|
||||
case 'subtitle1':
|
||||
case 'subtitle2':
|
||||
return 'div'
|
||||
case 'button':
|
||||
case 'overline':
|
||||
case 'srOnly':
|
||||
case 'caption':
|
||||
return 'span'
|
||||
case 'body1':
|
||||
case 'body2':
|
||||
default:
|
||||
return 'p'
|
||||
}
|
||||
}
|
||||
|
||||
const isHeading = (text: string): boolean => {
|
||||
return text.startsWith('h')
|
||||
}
|
||||
|
||||
const Typography: FC<TypographyProps> = ({ children, className, component, ...rest }) => {
|
||||
const [typographyRecipeArgs, allOtherTypographyProps] = typography.splitVariantProps(rest)
|
||||
const Component = component ?? variantToComponent(typographyRecipeArgs.variant)
|
||||
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
|
||||
typographyRecipeArgs.color = typographyRecipeArgs.color !== undefined
|
||||
? typographyRecipeArgs.color
|
||||
: typeof typographyRecipeArgs.variant === 'string' && isHeading(typographyRecipeArgs.variant)
|
||||
? 'primary'
|
||||
: undefined
|
||||
return (
|
||||
<Component
|
||||
className={cx(typography(typographyRecipeArgs), className)}
|
||||
{...allOtherTypographyProps}
|
||||
>
|
||||
{children}
|
||||
</Component>
|
||||
)
|
||||
}
|
||||
|
||||
export default Typography
|
||||
Reference in New Issue
Block a user