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

[SP0] 메인 페이지 개선 #168

Merged
merged 25 commits into from
Sep 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
b4563b9
feat: Timer 컴포넌트 추가
Sep 3, 2023
f123596
asset: DoSOPT 심볼 추가
Sep 3, 2023
c352368
feat: 모집 플로팅 배너 구현
Sep 3, 2023
91ce3e7
chore: pnpm-lock 버전 다운그레이드
Sep 3, 2023
9e86c10
style: 활동 섹션 스타일 개선
Sep 3, 2023
4bbbb73
asset: 활동 섹션 이미지 고화질로 대체
Sep 3, 2023
4a06fee
feat: 활동 섹션 이미지 폴더 index 파일 추가
Sep 3, 2023
087a479
chore: avif 포맷 지원 활성화
Sep 3, 2023
501b4e6
style: 세부 정보 섹션 스타일 개선
Sep 3, 2023
21deb64
feat: 모바일 접속시 상단 이동 버튼 감추기
Sep 3, 2023
165547d
feat: 모바일 접속시 배너 전체 영역 클릭 활성화
Sep 3, 2023
85291a4
style: 활동 후기 섹션 스타일 개선
Sep 3, 2023
a583982
fix: 모집 마감 시간 수정
Sep 4, 2023
496e89a
style: 헤더 및 배경 색상 변경
Sep 4, 2023
38a12ff
style: 배너 이미지 및 스타일 변경
Sep 4, 2023
4af4061
style: 히스토리 섹션 스타일 개선
Sep 4, 2023
7adf850
style: 파트 섹션 스타일 개선
Sep 4, 2023
c4ce07f
style: 세부 정보 태블릿 타이틀 중앙 정렬
Sep 4, 2023
132a9c2
style: 배너 섹션 스타일 개선
Sep 4, 2023
562434b
fix: 솝트 심볼 이미지 변경
Sep 4, 2023
5e745b2
fix: 모바일 환경에서만 배너 전체 영역 클릭 활성화
Sep 4, 2023
1f923f9
fix: 플로팅 배너 위치 수정
Sep 4, 2023
76d3e63
Merge branch 'develop' into feat/#159-main-page
Sep 6, 2023
c956961
fix: 파트 소개 띄어쓰기 수정
Sep 6, 2023
c6bd55f
fix: 배너 슬로건 위치 조정
Sep 7, 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
1 change: 1 addition & 0 deletions next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const nextConfig = {
'github.githubassets.com',
'img1.daumcdn.net',
],
formats: ['image/avif', 'image/webp'],
},
webpack: (config) => {
config.module.rules.push({
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"@types/qs": "6.9.7",
"axios": "^0.27.2",
"classcat": "^5.0.4",
"dayjs": "^1.11.3",
"nanoid": "4.0.2",
"next": "13.3.0",
"qs": "6.11.1",
Expand Down
6 changes: 6 additions & 0 deletions pnpm-lock.yaml

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

Binary file removed src/assets/sopt/main-page_banner-compression.png
Binary file not shown.
Binary file modified src/assets/sopt/main-page_banner.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion src/components/Header/header.module.scss
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
justify-content: center;
align-items: center;
position: fixed;
background-color: rgba(24, 24, 24, 0.9);
background-color: rgba(22, 22, 28, 0.9);
backdrop-filter: blur(20px);
z-index: 100;

Expand Down
47 changes: 47 additions & 0 deletions src/components/common/Timer/Timer.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { FC, useState } from 'react';
import useInterval from '@src/hooks/useInterval';
import { convertMsIntoDate } from '@src/utils/convertMsIntoDate';

interface TimerProps {
targetDate: Date;
prefix?: string;
suffix?: string;
endMessage: string;
}

type TimeObj = ReturnType<typeof convertMsIntoDate>;

const Timer: FC<TimerProps> = (props) => {
const [timeDiff, setTimeDiff] = useState<TimeObj | 'prepare' | 'timeEnd'>('prepare');

useInterval(() => {
const diff = props.targetDate.getTime() - Date.now();
if (diff >= 0) {
setTimeDiff(convertMsIntoDate(diff));
} else {
setTimeDiff('timeEnd');
}
}, 100);

if (timeDiff === 'prepare') {
return <> </>;
}
if (timeDiff === 'timeEnd') {
return <>{props.endMessage}</>;
}

return (
<>
{props.prefix}
{timeDiff.days}일 {padZero(timeDiff.hours)}:{padZero(timeDiff.minutes)}:
{padZero(timeDiff.seconds)}
{props.suffix}
</>
);
};

export default Timer;

const padZero = (num: number) => {
return `${num}`.padStart(2, '0');
};
1 change: 1 addition & 0 deletions src/components/common/Timer/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './Timer';
24 changes: 24 additions & 0 deletions src/hooks/useInterval.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { useEffect, useRef } from 'react';

type IntervalId = ReturnType<typeof setInterval>;

export default function useInterval(callback: () => void, delay: number) {
const intervalId = useRef<IntervalId | null>(null);

const clear = () => {
if (intervalId.current !== null) {
clearInterval(intervalId.current);
}
};

useEffect(() => {
intervalId.current = setInterval(callback, delay);
return () => {
clear();
};
}, [callback, delay]);

return {
clear,
};
}
2 changes: 1 addition & 1 deletion src/styles/global.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ export const global = css`
}

body {
background-color: #181818;
background-color: #16161c;
line-height: 1;
}

Expand Down
7 changes: 7 additions & 0 deletions src/utils/convertMsIntoDate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const convertMsIntoDate = (milliseconds: number) => {
const days = Math.floor(milliseconds / (1000 * 60 * 60 * 24));
const hours = Math.floor((milliseconds % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
const minutes = Math.floor((milliseconds % (1000 * 60 * 60)) / (1000 * 60));
const seconds = Math.floor((milliseconds % (1000 * 60)) / 1000);
return { days, hours, minutes, seconds };
};
9 changes: 7 additions & 2 deletions src/views/MainPage/MainPage.tsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,33 @@
import { Footer, Header, Layout, ScrollToTopButton } from '@src/components';
import { useIsMobile } from '@src/hooks/useDevice';
import {
ActivityDescription,
ActivityReview,
BannerImage,
DetailedInformation,
PartDescription,
RecruitFloatingBanner,
SoptHistory,
} from '@src/views/MainPage/components';
import styles from './main-page.module.scss';

function MainPage() {
const isMobile = useIsMobile();

return (
<>
<Layout>
<Header />
<ScrollToTopButton />
{!isMobile && <ScrollToTopButton />}
<RecruitFloatingBanner />
<BannerImage />
<div className={styles.container}>
<div className={styles.content}>
<SoptHistory />
<PartDescription />
<ActivityDescription />
<DetailedInformation />
<ActivityReview />
<DetailedInformation />
</div>
</div>
<Footer />
Expand Down
3 changes: 3 additions & 0 deletions src/views/MainPage/assets/arrow-right-grey.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/views/MainPage/assets/dosopt_symbol.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified src/views/MainPage/assets/sopt-activity/appjam.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions src/views/MainPage/assets/sopt-activity/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export { default as imgAppjam } from './appjam.png';
export { default as imgManagementMediaTeam } from './management-media-team.png';
export { default as imgSeminar } from './seminar.png';
export { default as imgSoptTerm } from './sopt-term.png';
export { default as imgSopkathon } from './sopkathon.png';
export { default as imgStudyNetworking } from './study-networking.png';
Binary file modified src/views/MainPage/assets/sopt-activity/management-media-team.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified src/views/MainPage/assets/sopt-activity/seminar.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified src/views/MainPage/assets/sopt-activity/sopkathon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified src/views/MainPage/assets/sopt-activity/sopt-term.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified src/views/MainPage/assets/sopt-activity/study-networking.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -1,54 +1,102 @@
import Image from 'next/image';
import { useIsDesktop, useIsMobile, useIsTablet } from '@src/hooks/useDevice';
import { useTabs } from '@src/hooks/useTabs';
import appJamImage from '@src/views/MainPage/assets/sopt-activity/appjam.png';
import managementMediaTeamImage from '@src/views/MainPage/assets/sopt-activity/management-media-team.png';
import seminarImage from '@src/views/MainPage/assets/sopt-activity/seminar.png';
import sopkathonImage from '@src/views/MainPage/assets/sopt-activity/sopkathon.png';
import soptTermImage from '@src/views/MainPage/assets/sopt-activity/sopt-term.png';
import studyAndNetworkingImage from '@src/views/MainPage/assets/sopt-activity/study-networking.png';
import {
imgAppjam,
imgManagementMediaTeam,
imgSeminar,
imgSopkathon,
imgSoptTerm,
imgStudyNetworking,
} from '@src/views/MainPage/assets/sopt-activity';
import cc from 'classcat';
import styles from './activity-description.module.scss';

const activityList = [
{
type: '정기세미나',
imgSrc: seminarImage,
content:
'활동 기간 동안 총 8회의 파트별 세미나를 통해 각자 자신의 파트에서 실력을 다져요. 각 파트장의 강연, 파트원간의 지식 공유, 외부 연사 초청 등 다양한 유형의 세미나가 진행돼요.',
imgSrc: imgSeminar,
Copy link
Member

Choose a reason for hiding this comment

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

요것 괜찮다면 ./constant.ts 만들어서 그곳에 옮겨도 좋을 것 같아요 ..!!

content: [
Copy link
Member

Choose a reason for hiding this comment

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

요렇게 구조 짠 것 너무너무 좋아요!
요것이 렌더되면 결과로 textnode가 생기는 것이니까,
content => nodes
data => textContent
요런 식으로 변수 이름을 바꾸어 보면 어떨까요.!?

{ data: '활동 기간 동안 총 ', highlight: false },
{ data: '8회의 파트별 세미나', highlight: true },
{
data: '를 통해 각자 자신의 파트에서 실력을 다져요. 각 파트장의 강연, 파트원간의 지식 공유, 외부 연사 초청 등 다양한 유형의 세미나가 진행돼요.',
highlight: false,
},
],
},
{
type: '솝커톤',
imgSrc: sopkathonImage,
content:
'무박 2일간 기획, 디자인, 개발 파트가 팀을 이뤄 최소 단위의 서비스를 제작하는 SOPT 내 단기 프로젝트예요. 앱잼에 앞서 팀 단위의 협업 과정을 빠르게 경험하며 IT 프로젝트에 대한 협업 감각을 익힐 수 있어요.',
nickName: 'SOPKATHON',
imgSrc: imgSopkathon,
content: [
{ data: '무박 2일', highlight: true },
{
data: '간 기획, 디자인, 개발 파트가 팀을 이뤄 최소 단위의 서비스를 제작하는 SOPT 내 ',
highlight: false,
},
{ data: '단기 프로젝트', highlight: true },
{
data: '예요. 앱잼에 앞서 팀 단위의 협업 과정을 빠르게 경험하며 IT 프로젝트에 대한 협업 감각을 익힐 수 있어요.',
highlight: false,
},
],
},
{
type: '앱잼',
imgSrc: appJamImage,
content:
'3~5주간 기획, 디자인, 개발 파트가 팀을 이뤄 하나의 웹 또는 앱 서비스를 제작하는 SOPT 내 장기 프로젝트예요. IT 창업을 위한 협업 과정을 경험하고, 최종 데모데이에서 각 파트 현직자들과 결과물을 공유하고 피드백을 받아요.',
nickName: 'APPJAM',
imgSrc: imgAppjam,
content: [
{ data: '3~5주간 ', highlight: true },
{
data: '기획, 디자인, 개발 파트가 팀을 이뤄 하나의 웹 또는 앱 서비스를 제작하는 SOPT 내 ',
highlight: false,
},
{ data: '장기 프로젝트', highlight: true },
{ data: '예요. IT 창업을 위한 협업 과정을 경험하고, 최종 ', highlight: false },
{ data: '데모데이', highlight: true },
{ data: '에서 각 파트 현직자들과 결과물을 공유하고 피드백을 받아요.', highlight: false },
],
},
{
type: '솝텀',
imgSrc: soptTermImage,
content:
'SOPT를 한 기수 이상 수료한 회원끼리 모여 자유로운 주제로 IT 프로젝트를 진행해요. SOPT에서 쌓은 실력을 기반으로 보다 자율적인 프로젝트를 진행할 수 있어요.',
nickName: 'SOPT-TERM',
type: '스터디&네트워킹',
imgSrc: imgStudyNetworking,
content: [
{ data: '각 파트의 실력을 심도있게 다질 수 있는 ', highlight: false },
{ data: '스터디', highlight: true },
{ data: '와 다양한 파트원들과 친목을 쌓을 수 있는 ', highlight: false },
{ data: '네트워킹', highlight: true },
{
data: '이 열려요. 자율적으로 참여하며 SOPT 활동을 더욱 유익하게 만들어 나갈 수 있어요.',
highlight: false,
},
],
},
{
type: '스터디&네트워킹',
imgSrc: studyAndNetworkingImage,
content:
'각 파트의 실력을 심도있게 다질 수 있는 스터디와 다양한 파트원들과 친목을 쌓을 수 있는 네트워킹이 열려요. 자율적으로 참여하며 SOPT 활동을 더욱 유익하게 만들어 나갈 수 있어요.',
type: '솝텀',
imgSrc: imgSoptTerm,
content: [
{ data: 'SOPT를 ', highlight: false },
{ data: '한 기수 이상 수료한 회원', highlight: true },
{
data: '끼리 모여 자유로운 주제로 IT 프로젝트를 진행해요. SOPT에서 쌓은 실력을 기반으로 보다 ',
highlight: false,
},
{ data: '자율적인 프로젝트', highlight: true },
{ data: '를 진행할 수 있어요.', highlight: false },
],
},
{
type: '운영팀&미디어팀',
imgSrc: managementMediaTeamImage,
content:
'운영팀에서는 SOPT 회원들의 원활한 네트워킹을 위한 다양한 행사를 기획해요. 미디어팀에서는 SOPT의 활동과 관련된 콘텐츠를 제작하여 SOPT를 대내외적으로 알려요.',
imgSrc: imgManagementMediaTeam,
content: [
{ data: '운영팀', highlight: true },
{ data: '에서는 SOPT 회원들의 원활한 ', highlight: false },
{ data: '네트워킹', highlight: true },
{ data: '을 위한 다양한 행사를 기획해요. ', highlight: false },
{ data: '미디어팀', highlight: true },
{ data: '에서는 SOPT의 활동과 관련된 ', highlight: false },
{ data: '콘텐츠를 제작', highlight: true },
{ data: '하여 SOPT를 대내외적으로 알려요.', highlight: false },
],
},
];

Expand Down Expand Up @@ -81,14 +129,16 @@ function MobileActivityDescription() {
src={currentTab.imgSrc}
alt="card"
fill
sizes="(max-width: 766px) 328px 585px"
sizes="(max-width: 766px) 309px, 585px"
/>
</div>
<div className={styles.imageDesc}>
<span className={styles.type}>{currentTab.type}</span>
{currentTab.nickName && <span className={styles.nickName}>{currentTab.nickName}</span>}
<div className={styles.cardContent}>
<p>
{currentTab.content.map(({ data, highlight }) =>
highlight ? <span>{data}</span> : data,
Comment on lines +137 to +138
Copy link
Contributor

Choose a reason for hiding this comment

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

comment;

bold 처리된 부분을 highlight에 따라 처리한 것 인상깊네요!

)}
</p>
</div>
<p className={styles.cardContent}>{currentTab.content}</p>
</article>
</div>
);
Expand All @@ -97,7 +147,7 @@ function MobileActivityDescription() {
function DesktopActivityDescription() {
return (
<div className={styles.content}>
{activityList.map(({ type, imgSrc, nickName, content }) => {
{activityList.map(({ type, imgSrc, content }) => {
return (
<article className={styles.card} key={type}>
<div className={styles.imageWrapper}>
Expand All @@ -110,11 +160,14 @@ function DesktopActivityDescription() {
540px"
/>
</div>
<div className={cc([styles.imageDesc, nickName && styles.nickNameInclude])}>
<div className={styles.imageDesc}>
<span className={styles.type}>{type}</span>
{nickName && <span className={styles.nickName}>{nickName}</span>}
</div>
<p className={styles.cardContent}>{content}</p>
<div className={styles.cardContent}>
<p>
{content.map(({ data, highlight }) => (highlight ? <span>{data}</span> : data))}
</p>
</div>
</article>
);
})}
Expand Down
Loading