feat: Add dialog to post detail page
This commit is contained in:
parent
e85ea41398
commit
0a05a0c6eb
2859
package-lock.json
generated
2859
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -10,7 +10,12 @@
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@emotion/react": "^11.11.1",
|
||||
"@emotion/styled": "^11.11.0",
|
||||
"@mui/material": "^5.14.16",
|
||||
"@mui/styled-engine-sc": "^6.0.0-alpha.4",
|
||||
"@toss/error-boundary": "^1.4.4",
|
||||
"@toss/use-overlay": "^1.3.6",
|
||||
"axios": "^1.5.0",
|
||||
"chart.js": "^4.4.0",
|
||||
"qs": "^6.11.2",
|
||||
@ -20,7 +25,7 @@
|
||||
"react-dom": "^18.2.0",
|
||||
"react-query": "^3.39.3",
|
||||
"react-router-dom": "^6.16.0",
|
||||
"styled-components": "^6.0.8",
|
||||
"styled-components": "^6.1.0",
|
||||
"zustand": "^4.4.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -1,8 +1,9 @@
|
||||
import { QueryClient, QueryClientProvider } from 'react-query';
|
||||
import Router from './Router';
|
||||
import GlobalStyle from './styles/global';
|
||||
import GlobalStyle from './styles/Global';
|
||||
import { ThemeProvider } from 'styled-components';
|
||||
import theme from './styles/Themes.styles';
|
||||
import { OverlayProvider } from '@toss/use-overlay';
|
||||
|
||||
const queryClient = new QueryClient();
|
||||
|
||||
@ -10,8 +11,10 @@ const App = () => {
|
||||
return (
|
||||
<ThemeProvider theme={theme}>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<GlobalStyle />
|
||||
<Router />
|
||||
<OverlayProvider>
|
||||
<GlobalStyle />
|
||||
<Router />
|
||||
</OverlayProvider>
|
||||
</QueryClientProvider>
|
||||
</ThemeProvider>
|
||||
);
|
||||
|
@ -12,8 +12,6 @@ import Login from './pages/Login';
|
||||
import Logout from './pages/Logout';
|
||||
import Signup from './pages/Signup';
|
||||
import WriteSteps from './pages/WriteSteps';
|
||||
import CurrencyInput from './components/CurrencyInput';
|
||||
import { useState } from 'react';
|
||||
import MyPage from './pages/MyPage';
|
||||
import PasswordChanger from './pages/PasswordChanger';
|
||||
import MyPost from './pages/MyPost';
|
||||
|
@ -69,6 +69,7 @@ const ProductPriceGraph = ({ id }) => {
|
||||
}
|
||||
return min;
|
||||
}, graphData[0]?.price);
|
||||
const minPriceString = minPrice?.toLocaleString() || '-';
|
||||
|
||||
return (
|
||||
<S.ProductPriceGraph className="priceGraph">
|
||||
@ -78,7 +79,9 @@ const ProductPriceGraph = ({ id }) => {
|
||||
data={graphStyleData}
|
||||
></Line>
|
||||
</S.GraphArea>
|
||||
<S.Text>최근 6개월 최저가: {minPrice}원</S.Text>
|
||||
<S.Text>
|
||||
최근 6개월간 최저 <span>{minPriceString}원</span>
|
||||
</S.Text>
|
||||
</S.ProductPriceGraph>
|
||||
);
|
||||
};
|
||||
|
34
src/components/Pagination/index.jsx
Normal file
34
src/components/Pagination/index.jsx
Normal file
@ -0,0 +1,34 @@
|
||||
import * as S from '../../styles/Pagination.styles';
|
||||
|
||||
function Pagination({ page, lastPage, setPage }) {
|
||||
return (
|
||||
<S.Pagination>
|
||||
<S.PaginationButton
|
||||
disabled={page === 1}
|
||||
onClick={() => setPage(1)}
|
||||
>
|
||||
처음
|
||||
</S.PaginationButton>
|
||||
<S.PaginationButton
|
||||
disabled={page === 1}
|
||||
onClick={() => setPage(page - 1)}
|
||||
>
|
||||
이전
|
||||
</S.PaginationButton>
|
||||
<S.PaginationButton
|
||||
disabled={page === lastPage}
|
||||
onClick={() => setPage(page + 1)}
|
||||
>
|
||||
다음
|
||||
</S.PaginationButton>
|
||||
<S.PaginationButton
|
||||
disabled={page === lastPage}
|
||||
onClick={() => setPage(lastPage)}
|
||||
>
|
||||
마지막
|
||||
</S.PaginationButton>
|
||||
</S.Pagination>
|
||||
);
|
||||
}
|
||||
|
||||
export default Pagination;
|
@ -9,10 +9,54 @@ import PanelLayout, {
|
||||
} from '../../components/PanelLayout';
|
||||
import ProductPriceGraph from '../../components/Graph/ProductPriceGraph';
|
||||
import { SubTitle } from '../../styles/Common.styles';
|
||||
import ButtonArea from '../../components/ButtonArea';
|
||||
import Button from '../../components/Button';
|
||||
import Spacer from '../../components/Spacer';
|
||||
import { useOverlay } from '@toss/use-overlay';
|
||||
import { Dialog, Slide } from '@mui/material';
|
||||
import React, { Suspense } from 'react';
|
||||
|
||||
const Transition = React.forwardRef(function Transition(props, ref) {
|
||||
return (
|
||||
<Slide
|
||||
direction="up"
|
||||
ref={ref}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
});
|
||||
|
||||
const PostDetail = () => {
|
||||
const overlay = useOverlay();
|
||||
const { id } = useParams();
|
||||
const { data } = usePost(id);
|
||||
const openOverlay = () => {
|
||||
overlay.open(({ isOpen, close }) => (
|
||||
<Dialog
|
||||
open={isOpen}
|
||||
onClose={close}
|
||||
TransitionComponent={Transition}
|
||||
PaperProps={{
|
||||
style: {
|
||||
padding: '8px 24px 24px 24px',
|
||||
borderRadius: '20px',
|
||||
},
|
||||
}}
|
||||
slotProps={{
|
||||
backdrop: {
|
||||
style: {
|
||||
backdropFilter: 'blur(5px)',
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<SubTitle>최근 거래 가격</SubTitle>
|
||||
<Suspense fallback={<></>}>
|
||||
<ProductPriceGraph id={data.product.id} />
|
||||
</Suspense>
|
||||
</Dialog>
|
||||
));
|
||||
};
|
||||
|
||||
return (
|
||||
<S.PostDetail>
|
||||
@ -25,16 +69,17 @@ const PostDetail = () => {
|
||||
<S.WrittenTime>{data.written_at}</S.WrittenTime>
|
||||
<PanelLayout>
|
||||
<LeftPanel>
|
||||
<SubTitle>최근 거래 가격</SubTitle>
|
||||
<ProductPriceGraph id={data.product.id} />
|
||||
<S.Text>{data.text}</S.Text>
|
||||
<S.Price>가격 {data.price.toLocaleString()}원</S.Price>
|
||||
</LeftPanel>
|
||||
<RightPanel>
|
||||
<SubTitle>제품 사진</SubTitle>
|
||||
<ImageViewer images={data?.images?.map((image) => image.image)} />
|
||||
</RightPanel>
|
||||
</PanelLayout>
|
||||
<ButtonArea>
|
||||
<S.Price>가격 {data.price.toLocaleString()}원</S.Price>
|
||||
<Button onClick={openOverlay}>최근 가격 보기</Button>
|
||||
</ButtonArea>
|
||||
</S.PostDetail>
|
||||
);
|
||||
};
|
||||
|
@ -27,6 +27,7 @@ export const Button = {
|
||||
background-color: ${({ theme }) => theme.colors.primary};
|
||||
`,
|
||||
secondary: styled(BaseButton)`
|
||||
background-color: ${({ theme }) => theme.colors.gray400};
|
||||
background-color: ${({ theme }) => theme.colors.gray300};
|
||||
color: ${({ theme }) => theme.colors.gray800};
|
||||
`,
|
||||
};
|
||||
|
@ -7,6 +7,7 @@ export const Title = styled.h1`
|
||||
margin: 0;
|
||||
user-select: none;
|
||||
box-sizing: border-box;
|
||||
color: ${({ theme }) => theme.colors.gray900};
|
||||
`;
|
||||
|
||||
export const SubTitle = styled.h2`
|
||||
@ -16,6 +17,7 @@ export const SubTitle = styled.h2`
|
||||
margin: 0;
|
||||
user-select: none;
|
||||
box-sizing: border-box;
|
||||
color: ${({ theme }) => theme.colors.gray900};
|
||||
`;
|
||||
|
||||
export const Input = styled.input`
|
||||
|
@ -11,7 +11,7 @@ export const Header = styled.header`
|
||||
border-bottom: ${({ theme }) => `1px solid ${theme.colors.gray200}`};
|
||||
text-align: center;
|
||||
user-select: none;
|
||||
z-index: 9999;
|
||||
z-index: 999;
|
||||
|
||||
.content {
|
||||
${({ theme }) => theme.maxWidth};
|
||||
@ -46,7 +46,7 @@ export const Nav = styled.nav`
|
||||
background-color: #fff;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 9998;
|
||||
z-index: 998;
|
||||
|
||||
& > .left,
|
||||
& > .right {
|
||||
|
8
src/styles/Pagination.styles.js
Normal file
8
src/styles/Pagination.styles.js
Normal file
@ -0,0 +1,8 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const Pagination = styled.div`
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
`;
|
||||
|
||||
export const PaginationButton = styled.button``;
|
@ -32,7 +32,9 @@ export const Text = styled.p`
|
||||
`;
|
||||
|
||||
export const Price = styled.div`
|
||||
margin-top: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
font-size: 18px;
|
||||
line-height: 1.7;
|
||||
font-weight: bold;
|
||||
|
@ -22,5 +22,11 @@ export const GraphArea = styled.div`
|
||||
|
||||
export const Text = styled.div`
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
margin-left: 16px;
|
||||
color: ${({ theme }) => theme.colors.gray900};
|
||||
|
||||
& > span {
|
||||
color: ${({ theme }) => theme.colors.primary};
|
||||
}
|
||||
`;
|
||||
|
@ -20,8 +20,7 @@ const colors = {
|
||||
const boxShadow = css`
|
||||
padding: 20px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid ${({ theme }) => theme.colors.gray200};
|
||||
box-shadow: 0px 4px 20px 0px #2060ee1a;
|
||||
box-shadow: 0px 2px 10px 0px #2060ee1a;
|
||||
background-color: ${({ theme }) => theme.colors.background};
|
||||
transition: all 0.3s ease;
|
||||
`;
|
||||
|
Loading…
Reference in New Issue
Block a user