feat: static site

* feat: mui support & basic theming

* feat: entgamers favicon

* feat: public images until dynamic content can be used

* feat: entgamers & gaming assets

* feat: eslint extra rules

* feat: mui theme modifications

* feat: fontawesome, gsap, bundle analyzer

* feat: common interfaces

* feat: basic layout

* chore: upadted dependencies

* chore: updated dependencies

* feat: updated link styles

* feat: layout now have better interfaces

* feat: basic seo component

* feat: static website

* feat: env variable rules in .gitignore

* feat: added lint to pre-commit
This commit is contained in:
2022-09-26 12:01:26 -05:00
committed by GitHub
parent 8573d61066
commit c3dae929c6
125 changed files with 3889 additions and 355 deletions
+38
View File
@@ -0,0 +1,38 @@
import Head from 'next/head'
import { FC } from 'react'
export type 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 && (
<>
<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 && (
<>
<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 && (
<>
<meta key="og_image" property="og:image" content={image} />
<meta key="twitter_image" property="twitter:image" content={image} />
</>
)}
</Head>
)
}
export default Seo
+27
View File
@@ -0,0 +1,27 @@
import { Container } from '@mui/material'
import { FC } from 'react'
import Header from '@components/layouts/Header'
import Footer from '@components/layouts/Footer'
import { 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
+75
View File
@@ -0,0 +1,75 @@
import { Container, Paper, Typography } from '@mui/material'
import NextLink from 'next/link'
import MuiLink from '@mui/material/Link'
import { FooterColumn } from '@interfaces'
import { faAngleRight } from '@fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
const Footer = () => {
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 />
<NextLink href={link.url} passHref>
<MuiLink>
{link.label}
</MuiLink>
</NextLink>
</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 &lt;3
</p>
</Typography>
</Container>
</Paper>
)
}
export default Footer
+1
View File
@@ -0,0 +1 @@
export { default } from './Footer'
+139
View File
@@ -0,0 +1,139 @@
import { faBars } from '@fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { AppBar, Box, Container, IconButton, NoSsr, ListItemButton, Divider } from '@mui/material'
import NextLink from 'next/link'
import dynamic from 'next/dynamic'
import { useRouter } from 'next/router'
import { useEffect, useState } from 'react'
import EntGamers from '@assets/logos/EntGamers'
import { Link } from '@interfaces'
const Drawer = dynamic(() => import('@mui/material/Drawer'), { ssr: false })
const List = dynamic(() => import('@mui/material/List'), { ssr: false })
const ListItemText = dynamic(() => import('@mui/material/ListItemText'), { ssr: false })
const MenuItems: Link[] = [
{ label: 'Home', url: '/' },
{ label: 'Clanes', url: '/clanes' }
]
const Header = () => {
const [scrolled, setScrolled] = useState(false)
const [openMenu, setOpenMenu] = useState(false)
const router = useRouter()
const handleScroll = () => {
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="/">
<a>
<EntGamers
width="40"
height="40"
/>
</a>
</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 }) => (
<NextLink key={`menu-item-${label}`} href={url} passHref>
<ListItemButton
component="a"
selected={router.pathname === url}
>
<ListItemText primary={label} />
</ListItemButton>
</NextLink>
))}
</List>
</Box>
</Drawer>
</NoSsr>
</>
)
}
export default Header
+1
View File
@@ -0,0 +1 @@
export { default } from './Header'
@@ -0,0 +1,89 @@
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 { FC } from 'react'
import { PositionJoinTeamProps } from '@interfaces'
const UnirseForm = dynamic(() => 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
@@ -0,0 +1,133 @@
import { useFormik } from 'formik'
import { Box, Button, TextField, Typography } from '@mui/material'
import { FC, useState } from 'react'
import { object, string } from 'yup'
import { UnirseFormData, UnirseFormProps } from '@interfaces'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faSpinner } from '@fortawesome/free-solid-svg-icons'
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 && !!formik.errors.name}
helperText={formik.touched.name && 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 && !!formik.errors.email}
helperText={formik.touched.email && 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 && !!formik.errors.discordUserName}
helperText={formik.touched.discordUserName && 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 && !!formik.errors.experience}
helperText={formik.touched.experience && 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
+68
View File
@@ -0,0 +1,68 @@
import { Box, Button, Container, Paper, Typography } from '@mui/material'
import Image from 'next/image'
import Link from 'next/link'
import { 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
+181
View File
@@ -0,0 +1,181 @@
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 { 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
@@ -0,0 +1,101 @@
import gsap, { Linear } from 'gsap'
import ScrollTrigger from 'gsap/dist/ScrollTrigger'
import ScrollToPlugin from 'gsap/dist/ScrollToPlugin'
import { Container, Paper } from '@mui/material'
import { FC, useEffect, useRef } from 'react'
import SocialSlider, { SocialSliderProps } from '@components/pages/home/socialNetworks/SocialSlider'
gsap.registerPlugin(ScrollTrigger, ScrollToPlugin)
export interface SocialNetworksProps {
socialNetworks: SocialSliderProps['slides']
}
const SocialNetworks: FC<SocialNetworksProps> = ({ socialNetworks }) => {
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'
>
<SocialSlider
slides={socialNetworks}
/>
</Paper>
</div>
</div>
</div>
</div>
)
}
export default SocialNetworks
+84
View File
@@ -0,0 +1,84 @@
import { Container, Button, Typography } from '@mui/material'
import NextLink from 'next/link'
import { FC } from 'react'
import ProfileCard, { 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'
}}
>
<NextLink href="/equipo" passHref>
<Button
variant="contained"
color="info"
component="a"
>
{viewTeamButtonText}
</Button>
</NextLink>
<NextLink href="/equipo/unirse" passHref>
<Button
variant="contained"
color="success"
component="a"
>
{joinTeamButtonText}
</Button>
</NextLink>
</div>
</Container>
</div>
)
}
export default Team
@@ -0,0 +1,95 @@
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faFacebook, faInstagram, faTwitch, faTwitter, faYoutube } from '@fortawesome/free-brands-svg-icons'
import { Button, Typography } from '@mui/material'
import { Navigation, Pagination, A11y } from 'swiper'
import { Swiper, SwiperSlide } from 'swiper/react'
import 'swiper/css'
import 'swiper/css/navigation'
import 'swiper/css/pagination'
import { FC } from 'react'
import { Button as ButtonType } from '@interfaces'
export interface SlideProps {
socialNetwork: 'facebook' | 'twitter' | 'instagram' | 'youtube' | 'twitch'
description: string
links: ButtonType[]
}
const Slide:FC<SlideProps> = ({ description, socialNetwork, links }) => {
return (
<div
css={{
marginInline: '36px',
marginBlock: '16px',
padding: '16px'
}}
>
<Typography variant="body1" component="div" align="center" >
{{
facebook: <FontAwesomeIcon style={{ filter: 'drop-shadow(2px 2px 2px rgb(0 0 0 / 0.4)' }}icon={faFacebook} size="5x" fixedWidth />,
twitter: <FontAwesomeIcon style={{ filter: 'drop-shadow(2px 2px 2px rgb(0 0 0 / 0.4)' }}icon={faTwitter} size="5x" fixedWidth />,
instagram: <FontAwesomeIcon style={{ filter: 'drop-shadow(2px 2px 2px rgb(0 0 0 / 0.4)' }}icon={faInstagram} size="5x" fixedWidth />,
youtube: <FontAwesomeIcon style={{ filter: 'drop-shadow(2px 2px 2px rgb(0 0 0 / 0.4)' }}icon={faYoutube} size="5x" fixedWidth />,
twitch: <FontAwesomeIcon style={{ filter: 'drop-shadow(2px 2px 2px rgb(0 0 0 / 0.4)' }}icon={faTwitch} size="5x" fixedWidth />
}[socialNetwork]}
</Typography>
<Typography sx={(theme) => ({ textShadow: `2px 2px 2px ${theme.palette.background.default}` })} variant="body1" align="center" >
{description}
</Typography>
<div
css={{
display: 'flex',
justifyContent: 'space-around',
alignItems: 'center',
marginBlock: '1rem'
}}
>
{links.map(({ url, label, color, variant }) => (
<Button
key={url}
href={url}
target="_blank"
rel="noopener noreferrer"
variant={variant}
color={color}
>
{label}
</Button>
))}
</div>
</div>
)
}
export interface SocialSliderProps {
slides: SlideProps[]
}
const SocialSlider: FC<SocialSliderProps> = ({ slides }) => {
return (
<Swiper
modules={[Navigation, Pagination, A11y]}
spaceBetween={16}
slidesPerView={1}
navigation
pagination={{ clickable: true }}
scrollbar={{ draggable: true }}
>
{slides.map(({ socialNetwork, description, links }) => (
<SwiperSlide
key={socialNetwork}
>
<Slide
socialNetwork={socialNetwork}
description={description}
links={links}
/>
</SwiperSlide>
))}
</Swiper>
)
}
export default SocialSlider
+160
View File
@@ -0,0 +1,160 @@
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 { FC } from 'react'
import { SocialLink } from '@interfaces'
import { faGlobe } from '@fortawesome/free-solid-svg-icons'
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'
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}
width={30}
height={30}
/>
</div>
</Tooltip>
)}
<div
css={{
position: 'absolute',
bottom: '-15px',
right: '0px'
}}
>
<NextImage
src={ButtonA}
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}
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