-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #80 from Duri-Salon/feat(salon)/mypage-ui
[feat] 미용사 마이페이지 api
- Loading branch information
Showing
30 changed files
with
1,827 additions
and
116 deletions.
There are no files selected for viewing
This file contains 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 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,104 @@ | ||
import { useEffect, useRef } from 'react'; | ||
|
||
import { | ||
DesignerInfo, | ||
Flex, | ||
HeightFitFlex, | ||
Pencil, | ||
Text, | ||
theme, | ||
WidthFitFlex, | ||
} from '@duri-fe/ui'; | ||
import styled from '@emotion/styled'; | ||
|
||
interface DesignerInfoAreaProps { | ||
id: number; | ||
name: string; | ||
age: number; | ||
gender: string; | ||
history: number; | ||
license: string[]; | ||
image: string; | ||
onEdit: boolean; | ||
setOnEdit: React.Dispatch<React.SetStateAction<boolean>>; | ||
} | ||
|
||
const DesignerInfoArea = ({ | ||
id, | ||
name, | ||
age, | ||
gender, | ||
history, | ||
license, | ||
image, | ||
onEdit, | ||
setOnEdit, | ||
}: DesignerInfoAreaProps) => { | ||
const designerInfoRef = useRef<HTMLDivElement>(null); | ||
|
||
useEffect(() => { | ||
const handleClickOutside = (e: MouseEvent) => { | ||
if ( | ||
designerInfoRef.current && | ||
!designerInfoRef.current.contains(e.target as Node) | ||
) { | ||
setOnEdit(false); | ||
} | ||
}; | ||
document.addEventListener('mousedown', handleClickOutside); | ||
return () => { | ||
document.removeEventListener('mousedown', handleClickOutside); | ||
}; | ||
}, [designerInfoRef]); | ||
|
||
return ( | ||
<HeightFitFlex | ||
direction="column" | ||
margin="18px 0" | ||
align="flex-start" | ||
gap={24} | ||
> | ||
<Text typo="Title3">디자이너 소개</Text> | ||
<DesignerInfoWrapper | ||
justify="flex-start" | ||
ref={designerInfoRef} | ||
onClick={() => setOnEdit(true)} | ||
> | ||
<DesignerInfo | ||
version="vertical" | ||
designerId={id} | ||
name={name} | ||
age={age} | ||
gender={gender === 'F' ? '여성' : '남성'} | ||
experience={history} | ||
roles={license} | ||
imageUrl={image} | ||
isNavigate={false} | ||
/> | ||
{onEdit && ( | ||
<ShopEditArea backgroundColor={theme.palette.Black} borderRadius={12}> | ||
<Pencil width={16} /> | ||
<Text typo="Label3" colorCode={theme.palette.White}> | ||
수정하기 | ||
</Text> | ||
</ShopEditArea> | ||
)} | ||
</DesignerInfoWrapper> | ||
</HeightFitFlex> | ||
); | ||
}; | ||
|
||
const DesignerInfoWrapper = styled(WidthFitFlex)` | ||
position: relative; | ||
`; | ||
|
||
const ShopEditArea = styled(Flex)` | ||
position: absolute; | ||
top: -4px; | ||
left: -4px; | ||
width: calc(100% + 8px); | ||
height: calc(100% + 8px); | ||
background-color: rgba(17, 17, 17, 0.5); | ||
`; | ||
|
||
export default DesignerInfoArea; |
This file contains 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,51 @@ | ||
import { | ||
Flex, | ||
HeightFitFlex, | ||
RatingStars, | ||
Text, | ||
WidthFitFlex, | ||
} from '@duri-fe/ui'; | ||
import { MyShopReviewType } from '@duri-fe/utils'; | ||
|
||
import { ShopReviewBox } from './ShopReviewBox'; | ||
|
||
interface ReviewPreviewProps { | ||
shopRating: number; | ||
reviewCnt: number; | ||
reviewData: MyShopReviewType[]; | ||
} | ||
|
||
const ReviewPreview = ({ | ||
shopRating, | ||
reviewCnt, | ||
reviewData, | ||
}: ReviewPreviewProps) => { | ||
return ( | ||
<HeightFitFlex | ||
direction="column" | ||
align="flex-start" | ||
margin="32px 0 0 0" | ||
gap={24} | ||
> | ||
<WidthFitFlex gap={7}> | ||
<Text typo="Title3">리뷰</Text> | ||
<WidthFitFlex> | ||
<Text typo="Label3">{shopRating}</Text> | ||
<RatingStars score={shopRating} size={14} /> | ||
<Text typo="Label3">({reviewCnt})</Text> | ||
</WidthFitFlex> | ||
</WidthFitFlex> | ||
{reviewData && reviewData.length > 0 ? ( | ||
reviewData.map((review) => ( | ||
<ShopReviewBox key={review.reviewId} review={review} /> | ||
)) | ||
) : ( | ||
<Flex height={48}> | ||
<Text>아직 등록된 리뷰가 없습니다.</Text> | ||
</Flex> | ||
)} | ||
</HeightFitFlex> | ||
); | ||
}; | ||
|
||
export default ReviewPreview; |
This file contains 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,172 @@ | ||
import { useState } from 'react'; | ||
import { useNavigate } from 'react-router-dom'; | ||
|
||
import { | ||
Button, | ||
Flex, | ||
Menu, | ||
Modal, | ||
ProfileImage, | ||
RatingStars, | ||
Text, | ||
theme, | ||
WidthFitFlex, | ||
} from '@duri-fe/ui'; | ||
import { useDeleteReview, useModal } from '@duri-fe/utils'; | ||
import styled from '@emotion/styled'; | ||
|
||
interface ReviewUserInfoProps { | ||
reviewId: number; | ||
userImageURL: string; | ||
userName: string; | ||
rating: number; | ||
createdAt: string; | ||
} | ||
|
||
export const ReviewUserInfo = ({ | ||
reviewId, | ||
createdAt, | ||
rating, | ||
userImageURL, | ||
userName, | ||
}: ReviewUserInfoProps) => { | ||
const navigate = useNavigate(); | ||
|
||
const { isOpenModal, toggleModal } = useModal(); | ||
|
||
const [isOpen, setIsOpen] = useState<boolean>(false); | ||
|
||
const { mutateAsync: deleteReview } = useDeleteReview(() => { | ||
navigate('/my/review', { replace: true }); | ||
}); | ||
|
||
const handleClickMenu = () => { | ||
setIsOpen(!isOpen); | ||
}; | ||
|
||
const handleClickModifyButton = () => { | ||
navigate('/my/review/modify', { state: reviewId }); | ||
}; | ||
|
||
const handleClickDeleteButton = () => { | ||
//삭제 모달 띄우기 | ||
toggleModal(); | ||
}; | ||
|
||
const handleClickDeleteConfirmButton = () => { | ||
deleteReview(reviewId); | ||
}; | ||
|
||
return ( | ||
<Wrapper justify="space-between"> | ||
{/* 사용자 프로필, 평점 */} | ||
<WidthFitFlex justify="flex-start" gap={15.58}> | ||
<ProfileImage | ||
width={34} | ||
height={34} | ||
borderRadius={34} | ||
src={userImageURL} | ||
/> | ||
<WidthFitFlex direction="column" gap={2} align="start"> | ||
<Text typo="Body3">{userName}</Text> | ||
<Flex> | ||
<RatingStars size={12} score={rating} /> | ||
</Flex> | ||
</WidthFitFlex> | ||
</WidthFitFlex> | ||
|
||
{/* 오른쪽 버튼, 작성일자 */} | ||
<WidthFitFlex gap={8}> | ||
<SingleLineText typo="Caption5" colorCode={theme.palette.Gray300}> | ||
{createdAt} | ||
</SingleLineText> | ||
<WidthFitFlex onClick={handleClickMenu}> | ||
<Menu width={23} height={23} /> | ||
</WidthFitFlex> | ||
</WidthFitFlex> | ||
{isOpen && ( | ||
<MenuCard | ||
direction="column" | ||
borderRadius={8} | ||
width={114} | ||
height={67} | ||
gap={8} | ||
> | ||
<MenuItem onClick={handleClickModifyButton}> | ||
<Text typo="Label3">수정하기</Text> | ||
</MenuItem> | ||
<MenuItem onClick={handleClickDeleteButton}> | ||
<Text typo="Label3">삭제하기</Text> | ||
</MenuItem> | ||
</MenuCard> | ||
)} | ||
{isOpenModal && ( | ||
<Modal | ||
isOpen={isOpenModal} | ||
toggleModal={toggleModal} | ||
title="후기를 삭제하시겠습니까?" | ||
closeIcon={false} | ||
> | ||
<Flex direction="column" gap={24}> | ||
<Flex direction="column"> | ||
<Text typo="Caption2" colorCode={theme.palette.Gray400}> | ||
후기 삭제 후 | ||
</Text> | ||
<Text typo="Caption2" colorCode={theme.palette.Gray400}> | ||
복구할 수 없습니다. | ||
</Text> | ||
</Flex> | ||
<Flex> | ||
<Button | ||
width="104px" | ||
height="47px" | ||
padding="10px" | ||
bg={theme.palette.Gray20} | ||
typo="Body3" | ||
onClick={toggleModal} | ||
> | ||
아니오 | ||
</Button> | ||
<Button | ||
width="145px" | ||
height="47px" | ||
padding="10px" | ||
bg={theme.palette.Alert} | ||
typo="Body3" | ||
onClick={handleClickDeleteConfirmButton} | ||
> | ||
네 | ||
</Button> | ||
</Flex> | ||
</Flex> | ||
</Modal> | ||
)} | ||
</Wrapper> | ||
); | ||
}; | ||
|
||
const Wrapper = styled(Flex)` | ||
position: relative; | ||
`; | ||
const MenuCard = styled(Flex)` | ||
position: absolute; | ||
top: 37.4px; | ||
right: 9px; | ||
background-color: ${theme.palette.White}; | ||
box-shadow: 0px 0px 4px 0px rgba(0, 0, 0, 0.1); | ||
`; | ||
const SingleLineText = styled(Text)` | ||
word-break: no-wrap; | ||
`; | ||
|
||
const MenuItem = styled.div` | ||
width: 100%; | ||
height: 100%; | ||
display: flex; | ||
justify-content: center; | ||
cursor: pointer; | ||
padding: 0 10px; // 좌우 여백을 추가하여 텍스트가 너무 붙지 않도록 조정 | ||
&:hover { | ||
background-color: ${theme.palette.Gray_White}; | ||
} | ||
`; |
Oops, something went wrong.