diff --git a/components/admin/tournament/TournamentList.tsx b/components/admin/tournament/TournamentList.tsx index 3a602ff2e..0d5d2883e 100644 --- a/components/admin/tournament/TournamentList.tsx +++ b/components/admin/tournament/TournamentList.tsx @@ -169,8 +169,8 @@ export default function TournamentList({ onClick={() => { setModal({ modalName: - 'ADMIN-TOURNAMENT_BRAKET_EDIT', - tournament: tournament, + 'ADMIN-TOURNAMENT_PARTICIPANT_EDIT', + tournamentId: tournament.tournamentId, }); }} > diff --git a/components/admin/tournament/TournamentSearchBarGroup.tsx b/components/admin/tournament/TournamentSearchBarGroup.tsx deleted file mode 100644 index 420f0f409..000000000 --- a/components/admin/tournament/TournamentSearchBarGroup.tsx +++ /dev/null @@ -1,98 +0,0 @@ -import React, { useState, useEffect, useRef } from 'react'; -import { useSetRecoilState } from 'recoil'; -import CancelOutlinedIcon from '@mui/icons-material/CancelOutlined'; -import CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutline'; -import { Input, InputAdornment, Button, Menu, MenuItem } from '@mui/material'; -import { instance } from 'utils/axios'; -import { errorState } from 'utils/recoil/error'; - -export default function TournamentSearchBarGroup() { - const [inputId, setInputId] = useState(''); - const [isIdExist, setIsIdExist] = useState(true); - const [isTyping, setIsTyping] = useState(false); - const [userList, setUserList] = useState([]); - const [menuOpen, setMenuOpen] = useState(false); - const inputRef = useRef(null); - const setError = useSetRecoilState(errorState); - - const inputChangeHandler = (event: React.ChangeEvent) => { - setInputId(event.target.value); - setIsTyping(true); - setIsIdExist(false); - }; - - const handleMenuClose = () => { - setMenuOpen(false); - }; - - const handleMenuItemClick = (id: string) => { - setInputId(id); - setIsIdExist(true); - handleMenuClose(); - }; - - useEffect(() => { - if (inputId === '' || isIdExist == true) { - return; - } - const identifier = setTimeout(async () => { - setIsTyping(false); - try { - const res = await instance.get( - `/pingpong/users/searches?intraId=${inputId}` - ); - const users = res.data.users; - setUserList(users); - if (users.length > 0 && users[0] !== inputId) { - setMenuOpen(true); - } - setIsIdExist(users.length === 1 && users[0] === inputId); - } catch (e) { - setError('JC02'); - } - }, 500); - return () => clearTimeout(identifier); - }, [inputId]); - - return ( - <> - - {isIdExist ? ( - - ) : ( - - )} - - ) - } - /> - - {userList.map((id) => ( - handleMenuItemClick(id)}> - {id} - - ))} - - {' '} - esc: 포커스 탈출{' '} - - - - - ); -} diff --git a/components/modal/admin/AdminTournamentParticipantEditModal.tsx b/components/modal/admin/AdminTournamentParticipantEditModal.tsx deleted file mode 100644 index 381bfd7f0..000000000 --- a/components/modal/admin/AdminTournamentParticipantEditModal.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { useEffect, useState } from 'react'; -import { useSetRecoilState } from 'recoil'; -import { Button } from '@mui/material'; -import { instanceInManage } from 'utils/axios'; -import { modalState } from 'utils/recoil/modal'; -import { toastState } from 'utils/recoil/toast'; -import TournamentSearchBarGroup from 'components/admin/tournament/TournamentSearchBarGroup'; -import styles from 'styles/admin/modal/AdminTournamentParticipantEditModal.module.scss'; - -export default function AdminTournamentParticipantEditModal(props: { - tournamentId: number; -}) { - const MAX_LENGTH = 30; - const setModal = useSetRecoilState(modalState); - const setSnackBar = useSetRecoilState(toastState); - - return ( -
-

참가자 수정

-
- -
    -
  • - jincpark - -
  • -
-
- -
-
- ); -} diff --git a/components/modal/admin/AdminTournamentParticipantEditModal/AdminSearchUserDropDownMenu.tsx b/components/modal/admin/AdminTournamentParticipantEditModal/AdminSearchUserDropDownMenu.tsx new file mode 100644 index 000000000..046561def --- /dev/null +++ b/components/modal/admin/AdminTournamentParticipantEditModal/AdminSearchUserDropDownMenu.tsx @@ -0,0 +1,37 @@ +import { Menu, MenuItem } from '@mui/material'; + +interface AdminSearchUserDropDownMenuProps { + inputRef: HTMLInputElement | null; + menuOpen: boolean; + onMenuClose: () => void; + userList: string[]; + onMenuClick: (id: string) => void; +} + +export default function AdminSearchUserDropDownMenu({ + inputRef, + menuOpen, + onMenuClose, + userList, + onMenuClick, +}: AdminSearchUserDropDownMenuProps) { + return ( + + {userList.map((id) => ( + onMenuClick(id)}> + {id} + + ))} + + {' '} + esc: 포커스 탈출{' '} + + + ); +} diff --git a/components/modal/admin/AdminTournamentParticipantEditModal/AdminTournamenSearchBar.tsx b/components/modal/admin/AdminTournamentParticipantEditModal/AdminTournamenSearchBar.tsx new file mode 100644 index 000000000..44f9f0dcb --- /dev/null +++ b/components/modal/admin/AdminTournamentParticipantEditModal/AdminTournamenSearchBar.tsx @@ -0,0 +1,42 @@ +import { RefObject } from 'react'; +import CancelOutlinedIcon from '@mui/icons-material/CancelOutlined'; +import CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutline'; +import { Input, InputAdornment } from '@mui/material'; + +interface AdminTournamenSearchBarProps { + inputRef: RefObject; + inputChangeHandler: (event: React.ChangeEvent) => void; + isIdExist: boolean; + inputId: string; + isTyping: boolean; +} + +export default function AdminTournamentSearchBar({ + inputRef, + inputChangeHandler, + isIdExist, + inputId, + isTyping, +}: AdminTournamenSearchBarProps) { + return ( + + {isIdExist ? ( + + ) : ( + + )} + + ) + } + /> + ); +} diff --git a/components/modal/admin/AdminTournamentParticipantEditModal/AdminTournamentParticipantEditModal.tsx b/components/modal/admin/AdminTournamentParticipantEditModal/AdminTournamentParticipantEditModal.tsx new file mode 100644 index 000000000..8e168491d --- /dev/null +++ b/components/modal/admin/AdminTournamentParticipantEditModal/AdminTournamentParticipantEditModal.tsx @@ -0,0 +1,40 @@ +import { useSetRecoilState } from 'recoil'; +import { Button } from '@mui/material'; +import { modalState } from 'utils/recoil/modal'; +import AdminTournamentSearchBarGroup from 'components/modal/admin/AdminTournamentParticipantEditModal/AdminTournamentSearchBarGroup'; +import useAdminTournamentParticipantEdit from 'hooks/admin/modal/useAdminTournamentParticipantEdit'; +import styles from 'styles/admin/modal/AdminTournamentParticipantEditModal.module.scss'; +import AdminTournamentParticipantList from './AdminTournamentParticipantList'; + +export default function AdminTournamentParticipantEditModal(props: { + tournamentId: number; +}) { + const setModal = useSetRecoilState(modalState); + const { setUserToAdd, participantList, participantDeleteHandler } = + useAdminTournamentParticipantEdit(props.tournamentId); + + return ( +
+

참가자 수정

+
+
+ +
+ +
+ +
+
+ ); +} diff --git a/components/modal/admin/AdminTournamentParticipantEditModal/AdminTournamentParticipantList.tsx b/components/modal/admin/AdminTournamentParticipantEditModal/AdminTournamentParticipantList.tsx new file mode 100644 index 000000000..c606eac08 --- /dev/null +++ b/components/modal/admin/AdminTournamentParticipantEditModal/AdminTournamentParticipantList.tsx @@ -0,0 +1,89 @@ +import { useState } from 'react'; +import { Button, TextField, InputAdornment } from '@mui/material'; +import { ITournamentUser } from 'types/admin/adminTournamentTypes'; +import styles from 'styles/admin/modal/AdminTournamentParticipantEditModal.module.scss'; +import AdminTournamentParticipantDeleteConfirmInput from './AdminTournemntParticipantDeleteConfirmInput'; + +interface AdminTournamentParticipantListProps { + participantList: ITournamentUser[]; + onDelete: (intraId: string) => Promise; +} + +export default function AdminTournamentParticipantList({ + participantList, + onDelete, +}: AdminTournamentParticipantListProps) { + const [deleteMode, setDeleteMode] = useState>({}); + const [isSame, setIsSame] = useState>({}); + + function toggleDeleteMode(userId: number) { + setDeleteMode((prev) => ({ [userId]: !prev[userId] })); + } + + function deleteHandler(userId: number, intraId: string) { + if (deleteMode[userId]) { + if (isSame[userId]) { + onDelete(intraId); + setDeleteMode({}); + } + } else { + toggleDeleteMode(userId); + } + } + + return ( +
    + {participantList.map((participant, index) => ( + <> + {deleteMode[participant.userId] && ( + + 삭제할 유저의 아이디를 입력해주세요. + + )} +
  • + {deleteMode[participant.userId] ? ( + <> + + + ) : ( +
    + {index + 1} + {participant.intraId}{' '} + {participant.isJoined ? '참가 중' : '대기 중'} +
    + )} + {deleteMode[participant.userId] && ( + + )} + +
  • + {deleteMode[participant.userId] && !isSame[participant.userId] && ( + + 아이디가 일치하지 않습니다! + + )} + + ))} +
+ ); +} diff --git a/components/modal/admin/AdminTournamentParticipantEditModal/AdminTournamentSearchBarGroup.tsx b/components/modal/admin/AdminTournamentParticipantEditModal/AdminTournamentSearchBarGroup.tsx new file mode 100644 index 000000000..e0f5523d6 --- /dev/null +++ b/components/modal/admin/AdminTournamentParticipantEditModal/AdminTournamentSearchBarGroup.tsx @@ -0,0 +1,88 @@ +import React, { useRef } from 'react'; +import { useSetRecoilState } from 'recoil'; +import { Button } from '@mui/material'; +import { ITournamentUser } from 'types/admin/adminTournamentTypes'; +import { mockInstance } from 'utils/mockAxios'; +import { toastState } from 'utils/recoil/toast'; +import useAdminSearchUser from 'hooks/admin/modal/useAdminSearchUser'; +import AdminSearchUserDropDownMenu from './AdminSearchUserDropDownMenu'; +import AdminTournamentSearchBar from './AdminTournamenSearchBar'; + +interface AdminTournamentSearchBarGroupProps { + onAddUser: React.Dispatch>; + tournamentId: number; +} + +export default function AdminTournamentSearchBarGroup({ + onAddUser, + tournamentId, +}: AdminTournamentSearchBarGroupProps) { + const { + inputId, + isIdExist, + isTyping, + menuOpen, + setMenuOpen, + userList, + isWaitingResponse, + setIsWaitingResponse, + inputChangeHandler, + handleMenuItemClick, + } = useAdminSearchUser(); + + const inputRef = useRef(null); + const setSnackBar = useSetRecoilState(toastState); + + async function addButtonHandler() { + if (isIdExist) { + setIsWaitingResponse(true); + try { + const res: { data: ITournamentUser } = await mockInstance.post( + `/admin/tournaments/${tournamentId}/users`, + { intraId: inputId } + ); + onAddUser(res.data); + setSnackBar({ + toastName: 'tournament user add noti', + severity: 'success', + message: '유저를 성공적으로 추가하였습니다!', + clicked: true, + }); + } catch (error: any) { + setSnackBar({ + toastName: 'tournament user add noti', + severity: 'error', + message: `🔥 ${error.response.data.message} 🔥`, + clicked: true, + }); + } + setIsWaitingResponse(false); + } + } + + return ( + <> + + setMenuOpen(false)} + userList={userList} + onMenuClick={handleMenuItemClick} + /> + + + ); +} diff --git a/components/modal/admin/AdminTournamentParticipantEditModal/AdminTournemntParticipantDeleteConfirmInput.tsx b/components/modal/admin/AdminTournamentParticipantEditModal/AdminTournemntParticipantDeleteConfirmInput.tsx new file mode 100644 index 000000000..90850a6a8 --- /dev/null +++ b/components/modal/admin/AdminTournamentParticipantEditModal/AdminTournemntParticipantDeleteConfirmInput.tsx @@ -0,0 +1,42 @@ +import { Dispatch, SetStateAction } from 'react'; +import { TextField, InputAdornment } from '@mui/material'; + +interface AdminTournamentParticipantDeleteConfirmInputProps { + isSame: boolean; + intraId: string; + userId: number; + setIsSame: Dispatch>>; +} + +export default function AdminTournamentParticipantDeleteConfirmInput({ + isSame, + intraId, + userId, + setIsSame, +}: AdminTournamentParticipantDeleteConfirmInputProps) { + function inputChangeHandler( + event: React.ChangeEvent + ) { + if (event.target.value === intraId) { + setIsSame({ [userId]: true }); + } else { + setIsSame({ [userId]: false }); + } + } + + return ( + {intraId} + ), + }} + onChange={inputChangeHandler} + /> + ); +} diff --git a/components/modal/modalType/AdminModal.tsx b/components/modal/modalType/AdminModal.tsx index f786f21da..a819a2e07 100644 --- a/components/modal/modalType/AdminModal.tsx +++ b/components/modal/modalType/AdminModal.tsx @@ -16,7 +16,7 @@ import DeletePenaltyModal from 'components/modal/admin/DeletePenaltyModal'; import DetailModal from 'components/modal/admin/DetailModal'; import AdminSeasonEdit from 'components/modal/admin/SeasonEdit'; import AdminEditTournamentBraket from '../admin/AdminEditTournamentBraket'; -import AdminTournamentParticipantEditModal from '../admin/AdminTournamentParticipantEditModal'; +import AdminTournamentParticipantEditModal from '../admin/AdminTournamentParticipantEditModal/AdminTournamentParticipantEditModal'; export default function AdminModal() { const { diff --git a/components/tournament-record/UserTournamentBracket.tsx b/components/tournament-record/UserTournamentBracket.tsx index b7f3d5095..e15693bb2 100644 --- a/components/tournament-record/UserTournamentBracket.tsx +++ b/components/tournament-record/UserTournamentBracket.tsx @@ -1,8 +1,9 @@ import { Match } from '@g-loot/react-tournament-brackets/dist/src/types'; -import { useCallback, useEffect, useState, useRef } from 'react'; -import { TournamentGame } from 'types/tournamentTypes'; +import { useQuery } from 'react-query'; +import { useSetRecoilState } from 'recoil'; import { convertTournamentGamesToBracketMatchs } from 'utils/handleTournamentGame'; import { mockInstance } from 'utils/mockAxios'; +import { errorState } from 'utils/recoil/error'; import TournamentBraket from 'components/tournament/TournamentBraket'; import useComponentSize from 'hooks/util/useComponentSize'; import styles from 'styles/tournament-record/UserTournamentBracket.module.scss'; @@ -14,34 +15,30 @@ interface UserTournamentBracketProps { export default function UserTournamentBraket({ tournamentId, }: UserTournamentBracketProps) { + const setError = useSetRecoilState(errorState); const [ref, size] = useComponentSize(); - const [bracketMatchs, setBracketMatchs] = useState([]); - const [isLoading, setIsLoading] = useState(false); + const fetchTournamentGames = async () => { + const res = await mockInstance.get(`/tournament/${tournamentId}/games`); + return convertTournamentGamesToBracketMatchs(res.data.games); + }; - const fetchTournamentGames = useCallback(async () => { - console.log('Fetching more data...'); - try { - const res = await mockInstance.get(`/tournament/${tournamentId}/games`); - setIsLoading(false); - const data: TournamentGame[] = res.data.games; - const bracketMatchs = convertTournamentGamesToBracketMatchs(data); - setBracketMatchs(bracketMatchs); - return data; - } catch (error) { - setIsLoading(false); - console.error('Error fetching data:', error); + const { + data: bracketMatches = [], + isLoading, + isError, + } = useQuery( + ['tournamentMatches', tournamentId], + () => fetchTournamentGames(), + { + enabled: !!tournamentId, // tournamentId가 undefined가 아닐 때만 작동하도록 + staleTime: 86400000, // 하루 } - }, [tournamentId]); + ); - useEffect(() => { - setIsLoading(true); - const identifier = setTimeout(() => { - console.log('fetching tournament game data..'); - fetchTournamentGames(); - }, 500); - return () => clearTimeout(identifier); - }, [tournamentId, fetchTournamentGames]); + if (isError) { + setError('JC03'); + } return (
@@ -49,7 +46,7 @@ export default function UserTournamentBraket({
) : ( )} diff --git a/hooks/admin/modal/useAdminSearchUser.ts b/hooks/admin/modal/useAdminSearchUser.ts new file mode 100644 index 000000000..092be6a10 --- /dev/null +++ b/hooks/admin/modal/useAdminSearchUser.ts @@ -0,0 +1,67 @@ +import { useState, useEffect, useCallback } from 'react'; +import { useSetRecoilState } from 'recoil'; +import { instance } from 'utils/axios'; +import { errorState } from 'utils/recoil/error'; + +export default function useAdminSearchUser() { + const [inputId, setInputId] = useState(''); + const [isIdExist, setIsIdExist] = useState(false); + const [isTyping, setIsTyping] = useState(false); + const [menuOpen, setMenuOpen] = useState(false); + const [userList, setUserList] = useState([]); + const [isWaitingResponse, setIsWaitingResponse] = useState(false); + const setError = useSetRecoilState(errorState); + + const inputChangeHandler = (event: React.ChangeEvent) => { + setInputId(event.target.value); + setIsTyping(true); + setIsIdExist(false); + }; + + const handleMenuItemClick = (id: string) => { + setInputId(id); + setIsIdExist(true); + setMenuOpen(false); + }; + + const fetchUserIntraId = useCallback(async () => { + try { + const res = await instance.get( + `/pingpong/users/searches?intraId=${inputId}` + ); + const users = res.data.users; + setUserList(users); + + if (users.length > 0 && users[0] !== inputId) { + setMenuOpen(true); + } + setIsIdExist(users.length === 1 && users[0] === inputId); + } catch (e) { + setError('JC02'); + } + }, [inputId, setError]); + + useEffect(() => { + if (inputId === '' || isIdExist) { + return; + } + const identifier = setTimeout(() => { + setIsTyping(false); + fetchUserIntraId(); + }, 500); + return () => clearTimeout(identifier); + }, [inputId, isIdExist, fetchUserIntraId]); + + return { + inputId, + isIdExist, + isTyping, + menuOpen, + setMenuOpen, + userList, + isWaitingResponse, + setIsWaitingResponse, + inputChangeHandler, + handleMenuItemClick, + }; +} diff --git a/hooks/admin/modal/useAdminTournamentParticipantEdit.ts b/hooks/admin/modal/useAdminTournamentParticipantEdit.ts new file mode 100644 index 000000000..5d227258a --- /dev/null +++ b/hooks/admin/modal/useAdminTournamentParticipantEdit.ts @@ -0,0 +1,68 @@ +import { useState, useCallback, useEffect } from 'react'; +import { useSetRecoilState } from 'recoil'; +import { ITournamentUser } from 'types/admin/adminTournamentTypes'; +import { mockInstance } from 'utils/mockAxios'; +import { toastState } from 'utils/recoil/toast'; + +export default function useAdminTournamentParticipantEdit( + tournamentId: number +) { + const setSnackBar = useSetRecoilState(toastState); + const [userToAdd, setUserToAdd] = useState({ + userId: 0, + intraId: '', + isJoined: false, + }); + const [participantList, setParticipantList] = useState([ + { userId: 0, intraId: '', isJoined: false }, + ]); + + const fetchParticipantList = useCallback(async () => { + try { + const res: { data: { users: ITournamentUser[] } } = + await mockInstance.get(`/admin/tournaments/${tournamentId}/users`); + setParticipantList(res.data.users); + } catch (error: any) { + setSnackBar({ + toastName: 'tournament user fetch noti', + severity: 'error', + message: `🔥 ${error.response.data.message} 🔥`, + clicked: true, + }); + } + }, [tournamentId, setSnackBar]); + + async function participantDeleteHandler(intraId: string) { + try { + await mockInstance.delete( + `/admin/tournaments/${tournamentId}/users/${intraId}` + ); + fetchParticipantList(); + setSnackBar({ + toastName: 'tournament user delete noti', + severity: 'success', + message: '유저를 성공적으로 삭제하였습니다!', + clicked: true, + }); + } catch (error: any) { + setSnackBar({ + toastName: 'tournament user delete noti', + severity: 'error', + message: `🔥 ${error.response.data.message} 🔥`, + clicked: true, + }); + } + } + + useEffect(() => { + fetchParticipantList(); + }, [fetchParticipantList]); + + useEffect(() => { + if (userToAdd.intraId !== '') { + setParticipantList((participantList) => [...participantList, userToAdd]); + } + }, [userToAdd]); + + return { setUserToAdd, participantList, participantDeleteHandler }; +} diff --git a/pages/api/pingpong/admin/tournament/[tournamentId]/users/[userId].ts b/pages/api/pingpong/admin/tournaments/[tournamentId]/users/[userId].ts similarity index 74% rename from pages/api/pingpong/admin/tournament/[tournamentId]/users/[userId].ts rename to pages/api/pingpong/admin/tournaments/[tournamentId]/users/[userId].ts index f89281499..c424afd4a 100644 --- a/pages/api/pingpong/admin/tournament/[tournamentId]/users/[userId].ts +++ b/pages/api/pingpong/admin/tournaments/[tournamentId]/users/[userId].ts @@ -5,12 +5,8 @@ export default function handler(req: NextApiRequest, res: NextApiResponse) { res.status(405).end('허용되지 않는 메소드입니다.'); return; } - const { intraId } = req.query; - if ( - intraId !== 'jincpark' && - intraId !== 'junhjeon' && - intraId !== 'jaehyuki' - ) { + const { userId } = req.query; + if (userId !== 'jincpark' && userId !== 'junhjeon' && userId !== 'jaehyuki') { res.status(404).end('존재하지 않는 유저입니다.'); } res.status(204).end('명단에서 유저 삭제 성공'); diff --git a/pages/api/pingpong/admin/tournament/[tournamentId]/users/index.ts b/pages/api/pingpong/admin/tournaments/[tournamentId]/users/index.ts similarity index 100% rename from pages/api/pingpong/admin/tournament/[tournamentId]/users/index.ts rename to pages/api/pingpong/admin/tournaments/[tournamentId]/users/index.ts diff --git a/styles/Layout/MegaPhone.module.scss b/styles/Layout/MegaPhone.module.scss index d8d143d79..805bbd787 100644 --- a/styles/Layout/MegaPhone.module.scss +++ b/styles/Layout/MegaPhone.module.scss @@ -1,12 +1,14 @@ +$bannerHeight: 3rem; + .rollingBanner { position: relative; display: flex; width: 100%; max-width: 28rem; - height: 2.8rem; + height: $bannerHeight; overflow: hidden; - background-color: rgba(0, 0, 0, 0.7); - border-radius: 10px; + background-color: rgba(0, 0, 0, 0.5); + border-radius: 0.7rem; justify-content: center; align-items: center; } @@ -19,11 +21,10 @@ .contentWrapper { display: flex; - height: 2.8rem; + height: $bannerHeight; flex-direction: column; - color: rgba(255, 255, 0, 0.8); + color: rgb(255, 255, 0); text-shadow: 0px 0px 5px rgba(255, 255, 255, 0.8); - background-color: rgba(0, 0, 0, 0.7); justify-content: flex-start; align-items: center; } diff --git a/styles/admin/modal/AdminTournamentParticipantEditModal.module.scss b/styles/admin/modal/AdminTournamentParticipantEditModal.module.scss index 2c14f0754..d68ae9c55 100644 --- a/styles/admin/modal/AdminTournamentParticipantEditModal.module.scss +++ b/styles/admin/modal/AdminTournamentParticipantEditModal.module.scss @@ -1,10 +1,44 @@ @import 'styles/admin/common.scss'; +$joinedColor: rgb(255, 86, 86); +$notJoinedColor: grey; + .whole { @include admin-modal; width: 25rem; height: 30rem; + overflow-y: auto; + color: black; + + .deleteInputTitle { + font-size: 0.7rem; + color: grey; + } + + .deleteInputWarning { + font-size: 0.7rem; + color: red; + } + + .titleContainer { + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; + } + + .stickyHeader { + position: sticky; + top: 0; + z-index: 1; + display: flex; + padding: 0.5rem; + background-color: white; + border: 1px solid grey; + border-radius: 1rem; + justify-content: space-evenly; + } h2 { font-weight: 400; @@ -21,12 +55,38 @@ li { display: flex; + padding-left: 0.5rem; + margin: 0.1rem 0; justify-content: space-between; align-items: center; + border-radius: 0.3rem; + } + + i { + margin-right: 0.7rem; + font-size: 0.8rem; + color: grey; + } + + .joined { + small { + font-size: 0.7rem; + color: $joinedColor; + } + } + + .notJoined { + small { + font-size: 0.7rem; + color: $notJoinedColor; + } } .buttonGroup { + position: sticky; + bottom: 0; display: flex; flex-direction: column; + background-color: white; } } diff --git a/types/admin/adminTournamentTypes.ts b/types/admin/adminTournamentTypes.ts index c0340043f..26465704a 100644 --- a/types/admin/adminTournamentTypes.ts +++ b/types/admin/adminTournamentTypes.ts @@ -21,6 +21,7 @@ export interface ITournamentUser { userId: number; intraId: string; isJoined: boolean; + resisteredDate?: Date; } export interface ITournamentEditInfo {