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

페이지 기능: 제품 상세 페이지 쿼리 훅 제작 및 msw 추가 #153

Merged
merged 8 commits into from
Feb 3, 2024
1 change: 0 additions & 1 deletion src/app/car-details/page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// app/hydratedPosts.jsx
import { dehydrate, Hydrate } from '@tanstack/react-query';

import getQueryClient from '@lib/getQueryClient';
Expand Down
2 changes: 1 addition & 1 deletion src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ function Home() {
<Spacing size={16} />
<div className={cx('productArticleContainer')}>
{MOCK_PRODUCT_LIST?.value.map((item) => {
return <ProductArticle key={item.id} itemData={item} />;
return <ProductArticle key={item.productNo} itemData={item} />;
})}
</div>
</div>
Expand Down
69 changes: 51 additions & 18 deletions src/app/product/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,18 @@

import classNames from 'classnames/bind';
import Image from 'next/image';
import { usePathname } from 'next/navigation';

import DropdownArrow from '@components/icons/DropdownArrow';
import Star from '@components/icons/Star';
import useProductDetails from '@remote/queries/product/useProductDetails';
import Accordion from '@shared/accordion/Accordion';
import AccordionBody from '@shared/accordion/body/AccordionBody';
import AccordionHeader from '@shared/accordion/header/AccordionHeader';
import AccordionItem from '@shared/accordion/item/AccordionItem';
import Flex from '@shared/flex/Flex';
import Header from '@shared/header/Header';
import Loader from '@shared/loader/Loader';
import Radio from '@shared/radio/Radio';
import Spacing from '@shared/spacing/Spacing';
import Text from '@shared/text/Text';
Expand All @@ -20,24 +23,54 @@ import styles from './page.module.scss';
const cx = classNames.bind(styles);

function ProductDetailsPage() {
const path = usePathname();
let productNo = Number(path.split('/').at(-1));

if (productNo == null) {
productNo = 1;
}
const { data: productDetailsData, isLoading } = useProductDetails(productNo);

if (isLoading || !productDetailsData) {
return <Loader />;
}

const {
brand,
productName,
upperItem,
grade,
reviewCnt,
companyName,
productType,
manufactureType,
manufactureMethod,
weight,
reportNumber,
mainSubstance,
usage,
usagePrecaution,
firstAid,
} = productDetailsData.value;

// TODO: 리뷰 기능 추가
return (
<>
<Header type="product" className={cx('product')} />
<Image src="/assets/product.png" alt="상품 이미지" width={375} height={375} />
<Flex direction="column" className={cx('productInfo')}>
<Text color="primary500" typography="t6">카믹스</Text>
<Text typography="t4" bold>아머올 세차용품 스피드 왁스 스프레이</Text>
<Text color="primary500" typography="t6">{brand}</Text>
<Text typography="t4" bold>{productName}</Text>
<Flex align="center">
<Text color="gray500" typography="t6">코팅제</Text>
<Text color="gray500" typography="t6">{upperItem}</Text>
<Spacing size={4} direction="vertical" />
<Text color="gray500" typography="t6">•</Text>
<Spacing size={4} direction="vertical" />
<Star size={14} />
<Spacing size={4} direction="vertical" />
<Text color="gray500" typography="t6">4.5</Text>
<Text color="gray500" typography="t6">{grade}</Text>
<Spacing size={4} direction="vertical" />
<Text color="gray500" typography="t6">(20)</Text>
<Text color="gray500" typography="t6">{reviewCnt}</Text>
</Flex>
</Flex>
<Flex>
Expand All @@ -59,42 +92,42 @@ function ProductDetailsPage() {
<Flex>
<Text typography="t7" className={cx('infoTitle')}>제품명</Text>
<Spacing size={8} direction="vertical" />
<Text color="gray600" typography="t6" className={cx('infoDescription')}>불스원 블랙홀 탈취제</Text>
<Text color="gray600" typography="t6" className={cx('infoDescription')}>{productName}</Text>
</Flex>
<Flex>
<Text typography="t7" className={cx('infoTitle')}>업체명</Text>
<Spacing size={8} direction="vertical" />
<Text color="gray600" typography="t6" className={cx('infoDescription')}>(주)불스원/직접생산</Text>
<Text color="gray600" typography="t6" className={cx('infoDescription')}>{companyName}</Text>
</Flex>
<Flex>
<Text typography="t7" className={cx('infoTitle')}>제품구분</Text>
<Spacing size={8} direction="vertical" />
<Text color="gray600" typography="t6" className={cx('infoDescription')}>대표</Text>
<Text color="gray600" typography="t6" className={cx('infoDescription')}>{productType}</Text>
</Flex>
<Flex>
<Text typography="t7" className={cx('infoTitle')}>품목</Text>
<Spacing size={8} direction="vertical" />
<Text color="gray600" typography="t6" className={cx('infoDescription')}>탈취제/방향 - 탈취제품</Text>
<Text color="gray600" typography="t6" className={cx('infoDescription')}>{upperItem}</Text>
</Flex>
<Flex>
<Text typography="t7" className={cx('infoTitle')}>제조구분</Text>
<Spacing size={8} direction="vertical" />
<Text color="gray600" typography="t6" className={cx('infoDescription')}>제포</Text>
<Text color="gray600" typography="t6" className={cx('infoDescription')}>{manufactureType}</Text>
</Flex>
<Flex>
<Text typography="t7" className={cx('infoTitle')}>제조방식</Text>
<Spacing size={8} direction="vertical" />
<Text color="gray600" typography="t6" className={cx('infoDescription')}>직접생산</Text>
<Text color="gray600" typography="t6" className={cx('infoDescription')}>{manufactureMethod}</Text>
</Flex>
<Flex>
<Text typography="t7" className={cx('infoTitle')}>중량</Text>
<Spacing size={8} direction="vertical" />
<Text color="gray600" typography="t6" className={cx('infoDescription')}>20~100(g)</Text>
<Text color="gray600" typography="t6" className={cx('infoDescription')}>{weight}</Text>
</Flex>
<Flex>
<Text typography="t7" className={cx('infoTitle')}>신고번호</Text>
<Spacing size={8} direction="vertical" />
<Text color="gray600" typography="t6" className={cx('infoDescription')}>GB23-13-0401</Text>
<Text color="gray600" typography="t6" className={cx('infoDescription')}>{reportNumber}</Text>
</Flex>
</AccordionBody>
</AccordionItem>
Expand All @@ -110,24 +143,24 @@ function ProductDetailsPage() {
className={cx('accordionBody')}
>
<Flex align="flex">
<Text typography="t7" className={cx('infoTitle')}>상세 성분</Text>
<Text typography="t7" className={cx('infoTitle')}>상세성분</Text>
<Spacing size={8} direction="vertical" />
<Text color="gray600" typography="t6" className={cx('infoDescription')}>리날로올, (E)-3,7-Dimethyl-2,6-octadien-1-ol,2-Methoxy-4-(2-propenyl)phenol; Eugenol, 4-Allyl-2-methoxyphenol, 4-Allyguaiacol,3,7-Dimethyl-6-octen-1-ol; Citronellol,d-리모넨,p-멘타-1,8-다이엔,리날로올,시트랄,3,7,7-트리메틸바이사이클로[4.1.0]헵-3-텐,d-리모넨,p-멘타-1,8-다이엔,아세트산 에틸</Text>
<Text color="gray600" typography="t6" className={cx('infoDescription')}>{mainSubstance}</Text>
</Flex>
<Flex>
<Text typography="t7" className={cx('infoTitle')}>제품용도</Text>
<Spacing size={8} direction="vertical" />
<Text color="gray600" typography="t6" className={cx('infoDescription')}>자동차용-실내용,일반용-실내공간용,일반용-밀폐공간용-옷장, 신발장</Text>
<Text color="gray600" typography="t6" className={cx('infoDescription')}>{usage}</Text>
</Flex>
<Flex>
<Text typography="t7" className={cx('infoTitle')}>주의사항</Text>
<Spacing size={8} direction="vertical" />
<Text color="gray600" typography="t6" className={cx('infoDescription')}>사용하시기 전에 표시 사항 및 사용방법을 확인하고 사용 하시오.</Text>
<Text color="gray600" typography="t6" className={cx('infoDescription')}>{usagePrecaution}</Text>
</Flex>
<Flex>
<Text typography="t7" className={cx('infoTitle')}>응급조치</Text>
<Spacing size={8} direction="vertical" />
<Text color="gray600" typography="t6" className={cx('infoDescription')}>피부자극 반응 또는 붉은 반점이 나타나면 의학적 조치를 받으시오.</Text>
<Text color="gray600" typography="t6" className={cx('infoDescription')}>{firstAid}</Text>
</Flex>
</AccordionBody>
</AccordionItem>
Expand Down
21 changes: 15 additions & 6 deletions src/components/shared/carousel/Banner.scss
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
/* stylelint-disable selector-class-pattern */
.container {
position: relative;

img {
width: auto;
margin: 0 auto;
}

.dotsCustom {
position: absolute;
bottom: 5px;
Expand All @@ -29,10 +25,23 @@
cursor: pointer;
}

/* stylelint-disable-next-line selector-class-pattern */
.dotsCustom li.slick-active button {
width: 24px;
border-radius: 8px;
background-color: var(--primary500);
}
}

.imageContainer {
position: relative;
width: 100%;
aspect-ratio: 16 / 9;
}

.slick-slide > div {
margin: 0 10px;
}

.slick-list {
margin: 0 -10px;
}
14 changes: 8 additions & 6 deletions src/components/shared/carousel/Banner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const settings = {
adaptiveHeight: true,
autoplaySpeed: 5000,
arrows: false,
centerPadding: '100px',
};

function Banner({ bannerData }: { bannerData: BannerType }) {
Expand All @@ -29,12 +30,13 @@ function Banner({ bannerData }: { bannerData: BannerType }) {
{bannerList.map((slide) => {
return (
<Link href={slide.link} key={slide.id}>
<Image
src={slide.src}
alt={slide.alt}
width={315}
height={140}
/>
<div className="imageContainer">
<Image
src={slide.imageSource}
alt={slide.alt}
fill
/>
</div>
</Link>
);
})}
Expand Down
2 changes: 1 addition & 1 deletion src/components/shared/carousel/RecommendList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ function RecommendList({ recommendProductsData }: { recommendProductsData: Recom
<Flex justify="center" align="center" direction="column" key={slide.id}>
<Link href={slide.link}>
<Image
src={slide.src}
src={slide.imageSource}
alt={slide.alt}
width={85}
height={85}
Expand Down
59 changes: 31 additions & 28 deletions src/components/shared/product-article/ProductArticle.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import classNames from 'classnames/bind';
import Image from 'next/image';
import Link from 'next/link';

import Heart from '@components/icons/Heart';
import { IProduct } from '@remote/api/types/home';
Expand All @@ -8,8 +9,8 @@ import Text from '@shared/text/Text';
import styles from './ProductArticle.module.scss';

interface ProductArticleProps {
isRow?: boolean;
itemData: IProduct
isRow?: boolean
itemData: Partial<IProduct>
}

const cx = classNames.bind(styles);
Expand All @@ -18,33 +19,35 @@ const warningIcon = '/assets/icons/warning.png';

function ProductArticle({ isRow = false, itemData }: ProductArticleProps) {
return (
<article className={cx('container', { row: isRow })}>
<div className={cx('imgBox')}>
<Image
src={itemData.img}
alt="제품 이미지"
width={0}
height={0}
sizes="100%"
style={{ width: '100%', height: 'inherit' }}
/>
<Image
className={cx('icon')}
src={itemData.warningLevel === 'warning' ? warningIcon : safeIcon}
alt={`${itemData.warningLevel} 아이콘 이미지`}
width={isRow ? 16 : 18}
height={isRow ? 16 : 18}
/>
</div>
<div className={cx('infoBox')}>
<Text className={cx('ellipsis')} typography="t8" whiteSpace="nowrap" color="primary500">{itemData.brand}</Text>
<Text className={cx('ellipsis')} typography="t6" color="gray900">{itemData.name}</Text>
<div>
<Text typography="t7" color="gray500">{itemData.category}</Text>
<Heart width={16} height={14} color="gray400" />
<Link href={`/product/${itemData.productNo}`}>
<article className={cx('container', { row: isRow })}>
<div className={cx('imgBox')}>
<Image
src={itemData.imageSource!}
alt="제품 이미지"
width={0}
height={0}
sizes="100%"
style={{ width: '100%', height: 'inherit' }}
/>
<Image
className={cx('icon')}
src={itemData.safetyStatus === 'warning' ? warningIcon : safeIcon}
alt={`${itemData.safetyStatus} 아이콘 이미지`}
width={isRow ? 16 : 18}
height={isRow ? 16 : 18}
/>
</div>
</div>
</article>
<div className={cx('infoBox')}>
<Text className={cx('ellipsis')} typography="t8" whiteSpace="nowrap" color="primary500">{itemData.brand}</Text>
<Text className={cx('ellipsis')} typography="t6" color="gray900">{itemData.productName}</Text>
<div>
<Text typography="t7" color="gray500">{itemData.upperItem}</Text>
<Heart width={16} height={14} color="gray400" />
</div>
</div>
</article>
</Link>
);
}
export default ProductArticle;
2 changes: 2 additions & 0 deletions src/mocks/handlers.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import { authHandlers } from './authHandler';
import { homeHandlers } from './homeHandler';
import { productHandlers } from './productHandler';

export const handlers = [
...authHandlers,
...homeHandlers,
...productHandlers,
];
Loading
Loading