Skip to content

Commit

Permalink
Merge pull request #182 from TEAM-FLASH/Feat/#166
Browse files Browse the repository at this point in the history
[S3] 스튜디오 상세정보 기능을 구현한다
  • Loading branch information
HuiseonDev authored Dec 27, 2024
2 parents a72bc64 + 6f6f53d commit 4e6b670
Show file tree
Hide file tree
Showing 2 changed files with 212 additions and 25 deletions.
205 changes: 190 additions & 15 deletions src/pages/Studio/StudioMain/StudioMain.tsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,28 @@
/** @jsxImportSource @emotion/react */

import Bookmark from '@components/Bookmark/Bookmark';
import Button from '@components/Button/Button';
import Header from '@components/Header/Header';
import KakaoMap from '@components/Kakao/KakaoMap';
import StudioNavigator from '@components/Navigator/StudioNavigator';
import Share from '@components/Share/Share';
import { css } from '@emotion/react';
import styled from '@emotion/styled';
import { useGetStudioDetail } from '@hooks/useGetStudioDetail';
import { DividerStyle, TypoBodyMdR, TypoBodyMdSb, TypoCapSmM, TypoCapSmR, TypoTitleMdSb } from '@styles/Common';
import { DividerStyle, TypoBodyMdR, TypoBodyMdSb, TypoCapSmM, TypoCapSmR, TypoTitleMdSb, TypoTitleXsM } from '@styles/Common';
import variables from '@styles/Variables';
import { useState } from 'react';
import { useNavigate, useParams } from 'react-router-dom';

///studio/detail/{studioId}

const StudioMain = () => {
const { _id } = useParams();
const { data, error } = useGetStudioDetail(`${_id}`);
const navigate = useNavigate();
const [isOpened, setIsOpened] = useState(false);

if (error instanceof Error) {
return <div>Error: {error.message}</div>;
}

if (!data) {
return <div>로딩</div>;
}
Expand All @@ -28,10 +31,31 @@ const StudioMain = () => {
const placeHolderImage = '/img/img-nopic.png';
const missingImgCount = data.portfolios.length < 5 ? 5 - data.portfolios.length : 0;
const portfolioWithPlaceHolders = [...data.portfolios, ...Array(missingImgCount).fill({ url: placeHolderImage })];
const day = { MONDAY: '월요일', TUESDAY: '화요일', WEDNESDAY: '수요일', THURSDAY: '목요일', FRIDAY: '금요일', SATURDAY: '토요일', SUNDAY: '일요일' };
const option = {
CHANGING_ROOM: '탈의실',
DRESSING_ROOM: '파우더룸',
HAIR_MAKEUP: '헤어, 메이크업 수정',
INDIVIDUAL_EDITING: '1:1 보정',
SUIT_RENTAL_FREE: '정장 대여',
ORIGINAL_FILES: '원본파일 제공',
PARKING_AREA: '주차',
};

const optionIcon = {
CHANGING_ROOM: '/img/icon-room.svg',
DRESSING_ROOM: '/img/icon-powder.svg',
HAIR_MAKEUP: '/img/icon-makeup.svg',
INDIVIDUAL_EDITING: '/img/icon-photo-edit.svg',
SUIT_RENTAL_FREE: '/img/icon-suit.svg',
ORIGINAL_FILES: '/img/icon-original-file.svg',
PARKING_AREA: '/img/icon-park.svg',
};

return (
<>
<Header customStyle={HeaderStyle} />

{/* 이미지 */}
<div css={portfolioPreviewStyle}>
{portfolioWithPlaceHolders.slice(0, 4).map((v, i) => (
Expand All @@ -57,8 +81,8 @@ const StudioMain = () => {
</div>
</div>
<div css={SocialActionsStyle}>
<p>공유</p>
<p>좋아요</p>
<Share title={data.name} description={data.description} imageUrl={data.portfolios[0].url} webUrl={window.location.href} />
<Bookmark id={+!_id} count={data.bookmark_count} isBookmarked={false} />
</div>
</div>

Expand All @@ -70,15 +94,15 @@ const StudioMain = () => {
</dt>
<dd>
<p className="highlight">영업중</p>
<p>{`${data.open_time} - ${data.close_time}`}</p>
{/* <p>{`${data.open_time} - ${data.close_time}`}</p> */}
</dd>
</div>
<div>
<dt>
<img src="/img/icon-location.svg" alt="주소" />
</dt>
<dd>
<p>{`${data.address}` === 'undefined' ? '주소 수집중' : `${data.address}`}</p>
<p>{`${data.address}` === 'undefined' ? '주소 수집중' : `${data.addressSi} ${data.addressGu} ${data.address}`}</p>
</dd>
</div>
<div>
Expand All @@ -92,14 +116,59 @@ const StudioMain = () => {
</dl>
</div>

{/* gray 여백 들어갈 자리 */}

{/* 네비게이션 바 */}
<StudioNavigator _id={_id || ''} />
{/* 홈 기본 정보 */}
<div css={descriptionStyle}>
<p className="descriptionTitle">매장소개</p>
<p>{`${data.description}`}</p>

{/* 홈 기본 정보 - 매장소개 */}
<div css={descriptionStyle(isOpened)}>
<p className="descriptionTitle">매장 소개</p>
<p className="textDisplay">{`${data.description}`}</p>
<span className="textMore" onClick={() => setIsOpened(!isOpened)}>
{isOpened ? '접기' : '더보기'}
</span>
</div>

{/* 홈 기본 정보 - 영업 정보 */}
<div css={openingHoursStyle}>
<p className="openingHoursTitle">영업 정보</p>
{data?.openingHours.length === 0
? '알 수 없음'
: data?.openingHours.map((v, i) => (
<dl key={i}>
<dt>{day[v.dayOfWeek as keyof typeof day]}</dt>
<dd>
{v.closed ? (
<p>정기 휴무</p>
) : (
<>
<time>{v.openTime.slice(0, 5)}</time>
<span>-</span>
<time>{v.closeTime.slice(0, 5)}</time>
</>
)}
</dd>
</dl>
))}
</div>

{/* 홈 기본정보 - 위치 정보 */}
<div css={mapStyle}>
<p>위치 정보</p>
<KakaoMap addressSi={data.addressSi} addressGu={data.addressGu} address={data.address} />
</div>

{/* 홈 기본정보 - 매장 정보 */}
<div css={optionsStyle}>
<p>매장 정보</p>
<div>
{data.options.length === 0
? '없음'
: data.options.map((v, i) => <Button key={i} text={option[v]} size="small" width="fit" variant="white" icon={<img src={optionIcon[v]} alt="필터 초기화" />} />)}
</div>
</div>

<div css={reservationStyle}>
<Button variant="black" text="예약하기" size="large" width="max" />
</div>
</>
);
Expand Down Expand Up @@ -246,4 +315,110 @@ const SocialActionsStyle = css`
color: ${variables.colors.gray700};
`;

const descriptionStyle = css``;
const descriptionStyle = (isOpened: boolean) => css`
padding: 2rem 0;
border-bottom: 0.1rem solid ${variables.colors.gray300};
& > .descriptionTitle {
${TypoTitleXsM};
color: ${variables.colors.black};
}
& > p {
padding-top: 1rem;
color: ${variables.colors.gray800};
${TypoBodyMdR};
}
& > .textDisplay {
overflow: hidden;
display: -webkit-box;
-webkit-line-clamp: ${isOpened ? 'none' : '3'};
-webkit-box-orient: vertical;
text-overflow: ellipsis;
}
& > .textMore {
color: ${variables.colors.gray600};
${TypoBodyMdR};
border-bottom: 0.1rem solid ${variables.colors.gray600};
cursor: pointer;
}
`;

const openingHoursStyle = css`
padding: 2rem 0;
border-bottom: 0.1rem solid ${variables.colors.gray300};
& > .openingHoursTitle {
${TypoTitleXsM};
margin-bottom: 0.2rem;
}
& > dl {
padding-top: 1rem;
padding-left: 0.8rem;
display: flex;
gap: 3rem;
& > dd {
display: flex;
gap: 0.8rem;
& > time {
display: flex;
justify-content: left;
}
& > time:first-of-type {
min-width: 4rem;
display: flex;
justify-content: left;
}
& > span {
margin-right: 0.2rem;
}
}
}
`;

const mapStyle = css`
padding: 2rem 0;
border-bottom: 0.1rem solid ${variables.colors.gray300};
& > p {
${TypoTitleXsM};
margin-bottom: 1rem;
}
`;

const optionsStyle = css`
padding: 2rem 0;
margin-bottom: 5rem;
& > p {
${TypoTitleXsM};
margin-bottom: 1rem;
}
& > div {
display: flex;
flex-wrap: wrap;
gap: 0.8rem;
}
`;

const reservationStyle = css`
background-color: ${variables.colors.white};
padding: 1rem 1.6rem 3rem 1.6rem;
position: fixed;
bottom: 0;
width: 100%;
left: 0;
z-index: 9;
& > button {
${TypoTitleXsM}
}
`;
32 changes: 22 additions & 10 deletions src/types/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,31 +37,43 @@ export interface IOptions {
updated_at: string;
}

export interface IOpeningHours {
closeTime: string;
closed: false;
dayOfWeek: string;
id: number;
openTime: string;
studioId: number;
studioName: string;
}

export interface IHolidays {
dayOfWeek: string;
id: number;
studioId: number;
studioName: string;
weekOfMonth: number;
}

export interface IStudioItem {
id: number;
vibe: string;
address: string;
addressSi: string;
addressGu: string;
name: string;
description: string;
address: string;
phone: string;
view_count: number;
rating: number;
bookmark_count: number;
review_count: number;
latitude: null | string;
longitude: null | string;
open_time: string;
close_time: string;
openingHours: IOpeningHours[];
subVibe: string;
portfolios: IPortfolio[];
menus: IMenus[];
options: [] | IOptions;
created_at: null | string;
updated_at: null | string;
day_of_week: 'MON' | 'TUE' | 'WED' | 'THU' | 'FRI' | 'SAT' | 'SUN';
bookmark: boolean;
options: [];
holydays: IHolidays[];
}

export interface IStudioRes<T> {
Expand Down

0 comments on commit 4e6b670

Please sign in to comment.