diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index eafc22e..7b4bb58 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -22,6 +22,7 @@ function App() { ItemProvider, ProgressProvider, ]; + return ( diff --git a/frontend/src/components/cards/SummaryOptionCard.tsx b/frontend/src/components/cards/SummaryOptionCard.tsx index 3069208..1eef106 100644 --- a/frontend/src/components/cards/SummaryOptionCard.tsx +++ b/frontend/src/components/cards/SummaryOptionCard.tsx @@ -22,9 +22,7 @@ export default function SummaryOptionCard({ -
- {title} -
+ {title} +{price.toLocaleString()} 원 @@ -34,32 +32,40 @@ export default function SummaryOptionCard({ } const Card = styled(DefaultCardStyle)` - position: relative; width: 103px; height: 107px; border-radius: 2px; + display: flex; + flex-direction: column; `; const OptionImg = styled.img` + object-fit: cover; border-radius: 1px 1px 0px 0px; width: 100%; height: 49px; background-color: rgba(211, 211, 211, 0.5); `; const OptionCardInfo = styled.div` - padding: 4px 8px; display: flex; + padding: 4px 8px; flex-direction: column; justify-content: space-between; + width: 100%; height: 57px; `; const OptionTitle = styled.div` ${BodyKrMedium5} + + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; `; const OptionPrice = styled.div` ${BodyKrMedium5} display: flex; + white-space: nowrap; justify-content: space-between; align-items: center; `; diff --git a/frontend/src/components/common/navbar/CarSelectContainer.tsx b/frontend/src/components/common/navbar/CarSelectContainer.tsx index d5d7c28..397402f 100644 --- a/frontend/src/components/common/navbar/CarSelectContainer.tsx +++ b/frontend/src/components/common/navbar/CarSelectContainer.tsx @@ -2,11 +2,12 @@ import { styled } from 'styled-components'; import CenterWrapper from '../layout/CenterWrapper'; import { useFetch } from '../../../hooks/useFetch'; import { CAR_LIST_API, IMG_URL } from '../../../utils/apis'; -import { Dispatch, useState } from 'react'; +import { Dispatch, useContext, useState } from 'react'; import { flexCenterCss } from '../../../utils/commonStyle'; import { BodyKrMedium3, BodyKrMedium4 } from '../../../styles/typefaces'; import DefaultCardStyle from '../card/DefaultCardStyle'; import { DimmedBackground } from '../../modal/DimmedBackground'; +import { ItemContext } from '../../../context/ItemProvider'; interface ICar { carTypeId: number; @@ -21,9 +22,16 @@ interface ICarSelectContainer { export default function CarSelectContainer({ visible, setMenuVisible }: ICarSelectContainer) { const { data } = useFetch(CAR_LIST_API); + const { selectedItem, setSelectedItem } = useContext(ItemContext); const [active, setActive] = useState(3); // 3: SUV const categoryList = ['수소/전기차', 'N', '승용', 'SUV', 'MVP', '소형트럭/택시', '트럭', '버스']; + const handleCarClick = (car: ICar) => { + setSelectedItem({ + type: 'SET_CAR', + value: { id: car.carTypeId, name: car.carTypeName, price: 0 }, + }); + }; const handleCategoryClick = (idx: number) => { setActive(idx); }; @@ -31,7 +39,6 @@ export default function CarSelectContainer({ visible, setMenuVisible }: ICarSele setMenuVisible((cur) => !cur); }; const isActive = (idx: number) => idx === active; - const categoryItemComponents = categoryList?.map((category, idx) => { return ( handleCategoryClick(idx)} key={idx}> @@ -41,7 +48,11 @@ export default function CarSelectContainer({ visible, setMenuVisible }: ICarSele }); const carItemComponents = data?.map((car, idx) => ( - + handleCarClick(car)} + key={idx} + > {car.carTypeName} @@ -99,6 +110,9 @@ const CarListWrapper = styled.div` margin-top: 20px; overflow: scroll; gap: 8px; + &::-webkit-scrollbar { + display: none; + } `; const CarItem = styled(DefaultCardStyle)` ${flexCenterCss} diff --git a/frontend/src/components/common/navbar/NavBar.tsx b/frontend/src/components/common/navbar/NavBar.tsx index dcafb6f..8c0dd9c 100644 --- a/frontend/src/components/common/navbar/NavBar.tsx +++ b/frontend/src/components/common/navbar/NavBar.tsx @@ -1,4 +1,4 @@ -import React, { useContext, useState } from 'react'; +import React, { useContext, useEffect, useState } from 'react'; import { useLocation, useNavigate } from 'react-router-dom'; import { css, styled, useTheme } from 'styled-components'; import { BodyKrMedium3, BodyKrRegular3, HeadingKrMedium6 } from '../../../styles/typefaces'; @@ -8,6 +8,7 @@ import { MESSAGE, PATH } from '../../../utils/constants'; import { CloseModalContext } from '../../../context/ModalProviders/CloseModalProvider'; import { ProgressContext } from '../../../context/ProgressProvider'; import CarSelectContainer from './CarSelectContainer'; +import { ItemContext } from '../../../context/ItemProvider'; interface INavItem extends React.HTMLAttributes { active: boolean; @@ -15,6 +16,7 @@ interface INavItem extends React.HTMLAttributes { export default function NavBar() { const navigate = useNavigate(); const { nextStepAvailable } = useContext(ProgressContext); + const { selectedItem } = useContext(ItemContext); const { pathname: currentPath } = useLocation(); const { setVisible: setCloseModalVisible } = useContext(CloseModalContext); const [menuVisible, setMenuVisible] = useState(false); @@ -36,6 +38,9 @@ export default function NavBar() { const handleCarSelectClick = () => { setMenuVisible((cur) => !cur); }; + useEffect(() => { + window.scrollTo(0, 0); + }, [currentPath]); return ( @@ -44,7 +49,7 @@ export default function NavBar() { handleNavItemClick(PATH.home)} /> - 펠리세이드 + {selectedItem.cartype.name} {menuVisible ? ( ) : ( diff --git a/frontend/src/components/details/SummaryItem.tsx b/frontend/src/components/details/SummaryItem.tsx index c0c7613..74a8054 100644 --- a/frontend/src/components/details/SummaryItem.tsx +++ b/frontend/src/components/details/SummaryItem.tsx @@ -44,7 +44,7 @@ const Item = styled.li` const Img = styled.img` width: 77px; height: 55px; - object-fit: cover; + object-fit: contain; `; const InfoWrapper = styled.div` display: flex; diff --git a/frontend/src/components/histogram/BarHistogram.tsx b/frontend/src/components/histogram/BarHistogram.tsx index a1b6728..e3a022a 100644 --- a/frontend/src/components/histogram/BarHistogram.tsx +++ b/frontend/src/components/histogram/BarHistogram.tsx @@ -54,8 +54,8 @@ export default function BarHistogram() { return ( - {soldCount}대 - 내 견적 + {soldCount.toLocaleString()}대 + 유사견적 ); @@ -87,7 +87,8 @@ export default function BarHistogram() { 들을 확인하고 비교해보세요. - 유사 출고 견적이란, 내 견적과 해시태그 유사도가 높은 다른 사람들의 실제 출고 견적이에요. + 유사 출고 견적이란, 판매량 및 내 견적과 선택 옵션 유사도가 높은 다른 사람들의 실제 출고 + 견적이에요. {quoteListLoading || !hasSimilarQuote || !quoteListData ? ( @@ -147,7 +148,7 @@ const CaptionWrapper = styled.div` margin-bottom: 20px; `; const Caption = styled.div` - width: 200px; + word-break: keep-all; `; const BlueText = styled.span` color: ${({ theme }) => theme.color.activeBlue}; diff --git a/frontend/src/components/modal/QuoteSummaryModal.tsx b/frontend/src/components/modal/QuoteSummaryModal.tsx index 637c5cc..33dcc51 100644 --- a/frontend/src/components/modal/QuoteSummaryModal.tsx +++ b/frontend/src/components/modal/QuoteSummaryModal.tsx @@ -71,11 +71,7 @@ export default function QuoteSummaryModal({ ...props }: IQuoteSummaryModal) { - +
{title && {title}} {name && {name}} - {price && + {price.toLocaleString()} 원} + + {price ? price.toLocaleString() : 0} 원 ); } @@ -181,7 +177,6 @@ const DetailSection = styled.div` const DetailWrapper = styled.div` display: flex; - justify-content: space-between; ${BodyKrRegular3} `; const DetailTitle = styled.div` diff --git a/frontend/src/components/modal/SimilarQuoteModal.tsx b/frontend/src/components/modal/SimilarQuoteModal.tsx index 5bbaff8..1cc6805 100644 --- a/frontend/src/components/modal/SimilarQuoteModal.tsx +++ b/frontend/src/components/modal/SimilarQuoteModal.tsx @@ -3,6 +3,7 @@ import { MouseEventHandler, useCallback, useContext, + useEffect, useRef, useState, } from 'react'; @@ -35,20 +36,22 @@ export default function SimilarQuoteModal({ ...props }: ISimilarQuoteModal) { const { totalPrice, selectedItem, setSelectedItem } = useContext(ItemContext); const { visible, setVisible, similarQuoteIdList } = useContext(SimilarQuoteModalContext); const [page, setPage] = useState(0); - const prevPrice = useRef(totalPrice); + const { data: similarQuoteData } = useSimilarDetail(similarQuoteIdList); + const CARD_SLIDE_MAX_PAGE = similarQuoteData ? similarQuoteData.length : 0; + const arrowLeftColor = page <= 0 ? theme.color.gray200 : theme.color.gray600; + const arrowRightColor = + page >= CARD_SLIDE_MAX_PAGE - 1 ? theme.color.gray200 : theme.color.gray600; const stopEvent: MouseEventHandler = (e) => { e.stopPropagation(); }; - const { data: similarQuoteData } = useSimilarDetail(similarQuoteIdList); const handlePrevPage = () => { - const prevPage = page <= 0 ? 0 : page - 1; - setPage(prevPage); + if (page - 1 < 0) return; + setPage(page - 1); }; const handleNextPage = () => { - const maxPage = similarQuoteData ? similarQuoteData.length : 0; - if (page >= maxPage - 1) return; + if (page + 1 >= CARD_SLIDE_MAX_PAGE) return; setPage(page + 1); }; @@ -111,91 +114,107 @@ export default function SimilarQuoteModal({ ...props }: ISimilarQuoteModal) { ); const difference = - similarQuoteData && similarQuoteData[page].reduce((acc, option) => acc + option.optionPrice, 0); + similarQuoteData && similarQuoteData?.length !== 0 + ? similarQuoteData[page].reduce((acc, option) => acc + option.optionPrice, 0) + : 0; + const prevPrice = useRef(totalPrice); + + useEffect(() => { + prevPrice.current = totalPrice; + }, [prevPrice, totalPrice]); return ( - {similarQuoteData ? ( - <> -
- setVisible(false)}> - - -
- - - - 내 견적과 비슷한 실제 출고 견적들을 확인하고 비교해보세요. - - - *유사 출고 견적이란, -
내 견적의 판매량과 선택 옵션 유사도가 높은 다른 사람들의 실제 출고 - 견적이에요. -
-
- -
- - - - - - - 유사견적서 - Le Blanc - - {selectedItem.modelType.powerTrain.name} - {selectedItem.modelType.bodyType.name} - {selectedItem.modelType.operation.name} - - {prevPrice.current.toLocaleString()}원 - + {difference?.toLocaleString()}원 - - - - - - - - -

내 견적에 없는 옵션이에요.

- {displayCards(page)} -
-
- - - -
- - 옵션 선택하기 - - - ) : ( - - )} + +
+ setVisible(false)}> + + +
+ {similarQuoteData ? ( + <> + + + + 내 견적과 비슷한 실제 출고 견적들을 확인하고 비교해보세요. + + + *유사 출고 견적이란, +
내 견적의 판매량과 선택 옵션 유사도가 높은 다른 사람들의 실제 출고 + 견적이에요. +
+
+ +
+ + + + + + + 유사견적서 + {selectedItem.trim.name} + + {selectedItem.modelType.powerTrain.name} + {selectedItem.modelType.bodyType.name} + {selectedItem.modelType.operation.name} + + {(prevPrice.current + difference!).toLocaleString()}원 + + {difference?.toLocaleString()}원 + + + + + + + + +

내 견적에 없는 옵션이에요.

+ {displayCards(page)} +
+
+ = CARD_SLIDE_MAX_PAGE - 1 ? 'default' : 'pointer' }} + > + + +
+ + ) : ( + + )} +
+ + 옵션 선택하기 +
); } const Modal = styled.div` - position: relative; - display: flex; + background: ${({ theme }) => theme.color.white}; + width: 850px; + height: 520px; + position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); - - width: 850px; - height: 520px; border-radius: 8px; - background: ${({ theme }) => theme.color.white}; +`; - padding: 20px 20px 72px 20px; +const Wrapper = styled.div` + height: 100%; display: flex; flex-direction: column; + position: relative; + padding: 20px 20px 72px 20px; `; - const Header = styled.div` display: flex; justify-content: flex-end; @@ -210,8 +229,7 @@ const InfoWrapper = styled.div` const TextWrapper = styled.div` display: flex; flex-direction: column; - justify-content: space-between; - width: 367px; + justify-content: space-around; gap: 15px; `; const TitleText = styled.div` @@ -306,6 +324,7 @@ const TypeTagWrapper = styled.div` display: flex; gap: 10px; height: 16px; + margin-bottom: 4px; `; const TypeTag = styled.div` ${flexCenterCss} diff --git a/frontend/src/components/priceStaticBar/SimilarPriceBar.tsx b/frontend/src/components/priceStaticBar/SimilarPriceBar.tsx index 9afcb7c..8153f59 100644 --- a/frontend/src/components/priceStaticBar/SimilarPriceBar.tsx +++ b/frontend/src/components/priceStaticBar/SimilarPriceBar.tsx @@ -1,4 +1,4 @@ -import { ChangeEvent, useCallback, useContext, useEffect, useState } from 'react'; +import { useCallback, useContext, useEffect, useState } from 'react'; import { styled } from 'styled-components'; import { flexCenterCss } from '../../utils/commonStyle'; import { BodyKrMedium5, BodyKrRegular4, HeadingKrMedium6 } from '../../styles/typefaces'; @@ -13,21 +13,16 @@ interface ISimilarPriceBar extends React.HTMLAttributes { export default function SimilarPriceBar({ similarPrice, ...props }: ISimilarPriceBar) { const { totalPrice, selectedItem } = useContext(ItemContext); - - const [budget, setBudget] = useState((selectedItem.trim.price + HIGHEST_PRICE) / 2); const [isOverBudget, setIsOverBudget] = useState(false); - const balance = ((isOverBudget ? -1 : 1) * (budget - totalPrice)).toLocaleString(); + const balance = ((isOverBudget ? -1 : 1) * (totalPrice - similarPrice)).toLocaleString(); const getBudgetStatus = useCallback(() => { - const status = budget - totalPrice; + const status = totalPrice - similarPrice; status >= 0 ? setIsOverBudget(false) : setIsOverBudget(true); - }, [budget, totalPrice]); - const handleChange = (event: ChangeEvent) => { - const newValue = Number(event.target.value); - setBudget(newValue); - }; + }, [similarPrice, totalPrice]); + useEffect(() => { getBudgetStatus(); - }, [budget, getBudgetStatus]); + }, [similarPrice, getBudgetStatus]); return ( @@ -39,13 +34,11 @@ export default function SimilarPriceBar({ similarPrice, ...props }: ISimilarPric diff --git a/frontend/src/components/priceStaticBar/SimilarPriceSlider.tsx b/frontend/src/components/priceStaticBar/SimilarPriceSlider.tsx index 0a1cd72..96dfe81 100644 --- a/frontend/src/components/priceStaticBar/SimilarPriceSlider.tsx +++ b/frontend/src/components/priceStaticBar/SimilarPriceSlider.tsx @@ -1,22 +1,18 @@ import { styled } from 'styled-components'; import { flexCenterCss } from '../../utils/commonStyle'; import { BodyKrRegular3, BodyKrRegular5 } from '../../styles/typefaces'; -import { ChangeEvent, useContext } from 'react'; +import { useContext } from 'react'; import { HIGHEST_PRICE, HUNDRED_THOUSAND_UNIT, TEN_THOUSAND_UNIT } from '../../utils/constants'; import { ItemContext } from '../../context/ItemProvider'; interface ISimilarPriceSlider extends React.HTMLAttributes { isOverBudget: boolean; - budget: number; similarPrice: number; percent: number; - handleChange: (event: ChangeEvent) => void; } export default function SimilarPriceSlider({ isOverBudget, - budget, similarPrice, percent, - handleChange, ...props }: ISimilarPriceSlider) { const { totalPrice, selectedItem } = useContext(ItemContext); @@ -30,8 +26,6 @@ export default function SimilarPriceSlider({ {selectedItem.trim.price / TEN_THOUSAND_UNIT}만원 {HIGHEST_PRICE / TEN_THOUSAND_UNIT}만원 - {selectedItem.trim.price / TEN_THOUSAND_UNIT}만원 - {HIGHEST_PRICE / TEN_THOUSAND_UNIT}만원 ); @@ -97,12 +89,10 @@ const SimilarMarkerSvg = styled(MarkerSvg)` fill: white; `; const PriceBar = styled.input.attrs<{ $percent: number; $isover: boolean }>( - ({ type, min, value, onChange, step }) => ({ + ({ type, min, step }) => ({ type: type, min: min, max: HIGHEST_PRICE, - value: value, - onChange: onChange, step: step, }) )` diff --git a/frontend/src/containers/OptionPage/OptionBannerContainer.tsx b/frontend/src/containers/OptionPage/OptionBannerContainer.tsx index 6c2ecb5..c0903ef 100644 --- a/frontend/src/containers/OptionPage/OptionBannerContainer.tsx +++ b/frontend/src/containers/OptionPage/OptionBannerContainer.tsx @@ -13,8 +13,8 @@ import { useContext, useEffect, useState } from 'react'; import { IMG_URL, OPTION_API } from '../../utils/apis'; import { DefaultOptionContext } from '../../context/PageProviders/DefaultOptionProvider'; import { SubOptionContext } from '../../context/PageProviders/SubOptionProvider'; -import { CAR_TYPE } from '../../utils/constants'; import { useFetch } from '../../hooks/useFetch'; +import { ItemContext } from '../../context/ItemProvider'; interface IHmgData { optionBoughtCount: number; @@ -48,11 +48,12 @@ interface IOptionBannerContainer { export default function OptionBannerContainer({ isDefault }: IOptionBannerContainer) { const defaultOptionContext = useContext(DefaultOptionContext); const subOptionContext = useContext(SubOptionContext); + const { selectedItem } = useContext(ItemContext); const { currentOptionIdx } = isDefault ? defaultOptionContext : subOptionContext; const optionType = isDefault ? 'default' : 'sub'; const { data: optionDetail, loading: optionDetailLoading } = useFetch( - `${OPTION_API}/${optionType}/detail/?carid=${CAR_TYPE}&optionid=${currentOptionIdx}` + `${OPTION_API}/${optionType}/detail/?carid=${selectedItem.trim.id}&optionid=${currentOptionIdx}` ); const [bannerInfo, setBannerInfo] = useState({ @@ -105,7 +106,7 @@ export default function OptionBannerContainer({ isDefault }: IOptionBannerContai <> {optionDetail && !optionDetailLoading && ( - + diff --git a/frontend/src/containers/OptionPage/OptionFooterContainer.tsx b/frontend/src/containers/OptionPage/OptionFooterContainer.tsx index 7c257a3..612251e 100644 --- a/frontend/src/containers/OptionPage/OptionFooterContainer.tsx +++ b/frontend/src/containers/OptionPage/OptionFooterContainer.tsx @@ -15,7 +15,7 @@ export default function OptionFooterContainer() { const Wrapper = styled.div` z-index: 999; - position: sticky; + position: fixed; bottom: 0; left: 0; width: 100%; diff --git a/frontend/src/containers/OuterColorPage/OuterColorBannerContainer.tsx b/frontend/src/containers/OuterColorPage/OuterColorBannerContainer.tsx index eb7d05d..38abeb0 100644 --- a/frontend/src/containers/OuterColorPage/OuterColorBannerContainer.tsx +++ b/frontend/src/containers/OuterColorPage/OuterColorBannerContainer.tsx @@ -96,13 +96,6 @@ export default function OuterColorBannerContainer() { [selectedItem, isLoaded] ); - const displayCar360Components = useCallback(() => { - return car360UrlsData?.map((url, idx) => { - const imgSrc = imgBlobUrl[url]; - return ; - }); - }, [car360UrlsData, imgBlobUrl, imgState]); - useEffect(() => { if (!car360UrlsData || loading) return; const abortController = new AbortController(); @@ -112,6 +105,8 @@ export default function OuterColorBannerContainer() { }; }, [downloadAndSaveImages, car360UrlsData, loading]); + const imgSrc = car360UrlsData ? imgBlobUrl[car360UrlsData[imgState.visibleIdx]] : ''; + return ( <> 360° - {imgState.imgLoading ? : displayCar360Components()} + {imgState.imgLoading || !car360UrlsData ? : } @@ -135,8 +130,7 @@ export default function OuterColorBannerContainer() { const OuterColorBanner = styled(Banner)` background: ${({ theme }) => theme.color.blueBg}; `; -const CarImg = styled.img<{ $visible: boolean }>` - display: ${({ $visible }) => ($visible ? 'block' : 'none')}; +const CarImg = styled.img` position: absolute; right: 0; width: 592px; diff --git a/frontend/src/containers/ResultPage/DetailContainer.tsx b/frontend/src/containers/ResultPage/DetailContainer.tsx index df41cf9..68ef9bc 100644 --- a/frontend/src/containers/ResultPage/DetailContainer.tsx +++ b/frontend/src/containers/ResultPage/DetailContainer.tsx @@ -61,7 +61,7 @@ export default function DetailContainer() { handleModifyClick={() => navigate(PATH.modelType)} /> )); - const colorSummaryItems = [selectedItem.innerColor, selectedItem.outerColor].map((color, idx) => { + const colorSummaryItems = [selectedItem.outerColor, selectedItem.innerColor].map((color, idx) => { return ( {optionSummaryItems} -
setOpenedIdx(3)}>
+
setOpenedIdx(3)}>
- + {selectedItem.options.length !== 0 && } ); } -const Wrapper = styled.div``; +const Wrapper = styled.div` + width: 310px; +`; diff --git a/frontend/src/containers/TrimPage/TrimBannerContainer.tsx b/frontend/src/containers/TrimPage/TrimBannerContainer.tsx index 63dcfb7..837de1b 100644 --- a/frontend/src/containers/TrimPage/TrimBannerContainer.tsx +++ b/frontend/src/containers/TrimPage/TrimBannerContainer.tsx @@ -39,8 +39,8 @@ export default function TrimBannerContainer() { const downloadAndSaveImages = useCallback(async () => { const imageBlobs = await Promise.all( - imageUrls.current.map(async (url) => { - const response = await fetch(url); + imageUrls.current.map(async (url, idx) => { + const response = await fetch(url + `?${idx}`); const blob = await response.blob(); return blob; }) @@ -54,6 +54,7 @@ export default function TrimBannerContainer() { const setImages = useCallback(() => { if (!trimData) return; + setImagesLoading(true); imageUrls.current = []; filterImageUrls(trimData); downloadAndSaveImages(); diff --git a/frontend/src/containers/TrimPage/TrimSelectContainer.tsx b/frontend/src/containers/TrimPage/TrimSelectContainer.tsx index 9473797..a645707 100644 --- a/frontend/src/containers/TrimPage/TrimSelectContainer.tsx +++ b/frontend/src/containers/TrimPage/TrimSelectContainer.tsx @@ -147,10 +147,10 @@ const TrimSection = styled.div` `; const TrimCard = styled(DefaultCardStyle)` + width: 25%; padding: 20px 16px 12px 16px; height: 158px; box-sizing: border-box; - width: 100%; `; const TrimTitle = styled.div` diff --git a/frontend/src/context/ItemProvider.tsx b/frontend/src/context/ItemProvider.tsx index 9145003..ff4deab 100644 --- a/frontend/src/context/ItemProvider.tsx +++ b/frontend/src/context/ItemProvider.tsx @@ -21,6 +21,7 @@ export interface IEfficiencyType { displacement: string; } export interface ISelectedItem { + cartype: defaultItemType; trim: defaultItemType; modelType: { powerTrain: detailItemType; @@ -70,6 +71,11 @@ interface IItemProvider { } const initialSelectedItem = { + cartype: { + id: 1, + name: '팰리세이드', + price: 0, + }, trim: { id: 1, name: '', @@ -188,7 +194,6 @@ export default function ItemProvider({ children }: IItemProvider) { useEffect(() => { if (!data || loading || error) return; - setSelectedItem({ type: 'SET_TRIM', value: { diff --git a/frontend/src/hooks/useSharedInfo.ts b/frontend/src/hooks/useSharedInfo.ts index a5fefdc..6abefc8 100644 --- a/frontend/src/hooks/useSharedInfo.ts +++ b/frontend/src/hooks/useSharedInfo.ts @@ -16,7 +16,7 @@ interface IColorInfo { colorImage: string; colorType: string; colorPrice: number; - colorBoughtCount: number | null; + colorBoughtCount: number; colorCarImage: string; } export interface ISharedInfo { diff --git a/frontend/src/pages/ModelTypePage.tsx b/frontend/src/pages/ModelTypePage.tsx index d5296d8..5831a2a 100644 --- a/frontend/src/pages/ModelTypePage.tsx +++ b/frontend/src/pages/ModelTypePage.tsx @@ -5,15 +5,16 @@ import ModelSelectContainer from '../containers/ModelTypePage/ModelTypeSelectCon import { useFetch } from '../hooks/useFetch'; import { MODEL_TYPE_API } from '../utils/apis'; import { IModelType, ModelTypeContext } from '../context/PageProviders/ModelTypeProvider'; -import { CAR_TYPE } from '../utils/constants'; import ErrorModal from '../components/modal/ErrorModal'; +import { ItemContext } from '../context/ItemProvider'; export default function ModelTypePage() { + const { selectedItem } = useContext(ItemContext); const { data: modelTypeData, loading: modelTypeLoading, error: modelTypeError, - } = useFetch(`${MODEL_TYPE_API}/list?carid=${CAR_TYPE}`); + } = useFetch(`${MODEL_TYPE_API}/list?carid=${selectedItem.trim.id}`); const { setModelType, setLoading } = useContext(ModelTypeContext); diff --git a/frontend/src/pages/OptionPage.tsx b/frontend/src/pages/OptionPage.tsx index dad38eb..d679800 100644 --- a/frontend/src/pages/OptionPage.tsx +++ b/frontend/src/pages/OptionPage.tsx @@ -89,7 +89,8 @@ export default function OptionPage() { const Wrapper = styled.div` height: 100vh; width: 100vw; - padding-bottom: 120px; `; -const ContentWrapper = styled.div``; +const ContentWrapper = styled.div` + padding-bottom: 120px; +`; diff --git a/frontend/src/pages/TrimPage.tsx b/frontend/src/pages/TrimPage.tsx index 9ca3535..435904c 100644 --- a/frontend/src/pages/TrimPage.tsx +++ b/frontend/src/pages/TrimPage.tsx @@ -5,13 +5,17 @@ import { useFetch } from '../hooks/useFetch'; import { TRIM_API } from '../utils/apis'; import { ICartype, TrimContext } from '../context/PageProviders/TrimProvider'; import ErrorModal from '../components/modal/ErrorModal'; +import { ItemContext } from '../context/ItemProvider'; export default function TrimPage() { - const { data, loading, error } = useFetch(`${TRIM_API}?cartype=${1}`); + const { selectedItem } = useContext(ItemContext); + const { data, loading, error } = useFetch( + `${TRIM_API}?cartype=${selectedItem.cartype.id}` + ); const { setData, setLoading } = useContext(TrimContext); - useEffect(() => { setData(data); + setLoading(loading); }, [data, loading, setData, setLoading]); diff --git a/frontend/src/reducer/itemReducer.ts b/frontend/src/reducer/itemReducer.ts index f44c76e..5c6d927 100644 --- a/frontend/src/reducer/itemReducer.ts +++ b/frontend/src/reducer/itemReducer.ts @@ -7,6 +7,7 @@ import { } from '../context/ItemProvider'; export type actionType = + | { type: 'SET_CAR'; value: defaultItemType } | { type: 'SET_TRIM'; value: defaultItemType } | { type: 'SET_POWER_TRAIN'; value: detailItemType } | { type: 'SET_OPERATION'; value: detailItemType } @@ -18,6 +19,8 @@ export type actionType = export default function itemReducer(state: ISelectedItem, action: actionType): ISelectedItem { switch (action.type) { + case 'SET_CAR': + return { ...state, cartype: action.value }; case 'SET_TRIM': return { ...state, trim: action.value }; case 'SET_POWER_TRAIN': diff --git a/frontend/src/utils/constants.ts b/frontend/src/utils/constants.ts index e590eeb..e9ec6eb 100644 --- a/frontend/src/utils/constants.ts +++ b/frontend/src/utils/constants.ts @@ -1,11 +1,11 @@ export const MAX_PAGE = 3; export const NUM_IN_A_PAGE = 4; -export const DEBOUNCE_TIME = 200; +export const DEBOUNCE_TIME = 300; export const TEN_THOUSAND_UNIT = 10_000; export const HUNDRED_THOUSAND_UNIT = 100_000; export const PERCENTAGE_LIMIT_VALUE = 5; export const MAX_TEXT_CNT = 16; -export const HIGHEST_PRICE = 80_000_000; +export const HIGHEST_PRICE = 66_420_000; export const PATH = { home: '/', trim: '/trim', @@ -32,11 +32,9 @@ export const OUTER_COLOR_FIRST_IDX = 3; export const HYUNDAI_URL = 'https://www.hyundai.com/kr/ko/e'; export const PAGE_ANIMATION_DURATION = 500; -export const CAR_TYPE = 1; // 팰리세이드 Object.freeze({ MAX_PAGE, - CAR_TYPE, NUM_IN_A_PAGE, HYUNDAI_URL, PATH,