feat: tournament mocks api, header component, navigation component, 2 sections of the overview page

This commit is contained in:
MTG2000
2022-08-31 14:52:10 +03:00
parent 5dea9526de
commit 840a9e50eb
17 changed files with 368 additions and 59 deletions

View File

@@ -213,11 +213,14 @@ export interface NexusGenObjects {
description: string; // String!
end_date: NexusGenScalars['Date']; // Date!
events: NexusGenRootTypes['TournamentEvent'][]; // [TournamentEvent!]!
events_count: number; // Int!
faqs: NexusGenRootTypes['TournamentFAQ'][]; // [TournamentFAQ!]!
id: number; // Int!
judges: NexusGenRootTypes['TournamentJudge'][]; // [TournamentJudge!]!
location: string; // String!
makers_count: number; // Int!
prizes: NexusGenRootTypes['TournamentPrize'][]; // [TournamentPrize!]!
projects_count: number; // Int!
start_date: NexusGenScalars['Date']; // Date!
thumbnail_image: string; // String!
title: string; // String!
@@ -488,11 +491,14 @@ export interface NexusGenFieldTypes {
description: string; // String!
end_date: NexusGenScalars['Date']; // Date!
events: NexusGenRootTypes['TournamentEvent'][]; // [TournamentEvent!]!
events_count: number; // Int!
faqs: NexusGenRootTypes['TournamentFAQ'][]; // [TournamentFAQ!]!
id: number; // Int!
judges: NexusGenRootTypes['TournamentJudge'][]; // [TournamentJudge!]!
location: string; // String!
makers_count: number; // Int!
prizes: NexusGenRootTypes['TournamentPrize'][]; // [TournamentPrize!]!
projects_count: number; // Int!
start_date: NexusGenScalars['Date']; // Date!
thumbnail_image: string; // String!
title: string; // String!
@@ -778,11 +784,14 @@ export interface NexusGenFieldTypeNames {
description: 'String'
end_date: 'Date'
events: 'TournamentEvent'
events_count: 'Int'
faqs: 'TournamentFAQ'
id: 'Int'
judges: 'TournamentJudge'
location: 'String'
makers_count: 'Int'
prizes: 'TournamentPrize'
projects_count: 'Int'
start_date: 'Date'
thumbnail_image: 'String'
title: 'String'

View File

@@ -277,11 +277,14 @@ type Tournament {
description: String!
end_date: Date!
events: [TournamentEvent!]!
events_count: Int!
faqs: [TournamentFAQ!]!
id: Int!
judges: [TournamentJudge!]!
location: String!
makers_count: Int!
prizes: [TournamentPrize!]!
projects_count: Int!
start_date: Date!
thumbnail_image: String!
title: String!

View File

@@ -62,6 +62,11 @@ const Tournament = objectType({
t.nonNull.date('end_date');
t.nonNull.string('location');
t.nonNull.string('website');
t.nonNull.int('events_count');
t.nonNull.int('makers_count');
t.nonNull.int('projects_count');
t.nonNull.list.nonNull.field('prizes', { type: TournamentPrize, });
t.nonNull.list.nonNull.field('judges', { type: TournamentJudge, });
t.nonNull.list.nonNull.field('faqs', { type: TournamentFAQ, });

View File

@@ -25,6 +25,8 @@ const ExplorePage = Loadable(React.lazy(() => import( /* webpackChunkName: "expl
const HackathonsPage = Loadable(React.lazy(() => import( /* webpackChunkName: "hackathons_page" */ "./features/Hackathons/pages/HackathonsPage/HackathonsPage")))
const TournamentDetailsPage = Loadable(React.lazy(() => import( /* webpackChunkName: "hackathons_page" */ "./features/Tournaments/pages/TournamentDetailsPage/TournamentDetailsPage")))
const DonatePage = Loadable(React.lazy(() => import( /* webpackChunkName: "donate_page" */ "./features/Donations/pages/DonatePage/DonatePage")))
const LoginPage = Loadable(React.lazy(() => import( /* webpackChunkName: "login_page" */ "./features/Auth/pages/LoginPage/LoginPage")))
const LogoutPage = Loadable(React.lazy(() => import( /* webpackChunkName: "logout_page" */ "./features/Auth/pages/LogoutPage/LogoutPage")))
@@ -103,6 +105,8 @@ function App() {
<Route path={PAGES_ROUTES.hackathons.default} element={<HackathonsPage />} />
<Route path={PAGES_ROUTES.tournament.byId} element={<TournamentDetailsPage />} />
<Route path={PAGES_ROUTES.donate.default} element={<DonatePage />} />
<Route path={PAGES_ROUTES.profile.editProfile} element={<EditProfilePage />} />

View File

@@ -1,46 +0,0 @@
import { Tournament } from '../types/tournaments.types'
export function getTournament(): Tournament {
return {
id: 12,
title: "The Long Night",
start_date: "2022-09-30T21:00:00.000Z",
end_date: "2022-10-30T22:00:00.000Z",
cover_image: "https://imagedelivery.net/wyrwp3c-j0gDDUWgnE7lig/1d5d2c86-fe46-4478-6909-bb3c425c0d00/public",
thumbnail_image: "https://imagedelivery.net/wyrwp3c-j0gDDUWgnE7lig/37fb9cd6-e4f1-43f9-c3fe-7c3e119d5600/public",
location: "Online",
website: "#",
description: `
Lorem ipsum dolor sit **amet**, consectetur adipiscing elit. Semper turpis est, ac eget nullam. In leo at pharetra morbi ornare eget. Ultrices posuere senectus purus nulla vitae volutpat id id suspendisse. Urna mattis nulla diam semper erat. Mattis gravida ultrices aliquam odio. Praesent viverra egestas sed elementum nisl imperdiet a, non.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Semper turpis est, ac eget nullam. In leo at pharetra morbi ornare eget. Ultrices posuere senectus purus nulla vitae volutpat id id suspendisse. Urna mattis nulla diam semper erat. Mattis gravida ultrices aliquam odio. Praesent viverra egestas sed elementum nisl imperdiet a, non.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Semper turpis est, ac eget nullam. In leo at pharetra morbi ornare eget. Ultrices posuere senectus purus nulla vitae volutpat id id suspendisse.
`, // markdown
prizes: [{
title: "stw3 champion",
amount: "$ 20k",
image: "https://imagedelivery.net/wyrwp3c-j0gDDUWgnE7lig/39217dcf-c900-46be-153f-169e3a1f0400/public",
},
{
title: "2nd place",
amount: "$ 5k",
image: "https://imagedelivery.net/wyrwp3c-j0gDDUWgnE7lig/39cdb7c8-5fbf-49ff-32cf-fdabc3aa2d00/public",
},
{
title: "3rd place ",
amount: "$ 2k",
image: "https://imagedelivery.net/wyrwp3c-j0gDDUWgnE7lig/75958797-73b2-4a62-52df-9f0f98c53900/public",
},
{
title: "best design ",
amount: "$ 1k",
image: "https://imagedelivery.net/wyrwp3c-j0gDDUWgnE7lig/fa7b7cdd-7c06-4ebe-1a2d-94af9d2dae00/public",
}],
events: [],
faqs: [],
judges: [],
}
}

View File

@@ -1,7 +1,67 @@
import React from 'react'
import { FaUsers } from 'react-icons/fa'
import Button from 'src/Components/Button/Button'
import Card from 'src/Components/Card/Card'
import Avatar from 'src/features/Profiles/Components/Avatar/Avatar'
import { Tournament } from 'src/graphql'
import PrizesSection from './PrizesSection/PrizesSection'
export default function OverviewPage() {
interface Props {
data: Pick<Tournament,
| "description"
| "prizes"
| "judges"
| "start_date"
| 'makers_count'
>
}
export default function OverviewPage({ data }: Props) {
return (
<div>OverviewPage</div>
<Card className='flex flex-col gap-42'>
<div className="grid grid-cols-1 md:grid-cols-3 gap-24 items-start">
<div className='md:col-span-2'>
<h2 className='text-body2 font-bolder text-gray-900'>Tournament details</h2>
<p className="text-body4 text-gray-600 mt-16 whitespace-pre-line">{data.description}</p>
</div>
<Card onlyMd className='flex flex-col gap-24'>
<div>
<p className="text-body5 text-gray-600">
<FaUsers className='text-body2 mr-4' /> <span className='align-middle'>+ {data.makers_count} makers</span>
</p>
<Button color='primary' fullWidth className='mt-16'>Register</Button>
</div>
<div>
<p className="text-body5 text-gray-900 font-medium">
Tournament starts in
</p>
<div className="grid grid-cols-3 gap-10 mt-16">
<div className="border border-gray-200 rounded-10 flex flex-col py-10 justify-center items-center text-primary-600 text-body3 font-medium">
17d
</div>
<div className="border border-gray-200 rounded-10 flex flex-col py-10 justify-center items-center text-primary-600 text-body3 font-medium">
12h
</div>
<div className="border border-gray-200 rounded-10 flex flex-col py-10 justify-center items-center text-primary-600 text-body3 font-medium">
36m
</div>
</div>
</div>
<div>
<p className="text-body5 text-gray-900 font-medium">
Sponsors
</p>
<div className="flex flex-wrap gap-12 mt-16">
<Avatar width={42} src='https://i.pravatar.cc/150?id=70' />
<Avatar width={42} src='https://i.pravatar.cc/150?id=80' />
<Avatar width={42} src='https://i.pravatar.cc/150?id=90' />
<Avatar width={42} src='https://i.pravatar.cc/150?id=100' />
</div>
</div>
</Card>
</div>
<PrizesSection prizes={data.prizes} />
</Card>
)
}

View File

@@ -0,0 +1,25 @@
import React from 'react'
import { Tournament } from 'src/graphql'
import styles from './styles.module.scss'
interface Props {
prizes: Tournament['prizes']
}
export default function PrizesSection({ prizes }: Props) {
return (
<div>
<h2 className='text-body2 font-bolder text-gray-900 mb-16'>Prizes</h2>
<div className={styles.grid}>
{prizes.map(prize => <div className='bg-gray-50 rounded-16 py-24 px-32'>
<img src={prize.image} className=' max-w-[64px]' alt="" />
<div>
<h3 className="text-h2">{prize.title}</h3>
<p className="text-h1 text-green-500">{prize.amount}</p>
</div>
</div>)}
</div>
</div>
)
}

View File

@@ -0,0 +1,88 @@
@import "/src/styles/mixins";
@import url("https://fonts.googleapis.com/css2?family=Luckiest+Guy&display=swap");
.grid {
font-family: "Luckiest Guy", cursive;
display: grid;
gap: 24px;
> div {
display: flex;
align-items: center;
h3 {
font-size: 20px;
color: white;
-webkit-text-stroke: 2px black;
}
p {
font-size: 24px;
}
&:first-child {
flex-direction: column;
justify-content: center;
align-items: center;
text-align: center;
img {
max-width: 100px;
}
h3 {
font-size: 24px;
margin: 40px auto 0 auto;
}
p {
font-size: 40px;
}
}
&:not(:first-child) {
justify-content: space-between;
text-align: right;
img {
max-width: 42px;
}
}
}
grid-auto-rows: 120px;
grid-template-columns: 1fr;
> div:first-child {
grid-row: span 3;
}
@include gt-md {
grid-auto-rows: 150px;
grid-template-columns: 1fr 1fr;
> div {
h3 {
font-size: 32px;
}
p {
font-size: 40px;
}
&:first-child {
grid-row: 1/4;
grid-column: 1/2;
img {
max-width: 160px;
}
h3 {
font-size: 32px;
margin: 24px auto 24px auto;
}
p {
font-size: 56px;
}
}
&:not(:first-child) {
img {
max-width: 64px;
}
}
}
}
}

View File

@@ -1,7 +0,0 @@
import React from 'react'
export default function TournamentBasePage() {
return (
<div>TournamentBasePage</div>
)
}

View File

@@ -0,0 +1,36 @@
import dayjs from 'dayjs'
import advancedFormat from 'dayjs/plugin/advancedFormat'
import React from 'react'
import { IoLocationOutline } from 'react-icons/io5'
import { Tournament } from 'src/graphql'
import Navigation from '../Navigation/Navigation'
dayjs.extend(advancedFormat)
interface Props {
data: Pick<Tournament,
| 'cover_image'
| 'thumbnail_image'
| 'title'
| 'start_date'
| 'end_date'
| 'location'>
}
export default function Header({ data }: Props) {
return (
<div className="w-full p-16 md:p-24 flex flex-col min-h-[240px] md:min-h-[280px] relative">
<img src={data.cover_image} className='absolute inset-0 h-full w-full object-cover object-top' alt="" />
<div className='absolute inset-0 h-full w-full bg-black bg-opacity-50 ' />
<div className="content-container text-white flex flex-col md:flex-row gap-16 md:gap-32 relative" style={{ marginTop: 'auto' }}>
<img src={data.thumbnail_image} className={'w-64 md:w-[128px] aspect-square rounded-16 md:rounded-24 border-2 border-gray-100'} alt="" />
<div className='flex flex-col gap-4'>
<p className="text-body6">TOURNAMENT 🏆</p>
<p className="text-body1 md:text-h2 font-bold">{data.title}</p>
<p className="text-body3"> {`${dayjs(data.start_date).format('Do')} - ${dayjs(data.end_date).format('Do MMMM, YYYY')}`}</p>
<p className='text-body5'><IoLocationOutline className="mr-8" /> {data.location}</p>
</div>
</div>
</div>
)
}

View File

@@ -0,0 +1,72 @@
import React, { useMemo } from 'react'
import { Link, NavLink } from 'react-router-dom'
import { Tournament } from 'src/graphql'
import { useCarousel } from 'src/utils/hooks'
interface Props {
data: Pick<Tournament, 'events_count' | 'makers_count' | 'projects_count'>
}
export default function Navigation({ data }: Props) {
const { viewportRef, scrollSlides, canScrollNext, canScrollPrev, isClickAllowed } = useCarousel({
align: 'start', slidesToScroll: 2,
containScroll: "trimSnaps",
})
const links = useMemo(() => [
{
text: "Overview",
path: "overview",
},
{
text: `Events (${data.events_count})`,
path: "events",
},
{
text: `Makers (${data.makers_count})`,
path: "makers",
},
{
text: `Projects (${data.projects_count})`,
path: "projects",
},
{
text: "Ideas",
path: "ideas",
},
{
text: "Resources",
path: "resources",
},
], [data.events_count, data.makers_count, data.projects_count])
return (
<div className="w-full bg-white py-16 border-b-2 border-gray-200">
<div className="relative group content-container">
<div className="overflow-hidden" ref={viewportRef}>
<div className="select-none w-full flex gap-16">
{links.map((link) => <NavLink
key={link.path}
to={link.path}
className={({ isActive }) => `
min-w-max rounded-48 px-16 py-8 cursor-pointer font-medium text-body5
active:scale-95 transition-transform
${isActive ? 'bg-primary-100 text-primary-600' : 'bg-gray-100 hover:bg-gray-200 text-gray-600'}`}
// onClick={() => filterClicked(f.value)}
role='button'
>
{link.text}
</NavLink>)}
</div>
</div>
{/* <button className={`absolute text-body6 w-[28px] aspect-square flex justify-center items-center left-0 -translate-x-1/2 top-1/2 -translate-y-1/2 rounded-full bg-white text-gray-400 opacity-0 ${canScrollPrev && 'group-hover:opacity-100'} active:scale-90 transition-opacity border border-gray-200 shadow-md`} onClick={() => scrollSlides(-1)}>
{"<"}
</button>
<button className={`absolute text-body6 w-[28px] aspect-square flex justify-center items-center right-0 translate-x-1/2 top-1/2 -translate-y-1/2 rounded-full bg-white text-gray-400 opacity-0 ${canScrollNext && 'group-hover:opacity-100'} active:scale-90 transition-opacity border border-gray-200 shadow-md`} onClick={() => scrollSlides(1)}>
{">"}
</button> */}
</div>
</div>
)
}

View File

@@ -0,0 +1,43 @@
import React from 'react'
import { useGetTournamentByIdQuery } from 'src/graphql'
import LoadingPage from 'src/Components/LoadingPage/LoadingPage'
import NotFoundPage from 'src/features/Shared/pages/NotFoundPage/NotFoundPage'
import Header from './Header/Header'
import { Navigate, Route, Routes } from 'react-router-dom'
import OverviewPage from '../OverviewPage/OverviewPage'
import { Helmet } from 'react-helmet'
import Navigation from './Navigation/Navigation'
export default function TournamentDetailsPage() {
const query = useGetTournamentByIdQuery({
variables: {
id: 12,
}
})
if (query.loading)
return <LoadingPage />
if (!query.data?.getTournamentById)
return <NotFoundPage />
return (
<div style={{
"--maxPageWidth": "910px"
} as any}>
<Helmet>
<title>{query.data.getTournamentById.title} Tournament</title>
</Helmet>
<Header data={query.data?.getTournamentById} />
<Navigation data={query.data?.getTournamentById} />
<div className="content-container !mt-24">
<Routes >
<Route index element={<Navigate to='overview' />} />
<Route path='overview' element={<OverviewPage data={query.data?.getTournamentById} />} />
</Routes>
</div>
</div>
)
}

View File

@@ -9,6 +9,11 @@ query GetTournamentById($id: Int!) {
end_date
location
website
events_count
makers_count
projects_count
prizes {
title
amount

View File

@@ -433,11 +433,14 @@ export type Tournament = {
description: Scalars['String'];
end_date: Scalars['Date'];
events: Array<TournamentEvent>;
events_count: Scalars['Int'];
faqs: Array<TournamentFaq>;
id: Scalars['Int'];
judges: Array<TournamentJudge>;
location: Scalars['String'];
makers_count: Scalars['Int'];
prizes: Array<TournamentPrize>;
projects_count: Scalars['Int'];
start_date: Scalars['Date'];
thumbnail_image: Scalars['String'];
title: Scalars['String'];
@@ -691,7 +694,7 @@ export type GetTournamentByIdQueryVariables = Exact<{
}>;
export type GetTournamentByIdQuery = { __typename?: 'Query', getTournamentById: { __typename?: 'Tournament', id: number, title: string, description: string, thumbnail_image: string, cover_image: string, start_date: any, end_date: any, location: string, website: string, prizes: Array<{ __typename?: 'TournamentPrize', title: string, amount: string, image: string }>, judges: Array<{ __typename?: 'TournamentJudge', name: string, jobTitle: string, avatar: string }>, events: Array<{ __typename?: 'TournamentEvent', id: number, title: string, image: string, description: string, date: any, location: string, website: string, type: string, links: Array<string> }>, faqs: Array<{ __typename?: 'TournamentFAQ', question: string, answer: string }> } };
export type GetTournamentByIdQuery = { __typename?: 'Query', getTournamentById: { __typename?: 'Tournament', id: number, title: string, description: string, thumbnail_image: string, cover_image: string, start_date: any, end_date: any, location: string, website: string, events_count: number, makers_count: number, projects_count: number, prizes: Array<{ __typename?: 'TournamentPrize', title: string, amount: string, image: string }>, judges: Array<{ __typename?: 'TournamentJudge', name: string, jobTitle: string, avatar: string }>, events: Array<{ __typename?: 'TournamentEvent', id: number, title: string, image: string, description: string, date: any, location: string, website: string, type: string, links: Array<string> }>, faqs: Array<{ __typename?: 'TournamentFAQ', question: string, answer: string }> } };
export type VoteMutationVariables = Exact<{
itemType: Vote_Item_Type;
@@ -1933,6 +1936,9 @@ export const GetTournamentByIdDocument = gql`
end_date
location
website
events_count
makers_count
projects_count
prizes {
title
amount

View File

@@ -10,8 +10,8 @@ export const tournaments: Tournament[] = [{
thumbnail_image: "https://imagedelivery.net/wyrwp3c-j0gDDUWgnE7lig/37fb9cd6-e4f1-43f9-c3fe-7c3e119d5600/public",
location: "Online",
website: "#",
description: `
Lorem ipsum dolor sit **amet**, consectetur adipiscing elit. Semper turpis est, ac eget nullam. In leo at pharetra morbi ornare eget. Ultrices posuere senectus purus nulla vitae volutpat id id suspendisse. Urna mattis nulla diam semper erat. Mattis gravida ultrices aliquam odio. Praesent viverra egestas sed elementum nisl imperdiet a, non.
description:
`Lorem ipsum dolor sit **amet**, consectetur adipiscing elit. Semper turpis est, ac eget nullam. In leo at pharetra morbi ornare eget. Ultrices posuere senectus purus nulla vitae volutpat id id suspendisse. Urna mattis nulla diam semper erat. Mattis gravida ultrices aliquam odio. Praesent viverra egestas sed elementum nisl imperdiet a, non.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Semper turpis est, ac eget nullam. In leo at pharetra morbi ornare eget. Ultrices posuere senectus purus nulla vitae volutpat id id suspendisse. Urna mattis nulla diam semper erat. Mattis gravida ultrices aliquam odio. Praesent viverra egestas sed elementum nisl imperdiet a, non.
@@ -37,6 +37,9 @@ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Semper turpis est, ac e
amount: "$ 1k",
image: "https://imagedelivery.net/wyrwp3c-j0gDDUWgnE7lig/fa7b7cdd-7c06-4ebe-1a2d-94af9d2dae00/public",
}],
events_count: 28,
makers_count: 668,
projects_count: 21,
events: [],
faqs: [],
judges: [],

View File

@@ -26,7 +26,7 @@ body {
@include gt-md {
--padding: 32px;
}
width: calc(min(100% - 2 * var(--padding), 1440px));
width: calc(min(100% - 2 * var(--padding), var(--maxPageWidth, 1440px)));
margin: 0 auto;
}

View File

@@ -81,6 +81,9 @@ export const PAGES_ROUTES = {
editProfile: "/edit-profile/*",
byId: "/profile/:id/*",
},
tournament: {
byId: "/tournaments/:id/*",
},
auth: {
login: "/login",
logout: "/logout",