-
Notifications
You must be signed in to change notification settings - Fork 1
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
[TMN-58] 회원가입 후 온보딩 #642
[TMN-58] 회원가입 후 온보딩 #642
Changes from all commits
596b999
12731f9
5c97c7d
61738be
7b3b4ea
c75c822
6ed88bc
05d6a34
cadc522
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
import React from 'react'; | ||
import Button from '@/components/Button/Button'; | ||
import Thumbnail from '@/components/Thumbnail/Thumbnail'; | ||
import { css } from '@styled-system/css'; | ||
|
||
interface RecommendFollowItemProps { | ||
id: number; | ||
nickname: string; | ||
profileImageUrl: string | null; | ||
tags: string[]; | ||
onChangeFollow: (id: number) => void; | ||
isFollowing: boolean; | ||
} | ||
|
||
function RecommendFollowItem({ | ||
profileImageUrl, | ||
nickname, | ||
tags, | ||
onChangeFollow, | ||
isFollowing, | ||
id, | ||
}: RecommendFollowItemProps) { | ||
const handleFollow = () => { | ||
onChangeFollow(id); | ||
}; | ||
|
||
return ( | ||
<div className={followItemWrapperCss}> | ||
<div className={leftWrapperCss}> | ||
<Thumbnail size={'h36'} variant={'filled'} url={profileImageUrl} /> | ||
<div> | ||
<p className={nicknameCss}>{nickname}</p> | ||
<ul className={tagListCss}> | ||
{tags.map((tag, index) => ( | ||
<li className={tagCss} key={index}> | ||
{tag} | ||
</li> | ||
))} | ||
</ul> | ||
</div> | ||
</div> | ||
<Button onClick={handleFollow} size={'small'} variant={isFollowing ? 'secondary' : 'primary'}> | ||
{isFollowing ? '팔로우' : '팔로잉'} | ||
</Button> | ||
</div> | ||
); | ||
} | ||
|
||
export default React.memo(RecommendFollowItem); | ||
|
||
const leftWrapperCss = css({ | ||
display: 'flex', | ||
alignItems: 'center', | ||
gap: '12px', | ||
}); | ||
|
||
const nicknameCss = css({ | ||
textStyle: 'subtitle4', | ||
color: 'text.secondary', | ||
}); | ||
const tagListCss = css({ | ||
display: 'flex', | ||
gap: '6px', | ||
}); | ||
|
||
const tagCss = css({ | ||
textStyle: 'body6', | ||
color: 'text.tertiary', | ||
}); | ||
|
||
const followItemWrapperCss = css({ | ||
display: 'flex', | ||
justifyContent: 'space-between', | ||
|
||
width: '100%', | ||
padding: '8px', | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
export const RECOMMENDATION = [ | ||
{ | ||
id: 41, | ||
nickname: '수미미칩', | ||
profileImageUrl: null, | ||
tags: ['#기타연주', '#감사일기', '#개발자'], | ||
}, | ||
{ | ||
id: 16, | ||
nickname: '123', | ||
profileImageUrl: 'https://kr.object.ncloudstorage.com/10mm-images/dev/member_profile/16/image.jpeg', | ||
tags: ['#카페출근', '#출근독서', '#스타벅스'], | ||
}, | ||
{ | ||
id: 15, | ||
nickname: '1212333331212', | ||
profileImageUrl: 'https://kr.object.ncloudstorage.com/10mm-images/dev/member_profile/15/image.jpeg', | ||
tags: ['#카페출근', '#출근독서', '#스타벅스'], | ||
}, | ||
]; | ||
|
||
export const RECOMMENDATION_REAL = [ | ||
{ | ||
id: 4, | ||
nickname: '도모', | ||
profileImageUrl: 'https://image.10mm.today/prod/member_profile/4/38e6a5a9-a547-4051-95ea-cc112feabe21.jpeg', | ||
tags: ['#기타연주', '#감사일기', '#개발자'], | ||
}, | ||
{ | ||
id: 2, | ||
nickname: 'ybchar', | ||
profileImageUrl: 'https://image.10mm.today/prod/member_profile/2/2443459a-94b5-4048-98df-5fe356378a62.jpeg', | ||
tags: ['#개발', '#산책하기'], | ||
}, | ||
{ | ||
id: 73, | ||
nickname: '안암위스키남', | ||
profileImageUrl: 'https://image.10mm.today/prod/member_profile/73/a362d028-ad4c-49aa-9912-e32bc041793d.jpeg', | ||
tags: ['#공부', '#헬스', '#디자인'], | ||
}, | ||
{ | ||
id: 1, | ||
nickname: '우보틀', | ||
profileImageUrl: 'https://image.10mm.today/prod/member_profile/1/b83aeeb8-9acd-4915-aee4-ea87ec142b3b.jpeg', | ||
tags: ['#카페출근', '#출근독서', '#스타벅스'], | ||
}, | ||
{ | ||
id: 8, | ||
nickname: '수미칩', | ||
profileImageUrl: 'https://image.10mm.today/prod/member_profile/8/70d5cb9b-95ff-406f-93bd-9a68a2d8d5b9.png', | ||
tags: ['#운동', '#뜨개질', '#폭주기관차'], | ||
}, | ||
{ | ||
id: 7, | ||
nickname: '유우비트', | ||
profileImageUrl: 'https://image.10mm.today/prod/member_profile/7/9fbdf25a-101e-4fb5-9956-6030e7407382.jpeg', | ||
tags: ['#운동', '#헬스', '#영화'], | ||
}, | ||
{ | ||
id: 9, | ||
nickname: '집가고시퍼', | ||
profileImageUrl: 'https://image.10mm.today/prod/member_profile/9/722d875d-eff0-498d-9644-e1c8eb514414.jpeg', | ||
tags: ['#디자인', '#회의'], | ||
}, | ||
]; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
'use client'; | ||
|
||
import { useCallback, useState } from 'react'; | ||
import { useRouter } from 'next/navigation'; | ||
import { FOLLOW_API } from '@/apis/follow'; | ||
import { RECOMMENDATION, RECOMMENDATION_REAL } from '@/app/onboarding/onboarding.constants'; | ||
import RecommendFollowItem from '@/app/onboarding/RecommendFollowItem'; | ||
import Button from '@/components/Button/Button'; | ||
import CenterTextHeader from '@/components/Header/CenterTextHeader'; | ||
import { EVENT_LOG_CATEGORY, EVENT_LOG_NAME } from '@/constants/eventLog'; | ||
import { ROUTER } from '@/constants/router'; | ||
import { eventLogger } from '@/utils'; | ||
import { getEnv } from '@/utils/appEnv'; | ||
import { css } from '@styled-system/css'; | ||
|
||
function OnboardingPage() { | ||
const [followList, setFollowList] = useState<number[]>([]); | ||
const router = useRouter(); | ||
const handleFollow = useCallback((id: number) => { | ||
eventLogger.logEvent(EVENT_LOG_CATEGORY.ONBOARDING, EVENT_LOG_NAME.ONBOARDING.SELECT_FOLLOW); | ||
setFollowList((prevState) => { | ||
if (prevState.includes(id)) { | ||
return prevState.filter((followId) => followId !== id); | ||
} | ||
|
||
return [...prevState, id]; | ||
}); | ||
}, []); | ||
|
||
const isSkip = followList.length === 0; | ||
|
||
const handleSkip = () => { | ||
eventLogger.logEvent(EVENT_LOG_CATEGORY.ONBOARDING, EVENT_LOG_NAME.ONBOARDING.CLICK_SKIP); | ||
router.replace(ROUTER.HOME); | ||
}; | ||
|
||
const handleComplete = async () => { | ||
await Promise.all( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 |
||
followList.map((id) => { | ||
return FOLLOW_API.addFollow(id); | ||
}), | ||
); | ||
eventLogger.logEvent(EVENT_LOG_CATEGORY.ONBOARDING, EVENT_LOG_NAME.ONBOARDING.CLICK_CONFIRM, { | ||
followList: followList.join(','), | ||
followCount: followList.length, | ||
}); | ||
router.replace(ROUTER.HOME); | ||
}; | ||
|
||
const follows = getEnv() === 'real' ? RECOMMENDATION_REAL : RECOMMENDATION; | ||
|
||
return ( | ||
<div> | ||
<CenterTextHeader | ||
title={'추천 친구'} | ||
rightComponent={ | ||
isSkip ? ( | ||
<Button variant={'ghost'} size={'medium'} onClick={handleSkip}> | ||
건너뛰기 | ||
</Button> | ||
) : ( | ||
<Button variant={'ghost'} size={'medium'} onClick={handleComplete}> | ||
완료 | ||
</Button> | ||
) | ||
} | ||
/> | ||
<div className={textSectionCss}> | ||
<img className={assetImagCss} src={'/assets/character/flag.png'} alt={'10mm character flag'} /> | ||
<p className={titleCss}>친구를 팔로우 해보세요!</p> | ||
<p className={subTitleCss}>팔로우를 통해 미션과 인증을 공유할 수 있어요.</p> | ||
</div> | ||
<ul className={followListCss}> | ||
{follows.map((props) => ( | ||
<RecommendFollowItem | ||
key={props.id.toString()} | ||
{...props} | ||
isFollowing={followList.includes(props.id)} | ||
onChangeFollow={handleFollow} | ||
/> | ||
))} | ||
</ul> | ||
</div> | ||
); | ||
} | ||
|
||
export default OnboardingPage; | ||
|
||
const followListCss = css({ | ||
padding: '0 16px', | ||
}); | ||
|
||
const assetImagCss = css({ | ||
width: '112px', | ||
height: '84px', | ||
}); | ||
|
||
const titleCss = css({ | ||
textStyle: 'title3', | ||
color: 'text.primary', | ||
marginBottom: '4px', | ||
}); | ||
|
||
const subTitleCss = css({ | ||
textStyle: 'body4', | ||
color: 'gray.gray600', | ||
}); | ||
|
||
const textSectionCss = css({ | ||
paddingTop: '28px', | ||
paddingBottom: '56px', | ||
|
||
display: 'flex', | ||
flexDirection: 'column', | ||
alignItems: 'center', | ||
justifyContent: 'center', | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import { css, cx } from '@styled-system/css'; | ||
import { center } from '@styled-system/patterns'; | ||
|
||
interface CenterTextHeaderProps { | ||
title: string; | ||
rightComponent?: React.ReactNode; | ||
} | ||
|
||
function CenterTextHeader({ title, rightComponent }: CenterTextHeaderProps) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 일단 임시로 사용하는 Header인거죠? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 네 빠르게 작업하기 위해서 추가한 컴포넌트입니다 |
||
return ( | ||
<div className={headerWrapperCss}> | ||
<div className={sectionWrapperCss} /> | ||
<div className={cx(sectionWrapperCss, centerCss)}> | ||
<h2 className={titleCss}>{title}</h2> | ||
</div> | ||
<div className={cx(sectionWrapperCss, flexEndCss)}>{rightComponent}</div> | ||
</div> | ||
); | ||
} | ||
|
||
export default CenterTextHeader; | ||
|
||
const headerWrapperCss = css({ | ||
height: '44px', | ||
display: 'flex', | ||
}); | ||
|
||
const titleCss = css({ | ||
color: 'text.primary', | ||
textStyle: 'subtitle1', | ||
}); | ||
|
||
const sectionWrapperCss = css({ | ||
width: '33%', | ||
}); | ||
|
||
const centerCss = center(); | ||
|
||
const flexEndCss = css({ | ||
display: 'flex', | ||
justifyContent: 'flex-end', | ||
alignItems: 'center', | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
process.env.node_env 애 떠러 REAL을 보여줄지 말지 컨트롤 하는건 어떨까요
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
node_env 인 dev 배포환경일때는 'production'으로 보이지 않나요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NEXT_PUBLIC_ENV 라는 환경변수를 따로 만들게요
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cadc522
여기서 getEnv 함수 만들었고, 버셀에 환경변수 추가해주었습니다.