mirror of
https://github.com/aljazceru/landscape-template.git
synced 2026-02-10 09:04:22 +01:00
refactor: split the makers page into sections & functionalities
This commit is contained in:
@@ -246,7 +246,6 @@ export interface NexusGenObjects {
|
||||
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!
|
||||
@@ -274,6 +273,11 @@ export interface NexusGenObjects {
|
||||
jobTitle: string; // String!
|
||||
name: string; // String!
|
||||
}
|
||||
TournamentMakersResponse: { // root type
|
||||
hasNext?: boolean | null; // Boolean
|
||||
hasPrev?: boolean | null; // Boolean
|
||||
makers: NexusGenRootTypes['User'][]; // [User!]!
|
||||
}
|
||||
TournamentPrize: { // root type
|
||||
amount: string; // String!
|
||||
image: string; // String!
|
||||
@@ -487,7 +491,7 @@ export interface NexusGenFieldTypes {
|
||||
getDonationsStats: NexusGenRootTypes['DonationsStats']; // DonationsStats!
|
||||
getFeed: NexusGenRootTypes['Post'][]; // [Post!]!
|
||||
getLnurlDetailsForProject: NexusGenRootTypes['LnurlDetails']; // LnurlDetails!
|
||||
getMakersInTournament: NexusGenRootTypes['User'][]; // [User!]!
|
||||
getMakersInTournament: NexusGenRootTypes['TournamentMakersResponse']; // TournamentMakersResponse!
|
||||
getMyDrafts: NexusGenRootTypes['Post'][]; // [Post!]!
|
||||
getPostById: NexusGenRootTypes['Post']; // Post!
|
||||
getProject: NexusGenRootTypes['Project']; // Project!
|
||||
@@ -577,6 +581,11 @@ export interface NexusGenFieldTypes {
|
||||
jobTitle: string; // String!
|
||||
name: string; // String!
|
||||
}
|
||||
TournamentMakersResponse: { // field return type
|
||||
hasNext: boolean | null; // Boolean
|
||||
hasPrev: boolean | null; // Boolean
|
||||
makers: NexusGenRootTypes['User'][]; // [User!]!
|
||||
}
|
||||
TournamentPrize: { // field return type
|
||||
amount: string; // String!
|
||||
image: string; // String!
|
||||
@@ -813,7 +822,7 @@ export interface NexusGenFieldTypeNames {
|
||||
getDonationsStats: 'DonationsStats'
|
||||
getFeed: 'Post'
|
||||
getLnurlDetailsForProject: 'LnurlDetails'
|
||||
getMakersInTournament: 'User'
|
||||
getMakersInTournament: 'TournamentMakersResponse'
|
||||
getMyDrafts: 'Post'
|
||||
getPostById: 'Post'
|
||||
getProject: 'Project'
|
||||
@@ -903,6 +912,11 @@ export interface NexusGenFieldTypeNames {
|
||||
jobTitle: 'String'
|
||||
name: 'String'
|
||||
}
|
||||
TournamentMakersResponse: { // field return type name
|
||||
hasNext: 'Boolean'
|
||||
hasPrev: 'Boolean'
|
||||
makers: 'User'
|
||||
}
|
||||
TournamentPrize: { // field return type name
|
||||
amount: 'String'
|
||||
image: 'String'
|
||||
|
||||
@@ -252,7 +252,7 @@ type Query {
|
||||
getDonationsStats: DonationsStats!
|
||||
getFeed(skip: Int = 0, sortBy: String, tag: Int = 0, take: Int = 10): [Post!]!
|
||||
getLnurlDetailsForProject(project_id: Int!): LnurlDetails!
|
||||
getMakersInTournament(roleId: Int, search: String, skip: Int = 0, take: Int = 10, tournamentId: Int!): [User!]!
|
||||
getMakersInTournament(roleId: Int, search: String, skip: Int = 0, take: Int = 10, tournamentId: Int!): TournamentMakersResponse!
|
||||
getMyDrafts(type: POST_TYPE!): [Post!]!
|
||||
getPostById(id: Int!, type: POST_TYPE!): Post!
|
||||
getProject(id: Int!): Project!
|
||||
@@ -374,6 +374,12 @@ type TournamentJudge {
|
||||
name: String!
|
||||
}
|
||||
|
||||
type TournamentMakersResponse {
|
||||
hasNext: Boolean
|
||||
hasPrev: Boolean
|
||||
makers: [User!]!
|
||||
}
|
||||
|
||||
type TournamentPrize {
|
||||
amount: String!
|
||||
image: String!
|
||||
|
||||
@@ -79,7 +79,11 @@ const Tournament = objectType({
|
||||
t.nonNull.string('website');
|
||||
|
||||
t.nonNull.int('events_count');
|
||||
t.nonNull.int('makers_count');
|
||||
t.nonNull.int('makers_count', {
|
||||
resolve(parent) {
|
||||
return prisma.user.count();
|
||||
}
|
||||
});
|
||||
t.nonNull.int('projects_count');
|
||||
|
||||
t.nonNull.list.nonNull.field('prizes', { type: TournamentPrize, });
|
||||
@@ -89,6 +93,29 @@ const Tournament = objectType({
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
const TournamentMakersResponse = objectType({
|
||||
name: 'TournamentMakersResponse',
|
||||
definition(t) {
|
||||
t.boolean('hasNext');
|
||||
t.boolean('hasPrev');
|
||||
|
||||
t.nonNull.list.nonNull.field('makers', { type: "User" })
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
const TournamentProjectsResponse = objectType({
|
||||
name: 'TournamentProjectsResponse',
|
||||
definition(t) {
|
||||
t.boolean('hasNext');
|
||||
t.boolean('hasPrev');
|
||||
|
||||
t.nonNull.list.nonNull.field('projects', { type: "Project" })
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
const getTournamentById = extendType({
|
||||
type: "Query",
|
||||
definition(t) {
|
||||
@@ -104,18 +131,20 @@ const getTournamentById = extendType({
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
|
||||
const getMakersInTournament = extendType({
|
||||
type: "Query",
|
||||
definition(t) {
|
||||
t.nonNull.list.nonNull.field('getMakersInTournament', {
|
||||
type: "User",
|
||||
t.nonNull.field('getMakersInTournament', {
|
||||
type: TournamentMakersResponse,
|
||||
args: {
|
||||
tournamentId: nonNull(intArg()),
|
||||
...paginationArgs({ take: 10 }),
|
||||
search: stringArg(),
|
||||
roleId: intArg(),
|
||||
},
|
||||
resolve(_, args) {
|
||||
async resolve(_, args) {
|
||||
|
||||
|
||||
let filters = [];
|
||||
@@ -147,15 +176,84 @@ const getMakersInTournament = extendType({
|
||||
})
|
||||
|
||||
|
||||
return prisma.user.findMany({
|
||||
const makers = await prisma.user.findMany({
|
||||
...(filters.length > 0 && {
|
||||
where: {
|
||||
AND: filters
|
||||
}
|
||||
}),
|
||||
skip: args.skip,
|
||||
take: args.take,
|
||||
take: args.take + 1,
|
||||
});
|
||||
|
||||
return {
|
||||
hasNext: makers.length === args.take + 1,
|
||||
hasPrev: args.skip !== 0,
|
||||
makers: makers.slice(0, args.take)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
const getProjectsInTournament = extendType({
|
||||
type: "Query",
|
||||
definition(t) {
|
||||
t.nonNull.field('getProjectsInTournament', {
|
||||
type: TournamentProjectsResponse,
|
||||
args: {
|
||||
tournamentId: nonNull(intArg()),
|
||||
...paginationArgs({ take: 10 }),
|
||||
search: stringArg(),
|
||||
roleId: intArg(),
|
||||
},
|
||||
async resolve(_, args) {
|
||||
|
||||
|
||||
let filters = [];
|
||||
|
||||
if (args.search) filters.push({
|
||||
OR: [
|
||||
{
|
||||
title: {
|
||||
contains: args.search,
|
||||
mode: 'insensitive'
|
||||
}
|
||||
},
|
||||
{
|
||||
description: {
|
||||
contains: args.search,
|
||||
mode: 'insensitive'
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
|
||||
// if (args.roleId) filters.push({
|
||||
// roles: {
|
||||
// some: {
|
||||
// roleId: args.roleId
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
|
||||
|
||||
const makers = await prisma.project.findMany({
|
||||
...(filters.length > 0 && {
|
||||
where: {
|
||||
AND: filters
|
||||
}
|
||||
}),
|
||||
skip: args.skip,
|
||||
take: args.take + 1,
|
||||
});
|
||||
|
||||
return {
|
||||
hasNext: makers.length === args.take + 1,
|
||||
hasPrev: args.skip !== 0,
|
||||
makers: makers.slice(0, args.take)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -13,10 +13,10 @@ import Avatar from 'src/features/Profiles/Components/Avatar/Avatar';
|
||||
import Badge from 'src/Components/Badge/Badge';
|
||||
import { createRoute, PAGES_ROUTES } from 'src/utils/routing';
|
||||
|
||||
type MakerType = GetMakersInTournamentQuery['getMakersInTournament'][number]
|
||||
type MakerType = GetMakersInTournamentQuery['getMakersInTournament']['makers'][number]
|
||||
|
||||
interface Props {
|
||||
maker: GetMakersInTournamentQuery['getMakersInTournament'][number],
|
||||
maker: MakerType,
|
||||
isMe?: boolean;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,126 +1,29 @@
|
||||
import { NetworkStatus } from '@apollo/client';
|
||||
import { useDebouncedCallback, useDebouncedState } from '@react-hookz/web';
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import { FaChevronLeft, FaChevronRight } from 'react-icons/fa';
|
||||
import IconButton from 'src/Components/IconButton/IconButton';
|
||||
import LoadingPage from 'src/Components/LoadingPage/LoadingPage';
|
||||
import { GenericMakerRole, GetMakersInTournamentQueryVariables, Tournament, useGetMakersInTournamentQuery, User } from 'src/graphql'
|
||||
import ScrollToTop from 'src/utils/routing/scrollToTop';
|
||||
import { Tournament, useMeTournamentQuery, User } from 'src/graphql'
|
||||
import MakerCard from './MakerCard/MakerCard';
|
||||
import MakerCardSkeleton from './MakerCard/MakerCard.Skeleton';
|
||||
import MakersFilters from './MakersFilters/MakersFilters';
|
||||
import ParticipantsSection from './ParticipantsSection/ParticipantsSection';
|
||||
|
||||
interface Props {
|
||||
data: Pick<Tournament,
|
||||
| 'id'>
|
||||
}
|
||||
|
||||
const ITEMS_PER_PAGE = 15;
|
||||
|
||||
export default function MakersPage({ data: { id } }: Props) {
|
||||
|
||||
const [page, setPage] = useState(0)
|
||||
const [searchFilter, setSearchFilter] = useState("");
|
||||
const [debouncedsearchFilter, setDebouncedSearchFilter] = useDebouncedState("", 500);
|
||||
const [roleFilter, setRoleFilter] = useState<GenericMakerRole | null>(null);
|
||||
|
||||
const loadingContainerCbRef = useCallback((e: HTMLDivElement) => {
|
||||
if (e)
|
||||
e.scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: "center"
|
||||
})
|
||||
}, [])
|
||||
|
||||
const [queryFilter, setQueryFilter] = useState<GetMakersInTournamentQueryVariables>({
|
||||
tournamentId: id,
|
||||
roleId: roleFilter?.id ?? null,
|
||||
search: debouncedsearchFilter,
|
||||
skip: ITEMS_PER_PAGE * page,
|
||||
take: ITEMS_PER_PAGE,
|
||||
});
|
||||
|
||||
|
||||
const query = useGetMakersInTournamentQuery({
|
||||
variables: queryFilter,
|
||||
notifyOnNetworkStatusChange: true,
|
||||
});
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
setPage(0);
|
||||
setQueryFilter(f => ({ ...f, search: debouncedsearchFilter, roleId: roleFilter?.id ?? null, skip: 0 }))
|
||||
}, [debouncedsearchFilter, roleFilter]);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if (query.networkStatus === NetworkStatus.loading) return <LoadingPage />
|
||||
|
||||
|
||||
const changeSearchFilter = (new_value: string) => {
|
||||
setSearchFilter(new_value);
|
||||
setDebouncedSearchFilter(new_value);
|
||||
}
|
||||
|
||||
|
||||
const nextPage = () => {
|
||||
setPage(p => p + 1)
|
||||
setQueryFilter(f => ({ ...f, skip: (f.skip ?? 0) + ITEMS_PER_PAGE }))
|
||||
}
|
||||
const prevPage = () => {
|
||||
if (page === 0) return
|
||||
setPage(p => p - 1)
|
||||
setQueryFilter(f => ({ ...f, skip: (f.skip ?? 0) - ITEMS_PER_PAGE }))
|
||||
}
|
||||
|
||||
|
||||
const isFetchingMore = query.networkStatus === NetworkStatus.setVariables;
|
||||
const itemsCount = query.data?.getMakersInTournament && query.data.getMakersInTournament.length;
|
||||
|
||||
const query = useMeTournamentQuery();
|
||||
|
||||
return (
|
||||
<div className='pb-42'>
|
||||
<div className="flex flex-col gap-16 lg:gap-24">
|
||||
{(query.data?.me ?? query.previousData?.me) && <MakerCard isMe maker={(query.data?.me ?? query.previousData?.me) as User} />}
|
||||
<div className="flex flex-col gap-16">
|
||||
<h3 className="text-body1 text-gray-900 font-bold mt-24">Makers 👾</h3>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-16 lg:gap-24">
|
||||
<MakersFilters
|
||||
searchValue={searchFilter}
|
||||
onSearchChange={changeSearchFilter}
|
||||
roleValue={roleFilter}
|
||||
onRoleChange={setRoleFilter}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{isFetchingMore ?
|
||||
<>
|
||||
<div ref={loadingContainerCbRef} >
|
||||
<MakerCardSkeleton />
|
||||
</div>
|
||||
<MakerCardSkeleton />
|
||||
<MakerCardSkeleton />
|
||||
</>
|
||||
{query.loading ?
|
||||
<MakerCardSkeleton />
|
||||
:
|
||||
(itemsCount !== 0 ?
|
||||
query.data?.getMakersInTournament.map(maker => <MakerCard key={maker.id} maker={maker} />) :
|
||||
<div className="py-80 text-center text-body2">
|
||||
<p className="text-gray-400">No makers found here...</p>
|
||||
</div>)
|
||||
query.data?.me ?
|
||||
<MakerCard isMe maker={query.data.me as User} />
|
||||
: null
|
||||
}
|
||||
|
||||
|
||||
<div className='flex justify-center gap-36 text-gray-400'>
|
||||
<IconButton isDisabled={!itemsCount || page === 0} onClick={prevPage}>
|
||||
<FaChevronLeft />
|
||||
</IconButton>
|
||||
<IconButton isDisabled={!itemsCount || itemsCount < ITEMS_PER_PAGE} onClick={nextPage} >
|
||||
<FaChevronRight />
|
||||
</IconButton>
|
||||
</div>
|
||||
<ParticipantsSection tournamentId={id} />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { FaChevronLeft, FaChevronRight } from 'react-icons/fa';
|
||||
import IconButton from 'src/Components/IconButton/IconButton';
|
||||
import { GetMakersInTournamentQueryVariables, useGetMakersInTournamentQuery } from 'src/graphql';
|
||||
import MakerCard from '../MakerCard/MakerCard';
|
||||
import MakerCardSkeleton from '../MakerCard/MakerCard.Skeleton';
|
||||
|
||||
interface Props {
|
||||
tournamentId: number
|
||||
searchFilter: string,
|
||||
roleFilter: number | null
|
||||
}
|
||||
|
||||
const ITEMS_PER_PAGE = 3;
|
||||
|
||||
export default function MakersList(props: Props) {
|
||||
|
||||
|
||||
const [page, setPage] = useState(0);
|
||||
const topContainerRef = useRef<HTMLDivElement>(null)
|
||||
const [scrollToTop, setScrollToTop] = useState(false)
|
||||
|
||||
const [queryFilter, setQueryFilter] = useState<GetMakersInTournamentQueryVariables>({
|
||||
tournamentId: props.tournamentId,
|
||||
roleId: props.roleFilter ?? null,
|
||||
search: props.searchFilter ?? null,
|
||||
skip: ITEMS_PER_PAGE * page,
|
||||
take: ITEMS_PER_PAGE,
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
const query = useGetMakersInTournamentQuery({
|
||||
variables: queryFilter,
|
||||
});
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
setPage(0);
|
||||
setQueryFilter(f => ({ ...f, search: props.searchFilter, roleId: props.roleFilter, skip: 0 }))
|
||||
}, [props.roleFilter, props.searchFilter]);
|
||||
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if (scrollToTop && topContainerRef.current) {
|
||||
topContainerRef.current.scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: "center"
|
||||
})
|
||||
setScrollToTop(false)
|
||||
}
|
||||
}, [scrollToTop])
|
||||
|
||||
|
||||
|
||||
const nextPage = () => {
|
||||
setPage(p => p + 1)
|
||||
setQueryFilter(f => ({ ...f, skip: (f.skip ?? 0) + ITEMS_PER_PAGE }))
|
||||
setScrollToTop(true)
|
||||
}
|
||||
const prevPage = () => {
|
||||
if (page === 0) return
|
||||
setPage(p => p - 1)
|
||||
setQueryFilter(f => ({ ...f, skip: (f.skip ?? 0) - ITEMS_PER_PAGE }))
|
||||
setScrollToTop(true)
|
||||
}
|
||||
|
||||
|
||||
const itemsCount = query.data?.getMakersInTournament && query.data.getMakersInTournament.makers.length;
|
||||
|
||||
return (
|
||||
<div >
|
||||
<div ref={topContainerRef}></div>
|
||||
<div className='flex flex-col gap-16 lg:gap-24'>
|
||||
{
|
||||
query.loading ?
|
||||
<>
|
||||
<div >
|
||||
<MakerCardSkeleton />
|
||||
</div>
|
||||
<MakerCardSkeleton />
|
||||
<MakerCardSkeleton />
|
||||
</>
|
||||
:
|
||||
(itemsCount !== 0 ?
|
||||
query.data?.getMakersInTournament.makers.map(maker => <MakerCard key={maker.id} maker={maker} />) :
|
||||
<div className="py-80 text-center text-body2">
|
||||
<p className="text-gray-400">No makers found here...</p>
|
||||
</div>)
|
||||
}
|
||||
< div className='flex justify-center gap-36 text-gray-400' >
|
||||
<IconButton isDisabled={!query.data?.getMakersInTournament.hasPrev} onClick={prevPage}>
|
||||
<FaChevronLeft />
|
||||
</IconButton>
|
||||
<IconButton isDisabled={!query.data?.getMakersInTournament.hasNext} onClick={nextPage} >
|
||||
<FaChevronRight />
|
||||
</IconButton>
|
||||
</div >
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import { useDebouncedState } from '@react-hookz/web';
|
||||
import { useState } from 'react'
|
||||
import { GenericMakerRole } from 'src/graphql'
|
||||
import MakersFilters from '../MakersFilters/MakersFilters';
|
||||
import MakersList from './MakersList';
|
||||
|
||||
interface Props {
|
||||
tournamentId: number
|
||||
}
|
||||
export default function ParticipantsSection({ tournamentId }: Props) {
|
||||
|
||||
const [searchFilter, setSearchFilter] = useState("");
|
||||
const [debouncedsearchFilter, setDebouncedSearchFilter] = useDebouncedState("", 500);
|
||||
const [roleFilter, setRoleFilter] = useState<GenericMakerRole | null>(null);
|
||||
|
||||
|
||||
|
||||
const changeSearchFilter = (new_value: string) => {
|
||||
setSearchFilter(new_value);
|
||||
setDebouncedSearchFilter(new_value);
|
||||
}
|
||||
|
||||
|
||||
|
||||
return (<>
|
||||
<div className="flex flex-col gap-16">
|
||||
<h3 className="text-body1 text-gray-900 font-bold mt-24">Makers 👾</h3>
|
||||
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-16 lg:gap-24">
|
||||
<MakersFilters
|
||||
searchValue={searchFilter}
|
||||
onSearchChange={changeSearchFilter}
|
||||
roleValue={roleFilter}
|
||||
onRoleChange={setRoleFilter}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<MakersList searchFilter={debouncedsearchFilter} roleFilter={roleFilter?.id ?? null} tournamentId={tournamentId} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import Card from 'src/Components/Card/Card';
|
||||
import Badge from 'src/Components/Badge/Badge';
|
||||
import Skeleton from 'react-loading-skeleton';
|
||||
|
||||
|
||||
export default function ProjectCardSkeleton() {
|
||||
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<div className="flex flex-wrap gap-24 items-start">
|
||||
<div className="shrink-0 w-64 md:w-80 aspect-square">
|
||||
<Skeleton borderRadius={16} width={"100%"} height={'100%'} />
|
||||
</div>
|
||||
<div className="flex flex-col gap-4 flex-1">
|
||||
<p className="text-body2 text-gray-900 font-bold"><Skeleton width={"15ch"} /> </p>
|
||||
<p className="text-body4 text-gray-600 font-medium"><Skeleton width={"8ch"} /> </p>
|
||||
<p className="text-body5 text-gray-600 font-medium"><Skeleton width={"35ch"} /> </p>
|
||||
<ul className="hidden md:flex flex-wrap gap-8 mt-4">
|
||||
{Array(3).fill(0).map((_, idx) => <li key={idx}><Badge size='sm' className='!text-body5'> <span className="opacity-0">Loading role</span> </Badge> </li>)}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<hr className="hidden md:block bg-gray-200 mt-24"></hr>
|
||||
|
||||
<div className="md:hidden mt-24">
|
||||
<p className="text-body5 text-gray-900 font-medium"><Skeleton width={"7ch"} /></p>
|
||||
<ul className="flex flex-wrap gap-8 mt-4">
|
||||
{Array(3).fill(0).map((_, idx) => <li key={idx}><Badge size='sm' className='!text-body5'> <span className="opacity-0">Loading role</span> </Badge> </li>)}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className="mt-24">
|
||||
<p className="text-body5 text-gray-900 font-medium"><Skeleton width={"7ch"} /></p>
|
||||
<ul className="flex flex-wrap gap-8 mt-12">
|
||||
{Array(3).fill(0).map((_, idx) => <li key={idx}><Badge size='sm' className='!text-body5'> <span className="opacity-0">Loading role</span> </Badge> </li>)} </ul>
|
||||
</div>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
import Button from "src/Components/Button/Button"
|
||||
import { GetMakersInTournamentQuery, } from "src/graphql";
|
||||
import { useAppDispatch, } from "src/utils/hooks";
|
||||
import Card from 'src/Components/Card/Card';
|
||||
import Avatar from 'src/features/Profiles/Components/Avatar/Avatar';
|
||||
import Badge from 'src/Components/Badge/Badge';
|
||||
import { createRoute } from 'src/utils/routing';
|
||||
|
||||
type MakerType = GetMakersInTournamentQuery['getMakersInTournament']['makers'][number]
|
||||
|
||||
interface Props {
|
||||
maker: MakerType,
|
||||
isMe?: boolean;
|
||||
}
|
||||
|
||||
export default function ProjectCard({ maker, isMe }: Props) {
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<div className="flex flex-wrap gap-24 items-start">
|
||||
<div className="shrink-0 w-64 md:w-80">
|
||||
<Avatar src={maker.avatar} width={'100%'}></Avatar>
|
||||
</div>
|
||||
<div className="flex flex-col gap-4 flex-1 overflow-hidden">
|
||||
<p className="text-body2 text-gray-900 font-bold overflow-hidden text-ellipsis">{maker.name}</p>
|
||||
{maker.jobTitle ? <p className="text-body4 text-gray-600 font-medium">{maker.jobTitle}</p>
|
||||
:
|
||||
<p className="text-body4 text-gray-400 font-medium">No job title</p>}
|
||||
{maker.roles.length ? <ul className="hidden md:flex flex-wrap gap-8 mt-4">
|
||||
{maker.roles.map(role => <li><Badge size='sm' className='!text-body5'>{role.icon} {role.title}</Badge> </li>)}
|
||||
</ul>
|
||||
:
|
||||
<p className="hidden md:block text-body4 text-gray-400">No roles added</p>
|
||||
}
|
||||
</div>
|
||||
{isMe && <span className="ml-auto hidden md:inline-block"><Button color='white' href={createRoute({ type: 'edit-profile' })} size='sm' className='ml-auto'>Edit Profile</Button></span>}
|
||||
</div>
|
||||
<hr className="hidden md:block bg-gray-200 mt-24"></hr>
|
||||
|
||||
<div className="md:hidden mt-24">
|
||||
<p className="text-body5 text-gray-900 font-medium mb-12">🌈 Roles</p>
|
||||
|
||||
{maker.roles.length ? <ul className="flex flex-wrap gap-8">
|
||||
{maker.roles.map(role => <li><Badge size='sm' className='!text-body5'>{role.icon} {role.title}</Badge> </li>)}
|
||||
</ul>
|
||||
:
|
||||
<p className="text-body4 text-gray-400">No roles added</p>
|
||||
}
|
||||
</div>
|
||||
|
||||
<div className="mt-24">
|
||||
<p className="text-body5 text-gray-900 font-medium mb-12">🛠️ Skills</p>
|
||||
{maker.skills.length ? <ul className="flex flex-wrap gap-8">
|
||||
{maker.skills.map(skill => <li><Badge size='sm' className='!text-body5'>{skill.title}</Badge> </li>)}
|
||||
</ul>
|
||||
:
|
||||
<p className="text-body4 text-gray-400">No skills added</p>
|
||||
}
|
||||
</div>
|
||||
{isMe && <Button fullWidth color='white' href={createRoute({ type: 'edit-profile' })} size='sm' className='mt-32 md:hidden'>Edit Profile</Button>} </Card>
|
||||
)
|
||||
}
|
||||
@@ -5,14 +5,6 @@ query GetMakersInTournament(
|
||||
$search: String
|
||||
$roleId: Int
|
||||
) {
|
||||
me {
|
||||
id
|
||||
name
|
||||
avatar
|
||||
jobTitle
|
||||
...UserRolesSkills
|
||||
}
|
||||
|
||||
getMakersInTournament(
|
||||
tournamentId: $tournamentId
|
||||
take: $take
|
||||
@@ -20,19 +12,33 @@ query GetMakersInTournament(
|
||||
search: $search
|
||||
roleId: $roleId
|
||||
) {
|
||||
hasNext
|
||||
hasPrev
|
||||
makers {
|
||||
id
|
||||
name
|
||||
avatar
|
||||
jobTitle
|
||||
roles {
|
||||
id
|
||||
icon
|
||||
title
|
||||
}
|
||||
skills {
|
||||
id
|
||||
title
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
query MeTournament {
|
||||
me {
|
||||
id
|
||||
name
|
||||
avatar
|
||||
jobTitle
|
||||
roles {
|
||||
id
|
||||
icon
|
||||
title
|
||||
}
|
||||
skills {
|
||||
id
|
||||
title
|
||||
}
|
||||
...UserRolesSkills
|
||||
}
|
||||
}
|
||||
|
||||
@@ -330,7 +330,7 @@ export type Query = {
|
||||
getDonationsStats: DonationsStats;
|
||||
getFeed: Array<Post>;
|
||||
getLnurlDetailsForProject: LnurlDetails;
|
||||
getMakersInTournament: Array<User>;
|
||||
getMakersInTournament: TournamentMakersResponse;
|
||||
getMyDrafts: Array<Post>;
|
||||
getPostById: Post;
|
||||
getProject: Project;
|
||||
@@ -555,6 +555,13 @@ export type TournamentJudge = {
|
||||
name: Scalars['String'];
|
||||
};
|
||||
|
||||
export type TournamentMakersResponse = {
|
||||
__typename?: 'TournamentMakersResponse';
|
||||
hasNext: Maybe<Scalars['Boolean']>;
|
||||
hasPrev: Maybe<Scalars['Boolean']>;
|
||||
makers: Array<User>;
|
||||
};
|
||||
|
||||
export type TournamentPrize = {
|
||||
__typename?: 'TournamentPrize';
|
||||
amount: Scalars['String'];
|
||||
@@ -809,7 +816,12 @@ export type GetMakersInTournamentQueryVariables = Exact<{
|
||||
}>;
|
||||
|
||||
|
||||
export type GetMakersInTournamentQuery = { __typename?: 'Query', me: { __typename?: 'MyProfile', id: number, name: string, avatar: string, jobTitle: string | null, skills: Array<{ __typename?: 'MakerSkill', id: number, title: string }>, roles: Array<{ __typename?: 'MakerRole', id: number, title: string, icon: string, level: RoleLevelEnum }> } | null, getMakersInTournament: Array<{ __typename?: 'User', id: number, name: string, avatar: string, jobTitle: string | null, roles: Array<{ __typename?: 'MakerRole', id: number, icon: string, title: string }>, skills: Array<{ __typename?: 'MakerSkill', id: number, title: string }> }> };
|
||||
export type GetMakersInTournamentQuery = { __typename?: 'Query', getMakersInTournament: { __typename?: 'TournamentMakersResponse', hasNext: boolean | null, hasPrev: boolean | null, makers: Array<{ __typename?: 'User', id: number, name: string, avatar: string, jobTitle: string | null, roles: Array<{ __typename?: 'MakerRole', id: number, icon: string, title: string }>, skills: Array<{ __typename?: 'MakerSkill', id: number, title: string }> }> } };
|
||||
|
||||
export type MeTournamentQueryVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
|
||||
export type MeTournamentQuery = { __typename?: 'Query', me: { __typename?: 'MyProfile', id: number, name: string, avatar: string, jobTitle: string | null, skills: Array<{ __typename?: 'MakerSkill', id: number, title: string }>, roles: Array<{ __typename?: 'MakerRole', id: number, title: string, icon: string, level: RoleLevelEnum }> } | null };
|
||||
|
||||
export type GetAllRolesQueryVariables = Exact<{ [key: string]: never; }>;
|
||||
|
||||
@@ -2148,13 +2160,6 @@ export type ProjectDetailsLazyQueryHookResult = ReturnType<typeof useProjectDeta
|
||||
export type ProjectDetailsQueryResult = Apollo.QueryResult<ProjectDetailsQuery, ProjectDetailsQueryVariables>;
|
||||
export const GetMakersInTournamentDocument = gql`
|
||||
query GetMakersInTournament($tournamentId: Int!, $take: Int, $skip: Int, $search: String, $roleId: Int) {
|
||||
me {
|
||||
id
|
||||
name
|
||||
avatar
|
||||
jobTitle
|
||||
...UserRolesSkills
|
||||
}
|
||||
getMakersInTournament(
|
||||
tournamentId: $tournamentId
|
||||
take: $take
|
||||
@@ -2162,22 +2167,26 @@ export const GetMakersInTournamentDocument = gql`
|
||||
search: $search
|
||||
roleId: $roleId
|
||||
) {
|
||||
id
|
||||
name
|
||||
avatar
|
||||
jobTitle
|
||||
roles {
|
||||
hasNext
|
||||
hasPrev
|
||||
makers {
|
||||
id
|
||||
icon
|
||||
title
|
||||
}
|
||||
skills {
|
||||
id
|
||||
title
|
||||
name
|
||||
avatar
|
||||
jobTitle
|
||||
roles {
|
||||
id
|
||||
icon
|
||||
title
|
||||
}
|
||||
skills {
|
||||
id
|
||||
title
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
${UserRolesSkillsFragmentDoc}`;
|
||||
`;
|
||||
|
||||
/**
|
||||
* __useGetMakersInTournamentQuery__
|
||||
@@ -2210,6 +2219,44 @@ export function useGetMakersInTournamentLazyQuery(baseOptions?: Apollo.LazyQuery
|
||||
export type GetMakersInTournamentQueryHookResult = ReturnType<typeof useGetMakersInTournamentQuery>;
|
||||
export type GetMakersInTournamentLazyQueryHookResult = ReturnType<typeof useGetMakersInTournamentLazyQuery>;
|
||||
export type GetMakersInTournamentQueryResult = Apollo.QueryResult<GetMakersInTournamentQuery, GetMakersInTournamentQueryVariables>;
|
||||
export const MeTournamentDocument = gql`
|
||||
query MeTournament {
|
||||
me {
|
||||
id
|
||||
name
|
||||
avatar
|
||||
jobTitle
|
||||
...UserRolesSkills
|
||||
}
|
||||
}
|
||||
${UserRolesSkillsFragmentDoc}`;
|
||||
|
||||
/**
|
||||
* __useMeTournamentQuery__
|
||||
*
|
||||
* To run a query within a React component, call `useMeTournamentQuery` and pass it any options that fit your needs.
|
||||
* When your component renders, `useMeTournamentQuery` returns an object from Apollo Client that contains loading, error, and data properties
|
||||
* you can use to render your UI.
|
||||
*
|
||||
* @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
|
||||
*
|
||||
* @example
|
||||
* const { data, loading, error } = useMeTournamentQuery({
|
||||
* variables: {
|
||||
* },
|
||||
* });
|
||||
*/
|
||||
export function useMeTournamentQuery(baseOptions?: Apollo.QueryHookOptions<MeTournamentQuery, MeTournamentQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useQuery<MeTournamentQuery, MeTournamentQueryVariables>(MeTournamentDocument, options);
|
||||
}
|
||||
export function useMeTournamentLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions<MeTournamentQuery, MeTournamentQueryVariables>) {
|
||||
const options = {...defaultOptions, ...baseOptions}
|
||||
return Apollo.useLazyQuery<MeTournamentQuery, MeTournamentQueryVariables>(MeTournamentDocument, options);
|
||||
}
|
||||
export type MeTournamentQueryHookResult = ReturnType<typeof useMeTournamentQuery>;
|
||||
export type MeTournamentLazyQueryHookResult = ReturnType<typeof useMeTournamentLazyQuery>;
|
||||
export type MeTournamentQueryResult = Apollo.QueryResult<MeTournamentQuery, MeTournamentQueryVariables>;
|
||||
export const GetAllRolesDocument = gql`
|
||||
query GetAllRoles {
|
||||
getAllMakersRoles {
|
||||
|
||||
@@ -36,6 +36,7 @@ import {
|
||||
GetAllRolesQuery,
|
||||
GetMakersInTournamentQuery,
|
||||
GetMakersInTournamentQueryVariables,
|
||||
MeTournamentQuery,
|
||||
} from 'src/graphql'
|
||||
|
||||
const delay = (ms = 1000) => new Promise((res) => setTimeout(res, ms + Math.random() * 1000))
|
||||
@@ -286,12 +287,21 @@ export const handlers = [
|
||||
)
|
||||
}),
|
||||
|
||||
graphql.query<MeTournamentQuery>('MeTournament', async (req, res, ctx) => {
|
||||
await delay()
|
||||
|
||||
return res(
|
||||
ctx.data({
|
||||
me: { ...me() }
|
||||
})
|
||||
)
|
||||
}),
|
||||
|
||||
graphql.query<GetMakersInTournamentQuery, GetMakersInTournamentQueryVariables>('GetMakersInTournament', async (req, res, ctx) => {
|
||||
await delay()
|
||||
|
||||
return res(
|
||||
ctx.data({
|
||||
me: { ...me() },
|
||||
getMakersInTournament: getMakersInTournament(req.variables)
|
||||
})
|
||||
)
|
||||
|
||||
@@ -99,10 +99,13 @@ export function getTournamentById(id: number) {
|
||||
|
||||
export function getMakersInTournament(vars: GetMakersInTournamentQueryVariables) {
|
||||
|
||||
const offsetStart = vars.skip ?? 0;
|
||||
const offsetEnd = offsetStart + (vars.take ?? 15)
|
||||
const take = vars.take ?? 15;
|
||||
const skip = vars.skip ?? 0;
|
||||
|
||||
return MOCK_DATA.users.slice(1)
|
||||
const offsetStart = skip;
|
||||
const offsetEnd = offsetStart + take;
|
||||
|
||||
const allMakers = MOCK_DATA.users.slice(1)
|
||||
.filter(u => {
|
||||
if (!vars.search) return true;
|
||||
return [u.name, u.jobTitle].some(attr => attr?.search(new RegExp(vars.search!, 'i')) !== -1)
|
||||
@@ -111,5 +114,12 @@ export function getMakersInTournament(vars: GetMakersInTournamentQueryVariables)
|
||||
if (!vars.roleId) return true;
|
||||
return u.roles.some(r => r.id === vars.roleId)
|
||||
})
|
||||
.slice(offsetStart, offsetEnd) as User[];
|
||||
.slice(offsetStart, offsetEnd + 1) as User[]
|
||||
;
|
||||
|
||||
return {
|
||||
hasNext: allMakers.length === take + 1,
|
||||
hasPrev: skip !== 0,
|
||||
makers: allMakers.slice(0, take)
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user