feat: basic apply form
This commit is contained in:
+96
-16
@@ -1,12 +1,40 @@
|
||||
import Typography from '@/components/ui/Typography'
|
||||
import { css } from '@/styled-system/css'
|
||||
import { css, cx } from '@/styled-system/css'
|
||||
import { Container } from '@/styled-system/jsx'
|
||||
import { center } from '@/styled-system/patterns'
|
||||
import { button } from '@/styled-system/recipes'
|
||||
import { button, card } from '@/styled-system/recipes'
|
||||
import { getClanMembers, getClanes } from 'entgamers-database/backend/clanes'
|
||||
import { getUser, type UserWithPreferencesList } from 'entgamers-database/backend/users'
|
||||
import NextImage from 'next/image'
|
||||
import NextLink from 'next/link'
|
||||
import { type Models } from 'node-appwrite'
|
||||
import { type FC } from 'react'
|
||||
|
||||
interface GetTeamsResponse {
|
||||
admins: UserWithPreferencesList
|
||||
moderators: UserWithPreferencesList
|
||||
collaborators: UserWithPreferencesList
|
||||
}
|
||||
|
||||
const getTeams = async (): Promise<GetTeamsResponse> => {
|
||||
const allClanes = await getClanes()
|
||||
const adminClanId = allClanes.teams.find(clan => clan.name === 'Admin')?.$id
|
||||
const moderatorClanId = allClanes.teams.find(clan => clan.name === 'Moderator')?.$id
|
||||
const collaboratorClanId = allClanes.teams.find(clan => clan.name === 'Collaborator')?.$id
|
||||
const adminMembers: Models.MembershipList = adminClanId !== undefined ? await getClanMembers(adminClanId) : { total: 0, memberships: [] }
|
||||
const moderatorMembers: Models.MembershipList = moderatorClanId !== undefined ? await getClanMembers(moderatorClanId) : { total: 0, memberships: [] }
|
||||
const collaboratorMembers: Models.MembershipList = collaboratorClanId !== undefined ? await getClanMembers(collaboratorClanId) : { total: 0, memberships: [] }
|
||||
const adminsPromises = adminMembers.memberships.map(async membership => await getUser(membership.userId))
|
||||
const moderatorsPromises = moderatorMembers.memberships.map(async membership => await getUser(membership.userId))
|
||||
const collaboratorsPromises = collaboratorMembers.memberships.map(async membership => await getUser(membership.userId))
|
||||
const [admins, moderators, collaborators] = await Promise.all([
|
||||
Promise.all(adminsPromises), Promise.all(moderatorsPromises), Promise.all(collaboratorsPromises)
|
||||
])
|
||||
return { admins: { total: admins.length, users: admins }, moderators: { total: moderators.length, users: moderators }, collaborators: { total: collaborators.length, users: collaborators } }
|
||||
}
|
||||
|
||||
const EquipoPage: FC = async () => {
|
||||
const { admins } = await getTeams()
|
||||
return (
|
||||
<Container>
|
||||
<Typography variant="h1" align="center">Equipo</Typography>
|
||||
@@ -17,7 +45,67 @@ const EquipoPage: FC = async () => {
|
||||
<Typography variant="body1">
|
||||
Los administradores son quienes se encargan de que todo funcione como es debido en la comunidad, desde la moderación de los grupos hasta la organización de eventos.
|
||||
</Typography>
|
||||
<Container
|
||||
{admins.total >= 1
|
||||
? (
|
||||
<Container
|
||||
className={css({
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
gap: 'medium',
|
||||
flexWrap: 'wrap',
|
||||
padding: 'medium',
|
||||
width: '100%'
|
||||
})}
|
||||
>
|
||||
{admins.users.map((user, index) => (
|
||||
<div
|
||||
key={`admin-${index}`}
|
||||
className={cx(card({ variant: 'retro' }).body, css({
|
||||
maxWidth: '300px',
|
||||
textAlign: 'center'
|
||||
}))}
|
||||
>
|
||||
<div
|
||||
className={cx(card({ variant: 'retro' }).media, center())}
|
||||
>
|
||||
<NextImage
|
||||
src={user.prefs.profilePicture ?? '/images/EntGamers.png'}
|
||||
alt={user.name !== '' ? user.name : `Usuario ${index + 1} avatar`}
|
||||
width={120}
|
||||
height={120}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
className={card({ variant: 'retro' }).content}
|
||||
>
|
||||
<Typography variant="h3" align="center">{user.name !== '' ? user.name : `Usuario ${index + 1}`}</Typography>
|
||||
{user.prefs.bio !== undefined && user.prefs.bio !== '' && (
|
||||
<Typography variant="body1">{user.prefs.bio}</Typography>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</Container>
|
||||
)
|
||||
: (
|
||||
<>
|
||||
<Typography variant="body2" color="info">
|
||||
Ups, parece que ahora mismo no hay administradores, pero en EntGamers siempre estamos estamos buscando gente que quiera organizar cosas para la comunidad, puedes contactarnos para formar parte de nuestro equipo haciendo click en el siguiente enlace.
|
||||
</Typography>
|
||||
</>
|
||||
)
|
||||
}
|
||||
<div className={center()}>
|
||||
<NextLink
|
||||
className={button({ color: 'info' })}
|
||||
href="/equipo/unirse?role=Admin"
|
||||
>
|
||||
¡Quiero ser Administrador!
|
||||
</NextLink>
|
||||
</div>
|
||||
{/* <Container
|
||||
className={css({
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
@@ -27,7 +115,7 @@ const EquipoPage: FC = async () => {
|
||||
flexWrap: 'wrap'
|
||||
})}
|
||||
>
|
||||
{/* {team.map((member, index) => (
|
||||
{team.map((member, index) => (
|
||||
<div
|
||||
key={`team-member-${index}`}
|
||||
className={cx(card({ variant: 'retro' }).body, css({
|
||||
@@ -70,16 +158,8 @@ const EquipoPage: FC = async () => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))} */}
|
||||
</Container>
|
||||
<div className={center()}>
|
||||
<NextLink
|
||||
className={button({ color: 'info' })}
|
||||
href="/equipo/unirse?role=administrator"
|
||||
>
|
||||
¡Quiero ser administrador!
|
||||
</NextLink>
|
||||
</div>
|
||||
))}
|
||||
</Container> */}
|
||||
<Typography variant="h2" align="center">Moderadores</Typography>
|
||||
<Typography variant="body1">
|
||||
Los moderadores son los encargados de mantener el orden en los grupos de la comunidad, así como de ayudar a los usuarios a resolver sus dudas.
|
||||
@@ -90,7 +170,7 @@ const EquipoPage: FC = async () => {
|
||||
<div className={center()}>
|
||||
<NextLink
|
||||
className={button({ color: 'info' })}
|
||||
href="/equipo/unirse?role=moderator"
|
||||
href="/equipo/unirse?role=Moderator"
|
||||
>
|
||||
¡Quiero ser moderador!
|
||||
</NextLink>
|
||||
@@ -105,7 +185,7 @@ const EquipoPage: FC = async () => {
|
||||
<div className={center()}>
|
||||
<NextLink
|
||||
className={button({ color: 'info' })}
|
||||
href="/equipo/unirse?role=collaborator"
|
||||
href="/equipo/unirse?role=Collaborator"
|
||||
>
|
||||
¡Quiero ser colaborador!
|
||||
</NextLink>
|
||||
|
||||
+171
-140
@@ -1,53 +1,70 @@
|
||||
'use client'
|
||||
import Alert from '@/components/ui/Alert'
|
||||
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 TextArea from '@/components/ui/form/TextArea'
|
||||
import { useAppDispatch } from '@/hooks/useAppDispatch'
|
||||
import { addAlert } from '@/state/feedbackSlice'
|
||||
import { css } from '@/styled-system/css'
|
||||
import { type Alert as AlertType } from '@/types/feedback'
|
||||
import { type TeamApplyData } from '@/types/teamApply'
|
||||
import { teamApplicationDataSchema, type TeamApplicationData } from '@/utilities/teamApplication'
|
||||
import { faChevronRight } from '@fortawesome/free-solid-svg-icons'
|
||||
import { FontAwesomeIcon, type FontAwesomeIconProps } from '@fortawesome/react-fontawesome'
|
||||
import { AppwriteException } from 'appwrite'
|
||||
import { nanoid } from '@reduxjs/toolkit'
|
||||
import { useFormik } from 'formik'
|
||||
import { AnimatePresence, motion } from 'framer-motion'
|
||||
import { useSearchParams } from 'next/navigation'
|
||||
import { useEffect, useState, type FC } from 'react'
|
||||
import { useEffect, type FC } from 'react'
|
||||
|
||||
const ApplyForm: FC = () => {
|
||||
const searchParams = useSearchParams()
|
||||
const [alert, setAlert] = useState<AlertType | undefined>(undefined)
|
||||
const dispatch = useAppDispatch()
|
||||
|
||||
const formik = useFormik<TeamApplyData>({
|
||||
const formik = useFormik<TeamApplicationData>({
|
||||
initialValues: {
|
||||
name: '',
|
||||
email: '',
|
||||
discordName: '',
|
||||
discord: '',
|
||||
message: '',
|
||||
role: 'administrator'
|
||||
role: 'Moderator',
|
||||
status: 'Pending'
|
||||
},
|
||||
onSubmit: async (_values) => {
|
||||
onSubmit: async (values) => {
|
||||
try {
|
||||
// await createTeamApply(values)
|
||||
await fetch('/api/teamAplications', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(values)
|
||||
})
|
||||
dispatch(addAlert({
|
||||
id: nanoid(),
|
||||
title: 'Formulario enviado',
|
||||
message: 'Gracias por interesarte en unirte al equipo',
|
||||
severity: 'success'
|
||||
}))
|
||||
} catch (error) {
|
||||
if (error instanceof AppwriteException) {
|
||||
setAlert({
|
||||
if (error instanceof Error) {
|
||||
dispatch(addAlert({
|
||||
id: nanoid(),
|
||||
title: 'Error al enviar el formulario',
|
||||
message: error.message,
|
||||
severity: 'error'
|
||||
})
|
||||
}))
|
||||
return
|
||||
}
|
||||
console.error('Error al enviar el formulario', error)
|
||||
setAlert({
|
||||
severity: 'error',
|
||||
dispatch(addAlert({
|
||||
id: nanoid(),
|
||||
title: 'Error al enviar el formulario',
|
||||
message: 'Hubo un error al enviar el formulario, por favor, intenta nuevamente.'
|
||||
})
|
||||
message: 'Error desconocido',
|
||||
severity: 'error'
|
||||
}))
|
||||
}
|
||||
}
|
||||
},
|
||||
validationSchema: teamApplicationDataSchema,
|
||||
isInitialValid: false
|
||||
})
|
||||
useEffect(() => {
|
||||
if (searchParams.has('role')) {
|
||||
@@ -73,36 +90,36 @@ const ApplyForm: FC = () => {
|
||||
<Button
|
||||
type='button'
|
||||
onClick={() => {
|
||||
formik.setFieldValue('role', 'moderator')
|
||||
formik.setFieldValue('role', 'Moderator')
|
||||
.catch((error) => {
|
||||
console.error(error)
|
||||
})
|
||||
}}
|
||||
disabled={formik.values.role === 'moderator'}
|
||||
disabled={formik.values.role === 'Moderator'}
|
||||
>
|
||||
Moderador
|
||||
</Button>
|
||||
<Button
|
||||
type='button'
|
||||
onClick={() => {
|
||||
formik.setFieldValue('role', 'administrator')
|
||||
formik.setFieldValue('role', 'Admin')
|
||||
.catch((error) => {
|
||||
console.error(error)
|
||||
})
|
||||
}}
|
||||
disabled={formik.values.role === 'administrator'}
|
||||
disabled={formik.values.role === 'Admin'}
|
||||
>
|
||||
Administrador
|
||||
</Button>
|
||||
<Button
|
||||
type='button'
|
||||
onClick={() => {
|
||||
formik.setFieldValue('role', 'collaborator')
|
||||
formik.setFieldValue('role', 'Collaborator')
|
||||
.catch((error) => {
|
||||
console.error(error)
|
||||
})
|
||||
}}
|
||||
disabled={formik.values.role === 'collaborator'}
|
||||
disabled={formik.values.role === 'Collaborator'}
|
||||
>
|
||||
Colaborador
|
||||
</Button>
|
||||
@@ -114,126 +131,140 @@ const ApplyForm: FC = () => {
|
||||
gap: 'medium'
|
||||
})}
|
||||
>
|
||||
{alert !== undefined && (
|
||||
<Alert
|
||||
severity={alert.severity}
|
||||
>
|
||||
{alert.title !== undefined && (
|
||||
<Typography variant='h5' component='div'>{alert.title}</Typography>
|
||||
)}
|
||||
{alert.message}
|
||||
</Alert>
|
||||
)}
|
||||
{formik.submitCount > 0 && (
|
||||
<div
|
||||
className={css({
|
||||
order: { base: 2, md: 1 }
|
||||
})}
|
||||
>
|
||||
<FormGroup>
|
||||
<label htmlFor='name'>Nombre</label>
|
||||
<Input
|
||||
id='name'
|
||||
type='text'
|
||||
value={formik.values.name}
|
||||
onChange={formik.handleChange}
|
||||
onBlur={formik.handleBlur}
|
||||
/>
|
||||
{formik.touched.name !== undefined && formik.errors.name !== undefined
|
||||
? (
|
||||
<Typography variant='caption' color='danger'>
|
||||
{formik.errors.name}
|
||||
</Typography>
|
||||
)
|
||||
: (
|
||||
<Typography variant='caption' color='info'>
|
||||
Tu nombre.
|
||||
</Typography>
|
||||
)}
|
||||
</FormGroup>
|
||||
<FormGroup>
|
||||
<label htmlFor='email'>Email</label>
|
||||
<Input
|
||||
id='email'
|
||||
type='email'
|
||||
value={formik.values.email}
|
||||
onChange={formik.handleChange}
|
||||
onBlur={formik.handleBlur}
|
||||
/>
|
||||
{formik.touched.email !== undefined && formik.errors.email !== undefined
|
||||
? (
|
||||
<Typography variant='caption' color='danger'>
|
||||
{formik.errors.email}
|
||||
</Typography>
|
||||
)
|
||||
: (
|
||||
<Typography variant='caption' color='info'>
|
||||
Tu email, para poder contactarte.
|
||||
</Typography>
|
||||
)
|
||||
}
|
||||
</FormGroup>
|
||||
<FormGroup>
|
||||
<label htmlFor='discordName'>Nombre de Discord</label>
|
||||
<Input
|
||||
id='discordName'
|
||||
type='text'
|
||||
value={formik.values.discordName}
|
||||
onChange={formik.handleChange}
|
||||
onBlur={formik.handleBlur}
|
||||
/>
|
||||
{formik.touched.discordName !== undefined && formik.errors.discordName !== undefined
|
||||
? (
|
||||
<Typography variant='caption' color='danger'>
|
||||
{formik.errors.discordName}
|
||||
</Typography>
|
||||
)
|
||||
: (
|
||||
<Typography variant='caption' color='info'>
|
||||
Tu nombre de Discord, para poder contactarte.
|
||||
</Typography>
|
||||
)
|
||||
}
|
||||
</FormGroup>
|
||||
<FormGroup>
|
||||
<label htmlFor='message'>Mensaje</label>
|
||||
<TextArea
|
||||
id='message'
|
||||
value={formik.values.message}
|
||||
onChange={formik.handleChange}
|
||||
onBlur={formik.handleBlur}
|
||||
/>
|
||||
{formik.touched.message !== undefined && formik.errors.message !== undefined
|
||||
? (
|
||||
<Typography variant='caption' color='danger'>
|
||||
{formik.errors.message}
|
||||
</Typography>
|
||||
)
|
||||
: (
|
||||
<Typography variant='caption' color='info'>
|
||||
¿Por que te gustaría unirte al equipo?, ¿Que te gustaría hacer?, etc.
|
||||
</Typography>
|
||||
)
|
||||
}
|
||||
</FormGroup>
|
||||
{formik.submitCount <= 0
|
||||
? (
|
||||
<div
|
||||
className={css({
|
||||
order: { base: 2, md: 1 }
|
||||
})}
|
||||
>
|
||||
<FormGroup>
|
||||
<label htmlFor='name'>Nombre</label>
|
||||
<Input
|
||||
id='name'
|
||||
type='text'
|
||||
value={formik.values.name}
|
||||
onChange={formik.handleChange}
|
||||
onBlur={formik.handleBlur}
|
||||
/>
|
||||
{formik.touched.name !== undefined && formik.errors.name !== undefined
|
||||
? (
|
||||
<Typography variant='caption' color='danger'>
|
||||
{formik.errors.name}
|
||||
</Typography>
|
||||
)
|
||||
: (
|
||||
<Typography variant='caption' color='info'>
|
||||
Tu nombre.
|
||||
</Typography>
|
||||
)}
|
||||
</FormGroup>
|
||||
<FormGroup>
|
||||
<label htmlFor='email'>Email</label>
|
||||
<Input
|
||||
id='email'
|
||||
type='email'
|
||||
value={formik.values.email}
|
||||
onChange={formik.handleChange}
|
||||
onBlur={formik.handleBlur}
|
||||
/>
|
||||
{formik.touched.email !== undefined && formik.errors.email !== undefined
|
||||
? (
|
||||
<Typography variant='caption' color='danger'>
|
||||
{formik.errors.email}
|
||||
</Typography>
|
||||
)
|
||||
: (
|
||||
<Typography variant='caption' color='info'>
|
||||
Tu email, para poder contactarte.
|
||||
</Typography>
|
||||
)
|
||||
}
|
||||
</FormGroup>
|
||||
<FormGroup>
|
||||
<label htmlFor='discord'>Nombre de Discord</label>
|
||||
<Input
|
||||
id='discord'
|
||||
type='text'
|
||||
value={formik.values.discord}
|
||||
onChange={formik.handleChange}
|
||||
onBlur={formik.handleBlur}
|
||||
/>
|
||||
{formik.touched.discord !== undefined && formik.errors.discord !== undefined
|
||||
? (
|
||||
<Typography variant='caption' color='danger'>
|
||||
{formik.errors.discord}
|
||||
</Typography>
|
||||
)
|
||||
: (
|
||||
<Typography variant='caption' color='info'>
|
||||
Tu nombre de Discord, para poder contactarte.
|
||||
</Typography>
|
||||
)
|
||||
}
|
||||
</FormGroup>
|
||||
<FormGroup>
|
||||
<label htmlFor='message'>Mensaje</label>
|
||||
<TextArea
|
||||
id='message'
|
||||
value={formik.values.message}
|
||||
onChange={formik.handleChange}
|
||||
onBlur={formik.handleBlur}
|
||||
/>
|
||||
{formik.touched.message !== undefined && formik.errors.message !== undefined
|
||||
? (
|
||||
<Typography variant='caption' color='danger'>
|
||||
{formik.errors.message}
|
||||
</Typography>
|
||||
)
|
||||
: (
|
||||
<Typography variant='caption' color='info'>
|
||||
¿Por que te gustaría unirte al equipo?, ¿Que te gustaría hacer?, etc.
|
||||
</Typography>
|
||||
)
|
||||
}
|
||||
</FormGroup>
|
||||
<div
|
||||
className={css({
|
||||
paddingBlock: 'medium'
|
||||
})}
|
||||
>
|
||||
<Button
|
||||
type='submit'
|
||||
disabled={!formik.isValid || !formik.dirty}
|
||||
fullWidth
|
||||
|
||||
>
|
||||
Enviar
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
: (
|
||||
<div
|
||||
className={css({
|
||||
order: { base: 2, md: 1 },
|
||||
paddingBlock: 'medium'
|
||||
})}
|
||||
>
|
||||
<Button
|
||||
type='submit'
|
||||
disabled={!formik.isValid || !formik.dirty}
|
||||
fullWidth
|
||||
|
||||
<div
|
||||
className={css({
|
||||
backgroundColor: 'surface',
|
||||
borderRadius: 'medium',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center'
|
||||
})}
|
||||
>
|
||||
Enviar
|
||||
</Button>
|
||||
<Typography variant='h2' align="center">¡Gracias por interesarte en unirte al equipo!</Typography>
|
||||
<Typography variant='body1'>
|
||||
El equipo de EntGamers se pondrá en contacto contigo a la brevedad posible.
|
||||
</Typography>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
<div
|
||||
className={css({
|
||||
overflow: 'hidden',
|
||||
@@ -242,7 +273,7 @@ const ApplyForm: FC = () => {
|
||||
})}
|
||||
>
|
||||
<AnimatePresence mode='wait' initial={false}>
|
||||
{formik.values.role === 'moderator' && (
|
||||
{formik.values.role === 'Moderator' && (
|
||||
<motion.div
|
||||
key={'motion-moderator'}
|
||||
transition={{ duration: 0.15, ease: 'easeInOut' }}
|
||||
@@ -272,7 +303,7 @@ const ApplyForm: FC = () => {
|
||||
</ul>
|
||||
</motion.div>
|
||||
)}
|
||||
{formik.values.role === 'collaborator' && (
|
||||
{formik.values.role === 'Collaborator' && (
|
||||
<motion.div
|
||||
key={'motion-collaborator'}
|
||||
transition={{ duration: 0.15, ease: 'easeInOut' }}
|
||||
@@ -302,7 +333,7 @@ const ApplyForm: FC = () => {
|
||||
</ul>
|
||||
</motion.div>
|
||||
)}
|
||||
{formik.values.role === 'administrator' && (
|
||||
{formik.values.role === 'Admin' && (
|
||||
<motion.div
|
||||
key={'motion-administrator'}
|
||||
transition={{ duration: 0.15, ease: 'easeInOut' }}
|
||||
|
||||
Reference in New Issue
Block a user