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

Feature/#152 해커톤 페이지 구현 #158

Merged
merged 19 commits into from
Aug 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
492dea9
feat: 클라이언트 서비스의 페이지네이션 컴포넌트 구현
Aug 13, 2024
dcc23ec
feat: 학생의 마일스톤 획득내역, 실적 목록 페이지네이션 처리
Aug 13, 2024
7d85a78
feat: 나의 마일스톤 획득 내역에 페이지네이션 처리
Aug 13, 2024
8127d7a
fix: 학생의 실적 목록 조회 api의 totalElements가 제대로 불러와지지 않는 문제 해결
Aug 13, 2024
3c4c210
feat: Title 컴포넌트 구현
Aug 14, 2024
9b5c366
Merge branch 'Feature/#150-학생의_마일스톤_획득내역_실적목록_페이지네이션_처리' of https://g…
Aug 14, 2024
107df9d
feat: 해커톤 목록 페이지 구현
Aug 14, 2024
11608ee
refactor: 불필요한 styled comopnent 삭제
Aug 14, 2024
ee540ba
refactor: 파일 다운로드 기능 deprecated
Aug 14, 2024
89625bc
feat: 외부에서 파일 리소스에 직접 접근할 수 있도록 설정
Aug 14, 2024
1747e11
feat: 외부에서 파일 리소스에 직접 접근할 수 있도록 설정
Aug 14, 2024
2a3af0a
Merge branch 'Feature/#155-url로_파일_리소스에_접근할_수_있도록_설정' of https://gith…
Aug 14, 2024
8ebef38
fix: 파일 다운로드 테스트 삭제
Aug 15, 2024
b15dc48
Merge branch 'Feature/#155-url로_파일_리소스에_접근할_수_있도록_설정' of https://gith…
Aug 15, 2024
e54b253
feat: 해커톤 진행 상태를 나타내는 라벨 추가
Aug 25, 2024
c5c8cfd
designed: 해커톤 간 여백 크기 키우기
Aug 25, 2024
a6ee7c5
Merge branch 'main' into Feature/#152-해커톤_페이지_구현
amaran-th Aug 25, 2024
d339775
build: classnames 라이브러리 설치
Aug 25, 2024
a591bdf
Merge branch 'Feature/#152-해커톤_페이지_구현' of https://github.com/SW-CSS/s…
Aug 25, 2024
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
3 changes: 3 additions & 0 deletions frontend/next.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ const nextConfig = {
compiler: {
styledComponents: true,
},
images: {
domains: ['localhost'],
},
};

export default nextConfig;
6 changes: 6 additions & 0 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"@tanstack/react-query-next-experimental": "^5.48.0",
"axios": "^1.7.2",
"babel-plugin-styled-components": "^2.1.4",
"classnames": "^2.5.1",
"formik": "^2.4.6",
"luxon": "^3.4.4",
"next": "14.2.3",
Expand Down
102 changes: 101 additions & 1 deletion frontend/src/app/(withSidebar)/hackathon/page.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,103 @@
const Page = () => <div>test</div>;
import Pagination from '@/app/components/Pagination';
import Title from '@/components/Title';
import { HackathonState } from '@/data/hackathon';
import { getHackathons } from '@/lib/api/server.api';
import classname from 'classnames';
import { headers } from 'next/headers';
import Image from 'next/image';
import Link from 'next/link';

const Page = async ({ searchParams }: { searchParams?: { [key: string]: string | undefined } }) => {
const headersList = headers();
const pathname = headersList.get('x-pathname') || '';

const page = searchParams?.page ? parseInt(searchParams.page, 10) : 1;

const hackathons = await getHackathons(page, 6);

const getHackathonState = (startDate: string, endDate: string) => {
const now = Date.now();
const start = Date.parse(startDate);
const end = Date.parse(endDate);
if (now < start) return HackathonState.UPCOMING;
if (end < now) return HackathonState.COMPLETED;
return HackathonState.IN_PROGRESS;
};

const getStatusLabelStyle = (state: HackathonState) => {
switch (state) {
case HackathonState.UPCOMING:
return 'border-green-400';
case HackathonState.IN_PROGRESS:
return 'border-primary-main';
case HackathonState.COMPLETED:
return 'border-gray-400';
default:
return '';
}
};
return (
<div className="flex w-full flex-col gap-4 rounded-sm bg-white p-5">
<Title
title="창의융합SW해커톤"
description="소프트웨어융합교육원에서는 2018년부터 매년 창의융합 SW 해커톤을 개최해오고 있습니다."
/>
<div className="h-0 w-full border border-border" />
{hackathons?.content && hackathons.content.length > 0 ? (
<div className="grid grid-cols-1 gap-6 py-4 sm:grid-cols-2 md:grid-cols-3">
{hackathons.content.map((hackathon) => (
<Link
key={hackathon.id}
href={`/hackathon/${hackathon.id}`}
className="flex w-full min-w-[200px] flex-col rounded-sm border-r border-border shadow-md transition-shadow hover:shadow-xl"
>
<div className="relative h-40 w-full">
<Image
src={process.env.NEXT_PUBLIC_FILE_URL + '/' + hackathon.thumbnailImageName}
alt={'해커톤 섬네일'}
className={classname(
'rounded-t-sm',
getHackathonState(hackathon.hackathonStartDate, hackathon.hackathonEndDate) ===
HackathonState.COMPLETED && 'grayscale',
)}
layout="fill"
objectFit="cover"
objectPosition="center"
quality={100}
/>
<div
className={classname(
'absolute left-0 top-0 h-0 w-0 rounded-ss-sm border-[36px] border-b-transparent border-r-transparent',
getStatusLabelStyle(getHackathonState(hackathon.hackathonStartDate, hackathon.hackathonEndDate)),
)}
/>
<div className="absolute left-1 top-4 w-[3em] -rotate-45 text-center text-sm font-bold text-white">
{getHackathonState(hackathon.hackathonStartDate, hackathon.hackathonEndDate)}
</div>
</div>
<div className="m-2 flex flex-col items-center justify-center gap-2 text-center">
<div className="font-bold">{hackathon.name}</div>
<div className="text-xs text-comment">
<p>
신청 기간: {hackathon.applyStartDate.replaceAll('-', '.')} ~{' '}
{hackathon.applyEndDate.replaceAll('-', '.')}
</p>
<p>
대회 기간: {hackathon.hackathonStartDate.replaceAll('-', '.')} ~{' '}
{hackathon.hackathonEndDate.replaceAll('-', '.')}
</p>
</div>
</div>
</Link>
))}
</div>
) : (
<div className="flex h-40 w-full items-center justify-center text-comment">해커톤 정보가 없습니다.</div>
)}

<Pagination currentPage={page} totalItems={hackathons?.totalElements ?? 0} pathname={pathname} pageSize={6} />
</div>
);
};

export default Page;

This file was deleted.

74 changes: 36 additions & 38 deletions frontend/src/app/(withSidebar)/milestone/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,45 +3,43 @@ import Image from 'next/image';
import * as S from './styled';

const Page = () => (
<S.ContentWrapper>
<S.Content>
<S.ImageWrapper maxWidth="230px" maxHeight="66px">
<Image src="/images/milestone/milestone_img01.png" priority={false} alt="" fill />
</S.ImageWrapper>
<S.Description>
<S.Title>마일스톤이란</S.Title>
마일스톤은 전공자들의 SW역량을 종합적으로 평가하기 위한 역량평가지수입니다.
<S.Content>
<S.ImageWrapper maxWidth="230px" maxHeight="66px">
<Image src="/images/milestone/milestone_img01.png" priority={false} alt="" fill />
</S.ImageWrapper>
<S.Description>
<S.Title>마일스톤이란</S.Title>
마일스톤은 전공자들의 SW역량을 종합적으로 평가하기 위한 역량평가지수입니다.
<br />
학생들은 교내외 여러 활동들을 통하여 실전적 SW역량, 글로벌 역량, 커뮤니케이션 역량을 균형있게 함양하고
SW중심대학사업단에서는 학생들의 적립된 마일스톤 점수에 따라 매년 장학생을 선발하고 있습니다.
</S.Description>
<S.ImageWrapper maxWidth="429px" maxHeight="208px" backgroundImage="/images/milestone/milestone_img02_bg.png">
<Image src="/images/milestone/milestone_img02.png" priority={false} alt="" fill />
</S.ImageWrapper>
<S.InformationList>
<S.Information>
<S.InformationTitle>마일스톤 획득 방법</S.InformationTitle>
각 영역별 활동 수행 시, 책정 기준에 따라 마일스톤을 획득할 수 있습니다.
<br />
학생들은 교내외 여러 활동들을 통하여 실전적 SW역량, 글로벌 역량, 커뮤니케이션 역량을 균형있게 함양하고
SW중심대학사업단에서는 학생들의 적립된 마일스톤 점수에 따라 매년 장학생을 선발하고 있습니다.
</S.Description>
<S.ImageWrapper maxWidth="429px" maxHeight="208px" backgroundImage="/images/milestone/milestone_img02_bg.png">
<Image src="/images/milestone/milestone_img02.png" priority={false} alt="" fill />
</S.ImageWrapper>
<S.InformationList>
<S.Information>
<S.InformationTitle>마일스톤 획득 방법</S.InformationTitle>
각 영역별 활동 수행 시, 책정 기준에 따라 마일스톤을 획득할 수 있습니다.
<br />
상세 내용은 아래 표를 참고해주세요.
</S.Information>
<S.Information>
<S.InformationTitle>마일스톤 평가기간</S.InformationTitle>
전년도 9월부터 당해년도 8월까지의 실적
<br />※ SW 창업, 오픈소스 SW 컨트리뷰션의 경우 당해년도 1월부터 9월까지의 실적만을 반영함.
</S.Information>
<S.Information>
<S.InformationTitle>마일스톤 확인 방법</S.InformationTitle>
SW역량지원시스템에서는 나의 마일스톤 현황을 한 눈에 볼 수 있도록 제공하고 있습니다.
<br />
로그인 후, 메인 페이지와 마이페이지에서 확인하실 수 있습니다.
</S.Information>
</S.InformationList>
<S.ImageWrapper maxWidth="890px" maxHeight="1028px">
<Image src="/images/milestone/milestone_img03.png" priority={false} alt="" fill />
</S.ImageWrapper>
</S.Content>
</S.ContentWrapper>
상세 내용은 아래 표를 참고해주세요.
</S.Information>
<S.Information>
<S.InformationTitle>마일스톤 평가기간</S.InformationTitle>
전년도 9월부터 당해년도 8월까지의 실적
<br />※ SW 창업, 오픈소스 SW 컨트리뷰션의 경우 당해년도 1월부터 9월까지의 실적만을 반영함.
</S.Information>
<S.Information>
<S.InformationTitle>마일스톤 확인 방법</S.InformationTitle>
SW역량지원시스템에서는 나의 마일스톤 현황을 한 눈에 볼 수 있도록 제공하고 있습니다.
<br />
로그인 후, 메인 페이지와 마이페이지에서 확인하실 수 있습니다.
</S.Information>
</S.InformationList>
<S.ImageWrapper maxWidth="890px" maxHeight="1028px">
<Image src="/images/milestone/milestone_img03.png" priority={false} alt="" fill />
</S.ImageWrapper>
</S.Content>
);

export default Page;
4 changes: 0 additions & 4 deletions frontend/src/app/(withSidebar)/milestone/styled.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,6 @@ interface ResponsiveImageProps {
backgroundImage?: string;
}

export const ContentWrapper = styled.div`
background-color: ${COLOR.border};
`;

export const Content = styled.div`
width: 100%;
background-color: white;
Expand Down
24 changes: 24 additions & 0 deletions frontend/src/components/Title/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import Link from 'next/link';

export interface TitleProps {
title: string;
description?: string;
urlText?: string;
url?: string;
}

const Title = ({ title, description, urlText, url }: TitleProps) => (
<div className="flex flex-col gap-4">
<div className="flex flex-grow justify-between">
<p className="cursor-default text-xl font-semibold">{title}</p>
{urlText && url && (
<Link href={url} className="flex items-center gap-1 text-sm text-comment">
{urlText}
</Link>
)}
</div>
<div className="text-comment">{description}</div>
</div>
);

export default Title;
3 changes: 1 addition & 2 deletions frontend/src/data/clientCategory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@ export const headerInfos: CategoryDto[] = [
description: '부산대학교에서는 매년 창의 융합 소프트웨어 해커톤을 진행하고 있습니다.',
inHeader: true,
sub: [
{ title: '진행중인 해커톤', url: '/hackathon', key: 'onGoingHackathon' },
{ title: '창의융합SW해커톤', url: '/hackathon/sw-hackathon', key: 'SWHackathon' },
{ title: '창의융합SW해커톤', url: '/hackathon', key: 'SWHackathon' },
{ title: 'SW문제 해결 경진대회', url: '/hackathon/sw-contest', key: 'problemContest' },
],
},
Expand Down
5 changes: 5 additions & 0 deletions frontend/src/data/hackathon.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export enum HackathonState {
UPCOMING = '예정',
IN_PROGRESS = '진행중',
COMPLETED = '종료',
}
80 changes: 80 additions & 0 deletions frontend/src/lib/api/server.api.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { MilestoneHistoryStatus } from '@/data/milestone';
import { server } from '@/lib/api/server.axios';
import {
HackathonPageableDto,
MilestoneHistoryDto,
MilestoneHistoryOfStudentPageableDto,
MilestoneHistoryPageableDto,
Expand Down Expand Up @@ -83,3 +84,82 @@ export async function getValidationStudentId(studentId: string) {

return response;
}

export async function getHackathons(page: number = 0, size: number = 10) {
const response = await server.get<HackathonPageableDto>('/hackathons', {
params: removeEmptyField({
page,
size,
}),
});
// TODO : API 구현
//return response?.data;
return {
content: [
{
id: 1,
name: '제5회 창의융합SW해커톤',
description: 'Hackathon 1 Description',
applyStartDate: '2022-01-01',
applyEndDate: '2022-01-31',
hackathonStartDate: '2022-01-01',
hackathonEndDate: '2022-01-31',
thumbnailImageName: 'ug0sO16.png',
},
{
id: 2,
name: 'Hackathon 1',
description: 'Hackathon 1 Description',
applyStartDate: '2022-01-01',
applyEndDate: '2022-01-31',
hackathonStartDate: '2022-01-01',
hackathonEndDate: '2022-01-31',
thumbnailImageName: 'hackathon_1.jpg',
},
{
id: 3,
name: 'Hackathon 1',
description: 'Hackathon 1 Description',
applyStartDate: '2022-01-01',
applyEndDate: '2022-01-31',
hackathonStartDate: '2022-01-01',
hackathonEndDate: '2022-01-31',
thumbnailImageName: 'hackathon_1.jpg',
},
{
id: 4,
name: 'Hackathon 1',
description: 'Hackathon 1 Description',
applyStartDate: '2022-01-01',
applyEndDate: '2022-01-31',
hackathonStartDate: '2022-01-01',
hackathonEndDate: '2022-01-31',
thumbnailImageName: 'hackathon_1.jpg',
},
{
id: 5,
name: 'Hackathon 1',
description: 'Hackathon 1 Description',
applyStartDate: '2022-01-01',
applyEndDate: '2022-01-31',
hackathonStartDate: '2022-01-01',
hackathonEndDate: '2022-01-31',
thumbnailImageName: 'hackathon_1.jpg',
},
],
empty: false,
first: true,
last: false,
sort: {
empty: false,
sorted: false,
unsorted: true,
},
number: 0,
pageable: '',
pageSize: 6,
size: 6,
totalElements: 8,
totalPages: 2,
};
}
14 changes: 14 additions & 0 deletions frontend/src/types/common.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,3 +163,17 @@ interface StudentMemberReferenceDto {
id: number;
name: string;
}

export interface HackathonOverviewDto {
id: number;
name: string;
applyStartDate: string;
applyEndDate: string;
hackathonStartDate: string;
hackathonEndDate: string;
thumbnailImageName: string;
}

export interface HackathonPageableDto extends Pageable {
content: HackathonOverviewDto[];
}