diff --git a/components/modal/modalType/TournamentModal.tsx b/components/modal/modalType/TournamentModal.tsx index aea7d3053..3dd47174b 100644 --- a/components/modal/modalType/TournamentModal.tsx +++ b/components/modal/modalType/TournamentModal.tsx @@ -1,5 +1,6 @@ import { useRecoilValue } from 'recoil'; import { modalState } from 'utils/recoil/modal'; +import TournamentManualModal from '../tournament/TournamentManualModal'; import TournamentRegistryModal from '../tournament/TournamentRegistryModal'; export default function TournamentModal() { @@ -9,6 +10,7 @@ export default function TournamentModal() { 'TOURNAMENT-REGISTRY': tournamentInfo ? ( ) : null, + 'TOURNAMENT-MANUAL': , }; if (!modalName) return null; diff --git a/components/modal/tournament/TournamentManualModal.tsx b/components/modal/tournament/TournamentManualModal.tsx new file mode 100644 index 000000000..65fce0f1b --- /dev/null +++ b/components/modal/tournament/TournamentManualModal.tsx @@ -0,0 +1,109 @@ +import { useSetRecoilState } from 'recoil'; +import { modalState } from 'utils/recoil/modal'; +import { + ModalButtonContainer, + ModalButton, +} from 'components/modal/ModalButton'; +import styles from 'styles/modal/match/MatchManualModal.module.scss'; + +type contentType = { + title: React.ReactNode; + description: string[]; +}; + +type contentsType = contentType[]; + +// TODO : 토너먼트 경기에 대한 룰 설명 +const modalContents: contentsType = [ + { + title: , + description: [ + '11점 3판 2선승제', + '경기시간은 슬롯에 표기', + '점수가 10:10 인 경우 듀스', + '듀스인 경우, 2점 차가 나면 경기 종료', + '탁구채를 잡지 않은 손으로 탁구대를 짚으면 실점', + '탁구대 및 네트가 아닌 곳에 공이 맞을 시 실점', + ], + }, + { + title: , + description: [ + '첫 세트만 서브 게임 진행', + '서브 게임 승자부터 세트별 교대로 서브', + '서브는 2점마다 교대하며, 듀스일 때는 1점마다 교대', + '서브 시작 시 상대방에게 신호 (e.g. 서브하겠습니다.)', + '서브 시 공이 네트에 맞고 넘어가면 다시 서브', + ], + }, + { + title: , + description: [ + '경기 종료 후 그 자리에서 세트 점수 입력', + '종료시간에 다음 경기가 있을 시 현재 스코어가 높은 선수가 승리', + '다음 경기가 없을 시 계속 진행 가능', + ], + }, + { + title: , + description: [ + `매치가 시작 되었으나 상대방이 나오지 않는다면 3분이 지날 때 마다 세트 점수 1점씩 획득`, + '6분이 지났을 때도 나오지 않았다면 세트 점수 2:0 승리 처리', + ], + }, +]; + +export default function TournamentManualModal() { + const setModal = useSetRecoilState(modalState); + + return ( +
+
토너먼트 규칙
+
    + {modalContents.map( + ( + item: { + title: React.ReactNode; + description: string[]; + }, + index + ) => ( +
  • + {item.title} +
      + {item.description.map((e, idx) => ( +
    • {e}
    • + ))} +
    +
  • + ) + )} +
+ + setModal({ modalName: null })} + style='positive' + value='확인' + /> + +
+ ); +} + +type contentTitleProps = { + title: string; + icon?: React.ReactNode | string; +}; + +function ContentTitle({ title, icon }: contentTitleProps) { + icon = typeof icon === 'string' ? {icon} : icon; + return ( +
+ {icon ? icon : null} + {title} +
+ ); +} diff --git a/components/tournament/UserTournamentBracket.tsx b/components/tournament/UserTournamentBracket.tsx index 987890569..d3ca1c027 100644 --- a/components/tournament/UserTournamentBracket.tsx +++ b/components/tournament/UserTournamentBracket.tsx @@ -11,11 +11,13 @@ import styles from 'styles/tournament/UserTournamentBracket.module.scss'; interface UserTournamentBracketProps { tournamentId: number | undefined; + state?: string | undefined; queryStaleTime: number; } export default function UserTournamentBraket({ tournamentId, + state, queryStaleTime, }: UserTournamentBracketProps) { const setError = useSetRecoilState(errorState); @@ -31,6 +33,8 @@ export default function UserTournamentBraket({ const { data: bracketMatches = [], isLoading, + isFetching, + refetch, isError, } = useQuery( ['tournamentMatches', tournamentId], @@ -50,10 +54,20 @@ export default function UserTournamentBraket({ {isLoading ? ( ) : ( - + <> + {state === 'LIVE' && ( + + )} + + )} ); diff --git a/pages/tournament.tsx b/pages/tournament.tsx index baab6b862..8804390d7 100644 --- a/pages/tournament.tsx +++ b/pages/tournament.tsx @@ -1,3 +1,5 @@ +import { useSetRecoilState } from 'recoil'; +import { modalState } from 'utils/recoil/modal'; import TournamentCard from 'components/tournament/TournamentCard'; import UserTournamentBraket from 'components/tournament/UserTournamentBracket'; import useBeforeLiveTournamentData from 'hooks/tournament/useBeforeLiveTournamentData'; @@ -5,10 +7,22 @@ import styles from 'styles/tournament/TournamentContainer.module.scss'; export default function Tournament() { const { data, isLoading } = useBeforeLiveTournamentData(); + const setModal = useSetRecoilState(modalState); + + const openManual = () => { + setModal({ modalName: 'TOURNAMENT-MANUAL' }); + }; return (
-

Tournament

+
+

Tournament

+
+ +
+
예정된 토너먼트
{data?.beforeTournament.length === 0 ? (
@@ -29,19 +43,16 @@ export default function Tournament() { 진행중인 토너먼트가 없습니다.
) : ( - data && - data?.liveTournament.length > 0 && ( + data?.liveTournament.map((tournament) => ( <> - + - ) + )) )}
); diff --git a/styles/tournament/TournamentContainer.module.scss b/styles/tournament/TournamentContainer.module.scss index 786bd536a..15fd6cdcc 100644 --- a/styles/tournament/TournamentContainer.module.scss +++ b/styles/tournament/TournamentContainer.module.scss @@ -4,11 +4,15 @@ @include pageWrap; } -.title { - @include pageTitle; - width: fit-content; - padding-bottom: 1rem; - cursor: pointer; +.titleWrap { + display: flex; + justify-content: space-between; + .title { + @include pageTitle; + width: fit-content; + padding-bottom: 1rem; + cursor: pointer; + } } .tournamentCardContainer { @@ -43,3 +47,19 @@ justify-content: center; align-content: center; } +.buttonWrap { + display: flex; + margin-right: 0.6rem; + margin-bottom: 1rem; + align-items: center; + + .manual { + @include match-noti-button; + width: 4.2rem; + height: 1.6rem; + padding: 0; + margin: 0 0.8rem 0 0; + font-size: $small-font; + border-radius: $small-radius; + } +} diff --git a/styles/tournament/UserTournamentBracket.module.scss b/styles/tournament/UserTournamentBracket.module.scss index 1026dd6a6..653efe428 100644 --- a/styles/tournament/UserTournamentBracket.module.scss +++ b/styles/tournament/UserTournamentBracket.module.scss @@ -1,14 +1,48 @@ +@import 'styles/common.scss'; + .bracketContainer { + position: relative; display: flex; width: 100%; height: 30rem; margin-top: 1rem; + margin-bottom: 1.5rem; overflow: hidden; background-color: rgba(112, 0, 225, 0.17); border: 1px solid #c67dff; border-radius: 26px; align-items: center; justify-content: center; + + button { + position: absolute; + top: 1rem; + right: 1rem; + z-index: 100; + display: flex; + width: 1.8rem; + height: 1.8rem; + padding: 0; + background-color: rgba(202, 202, 202, 0.561); + border: none; + border-radius: 50%; + align-items: center; + justify-content: center; + } +} + +@keyframes rotate { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +.refetching { + background-color: rgb(202, 202, 202) !important; + animation: rotate 0.5s linear infinite; } .noTournamentText { diff --git a/types/modalTypes.ts b/types/modalTypes.ts index 4bab821bd..e4a4e45f2 100644 --- a/types/modalTypes.ts +++ b/types/modalTypes.ts @@ -33,7 +33,7 @@ type EditItemModal = 'MEGAPHONE'; type StoreModal = 'MANUAL' | 'COIN_HISTORY'; -type TournamentModal = 'REGISTRY'; +type TournamentModal = 'REGISTRY' | 'MANUAL'; type AdminModal = | 'PROFILE'