-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch '67-대회-생성-페이지-구현' of https://github.com/boostcampwm2023/…
…web12-algo-with-me into 67-대회-생성-페이지-구현
- Loading branch information
Showing
12 changed files
with
369 additions
and
42 deletions.
There are no files selected for viewing
11 changes: 11 additions & 0 deletions
11
frontend/src/components/Main/Buttons/GoToCreateCompetitionLink.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import { Link } from 'react-router-dom'; | ||
|
||
export default function GoToCreateCompetitionLink() { | ||
// TODO: 로그인 여부에 따른 페이지 이동 설정 | ||
|
||
return ( | ||
<Link to="/contest/create"> | ||
<button>대회 생성</button> | ||
</Link> | ||
); | ||
} |
4 changes: 4 additions & 0 deletions
4
frontend/src/components/Main/Buttons/JoinCompetitionButton.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
export default function JoinCompetitionButton() { | ||
// TODO: 대회에 참여하는 로직 작성 / 참여하기 활성화 로직 작성 | ||
return <button>참여하기</button>; | ||
} |
10 changes: 10 additions & 0 deletions
10
frontend/src/components/Main/Buttons/ViewDashboardButton.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import { Link } from 'react-router-dom'; | ||
|
||
export default function ViewDashboardButton(props: { id: number }) { | ||
const dashboardLink = `/contest/dashboard/${props.id}`; | ||
return ( | ||
<Link to={dashboardLink}> | ||
<button>대시보드 보기</button> | ||
</Link> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
import { useEffect, useState } from 'react'; | ||
import { Link } from 'react-router-dom'; | ||
|
||
import JoinCompetitionButton from '@/components/Main/Buttons/JoinCompetitionButton'; | ||
import ViewDashboardButton from '@/components/Main/Buttons/ViewDashboardButton'; | ||
import secToTime from '@/utils/secToTime'; | ||
const generateMockData = () => { | ||
// API배포가 완료되면 삭제 에정 | ||
return [ | ||
{ | ||
id: 1, | ||
name: '테스트 대회 이름', | ||
detail: '테스트 대회 설명', | ||
maxParticipants: 70, | ||
startsAt: '2023-11-14T08:35:24.358Z', | ||
endsAt: '2023-11-20T12:13:04.005Z', | ||
createdAt: '2023-11-14T08:35:24.358Z', | ||
updatedAt: '2023-11-21T02:28:43.955Z', | ||
}, | ||
{ | ||
id: 2, | ||
name: 'ICPC 서울', | ||
detail: '이거슨 아이씨피씨입니다', | ||
maxParticipants: 1000, | ||
startsAt: '2023-11-21T07:10:44.456Z', | ||
endsAt: '2023-11-21T10:10:44.456Z', | ||
createdAt: '2023-11-21T07:50:58.686Z', | ||
updatedAt: '2023-11-21T07:50:58.686Z', | ||
}, | ||
{ | ||
id: 3, | ||
name: '천하제일코딩대회', | ||
detail: '^오^', | ||
maxParticipants: 10, | ||
startsAt: '2023-11-21T07:10:44.456Z', | ||
endsAt: '2023-11-21T10:10:44.456Z', | ||
createdAt: '2023-11-21T07:57:07.563Z', | ||
updatedAt: '2023-11-21T07:57:07.563Z', | ||
}, | ||
{ | ||
id: 4, | ||
name: 'fe테스트대회', | ||
detail: '가나다라마바사', | ||
maxParticipants: 3, | ||
startsAt: '2023-11-22T01:20:00.000Z', | ||
endsAt: '2023-11-23T01:20:00.000Z', | ||
createdAt: '2023-11-22T10:22:03.723Z', | ||
updatedAt: '2023-11-22T10:22:03.723Z', | ||
}, | ||
{ | ||
id: 5, | ||
name: '가나다라', | ||
detail: '마바사아자차카타파하', | ||
maxParticipants: 3, | ||
startsAt: '2023-11-23T03:00:00.000Z', | ||
endsAt: '2023-11-23T04:00:00.000Z', | ||
createdAt: '2023-11-22T12:00:46.942Z', | ||
updatedAt: '2023-11-22T12:00:46.942Z', | ||
}, | ||
]; | ||
}; | ||
|
||
interface Competition { | ||
id: number; | ||
name: string; | ||
startsAt: string; | ||
endsAt: string; | ||
maxParticipants: number; | ||
} | ||
|
||
const getCompetitionDetailURL = (competitionId: number) => `/contest/detail/${competitionId}`; | ||
|
||
function formatTimeRemaining(startsAt: string, endsAt: string): string { | ||
const now = new Date(); | ||
const startDate = new Date(startsAt); | ||
const endDate = new Date(endsAt); | ||
|
||
if (endDate.getTime() < now.getTime()) { | ||
return '종료'; | ||
} else if (startDate.getTime() > now.getTime()) { | ||
const timeDiff = startDate.getTime() - now.getTime(); | ||
const { days, hours, minutes, seconds } = secToTime(timeDiff); | ||
|
||
return `시작까지 ${days}일 ${hours}:${minutes}:${seconds}`; | ||
} else { | ||
return '진행중'; | ||
} | ||
} | ||
|
||
export default function CompetitionList() { | ||
const [competitions, setCompetitions] = useState<Competition[]>([]); | ||
|
||
useEffect(() => { | ||
// 실제 API 요청 대신 목업 데이터 사용 -> TODO: API배포가 완료되면 API처리하는 코드로 바꿔야함 | ||
const mockData = generateMockData(); | ||
setCompetitions(mockData); | ||
}, []); | ||
|
||
// TODO: 대회 시작 전에 들어와서 대회가 시작된 뒤에 참여 버튼을 누르면 서버에서 거절하고 화면에 alert을 띄우고 새로고침 | ||
return ( | ||
<div> | ||
<table> | ||
<thead> | ||
<tr> | ||
<th>대회 이름</th> | ||
<th>시작 시간</th> | ||
<th>종료 시간</th> | ||
<th>상태</th> | ||
<th>참여</th> | ||
<th>대시보드</th> | ||
</tr> | ||
</thead> | ||
<tbody> | ||
{competitions.map((competition) => ( | ||
<tr key={competition.id}> | ||
<td> | ||
<Link to={getCompetitionDetailURL(competition.id)}>{competition.name}</Link> | ||
</td> | ||
<td>{new Date(competition.startsAt).toLocaleString()}</td> | ||
<td>{new Date(competition.endsAt).toLocaleString()}</td> | ||
<td>{formatTimeRemaining(competition.startsAt, competition.endsAt)}</td> | ||
<td> | ||
{competition.startsAt > new Date().toISOString() && <JoinCompetitionButton />} | ||
</td> | ||
<td> | ||
<ViewDashboardButton id={competition.id} /> | ||
</td> | ||
</tr> | ||
))} | ||
</tbody> | ||
</table> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
export const SITE = { | ||
NAME: 'Alog With Me', | ||
PAGE_DESCRIPTION: '천하제일코딩테스트', | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import { useEffect, useState } from 'react'; | ||
|
||
import axios from 'axios'; | ||
|
||
interface Competition { | ||
id: number; | ||
name: string; | ||
detail: string; | ||
maxParticipants: number; | ||
startsAt: string; | ||
endsAt: string; | ||
createdAt: string; | ||
updatedAt: string; | ||
} | ||
|
||
const notFoundCompetition: Competition = { | ||
id: 0, | ||
name: 'Competition Not Found', | ||
detail: 'Competition Not Found', | ||
maxParticipants: 0, | ||
startsAt: 'Competition Not Found', | ||
endsAt: 'Competition Not Found', | ||
createdAt: 'Competition Not Found', | ||
updatedAt: 'Competition Not Found', | ||
}; | ||
|
||
export const useCompetition = (competitionId: number) => { | ||
const problems = [1, 2, 3]; // TODO: 대회에 해당하는 문제의 id를 유동적으로 채워넣을 수 있게 수정해야함 | ||
const [competition, setCompetition] = useState<Competition>(notFoundCompetition); | ||
|
||
useEffect(() => { | ||
axios | ||
.get(`http://101.101.208.240:3000/competitions/${competitionId}`) | ||
.then((response) => { | ||
setCompetition(response.data); | ||
}) | ||
.catch((error) => { | ||
console.error('Error fetching competition data:', error); | ||
}); | ||
}, [competitionId]); | ||
|
||
return { | ||
problems, | ||
competition, | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import { useEffect, useState } from 'react'; | ||
|
||
import axios from 'axios'; | ||
|
||
interface CompetitionProblem { | ||
id: number; | ||
title: string; | ||
timeLimit: number; | ||
memoryLimit: number; | ||
content: string; | ||
createdAt: string; | ||
solutionCode: string; | ||
testcases: string; | ||
} | ||
|
||
const notFoundProblem: CompetitionProblem = { | ||
id: 0, | ||
title: 'Problem Not Found', | ||
timeLimit: 0, | ||
memoryLimit: 0, | ||
content: 'The requested problem could not be found.', | ||
solutionCode: '', | ||
testcases: '', | ||
createdAt: new Date().toISOString(), | ||
}; | ||
|
||
export const useProblem = (problemId: number) => { | ||
const [problem, setProblem] = useState<CompetitionProblem>(notFoundProblem); | ||
|
||
const fetchProblem = async (id: number) => { | ||
try { | ||
const response = await axios.get(`http://101.101.208.240:3000/competitions/problems/${id}`); | ||
const fetchedProblem = response.data; | ||
setProblem(fetchedProblem || notFoundProblem); | ||
} catch (error) { | ||
console.error('Error fetching problem data:', error); | ||
} | ||
}; | ||
|
||
useEffect(() => { | ||
fetchProblem(problemId); | ||
}, [problemId]); | ||
|
||
return { | ||
problem, | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { css } from '@style/css'; | ||
|
||
import GoToCreateCompetitionLink from '@/components/Main/Buttons/GoToCreateCompetitionLink'; | ||
import CompetitionList from '@/components/Main/CompetitionList'; | ||
import { SITE } from '@/constants'; | ||
|
||
function MainPage() { | ||
return ( | ||
<main className={style}> | ||
<span className={ProjectNameStyle}>{SITE.NAME} </span> | ||
<span>{SITE.PAGE_DESCRIPTION} </span> | ||
<GoToCreateCompetitionLink /> | ||
<CompetitionList /> | ||
</main> | ||
); | ||
} | ||
|
||
export default MainPage; | ||
|
||
const ProjectNameStyle = css({ | ||
fontSize: '70px', | ||
}); | ||
|
||
const style = css({ | ||
display: 'grid', | ||
placeItems: 'center', | ||
height: '500px', | ||
}); |
Oops, something went wrong.