Skip to content

Commit

Permalink
Merge pull request #319 from softeerbootcamp-2nd/feat/quote-summary-ui
Browse files Browse the repository at this point in the history
[FEAT] #316: 견적 요약 모달 창
  • Loading branch information
kimdaye77 authored Aug 19, 2023
2 parents b40a2ff + 9b61a0a commit 0bfba54
Show file tree
Hide file tree
Showing 16 changed files with 475 additions and 209 deletions.
2 changes: 2 additions & 0 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import SimilarQuoteModalProvider from './context/SimilarQuoteModalProvider';
import GuideModalProvider from './context/GuideModalProvider';
import ItemProvider from './context/ItemProvider';
import { useEffect } from 'react';
import QuoteSummaryModalProvider from './context/QuoteSummaryModalProvider';

function App() {
useEffect(() => {
Expand All @@ -19,6 +20,7 @@ function App() {
CloseModalProvider,
SimilarQuoteModalProvider,
GuideModalProvider,
QuoteSummaryModalProvider,
ItemProvider,
];
return (
Expand Down
45 changes: 45 additions & 0 deletions frontend/src/components/common/buttons/ToggleButtons.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { styled } from 'styled-components';
import { BodyKrRegular3 } from '../../../styles/typefaces';
import { flexCenterCss } from '../../../utils/commonStyle';
import { useState } from 'react';

type buttonType = 'outerColor' | 'innerColor';

export default function ToggleButtons() {
const [selectedButton, setSelectedButton] = useState<buttonType>('outerColor');

const toggle = () => {
if (selectedButton === 'outerColor') {
setSelectedButton('innerColor');
} else {
setSelectedButton('outerColor');
}
};

return (
<ToggleButtonContainer onClick={toggle}>
<Button $active={selectedButton === 'outerColor'}>외장</Button>
<Button $active={selectedButton === 'innerColor'}>내장</Button>
</ToggleButtonContainer>
);
}

const Button = styled.button<{ $active: boolean }>`
width: 100%;
${BodyKrRegular3}
height: 36px;
border-radius: 18px;
color: ${({ $active, theme }) => ($active ? theme.color.white : theme.color.primaryColor)};
background-color: ${({ $active, theme }) => ($active ? theme.color.primaryColor : 'transparent')};
`;
const ToggleButtonContainer = styled.div`
${flexCenterCss}
justify-content: space-between;
padding: 0 5px;
gap: 5px;
width: 213px;
height: 48px;
border-radius: 24px;
border: 1px solid ${({ theme }) => theme.color.gray100};
cursor: pointer;
`;
9 changes: 5 additions & 4 deletions frontend/src/components/details/Details.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
import { styled, useTheme } from 'styled-components';
import { HTMLAttributes } from 'react';
import { HeadingKrMedium5 } from '../../styles/typefaces';
import { BodyKrMedium3, HeadingKrMedium5 } from '../../styles/typefaces';
import { ArrowDown, ArrowUp } from '../common/icons/Icons';

interface IDetails extends HTMLAttributes<HTMLDivElement> {
title: string;
open?: boolean;
desc: string;
}

export default function Details({ title, open = false, ...props }: IDetails) {
export default function Details({ title, open = false, desc, ...props }: IDetails) {
const theme = useTheme();
const arrowColor = theme.color.gray900;
const totalPrice = 1_000_000;

return (
<Wrapper>
<Summary {...props}>
<span>{title}</span>
<RightDiv>
<Price>+{totalPrice.toLocaleString()}</Price>
<Price>{desc}</Price>
{open ? <ArrowUp fill={arrowColor} /> : <ArrowDown fill={arrowColor} />}
</RightDiv>
</Summary>
Expand Down Expand Up @@ -46,6 +46,7 @@ const Price = styled.span`
margin-right: 20px;
`;
const RightDiv = styled.div`
${BodyKrMedium3}
display: flex;
align-items: center;
color: ${({ theme }) => theme.color.primaryColor};
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/components/details/SummaryItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export default function SummaryItem({ imgSrc, itemName, selectedName, price }: I
</LeftInfo>
<RightInfo>
<ModifyButton>수정하기</ModifyButton>
<Price>+ {price}</Price>
<Price>+ {price.toLocaleString()}</Price>
</RightInfo>
</InfoWrapper>
</Item>
Expand Down Expand Up @@ -62,5 +62,6 @@ const ItemName = styled.div`
`;
const ModifyButton = styled.button`
color: ${({ theme }) => theme.color.primaryColor};
text-align: end;
`;
const SelectedName = styled.div``;
214 changes: 214 additions & 0 deletions frontend/src/components/modal/QuoteSummaryModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
import { HTMLAttributes, useContext } from 'react';
import { useNavigate } from 'react-router-dom';
import { styled } from 'styled-components';
import { QuoteSummaryModalContext } from '../../context/QuoteSummaryModalProvider';
import { PATH } from '../../utils/constants';
import { ItemContext } from '../../context/ItemProvider';
import RectButton from '../common/buttons/RectButton';
import { CloseIcon } from '../common/icons/Icons';
import ToggleButtons from '../common/buttons/ToggleButtons';
import { flexCenterCss } from '../../utils/commonStyle';
import {
BodyKrRegular3,
BodyKrRegular4,
HeadingKrMedium2,
HeadingKrMedium5,
} from '../../styles/typefaces';
import { DimmedBackground } from './DimmedBackground';
import WhiteModal from './WhiteModal';

interface IQuoteSummaryModal extends HTMLAttributes<HTMLDivElement> {}
interface IDetail {
title: string;
name: string;
price: number;
}
export default function QuoteSummaryModal({ ...props }: IQuoteSummaryModal) {
const { selectedItem, totalPrice } = useContext(ItemContext);
const { visible, setVisible } = useContext(QuoteSummaryModalContext);
const navigate = useNavigate();

const handleOkButtonClick = () => {
setVisible(false);
navigate(PATH.result);
};
const handleCloseButtonClicke = () => {
setVisible(false);
};

const optionNames = selectedItem.options.map((option) => {
return option.name;
});

return (
<DimmedBackground $displayDimmed={visible} {...props}>
<QuoteModal onClick={(e) => e.stopPropagation()}>
<Header>
<ModalTitle>견적요약</ModalTitle>
<CloseButton onClick={handleCloseButtonClicke}>
<CloseIcon />
</CloseButton>
</Header>
<Body>
<Row>
<ImgSection>
<CarImg></CarImg>
<ToggleButtons />
</ImgSection>
<DetailSection>
<Detail title="모델" name="팰리세이드" price={0} />
<Detail
title={'트림'}
name={selectedItem.trim.name}
price={selectedItem.trim.price}
/>
<Hr />
<Detail
title="파워트레인"
name={selectedItem.modelType.powerTrain.name}
price={selectedItem.modelType.powerTrain.price}
/>
<Detail
title="바디타입"
name={selectedItem.modelType.bodyType.name}
price={selectedItem.modelType.bodyType.price}
/>
<Detail
title="구동방식"
name={selectedItem.modelType.operation.name}
price={selectedItem.modelType.bodyType.price}
/>
<Hr />
<Detail
title="외장색상"
name={selectedItem.outerColor.name}
price={selectedItem.outerColor.price}
/>
<Detail
title="내장색상"
name={selectedItem.innerColor.name}
price={selectedItem.innerColor.price}
/>
<Hr />
<Detail title="옵션" name={optionNames.join(', ')} price={0} />
</DetailSection>
</Row>
<PriceSection>
<PriceCaption>현재 총 가격</PriceCaption>
<Price>{totalPrice.toLocaleString()}</Price>
</PriceSection>
</Body>
<Footer>
<OkButton type="popup" onClick={handleOkButtonClick}>
견적 완료하기
</OkButton>
</Footer>
</QuoteModal>
</DimmedBackground>
);
}

function Detail({ title, name, price }: IDetail) {
return (
<DetailWrapper>
<DetailTitle>{title}</DetailTitle>
<DetailName>{name}</DetailName>
<DetailPrice>+ {price.toLocaleString()}</DetailPrice>
</DetailWrapper>
);
}

const QuoteModal = styled(WhiteModal)`
display: flex;
justify-content: space-between;
flex-direction: column;
width: 850px;
height: 520px;
`;

const OkButton = styled(RectButton)``;

const Header = styled.div`
${flexCenterCss}
height: 53px;
`;
const ModalTitle = styled.h1`
${HeadingKrMedium5}
`;
const CloseButton = styled.button`
position: absolute;
right: 10px;
`;
const Row = styled.div`
${flexCenterCss}
gap: 32px;
margin-bottom: 40px;
`;
const Body = styled.div`
padding: 24px 36px;
`;
const ImgSection = styled.div`
${flexCenterCss}
width: 50%;
flex-direction: column;
`;
const CarImg = styled.img`
height: auto;
`;
const DetailSection = styled.div`
display: flex;
flex-direction: column;
width: 50%;
gap: 8px;
`;

const DetailWrapper = styled.div`
display: flex;
justify-content: space-between;
${BodyKrRegular3}
`;
const DetailTitle = styled.div`
color: ${({ theme }) => theme.color.gray500};
text-align: start;
width: 100px;
`;
const DetailName = styled.div`
display: inline-block;
color: ${({ theme }) => theme.color.gray900};
text-align: start;
width: 140px;
overflow: hidden;
white-space: nowrap;
&::-webkit-scrollbar {
display: none;
}
text-overflow: ellipsis;
`;
const DetailPrice = styled.div`
color: ${({ theme }) => theme.color.gray900};
text-align: end;
margin-left: auto;
`;
const PriceSection = styled.div`
display: flex;
justify-content: flex-end;
align-items: center;
`;
const PriceCaption = styled.span`
${BodyKrRegular4}
color: ${({ theme }) => theme.color.gray700};
`;
const Price = styled.span`
${HeadingKrMedium2}
color: ${({ theme }) => theme.color.primaryColor};
margin-left: 16px;
`;

const Footer = styled.div``;

const Hr = styled.div`
width: 100%;
height: 1px;
background-color: ${({ theme }) => theme.color.gray100};
margin: 7px 0;
`;
18 changes: 18 additions & 0 deletions frontend/src/components/modal/WhiteModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { HTMLAttributes } from 'react';
import { styled } from 'styled-components';

interface IWhiteModal extends HTMLAttributes<HTMLDivElement> {}

export default function WhiteModal({ ...props }: IWhiteModal) {
return <Wrapper {...props}></Wrapper>;
}

const Wrapper = styled.div`
position: relative;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
overflow: hidden;
border-radius: 4px;
background-color: ${({ theme }) => theme.color.white};
`;
14 changes: 10 additions & 4 deletions frontend/src/components/summary/PriceSummary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,33 @@ import { BodyKrRegular4, HeadingKrMedium2 } from '../../styles/typefaces';
import { useNavigate } from 'react-router-dom';
import { useContext } from 'react';
import { ItemContext } from '../../context/ItemProvider';
import { QuoteSummaryModalContext } from '../../context/QuoteSummaryModalProvider';

interface IPriceSummary extends React.HTMLAttributes<HTMLDivElement> {
nextPagePath: string;
}

export default function PriceSummary({ nextPagePath, ...props }: IPriceSummary) {
const { totalPrice } = useContext(ItemContext);

const { setVisible: setQuoteSummaryModalVisible } = useContext(QuoteSummaryModalContext);
const navigate = useNavigate();
const handleButtonClick = () => {
const handleNextButtonClick = () => {
navigate(nextPagePath);
};
const handleSummaryButtonClick = () => {
setQuoteSummaryModalVisible(true);
};
return (
<SummaryWrapper {...props}>
<InfoWrapper>
<RoundButton type="price">견적 요약</RoundButton>
<RoundButton type="price" onClick={handleSummaryButtonClick}>
견적 요약
</RoundButton>
<TotalPriceText>
현재 총 가격<HighLightText>{totalPrice.toLocaleString()}</HighLightText>
</TotalPriceText>
</InfoWrapper>
<RectButton type="price" onClick={handleButtonClick}>
<RectButton type="price" onClick={handleNextButtonClick}>
다음
</RectButton>
</SummaryWrapper>
Expand Down
Loading

0 comments on commit 0bfba54

Please sign in to comment.