Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#138] 대회 세부정보 페이지 생성 #149

Merged
merged 17 commits into from
Nov 29, 2023
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
de2b449
feat: 대회 세부정보 페이지 작성
dmdmdkdkr Nov 28, 2023
9dcc76b
feat: 대회 세부정보 페이지 작성
dmdmdkdkr Nov 28, 2023
d53e611
refactor: 작성된 모듈을 사용하도록 수정, list대신 table을 사용하도록 수정
dmdmdkdkr Nov 28, 2023
09e7a43
feat: 대회 입장 버튼 컴포넌트 작성
dmdmdkdkr Nov 28, 2023
f482971
feat: 시간에 따른 대회 세부정보 페이지 컴포넌트로 분리 및 구분 로직 페이지컴포넌트에 작성
dmdmdkdkr Nov 28, 2023
df0c94d
refactor: 시간에 따른 컴포넌트 재분리
dmdmdkdkr Nov 28, 2023
826df7b
refactor: 대회 세부정보 페이지에서 시간에 따른 페이지 분류 코드 컴포넌트화, 중복되는 코드 컴포넌트화
dmdmdkdkr Nov 28, 2023
fd3cbfc
Merge branch 'fe-dev' of https://github.com/boostcampwm2023/web12-alg…
dmdmdkdkr Nov 28, 2023
16e8c11
refactor: 사용되지 않는 css 코드 삭제
dmdmdkdkr Nov 28, 2023
1e0f8b7
refactor: 컴포넌트명 통일화
dmdmdkdkr Nov 28, 2023
f8439ef
update: 끝난 대회는 입장 못 하게 하는 로직 추가작성
dmdmdkdkr Nov 28, 2023
15235b8
feat: 한국 시간 기준 24시 표기법으로 날짜를 반환하는 함수 작성
dmdmdkdkr Nov 28, 2023
3c7f46f
refactor: 대회 시작 및 종료 시간을 12시 표기법에서 24시 표기법으로 변경
dmdmdkdkr Nov 28, 2023
7dc0fb3
feat: formatKoreanDateTime이 'yyyy.mm.dd. hh:mm'을 받아도 반환할 수 있도록 코드 추가
dmdmdkdkr Nov 29, 2023
a9243a3
refactor: formatDate함수 확장
dmdmdkdkr Nov 29, 2023
82231ee
refactor: formatDate함수 확장
dmdmdkdkr Nov 29, 2023
80981df
Merge branch 'fe-dev' of https://github.com/boostcampwm2023/web12-alg…
dmdmdkdkr Nov 29, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions frontend/src/components/CompetitionDetail/AfterCompetition.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { css } from '@style/css';

import { CompetitionInfo } from '@/apis/competitions';

import CompetitionDetailInfo from './CompetitionDetailInfo';
import ProblemList from './ProblemList';

interface Props {
competitionId: number;
competition: CompetitionInfo;
competitionSchedule: string;
}

const AFTER_COMPETITION_TEXT = ' 종료';

export default function AfterCompetition({
competitionId,
competition,
competitionSchedule,
}: Props) {
return (
<div className={containerStyle}>
<CompetitionDetailInfo
competition={competition}
text={AFTER_COMPETITION_TEXT}
competitionSchedule={competitionSchedule}
/>
<ProblemList competitionId={competitionId} />
</div>
);
}

const containerStyle = css({
justifyContent: 'space-between',
alignItems: 'center',
padding: '16px',
border: '1px solid #ccc',
});
51 changes: 51 additions & 0 deletions frontend/src/components/CompetitionDetail/BeforeCompetition.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { css } from '@style/css';

import { CompetitionInfo } from '@/apis/competitions';

import JoinCompetitionButton from '../Main/Buttons/JoinCompetitionButton';
import EnterCompetitionButton from './Buttons/EnterCompetitionButton';
import CompetitionDetailInfo from './CompetitionDetailInfo';

interface Props {
competitionId: number;
competition: CompetitionInfo;
startsAt: Date;
endsAt: Date;
competitionSchedule: string;
}

export default function BeforeCompetition({
competitionId,
competition,
startsAt,
endsAt,
competitionSchedule,
}: Props) {
const BEFORE_COMPETITION_TEXT = ` 시작 전`;

return (
<div className={containerStyle}>
<CompetitionDetailInfo
competition={competition}
text={BEFORE_COMPETITION_TEXT}
competitionSchedule={competitionSchedule}
/>
<div className={buttonContainerStyle}>
<JoinCompetitionButton id={competitionId} />
<EnterCompetitionButton id={competitionId} startsAt={startsAt} endsAt={endsAt} />
</div>
</div>
);
}

const containerStyle = css({
justifyContent: 'space-between',
alignItems: 'center',
padding: '16px',
border: '1px solid #ccc',
});

const buttonContainerStyle = css({
display: 'flex',
gap: '16px',
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { useNavigate } from 'react-router-dom';

import useAuth from '@/hooks/login/useAuth';

interface Props {
id: number;
startsAt: Date;
endsAt: Date;
}

export default function EnterCompetitionButton({ id, startsAt, endsAt }: Props) {
const competitionLink = `/contest/${id}`;
const { isLoggedin } = useAuth();
const navigate = useNavigate();

const handleNavigate = () => {
const currentTime = new Date();

if (!isLoggedin) {
alert('로그인이 필요합니다.');
navigate('/login');
} else if (currentTime < startsAt) {
alert('아직 대회가 시작되지 않았습니다. 다시 시도해주세요');
window.location.reload();
} else if (currentTime >= endsAt) {
alert('해당 대회는 종료되었습니다.');
window.location.reload();
} else {
navigate(competitionLink);
}
};

return <button onClick={handleNavigate}>대회 입장</button>;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// CompetitionDetailContent.js
import { CompetitionInfo } from '@/apis/competitions';
import AfterCompetition from '@/components/CompetitionDetail/AfterCompetition';
import BeforeCompetition from '@/components/CompetitionDetail/BeforeCompetition';
import DuringCompetition from '@/components/CompetitionDetail/DuringCompetition';

interface Props {
competitionId: number;
competition: CompetitionInfo;
startsAt: Date;
endsAt: Date;
competitionSchedule: string;
}

export function CompetitionDetailContent({
competitionId,
competition,
startsAt,
endsAt,
competitionSchedule,
}: Props) {
const currentDate = new Date();

if (currentDate < startsAt) {
return (
<BeforeCompetition
{...{ competitionId, competition, startsAt, endsAt, competitionSchedule }}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

생각 못해본 방법인데 좋네요

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저도 이번에 하다가 써봤는데 되더라고요:D

/>
);
} else if (currentDate < endsAt) {
return (
<DuringCompetition
{...{ competitionId, competition, startsAt, endsAt, competitionSchedule }}
/>
);
} else {
return <AfterCompetition {...{ competitionId, competition, competitionSchedule }} />;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { css } from '@style/css';

import { CompetitionInfo } from '@/apis/competitions';

interface Props {
competition: CompetitionInfo;
text: string;
competitionSchedule: string;
}

export default function CompetitionDetailInfo({ competition, text, competitionSchedule }: Props) {
return (
<div className={infoContainerStyle}>
<div>
<span className={competitionNameStyle}>{competition.name}</span>
<span className={statusTextStyle}>{text}</span>
</div>
<span className={statusTextStyle}>{competitionSchedule}</span>
<div className={additionalTextStyle}>{competition.detail}</div>
</div>
);
}

const infoContainerStyle = css({
display: 'flex',
flexDirection: 'column',
});

const competitionNameStyle = css({
fontSize: '18px',
fontWeight: 'bold',
color: 'black',
marginBottom: '8px',
});

const statusTextStyle = css({
fontSize: '12px',
color: 'gray',
marginBottom: '8px',
});

const additionalTextStyle = css({
fontSize: '14px',
color: 'black',
marginBottom: '8px',
});
42 changes: 42 additions & 0 deletions frontend/src/components/CompetitionDetail/DuringCompetition.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { css } from '@style/css';

import { CompetitionInfo } from '@/apis/competitions';

import EnterCompetitionButton from './Buttons/EnterCompetitionButton';
import CompetitionDetailInfo from './CompetitionDetailInfo';

interface Props {
competitionId: number;
competition: CompetitionInfo;
startsAt: Date;
endsAt: Date;
competitionSchedule: string;
}

const DURING_COMPETITION_TEXT = ' 진행중';

export default function DuringCompetition({
competitionId,
competition,
startsAt,
endsAt,
competitionSchedule,
}: Props) {
return (
<div className={containerStyle}>
<CompetitionDetailInfo
competition={competition}
text={DURING_COMPETITION_TEXT}
competitionSchedule={competitionSchedule}
/>
<EnterCompetitionButton id={competitionId} startsAt={startsAt} endsAt={endsAt} />
</div>
);
}

const containerStyle = css({
justifyContent: 'space-between',
alignItems: 'center',
padding: '16px',
border: '1px solid #ccc',
});
34 changes: 34 additions & 0 deletions frontend/src/components/CompetitionDetail/ProblemList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { Link } from 'react-router-dom';

import { useCompetitionProblemList } from '@/hooks/problem';

interface Props {
competitionId: number;
}

export default function ProblemList({ competitionId }: Props) {
const { problemList } = useCompetitionProblemList(competitionId);

return (
<div>
<table>
<thead>
<tr>
<th>번호</th>
<th>문제 제목</th>
</tr>
</thead>
<tbody>
{problemList.map((problem) => (
<tr key={problem.id}>
<td>{problem.id}</td>
<td>
<Link to={`/problem/${problem.id}`}>{problem.title}</Link>
</td>
</tr>
))}
</tbody>
</table>
</div>
);
}
32 changes: 32 additions & 0 deletions frontend/src/pages/CompetitionDetailPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { useParams } from 'react-router-dom';

import { CompetitionDetailContent } from '@/components/CompetitionDetail/CompetitionDetailContent';
import Header from '@/components/Header';
import { useCompetition } from '@/hooks/competition';
import { formatKoreanDateTime } from '@/utils/date';

export default function CompetitionDetailPage() {
const { id } = useParams<{ id: string }>();
const competitionId: number = id ? parseInt(id, 10) : -1;
const { competition } = useCompetition(competitionId);
// 대회 상태에 따른 페이지를 구성하기 위해 현재 날짜, 시작 시간, 종료 시간을 가져옴
const startsAt = new Date(competition?.startsAt || '');
const endsAt = new Date(competition?.endsAt || '');
const formattedStartsAt = formatKoreanDateTime(startsAt);
const formattedEndsAt = formatKoreanDateTime(endsAt);

const competitionSchedule = `시작: ${formattedStartsAt} 종료: ${formattedEndsAt}`;

Comment on lines +18 to +19
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

취향이긴 한데,
이렇게 구현하면
오전 - 오후나
오후 - 오전이 나올 수 있을텐데,
2023.11.29 오후 11.30 ~ 2023.11.30 오전 3시.30분 보다
2023.11.29 23:30:00 ~ 2023.11.30 03:30:00 이게 알아보기 좋지 않을까요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

확실히 그렇네요, 감사합니다!

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

우찬님 변경하시고 어떻게 나오는지 스샷 하나만 첨부해주세요!

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

이제 netlify를 사용하기 때문에 이 링크로 확인할 수 있습니다

return (
<div>
<Header />
<CompetitionDetailContent
competitionId={competitionId}
competition={competition}
startsAt={startsAt}
endsAt={endsAt}
competitionSchedule={competitionSchedule}
/>
</div>
);
}
9 changes: 1 addition & 8 deletions frontend/src/pages/ContestPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -82,15 +82,13 @@ export default function ContestPage() {
submitSolution(form);
}


const { endsAt } = competition;

function handleOpenModal() {
modal.open();
}

const problems = problemList.map((problem) => problem.id);


return (
<main className={style}>
Expand Down Expand Up @@ -160,8 +158,3 @@ const problemTitleStyle = css({
const execButtonStyle = css({
color: 'black',
});

const rowStyle = css({
display: 'flex',
justifyContent: 'space-between',
});
5 changes: 5 additions & 0 deletions frontend/src/router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import MainPage from '@/pages/MainPage';
import ProblemPage from '@/pages/ProblemPage';

import App from './App';
import CompetitionDetailPage from './pages/CompetitionDetailPage';

const router = createBrowserRouter([
{
Expand All @@ -30,6 +31,10 @@ const router = createBrowserRouter([
element: <CreateCompetitionPage />,
},
{ path: '/login', element: <LoginPage /> },
{
path: '/contest/detail/:id',
element: <CompetitionDetailPage />,
},
],
},
]);
Expand Down
16 changes: 16 additions & 0 deletions frontend/src/utils/date/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,19 @@ export const formatMilliSecond = (ms: number, form: string) => {
}
return '';
};

export const formatKoreanDateTime = (dateString: string | number | Date) => {
const date = new Date(dateString);
if (isNaN(date.getTime())) {
// Invalid date, handle error or return default value
return 'Invalid Date';
}

const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const hours = String(date.getHours()).padStart(2, '0');
const minutes = String(date.getMinutes()).padStart(2, '0');

return `${year}. ${month}. ${day}. ${hours}:${minutes}`;
};
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아 formatDate 함수를 확장해달라는 얘기였어요.

export const formatDate = (date: Date, form: string) => {
  if (form === 'YYYY-MM-DDThh:mm') {
    return date.toISOString().slice(0, 'YYYY-MM-DDThh:mm'.length);
  }

  if(form === 'YYYY. MM. DD. hh:mm') { ... } // 여기
  return '';
};

이런식으로요

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아, 제가 착각했네요:ㅇ
formatDate 함수는 유석님이 작성하신 함수긴 한데, 말씀하신 대로 확장해볼게요!