feat: wired the registeration journey with the api, success modal, reactive data

This commit is contained in:
MTG2000
2022-09-08 14:53:08 +03:00
parent 97d31c3360
commit a0f09aaa33
35 changed files with 878 additions and 118 deletions

View File

@@ -21,7 +21,7 @@ const fetchLnurlAuth = async () => {
return data;
}
const useLnurlQuery = () => {
export const useLnurlQuery = () => {
const [loading, setLoading] = useState(true)
const [error, setError] = useState<any>(null);
const [data, setData] = useState<{ lnurl: string, session_token: string }>({ lnurl: '', session_token: '' })

View File

@@ -64,7 +64,7 @@ export default function LinkedAccountsCard({ value, onChange }: Props) {
<Button color='none' size='sm' className='mt-16 text-gray-600 hover:bg-gray-50' onClick={connectNewWallet}>
+ Add another wallet
</Button>}
<InfoCard>
<InfoCard className='mt-24'>
<span className="font-bold">💡 Note:</span> if you link a wallet that was used to create another account previously, you won't be able to login to that account until you remove it from here.
</InfoCard>
</Card>

View File

@@ -46,7 +46,7 @@ export default function UpdateSkillsCard(props: Props) {
</li>)}
</ul>}
<InfoCard>
<InfoCard className='mt-24'>
<span className="font-bold"> Can't find a specific skill?</span> You can suggest it to be added <a href="https://github.com/peakshift/makers.bolt.fun/discussions/143" target='_blank' rel="noreferrer" className='font-bold underline'>here</a>
</InfoCard>
</Card>

View File

@@ -1,5 +1,6 @@
import Card from 'src/Components/Card/Card'
import { User } from 'src/graphql';
import { Link } from 'react-router-dom'
@@ -26,15 +27,22 @@ export default function TournamentsCard({ tournaments, isOwner }: Props) {
<ul className=' flex flex-wrap gap-x-8 gap-y-20'>
{
tournaments.map((tournament) => {
const isLive = ((new Date() < new Date(tournament.end_date)) && (new Date() > new Date(tournament.start_date)));
return <li key={tournament.id} className="flex gap-16 items-center">
<img src={tournament.thumbnail_image} className='w-48 border-2 border-gray-100 aspect-square rounded-16 object-cover' alt="" />
<div>
<p className="text-gray-900 font-medium">{tournament.title}</p>
<p className={`${isLive ? "text-green-500" : "text-warning-500"} text-body5 font-medium`}>&#8226; {isLive ? "Live" : "Completed"}</p>
</div>
const status = getDateStatus(tournament.start_date, tournament.end_date)
return <li key={tournament.id}>
<Link to={'/tournaments/' + tournament.id} className="flex gap-16 items-center">
<img src={tournament.thumbnail_image} className='w-48 border-2 border-gray-100 aspect-square rounded-16 object-cover' alt="" />
<div>
<p className="text-gray-900 font-medium">{tournament.title}</p>
<p className={`
text-body5 font-medium
${status === 'live' && 'text-green-500'}
${status === 'upcoming' && 'text-violet-500'}
${status === 'finished' && 'text-warning-500'}
`}>&#8226; {status === 'live' && "Running"}
{status === 'upcoming' && "Upcoming"}
{status === 'finished' && "Completed"} </p>
</div>
</Link>
</li>
})}
</ul>
@@ -42,3 +50,15 @@ export default function TournamentsCard({ tournaments, isOwner }: Props) {
</Card>
)
}
function getDateStatus(start: string, end: string) {
const start_date = new Date(start);
const now_date = new Date();
const end_date = new Date(end);
if (now_date < start_date) return 'upcoming'
if (now_date >= start_date && now_date <= end_date) return 'live'
return 'finished'
}

View File

@@ -15,12 +15,6 @@ export default function LinkingAccountModal({ onClose, direction, maker, ...prop
const links = [
{
hasValue: maker.email,
text: maker.email,
icon: FiMail,
url: maker.email && `mailto:${maker.email}`
},
{
hasValue: maker.twitter,
text: maker.twitter,

View File

@@ -11,7 +11,9 @@ interface Props {
export default function MakersPage({ data: { id } }: Props) {
const query = useMeTournamentQuery();
const query = useMeTournamentQuery({
variables: { inTournamentId: id }
});
return (
<div className='pb-42'>
@@ -19,7 +21,7 @@ export default function MakersPage({ data: { id } }: Props) {
{query.loading ?
<MakerCardSkeleton />
:
query.data?.me ?
query.data?.me?.in_tournament ?
<MakerCard isMe maker={query.data.me as User} />
: null
}

View File

@@ -1,13 +1,3 @@
query MeTournament {
me {
id
name
avatar
jobTitle
...UserRolesSkills
}
}
query GetAllRoles {
getAllMakersRoles {
id

View File

@@ -17,9 +17,10 @@ interface Props {
| 'faqs'
>
avatars: string[]
isRegistered: boolean;
}
export default function OverviewPage({ data, avatars }: Props) {
export default function OverviewPage({ data, avatars, isRegistered }: Props) {
return (
<Card onlyMd className='flex flex-col gap-42'>
<div className="grid grid-cols-1 md:grid-cols-3 gap-24 items-start">
@@ -30,7 +31,7 @@ export default function OverviewPage({ data, avatars }: Props) {
>
</div>
</div>
<RegisterCard makers_count={data.makers_count} start_date={data.start_date} avatars={avatars} />
<RegisterCard makers_count={data.makers_count} start_date={data.start_date} avatars={avatars} isRegistered={isRegistered} />
</div>
<PrizesSection prizes={data.prizes} />
<JudgesSection judges={data.judges} />

View File

@@ -1,30 +1,57 @@
import React from 'react'
import { FaUsers } from 'react-icons/fa'
import { useParams } from 'react-router-dom'
import Button from 'src/Components/Button/Button'
import Card from 'src/Components/Card/Card'
import Avatar from 'src/features/Profiles/Components/Avatar/Avatar'
import { openModal } from 'src/redux/features/modals.slice'
import { useCountdown } from 'src/utils/hooks'
import { useAppDispatch, useAppSelector } from "src/utils/hooks";
interface Props {
start_date: string;
makers_count: number
avatars: string[]
isRegistered: boolean;
}
export default function RegisterCard({ makers_count, start_date, avatars }: Props) {
export default function RegisterCard({ makers_count, start_date, avatars, isRegistered }: Props) {
const counter = useCountdown(start_date)
const { id: tournamentId } = useParams()
const isLoggedIn = useAppSelector(state => !!state.user.me)
const dispatch = useAppDispatch()
const onRegister = () => {
if (!tournamentId) return;
if (isLoggedIn)
dispatch(openModal({
Modal: "RegisterTournamet_ConfrimAccount",
props: {
tournamentId: Number(tournamentId)
}
}))
else
dispatch(openModal({
Modal: "RegisterTournamet_Login",
props: {
tournamentId: Number(tournamentId)
}
}))
}
return (
<Card onlyMd className='flex flex-col gap-24'>
<div>
<p className="text-body5 text-gray-600">
<div className="flex">
{avatars.map((img, idx) => <div className='w-[16px] h-32 relative'><Avatar key={idx} src={img} width={32} className='absolute top-0 left-0 min-w-[32px] !border-white' /></div>)}
<span className='self-center ml-24 font-medium '>+ {makers_count} makers</span>
</div>
<p className="text-body5 text-gray-600 flex">
{avatars.map((img, idx) => <div className='w-[16px] h-32 relative'><Avatar key={idx} src={img} width={32} className='absolute top-0 left-0 min-w-[32px] !border-white' /></div>)}
<span className='self-center ml-24 font-medium '>+ {makers_count} makers</span>
</p>
<Button color='primary' fullWidth className='mt-16'>Register</Button>
<Button color='primary' disabled={isRegistered} fullWidth className='mt-16' onClick={onRegister}>{isRegistered ? "Registered!" : "Register Now"}</Button>
</div>
<div>
{counter.isExpired ?

View File

@@ -0,0 +1,68 @@
import { motion } from 'framer-motion'
import { ModalCard, modalCardVariants } from 'src/Components/Modals/ModalsContainer/ModalsContainer'
import { IoClose } from 'react-icons/io5';
import Avatar from 'src/features/Profiles/Components/Avatar/Avatar';
import { useAppDispatch, useAppSelector } from "src/utils/hooks";
import Button from 'src/Components/Button/Button';
import { Direction, replaceModal } from 'src/redux/features/modals.slice';
interface Props extends ModalCard {
tournamentId: number
}
export default function ConfirmAccount({ onClose, direction, tournamentId, ...props }: Props) {
const me = useAppSelector(state => state.user.me)
const dispatch = useAppDispatch();
if (!me)
return null;
const onCancel = () => onClose?.();
const onContinue = () => {
dispatch(replaceModal({
Modal: "RegisterTournamet_RegistrationDetails",
direction: Direction.NEXT,
props: {
tournamentId
}
}))
}
return (
<motion.div
custom={direction}
variants={modalCardVariants}
initial='initial'
animate="animate"
exit='exit'
className="modal-card max-w-[442px] rounded-xl relative"
>
<div className="p-24">
<IoClose className='absolute text-body2 top-24 right-24 hover:cursor-pointer' onClick={onClose} />
<h2 className='text-h5 font-bold text-center'>Register for tournament</h2>
</div>
<hr className="bg-gray-200" />
<div className='flex flex-col justify-center gap-16 items-center text-center p-24'>
<Avatar src={me.avatar} width={80} />
<div className="flex flex-col gap-4">
<p className="text-body3 text-gray-900">{me.name}</p>
<p className="text-body4 text-gray-600">{me.jobTitle}</p>
</div>
<p className="text-body4 text-gray-600">You are currently signed in using this profile. Would you like to continue with your Tournament registration?</p>
<div className="grid grid-cols-2 gap-16 w-full">
<Button color='gray' onClick={onCancel}>Cancel</Button>
<Button color='primary' onClick={onContinue}>Continue</Button>
</div>
</div>
</motion.div>
)
}

View File

@@ -0,0 +1,3 @@
import { lazyModal } from 'src/utils/helperFunctions';
export const { LazyComponent: ConfirmAccount } = lazyModal(() => import('./ConfirmAccount'))

View File

@@ -0,0 +1,173 @@
import { motion } from 'framer-motion'
import { ModalCard, modalCardVariants } from 'src/Components/Modals/ModalsContainer/ModalsContainer'
import { FiCopy } from "react-icons/fi";
import { IoClose, IoRocketOutline } from 'react-icons/io5';
import { useMeTournamentQuery } from 'src/graphql';
import Button from 'src/Components/Button/Button';
import { QRCodeSVG } from 'qrcode.react';
import { Grid } from 'react-loader-spinner';
import { useCallback, useEffect, useState } from 'react';
import { CONSTS } from 'src/utils';
import useCopyToClipboard from 'src/utils/hooks/useCopyToClipboard';
import { useLnurlQuery } from 'src/features/Auth/pages/LoginPage/LoginPage';
import { useAppDispatch } from 'src/utils/hooks';
import { Direction, replaceModal } from 'src/redux/features/modals.slice';
interface Props extends ModalCard {
tournamentId: number
}
export default function LinkingAccountModal({ onClose, direction, tournamentId, ...props }: Props) {
const [copied, setCopied] = useState(false);
const { loadingLnurl, data: { lnurl, session_token }, error } = useLnurlQuery();
const clipboard = useCopyToClipboard()
const dispatch = useAppDispatch();
useEffect(() => {
setCopied(false);
}, [lnurl])
const meQuery = useMeTournamentQuery({
variables: {
inTournamentId: tournamentId
},
onCompleted: (data) => {
if (data.me) {
const already_registerd = data.me.in_tournament;
if (already_registerd)
onClose?.();
else dispatch(replaceModal({
Modal: "RegisterTournamet_RegistrationDetails",
direction: Direction.NEXT,
props: { tournamentId }
}))
}
}
});
const copyToClipboard = () => {
setCopied(true);
clipboard(lnurl);
}
const refetch = meQuery.refetch;
const startPolling = useCallback(
() => {
const interval = setInterval(() => {
fetch(CONSTS.apiEndpoint + '/is-logged-in', {
credentials: 'include',
headers: {
session_token
}
}).then(data => data.json())
.then(data => {
if (data.logged_in) {
clearInterval(interval)
refetch();
}
})
}, 2000);
return interval;
}
, [refetch, session_token],
)
useEffect(() => {
let interval: NodeJS.Timer;
if (lnurl)
interval = startPolling();
return () => {
clearInterval(interval)
}
}, [lnurl, startPolling])
let content = <></>
if (error)
content = <div className="flex flex-col gap-24 items-center">
<p className="text-body3 text-red-500 font-bold">Something wrong happened...</p>
<a href='/login' className="text body4 text-gray-500 hover:underline">Please try again</a>
</div>
else if (loadingLnurl)
content = <div className="flex flex-col gap-24 py-48 items-center">
<Grid color="var(--primary)" width="150" />
<p className="text-body3 font-bold">Fetching Lnurl-Auth link</p>
</div>
else
content = <div className="flex flex-col justify-center gap-24 items-center text-center" >
<a href={`lightning:${lnurl}`} >
<QRCodeSVG
width={280}
height={280}
value={lnurl}
bgColor='transparent'
imageSettings={{
src: '/assets/images/nut_3d.png',
width: 16,
height: 16,
excavate: true,
}}
/>
</a>
<p className="text-gray-600 text-body4 text-left">
To register for this tournament, you need a maker profile. Luckily, this is very easy!
<br />
<br />
To sign in or create an account, just scan this QR, or click to connect using any lightning wallet like <a href="https://getalby.com" className='underline' target='_blank' rel="noreferrer">Alby</a> or <a href="https://breez.technology/" className='underline' target='_blank' rel="noreferrer">Breez</a>.
</p>
<div className="w-full grid grid-cols-2 gap-16">
<a href={`lightning:${lnurl}`}
className='block text-body4 text-center text-white bg-primary-500 hover:bg-primary-600 rounded-10 px-16 py-12 active:scale-90 transition-transform'
>Click to connect <IoRocketOutline /></a>
<Button
color='gray'
onClick={copyToClipboard}
>{copied ? "Copied" : "Copy"} <FiCopy /></Button>
<a href={`https://wiki.ion.radar.tech/tutorials/wallets`} target='_blank' rel="noreferrer"
className='col-span-2 block text-body4 text-center text-gray-900 border border-gray-200 rounded-10 px-16 py-12 active:scale-90 transition-transform'
>What is a lightning wallet?</a>
</div>
</div>;
return (
<motion.div
custom={direction}
variants={modalCardVariants}
initial='initial'
animate="animate"
exit='exit'
className="modal-card max-w-[442px] rounded-xl relative"
>
<div className="p-24">
<IoClose className='absolute text-body2 top-24 right-24 hover:cursor-pointer' onClick={onClose} />
<h2 className='text-h5 font-bold text-center'>Connect your maker profile</h2>
</div>
<hr className="bg-gray-200" />
<div className=' p-24'>
{content}
</div>
</motion.div>
)
}

View File

@@ -0,0 +1,3 @@
import { lazyModal } from 'src/utils/helperFunctions';
export const { LazyComponent: LoginModal } = lazyModal(() => import('./LoginModal'))

View File

@@ -0,0 +1,181 @@
import { motion } from 'framer-motion'
import { ModalCard, modalCardVariants } from 'src/Components/Modals/ModalsContainer/ModalsContainer'
import { IoClose } from 'react-icons/io5';
import { useAppDispatch, useAppSelector } from "src/utils/hooks";
import Button from 'src/Components/Button/Button';
import { Direction, replaceModal } from 'src/redux/features/modals.slice';
import BasicSelectInput from 'src/Components/Inputs/Selects/BasicSelectInput/BasicSelectInput';
import { GetTournamentByIdDocument, TournamentMakerHackingStatusEnum, useRegisterInTournamentMutation } from 'src/graphql';
import InfoCard from 'src/Components/InfoCard/InfoCard';
import * as yup from "yup";
import { yupResolver } from '@hookform/resolvers/yup';
import { Controller, SubmitHandler, useForm } from 'react-hook-form';
import { NotificationsService } from "src/services/notifications.service";
interface Props extends ModalCard {
tournamentId: number
}
const hackingStatusOptions = [
{
label: "Hacking han solo 👻",
value: TournamentMakerHackingStatusEnum.Solo
}, {
label: "Open to connect 👋",
value: TournamentMakerHackingStatusEnum.OpenToConnect
},
]
interface IFormInputs {
email: string;
agreement: boolean;
hacking_status: typeof hackingStatusOptions[number];
}
const schema: yup.SchemaOf<IFormInputs> = yup.object({
email: yup.string().required().email(),
hacking_status: yup.object().shape({
label: yup.string().required(),
value: yup.string().required()
}).required(),
agreement: yup.boolean().required().isTrue("You won't be able to follow the updates/events of the tournament if you don't allow this"),
}).required();
export default function RegistrationDetails({ onClose, direction, ...props }: Props) {
const me = useAppSelector(state => state.user.me)
const dispatch = useAppDispatch();
const [mutate, mutationStatus] = useRegisterInTournamentMutation()
const { handleSubmit, control, register, formState: { errors }, } = useForm<IFormInputs>({
mode: "onChange",
resolver: yupResolver(schema),
defaultValues: {
email: "",
hacking_status: hackingStatusOptions[0]
}
});
if (!me)
return null;
const onCancel = () => onClose?.();
const onSubmit: SubmitHandler<IFormInputs> = data => {
mutate({
variables: {
data: {
email: data.email,
hacking_status: data.hacking_status.value,
},
tournamentId: Number(props.tournamentId)
},
onCompleted: (data) => {
if (data.registerInTournament?.in_tournament) {
dispatch(replaceModal({
Modal: "RegisterTournamet_RegistrationSuccess",
direction: Direction.NEXT
}))
}
},
refetchQueries: [{
query: GetTournamentByIdDocument,
variables: {
id: props.tournamentId
}
}]
})
.catch(() => {
NotificationsService.error("A network error happned...")
mutationStatus.reset()
})
}
return (
<motion.div
custom={direction}
variants={modalCardVariants}
initial='initial'
animate="animate"
exit='exit'
className="modal-card max-w-[442px] rounded-xl relative "
>
<div className="p-24">
<IoClose className='absolute text-body2 top-24 right-24 hover:cursor-pointer' onClick={onClose} />
<h2 className='text-h5 font-bold text-center'>Register for tournament</h2>
</div>
<hr className="bg-gray-200" />
<form onSubmit={handleSubmit(onSubmit)} className='flex flex-col gap-24 p-24'>
<p className="text-body4 text-gray-600">Please provide us with some additional details below.</p>
<div className='flex flex-col gap-8'>
<label className="text-body5 text-gray-600 font-medium">Hacking status</label>
<Controller
name="hacking_status"
control={control}
render={({ field: { value, onChange } }) => <BasicSelectInput
isMulti={false}
labelField='label'
valueField='value'
placeholder='Your hacking status'
value={value}
onChange={onChange}
options={hackingStatusOptions}
/>}
/>
<InfoCard >
<span className="font-bold">👋 Details:</span> other makers will be able to see your hacker card and send you Team Up requests.
</InfoCard>
</div>
<div className='flex flex-col gap-8'>
<label className="text-body5 text-gray-600 font-medium">Email address*</label>
<div className="input-wrapper relative">
<input
type='text'
className="input-text"
placeholder="johndoe@gmail.com"
{...register("email")}
/>
</div>
{errors.email && <p className="input-error">
{errors.email.message}
</p>}
<div className="mt-12 flex gap-12">
<input
className='input-checkbox self-center cursor-pointer'
type="checkbox"
{...register('agreement', {})} />
<label className="text-body5 text-gray-600" >
Send me news and updates about the tournament.
<br />
No spam!
</label>
</div>
{errors.agreement && <p className="input-error">
{errors.agreement.message}
</p>}
</div>
<div className="grid grid-cols-2 gap-16">
<Button color='gray' onClick={onCancel}>Cancel</Button>
<Button type='submit' color='primary'>Continue</Button>
</div>
</form>
</motion.div>
)
}

View File

@@ -0,0 +1,3 @@
import { lazyModal } from 'src/utils/helperFunctions';
export const { LazyComponent: RegistrationDetails } = lazyModal(() => import('./RegistrationDetails'))

View File

@@ -0,0 +1,9 @@
mutation RegisterInTournament(
$tournamentId: Int!
$data: RegisterInTournamentInput
) {
registerInTournament(tournament_id: $tournamentId, data: $data) {
id
in_tournament(id: $tournamentId)
}
}

View File

@@ -0,0 +1,72 @@
import { motion } from 'framer-motion'
import { ModalCard, modalCardVariants } from 'src/Components/Modals/ModalsContainer/ModalsContainer'
import { IoClose } from 'react-icons/io5';
import Avatar from 'src/features/Profiles/Components/Avatar/Avatar';
import { useAppSelector } from "src/utils/hooks";
import Button from 'src/Components/Button/Button';
import Confetti from "react-confetti";
import { Portal } from 'src/Components/Portal/Portal';
interface Props extends ModalCard {
}
export default function RegistrationSuccess({ onClose, direction, ...props }: Props) {
const me = useAppSelector(state => state.user.me)
if (!me)
throw new Error("User not defined");
return (
<motion.div
custom={direction}
variants={modalCardVariants}
initial='initial'
animate="animate"
exit='exit'
className="modal-card max-w-[442px] rounded-xl relative"
>
<div className="p-24">
<IoClose className='absolute text-body2 top-24 right-24 hover:cursor-pointer' onClick={onClose} />
<h2 className='text-h5 font-bold text-center'>Registration succeeded!! </h2>
</div>
<hr className="bg-gray-200" />
<div className='flex flex-col justify-center gap-16 items-center text-center p-24'>
<Avatar src={me.avatar} width={80} />
<div className="flex flex-col gap-4">
<p className="text-body3 text-gray-900 font-medium">{me.name}</p>
<p className="text-body4 text-gray-600">{me.jobTitle}</p>
</div>
<p className="text-body4 text-gray-600">Nice work! Youve successfully registered for the tournament. You can get started with some of the options below!</p>
<div className="flex w-full gap-8 items-center">
<div className={`shrink-0 flex flex-col justify-center items-center bg-gray-50 rounded-8 w-48 h-48`}>👾</div>
<div className="self-center px-16 text-left">
<p className="text-body4 text-gray-900 font-medium">Complete your maker profile</p>
<p className="text-body5 text-gray-400">Add details to your maker profile so you stand out.</p>
</div>
</div>
<div className="flex w-full gap-8 items-center">
<div className={`shrink-0 flex flex-col justify-center items-center bg-gray-50 rounded-8 w-48 h-48`}>🤝</div>
<div className="self-center px-16 text-left">
<p className="text-body4 text-gray-900 font-medium">Find makers to team up with</p>
<p className="text-body5 text-gray-400">Recruit or find makers to team up with.</p>
</div>
</div>
<div className="flex flex-col gap-16 w-full mt-24">
<Button fullWidth color='primary'>👾 Complete maker profile</Button>
<Button fullWidth color='gray'>🤝 Team up with other makers</Button>
</div>
</div>
</motion.div>
)
}

View File

@@ -0,0 +1,3 @@
import { lazyModal } from 'src/utils/helperFunctions';
export const { LazyComponent: RegistrationSuccess } = lazyModal(() => import('./RegistrationSuccess'))

View File

@@ -0,0 +1,11 @@
import { LoginModal } from './LoginModal'
import { ConfirmAccount } from './ConfirmAccount'
import { RegistrationDetails } from './RegistrationDetails'
import { RegistrationSuccess } from './RegistrationSuccess'
export const RegistrationModals = {
LoginModal,
RegistrationDetails,
ConfirmAccount,
RegistrationSuccess,
}

View File

@@ -41,7 +41,7 @@ export default function Navigation({ data }: Props) {
path: "resources",
isDisabled: true,
},
], [data.events_count])
], [data.events_count, data.makers_count, data.projects_count])
return (
<div className="w-full bg-white py-16 border-b border-gray-200 sticky-top-element z-10">

View File

@@ -1,30 +1,34 @@
import Header from './Header/Header'
import { Navigate, Route, Routes } from 'react-router-dom'
import { Navigate, Route, Routes, useParams } from 'react-router-dom'
import OverviewPage from '../OverviewPage/OverviewPage'
import { Helmet } from 'react-helmet'
import Navigation from './Navigation/Navigation'
import EventsPage from '../EventsPage/EventsPage'
import MakersPage from '../MakersPage/MakersPage'
import ProjectsPage from '../ProjectsPage/ProjectsPage'
import { useGetTournamentByIdQuery } from 'src/graphql'
import { useGetTournamentByIdQuery, GetTournamentByIdQuery } from 'src/graphql'
import LoadingPage from 'src/Components/LoadingPage/LoadingPage'
import NotFoundPage from 'src/features/Shared/pages/NotFoundPage/NotFoundPage'
export type MeTournament = GetTournamentByIdQuery['me']
export default function TournamentDetailsPage() {
const query = useGetTournamentByIdQuery({
variables: {
id: 12,
},
const { id } = useParams()
const tournaemntQuery = useGetTournamentByIdQuery({
variables: {
id: Number(id)!,
},
skip: !id
})
if (query.loading)
if (tournaemntQuery.loading)
return <LoadingPage />
if (!query.data?.getTournamentById)
if (!tournaemntQuery.data?.getTournamentById)
return <NotFoundPage />
return (
@@ -32,18 +36,18 @@ export default function TournamentDetailsPage() {
"--maxPageWidth": "910px"
} as any}>
<Helmet>
<title>{query.data.getTournamentById.title} Tournament</title>
<title>{tournaemntQuery.data.getTournamentById.title} Tournament</title>
</Helmet>
<Header data={query.data.getTournamentById} />
<Navigation data={query.data.getTournamentById} />
<Header data={tournaemntQuery.data.getTournamentById} />
<Navigation data={tournaemntQuery.data.getTournamentById} />
<div className="content-container !mt-24">
<Routes >
<Route index element={<Navigate to='overview' />} />
<Route path='overview' element={<OverviewPage data={query.data.getTournamentById} avatars={query.data.getMakersInTournament.makers.map(m => m.avatar)} />} />
<Route path='events' element={<EventsPage data={query.data.getTournamentById} />} />
<Route path='makers' element={<MakersPage data={query.data.getTournamentById} />} />
<Route path='projects' element={<ProjectsPage data={query.data.getTournamentById} />} />
<Route path='overview' element={<OverviewPage data={tournaemntQuery.data.getTournamentById} avatars={tournaemntQuery.data.getMakersInTournament.makers.map(m => m.avatar)} isRegistered={!!tournaemntQuery.data.me?.in_tournament} />} />
<Route path='events' element={<EventsPage data={tournaemntQuery.data.getTournamentById} />} />
<Route path='makers' element={<MakersPage data={tournaemntQuery.data.getTournamentById} />} />
<Route path='projects' element={<ProjectsPage data={tournaemntQuery.data.getTournamentById} />} />
</Routes>
</div>
</div>

View File

@@ -0,0 +1,12 @@
query MeTournament($inTournamentId: Int!) {
me {
id
name
avatar
jobTitle
in_tournament(id: $inTournamentId)
...UserRolesSkills
}
}

View File

@@ -48,4 +48,15 @@ query GetTournamentById($id: Int!) {
avatar
}
}
me {
id
name
avatar
jobTitle
in_tournament(id: $id)
...UserRolesSkills
}
}