Skip to content

Commit

Permalink
Merge pull request #176 from second-hand-team06/fe-feat/#171-product-…
Browse files Browse the repository at this point in the history
…detail-header-more-button

[FE] 상품 상세 페이지 Header의 [더보기] 버튼 기능 #171
  • Loading branch information
saseungg authored Jun 28, 2023
2 parents 66185bd + 381c788 commit 91b3ec7
Show file tree
Hide file tree
Showing 8 changed files with 213 additions and 21 deletions.
79 changes: 79 additions & 0 deletions fe/src/components/ProductDetail/ProductDetailHeader/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { useState } from 'react';
import { useNavigate } from 'react-router-dom';

import { ICON_NAME, REQUEST_URL } from '@constants/index';

import useFetch, { REQUEST_METHOD } from '@hooks/useFetch';

import Icon from '@components/common/Icon';
import Modal from '@components/common/Modal';
import Popup from '@components/common/Popup';
import ModalPortal from '@components/ModalPortal';
import * as S from './style';

interface ProductDetailHeaderProps {
postId: number;
isSeller: boolean;
}

const ProductDetailHeader = ({ postId, isSeller }: ProductDetailHeaderProps) => {
const [isModalOpen, setIsModalOpen] = useState(false);
const [isPopupOpen, setIsPopupOpen] = useState(false);
const navigate = useNavigate();

const { responseState: deletePostState, fetchData: deletePost } = useFetch({
url: `${REQUEST_URL.POSTS}/${postId}`,
options: {
method: REQUEST_METHOD.DELETE,
headers: { Authorization: `Bearer ${localStorage.getItem('Token')}` },
},
skip: true,
});

const deleteProduct = () => {
deletePost();

if (deletePostState === 'ERROR') {
alert('게시글 삭제에 싪패했습니다.');
return;
}

navigate('/');
};

const openPopup = () => setIsPopupOpen(true);

return (
<S.Header>
<button onClick={() => navigate(-1)}>
<Icon name={ICON_NAME.CHEVRON_LEFT} />
</button>
{isSeller && (
<button onClick={() => setIsModalOpen(true)}>
<Icon name={ICON_NAME.ELLIPSIS} />
</button>
)}
{isPopupOpen && (
<ModalPortal>
<Popup text="정말로 해당 게시물을 삭제할 것입니까?">
<S.ClosePopupButton onClick={() => setIsPopupOpen(false)}>아니오</S.ClosePopupButton>
<S.DeletePostButton onClick={deleteProduct}></S.DeletePostButton>
</Popup>
</ModalPortal>
)}
{isModalOpen && (
<ModalPortal>
<Modal
options={[
{ text: '게시글 수정', colorType: 'default', handler: () => console.log('게시글 수정') },
{ text: '삭제', colorType: 'warning', handler: openPopup },
]}
closeModalHandler={() => setIsModalOpen(false)}
/>
</ModalPortal>
)}
</S.Header>
);
};

export default ProductDetailHeader;
36 changes: 36 additions & 0 deletions fe/src/components/ProductDetail/ProductDetailHeader/style.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import styled from 'styled-components';

const ClosePopupButton = styled.button`
width: 100%;
max-width: 200px;
padding: 8px 0;
border: 1px solid ${({ theme }) => theme.colors.neutral.border.default};
border-radius: 10px;
color: ${({ theme }) => theme.colors.neutral.text.default};
`;

const DeletePostButton = styled.button`
width: 100%;
max-width: 200px;
padding: 8px 0;
border: 1px solid ${({ theme }) => theme.colors.neutral.border.default};
border-radius: 10px;
color: ${({ theme }) => theme.colors.system.warning};
`;

const Header = styled.header`
display: flex;
justify-content: space-between;
align-items: center;
position: fixed;
width: 100%;
height: 44px;
padding: 16px;
`;

export { ClosePopupButton, DeletePostButton, Header };
10 changes: 0 additions & 10 deletions fe/src/components/ProductDetail/ProductDetailMain/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { useState } from 'react';
import { useNavigate } from 'react-router-dom';

import { ICON_NAME } from '@constants/index';
import { getTextWithTimeStamp } from '@utils/index';
Expand Down Expand Up @@ -35,17 +34,8 @@ const ProductDetailMain = ({
}: ProductDetailMainProps) => {
const [isModalOpen, setIsModalOpen] = useState(false);

const navigate = useNavigate();

return (
<>
<S.Header>
<button onClick={() => navigate(-1)}>
<Icon name={ICON_NAME.CHEVRON_LEFT} />
</button>
<Icon name={ICON_NAME.ELLIPSIS} />
</S.Header>

<S.Product>
<S.ProductImgListLayout>
<S.ProductImgList>
Expand Down
11 changes: 0 additions & 11 deletions fe/src/components/ProductDetail/ProductDetailMain/style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,6 @@ import styled, { css } from 'styled-components';

import Button from '@components/common/Button';

const Header = styled.header`
display: flex;
justify-content: space-between;
align-items: center;
position: fixed;
width: 100%;
height: 44px;
padding: 16px;
`;
const Product = styled.section`
overflow-y: scroll;
Expand Down Expand Up @@ -186,7 +176,6 @@ const ChattingDetailButton = styled(Button)`
`;

export {
Header,
Product,
ProductInfo,
ProductImgListLayout,
Expand Down
24 changes: 24 additions & 0 deletions fe/src/components/common/Popup/Popup.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import type { Meta, StoryObj } from '@storybook/react';

import Popup from '.';

const popupMeta: Meta<typeof Popup> = {
title: 'common/Popup',
component: Popup,
};

export default popupMeta;

type PopupStory = StoryObj<typeof Popup>;

export const PrimaryPopup: PopupStory = {
args: {
text: '정말로 삭제하시겠습니까?',
children: (
<>
<button style={{ width: '100px', border: '1px solid black' }}></button>
<button style={{ width: '100px', border: '1px solid black' }}>아니오</button>
</>
),
},
};
20 changes: 20 additions & 0 deletions fe/src/components/common/Popup/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import * as S from './style';

interface PopupProps {
text: string;
children: React.ReactNode;
}

const Popup = ({ text, children }: PopupProps) => {
return (
<>
<S.Popup>
<span>{text}</span>
<S.ButtonsLayout>{children}</S.ButtonsLayout>
</S.Popup>
<S.Overlay />
</>
);
};

export default Popup;
52 changes: 52 additions & 0 deletions fe/src/components/common/Popup/style.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import styled from 'styled-components';

const Popup = styled.div`
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
gap: 15px;
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 80%;
padding: 30px 20px;
background-color: ${({ theme }) => theme.colors.neutral.background.weak};
border-radius: 15px;
font-size: ${({ theme }) => theme.fonts.callout.fontSize};
font-weight: ${({ theme }) => theme.fonts.callout.fontWeight};
line-height: ${({ theme }) => theme.fonts.callout.lineHeight};
color: ${({ theme }) => theme.colors.neutral.text.strong};
z-index: 20;
`;

const ButtonsLayout = styled.div`
display: flex;
justify-content: center;
gap: 15px;
width: 100%;
`;

const Overlay = styled.div`
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
overflow: auto;
width: 100%;
height: 100%;
background-color: ${({ theme }) => theme.colors.neutral.overLay};
z-index: 15;
`;

export { Popup, ButtonsLayout, Overlay };
2 changes: 2 additions & 0 deletions fe/src/pages/ProductDetail/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { REQUEST_URL } from '@constants/index';

import useFetch, { REQUEST_METHOD } from '@hooks/useFetch';

import ProductDetailHeader from '@components/ProductDetail/ProductDetailHeader';
import ProductDetailMain from '@components/ProductDetail/ProductDetailMain';
import ProductDetailToolBar from '@components/ProductDetail/ProductDetailToolBar';

Expand Down Expand Up @@ -107,6 +108,7 @@ const ProductDetail = () => {
{responseState === 'LOADING' && <div>loading</div>}
{responseState === 'SUCCESS' && postData && (
<>
<ProductDetailHeader postId={postData.id} isSeller={postData.isSeller} />
<ProductDetailMain {...postData} interestCount={interestCount} />
<ProductDetailToolBar
isInterested={isInterested}
Expand Down

0 comments on commit 91b3ec7

Please sign in to comment.