-
Notifications
You must be signed in to change notification settings - Fork 1
[DP-564] 픽픽픽 상세 페이지 V2 적용 #301
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
Merged
Merged
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
38bc074
feat: VoteButtonV2 구현
minyoungg-kim f8573bc
feat: VoteCardV2 구현
minyoungg-kim 73f1569
fix: pickDetailPage에 v2 적용
minyoungg-kim c93800b
fix: border -> outline 으로 수정
minyoungg-kim 7c1002f
fix: VoteCardV2 layout 수정
minyoungg-kim 99ba818
fix: [wip] 픽픽픽 상세 페이지 ui 수정
minyoungg-kim b1cee5a
Fix : 여러 해상도에서 UI 깨지는 부분 보완 (고정 width값 제거 , 깨지지 않도록 min-w적용 )
mandelina File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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 hidden or 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,115 @@ | ||
| import { motion } from 'framer-motion'; | ||
|
|
||
| import Image from 'next/image'; | ||
| import { useRouter } from 'next/router'; | ||
|
|
||
| import { cn } from '@utils/mergeStyle'; | ||
|
|
||
| import { useToastVisibleStore } from '@stores/toastVisibleStore'; | ||
|
|
||
| import PickCheck from '@public/image/pickpickpick/pick-check.svg'; | ||
| import PickNope from '@public/image/pickpickpick/pick-nope.svg'; | ||
| import PointUp from '@public/image/pickpickpick/point-up.svg'; | ||
|
|
||
| import { useMediaQueryContext } from '@/contexts/MediaQueryContext'; | ||
|
|
||
| import { usePostVote } from '../apiHooks/usePostVote'; | ||
| import { PickOptionData } from '../types/pickDetailData'; | ||
|
|
||
| interface VoteButtonProps { | ||
| pickOptionData?: PickOptionData; | ||
| dataIsVoted?: boolean; | ||
| pickOrder: 'first' | 'second'; | ||
| } | ||
|
|
||
| export default function VoteButtonV2({ pickOptionData, dataIsVoted, pickOrder }: VoteButtonProps) { | ||
| const { id: optionId, isPicked, percent, voteTotalCount } = pickOptionData ?? {}; | ||
|
|
||
| const { mutate: postVoteMutate } = usePostVote(); | ||
|
|
||
| const router = useRouter(); | ||
| const { id } = router.query; | ||
|
|
||
| const { setToastVisible } = useToastVisibleStore(); | ||
|
|
||
| const { isMobile } = useMediaQueryContext(); | ||
|
|
||
| const handleVote = () => { | ||
| if (!isPicked) { | ||
| return postVoteMutate({ pickId: id as string, pickOptionId: optionId }); | ||
| } | ||
|
|
||
| return setToastVisible({ | ||
| message: '동일한 픽픽픽 선택지에 투표할 수 없습니다.', | ||
| type: 'error', | ||
| }); | ||
| }; | ||
|
|
||
| const renderVoteResult = () => { | ||
| if (!dataIsVoted) { | ||
| return ( | ||
| <> | ||
| <div className='flex items-center gap-[1rem]'> | ||
| <span className='st2 font-bold text-gray200'>?? %</span> | ||
| <div className='w-[23.7rem] h-[1.6rem] rounded-[1rem] bg-gray200'></div> | ||
| </div> | ||
|
|
||
| <span className='st2 font-bold text-white flex gap-[1rem]'> | ||
| <Image src={PointUp} alt='위를 가리키는 손가락 아이콘' /> | ||
| PICK {pickOrder === 'first' ? 'A' : 'B'} | ||
| </span> | ||
| </> | ||
| ); | ||
| } | ||
|
|
||
| const isNotVotedOrPicked = !isPicked; | ||
|
|
||
| const percentageColor = isNotVotedOrPicked ? 'text-gray300' : 'text-white'; | ||
| const voteCountColor = isNotVotedOrPicked ? 'text-gray300' : 'text-primary200'; | ||
| const percentageBarColor = isNotVotedOrPicked ? 'bg-gray300' : 'bg-primary200'; | ||
|
|
||
| return ( | ||
| <> | ||
| <div className='flex items-center gap-[1rem]'> | ||
| <span className={cn(`st2 font-bold text-gray200 ${percentageColor}`)}>{percent} %</span> | ||
| <div | ||
| className={cn(`h-[1.6rem] rounded-[1rem] ${percentageBarColor}`)} | ||
| style={{ width: `${23.7 * (percent ?? 1) * 0.01}rem` }} | ||
| ></div> | ||
| <span className={cn(`c1 font-bold ${voteCountColor}`)}>{voteTotalCount}표</span> | ||
| </div> | ||
|
|
||
| {isPicked ? ( | ||
| <span className='st2 font-bold text-white flex gap-[1rem]'> | ||
| <Image src={PickCheck} alt='체크된 아이콘' /> | ||
| PICK! | ||
| </span> | ||
| ) : ( | ||
| <span className='st2 font-bold text-gray300 flex gap-[1rem]'> | ||
| <Image src={PickNope} alt='엑스 아이콘' /> | ||
| NOPE | ||
| </span> | ||
| )} | ||
| </> | ||
| ); | ||
| }; | ||
|
|
||
| const VOTE_BUTTON_STYLE = `rounded-[1.6rem] border border-gray300 flex flex-col justify-center gap-[2rem] p-[2.4rem] w-full | ||
| ${isMobile ? '' : 'min-w-[16rem] max-h-[28.7rem]'}`; | ||
|
|
||
| const votebuttonClass = cn(VOTE_BUTTON_STYLE, { | ||
| 'bg-primary400 border-primary400': isPicked && dataIsVoted, | ||
| 'bg-gray500 border-gray400': !isPicked && dataIsVoted, | ||
| }); | ||
|
|
||
| return ( | ||
| <motion.button | ||
| whileHover={isMobile ? '' : { scale: 0.95 }} | ||
| whileTap={{ scale: 0.9 }} | ||
| onClick={handleVote} | ||
| className={votebuttonClass} | ||
| > | ||
| {renderVoteResult()} | ||
| </motion.button> | ||
| ); | ||
| } |
This file contains hidden or 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,110 @@ | ||
| import { useState } from 'react'; | ||
|
|
||
| import Image from 'next/image'; | ||
|
|
||
| import AngleDownPoint from '@public/image/pickpickpick/angle-down-point.svg'; | ||
| import AngleUpPoint from '@public/image/pickpickpick/angle-up-point.svg'; | ||
|
|
||
| import { useMediaQueryContext } from '@/contexts/MediaQueryContext'; | ||
|
|
||
| import { PickOptionData } from '../types/pickDetailData'; | ||
| import MarkdownViewer from './MarkdownViewer'; | ||
| import VoteButtonV2 from './VoteButtonV2'; | ||
|
|
||
| export default function VoteCardV2({ | ||
| dataIsVoted, | ||
| pickDetailOptionData, | ||
| pickOrder, | ||
| }: { | ||
| dataIsVoted?: boolean; | ||
| pickDetailOptionData?: PickOptionData; | ||
| pickOrder: 'first' | 'second'; | ||
| }) { | ||
| const [isFullContents, setFullContents] = useState(false); | ||
| const { isMobile } = useMediaQueryContext(); | ||
|
|
||
| const handleFullContents = () => { | ||
| setFullContents(!isFullContents); | ||
| }; | ||
|
|
||
| return ( | ||
| <div className={`flex-1 flex flex-col gap-[2.4rem]`}> | ||
| <VoteButtonV2 | ||
| pickOptionData={pickDetailOptionData} | ||
| dataIsVoted={dataIsVoted} | ||
| pickOrder={pickOrder} | ||
| /> | ||
| <div | ||
| className={`py-[1.6rem] rounded-[1.6rem] outline flex flex-col w-full bg-black | ||
| ${isMobile ? 'px-[2.4rem]' : 'px-[4rem] '} | ||
| ${pickDetailOptionData?.isPicked ? 'outline-primary400 outline-[0.2rem]' : 'outline-gray500 outline-[0.1rem]'} | ||
| ${isFullContents ? '' : 'max-h-[58.8rem]'} | ||
| `} | ||
| > | ||
| <div className='relative overflow-hidden'> | ||
| <div | ||
| className={`transition-[max-height] duration-300 ease-in-out ${ | ||
| isFullContents ? 'max-h-none' : `${isMobile ? 'max-h-[32rem]' : ''} ` | ||
| }`} | ||
| > | ||
| <p className='py-[2.4rem] pb-[3.2rem] text-st1 leading-[2.8rem] font-semibold'> | ||
| {pickDetailOptionData?.title} | ||
| </p> | ||
|
|
||
| {(pickDetailOptionData?.content || | ||
| pickDetailOptionData?.pickDetailOptionImages?.length !== 0) && ( | ||
| <div className='border-t-[0.1rem] border-t-gray600 pt-[2.4rem] pb-[4rem] flex flex-col gap-[2.4rem]'> | ||
| {pickDetailOptionData?.content && ( | ||
| <MarkdownViewer pickDetailOptionContents={pickDetailOptionData?.content} /> | ||
| )} | ||
|
|
||
| {pickDetailOptionData?.pickDetailOptionImages?.length !== 0 && ( | ||
| <> | ||
| <p className='p2 font-light text-gray200 py-[2.4rem]'>첨부 이미지</p> | ||
| <div className='flex flex-col gap-[2.4rem]'> | ||
| {pickDetailOptionData?.pickDetailOptionImages?.map((optionImage) => ( | ||
| <img | ||
| src={optionImage.imageUrl} | ||
| alt={`픽픽픽 옵션 이미지-${optionImage.id}`} | ||
| key={optionImage.id} | ||
| className='rounded-[1.2rem]' | ||
| /> | ||
| ))} | ||
| </div> | ||
| </> | ||
| )} | ||
| </div> | ||
| )} | ||
| </div> | ||
|
|
||
| {/* gradient overlay */} | ||
| {!isFullContents && | ||
| (pickDetailOptionData?.content || | ||
| pickDetailOptionData?.pickDetailOptionImages?.length !== 0) && ( | ||
| <div className='absolute bottom-0 left-0 right-0 h-[17rem] bg-gradient-to-t from-black to-transparent z-20 pointer-events-none'></div> | ||
| )} | ||
| </div> | ||
|
|
||
| {(pickDetailOptionData?.content || | ||
| pickDetailOptionData?.pickDetailOptionImages?.length !== 0) && ( | ||
| <button | ||
| className='p2 font-bold text-secondary400 flex items-center gap-[0.8rem] justify-center mt-[4rem] mb-[1.6rem]' | ||
| onClick={handleFullContents} | ||
| > | ||
| {isFullContents ? ( | ||
| <> | ||
| <span>내용 접기</span> | ||
| <Image src={AngleUpPoint} alt='위 방향 화살표' /> | ||
| </> | ||
| ) : ( | ||
| <> | ||
| <span>내용 전체보기</span> | ||
| <Image src={AngleDownPoint} alt='아래 방향 화살표' /> | ||
| </> | ||
| )} | ||
| </button> | ||
| )} | ||
| </div> | ||
| </div> | ||
| ); | ||
| } |
This file contains hidden or 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
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.