feat: Add dialog to post detail page
This commit is contained in:
parent
e85ea41398
commit
0a05a0c6eb
2855
package-lock.json
generated
2855
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -10,7 +10,12 @@
|
|||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"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/error-boundary": "^1.4.4",
|
||||||
|
"@toss/use-overlay": "^1.3.6",
|
||||||
"axios": "^1.5.0",
|
"axios": "^1.5.0",
|
||||||
"chart.js": "^4.4.0",
|
"chart.js": "^4.4.0",
|
||||||
"qs": "^6.11.2",
|
"qs": "^6.11.2",
|
||||||
@ -20,7 +25,7 @@
|
|||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-query": "^3.39.3",
|
"react-query": "^3.39.3",
|
||||||
"react-router-dom": "^6.16.0",
|
"react-router-dom": "^6.16.0",
|
||||||
"styled-components": "^6.0.8",
|
"styled-components": "^6.1.0",
|
||||||
"zustand": "^4.4.1"
|
"zustand": "^4.4.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import { QueryClient, QueryClientProvider } from 'react-query';
|
import { QueryClient, QueryClientProvider } from 'react-query';
|
||||||
import Router from './Router';
|
import Router from './Router';
|
||||||
import GlobalStyle from './styles/global';
|
import GlobalStyle from './styles/Global';
|
||||||
import { ThemeProvider } from 'styled-components';
|
import { ThemeProvider } from 'styled-components';
|
||||||
import theme from './styles/Themes.styles';
|
import theme from './styles/Themes.styles';
|
||||||
|
import { OverlayProvider } from '@toss/use-overlay';
|
||||||
|
|
||||||
const queryClient = new QueryClient();
|
const queryClient = new QueryClient();
|
||||||
|
|
||||||
@ -10,8 +11,10 @@ const App = () => {
|
|||||||
return (
|
return (
|
||||||
<ThemeProvider theme={theme}>
|
<ThemeProvider theme={theme}>
|
||||||
<QueryClientProvider client={queryClient}>
|
<QueryClientProvider client={queryClient}>
|
||||||
|
<OverlayProvider>
|
||||||
<GlobalStyle />
|
<GlobalStyle />
|
||||||
<Router />
|
<Router />
|
||||||
|
</OverlayProvider>
|
||||||
</QueryClientProvider>
|
</QueryClientProvider>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
);
|
);
|
||||||
|
@ -12,8 +12,6 @@ import Login from './pages/Login';
|
|||||||
import Logout from './pages/Logout';
|
import Logout from './pages/Logout';
|
||||||
import Signup from './pages/Signup';
|
import Signup from './pages/Signup';
|
||||||
import WriteSteps from './pages/WriteSteps';
|
import WriteSteps from './pages/WriteSteps';
|
||||||
import CurrencyInput from './components/CurrencyInput';
|
|
||||||
import { useState } from 'react';
|
|
||||||
import MyPage from './pages/MyPage';
|
import MyPage from './pages/MyPage';
|
||||||
import PasswordChanger from './pages/PasswordChanger';
|
import PasswordChanger from './pages/PasswordChanger';
|
||||||
import MyPost from './pages/MyPost';
|
import MyPost from './pages/MyPost';
|
||||||
|
@ -69,6 +69,7 @@ const ProductPriceGraph = ({ id }) => {
|
|||||||
}
|
}
|
||||||
return min;
|
return min;
|
||||||
}, graphData[0]?.price);
|
}, graphData[0]?.price);
|
||||||
|
const minPriceString = minPrice?.toLocaleString() || '-';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<S.ProductPriceGraph className="priceGraph">
|
<S.ProductPriceGraph className="priceGraph">
|
||||||
@ -78,7 +79,9 @@ const ProductPriceGraph = ({ id }) => {
|
|||||||
data={graphStyleData}
|
data={graphStyleData}
|
||||||
></Line>
|
></Line>
|
||||||
</S.GraphArea>
|
</S.GraphArea>
|
||||||
<S.Text>최근 6개월 최저가: {minPrice}원</S.Text>
|
<S.Text>
|
||||||
|
최근 6개월간 최저 <span>{minPriceString}원</span>
|
||||||
|
</S.Text>
|
||||||
</S.ProductPriceGraph>
|
</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';
|
} from '../../components/PanelLayout';
|
||||||
import ProductPriceGraph from '../../components/Graph/ProductPriceGraph';
|
import ProductPriceGraph from '../../components/Graph/ProductPriceGraph';
|
||||||
import { SubTitle } from '../../styles/Common.styles';
|
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 PostDetail = () => {
|
||||||
|
const overlay = useOverlay();
|
||||||
const { id } = useParams();
|
const { id } = useParams();
|
||||||
const { data } = usePost(id);
|
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 (
|
return (
|
||||||
<S.PostDetail>
|
<S.PostDetail>
|
||||||
@ -25,16 +69,17 @@ const PostDetail = () => {
|
|||||||
<S.WrittenTime>{data.written_at}</S.WrittenTime>
|
<S.WrittenTime>{data.written_at}</S.WrittenTime>
|
||||||
<PanelLayout>
|
<PanelLayout>
|
||||||
<LeftPanel>
|
<LeftPanel>
|
||||||
<SubTitle>최근 거래 가격</SubTitle>
|
|
||||||
<ProductPriceGraph id={data.product.id} />
|
|
||||||
<S.Text>{data.text}</S.Text>
|
<S.Text>{data.text}</S.Text>
|
||||||
<S.Price>가격 {data.price.toLocaleString()}원</S.Price>
|
|
||||||
</LeftPanel>
|
</LeftPanel>
|
||||||
<RightPanel>
|
<RightPanel>
|
||||||
<SubTitle>제품 사진</SubTitle>
|
<SubTitle>제품 사진</SubTitle>
|
||||||
<ImageViewer images={data?.images?.map((image) => image.image)} />
|
<ImageViewer images={data?.images?.map((image) => image.image)} />
|
||||||
</RightPanel>
|
</RightPanel>
|
||||||
</PanelLayout>
|
</PanelLayout>
|
||||||
|
<ButtonArea>
|
||||||
|
<S.Price>가격 {data.price.toLocaleString()}원</S.Price>
|
||||||
|
<Button onClick={openOverlay}>최근 가격 보기</Button>
|
||||||
|
</ButtonArea>
|
||||||
</S.PostDetail>
|
</S.PostDetail>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -27,6 +27,7 @@ export const Button = {
|
|||||||
background-color: ${({ theme }) => theme.colors.primary};
|
background-color: ${({ theme }) => theme.colors.primary};
|
||||||
`,
|
`,
|
||||||
secondary: styled(BaseButton)`
|
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;
|
margin: 0;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
color: ${({ theme }) => theme.colors.gray900};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const SubTitle = styled.h2`
|
export const SubTitle = styled.h2`
|
||||||
@ -16,6 +17,7 @@ export const SubTitle = styled.h2`
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
color: ${({ theme }) => theme.colors.gray900};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const Input = styled.input`
|
export const Input = styled.input`
|
||||||
|
@ -11,7 +11,7 @@ export const Header = styled.header`
|
|||||||
border-bottom: ${({ theme }) => `1px solid ${theme.colors.gray200}`};
|
border-bottom: ${({ theme }) => `1px solid ${theme.colors.gray200}`};
|
||||||
text-align: center;
|
text-align: center;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
z-index: 9999;
|
z-index: 999;
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
${({ theme }) => theme.maxWidth};
|
${({ theme }) => theme.maxWidth};
|
||||||
@ -46,7 +46,7 @@ export const Nav = styled.nav`
|
|||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
position: sticky;
|
position: sticky;
|
||||||
top: 0;
|
top: 0;
|
||||||
z-index: 9998;
|
z-index: 998;
|
||||||
|
|
||||||
& > .left,
|
& > .left,
|
||||||
& > .right {
|
& > .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`
|
export const Price = styled.div`
|
||||||
margin-top: 8px;
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
line-height: 1.7;
|
line-height: 1.7;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
@ -22,5 +22,11 @@ export const GraphArea = styled.div`
|
|||||||
|
|
||||||
export const Text = styled.div`
|
export const Text = styled.div`
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
margin-left: 16px;
|
margin-left: 16px;
|
||||||
|
color: ${({ theme }) => theme.colors.gray900};
|
||||||
|
|
||||||
|
& > span {
|
||||||
|
color: ${({ theme }) => theme.colors.primary};
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
|
@ -20,8 +20,7 @@ const colors = {
|
|||||||
const boxShadow = css`
|
const boxShadow = css`
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
border: 1px solid ${({ theme }) => theme.colors.gray200};
|
box-shadow: 0px 2px 10px 0px #2060ee1a;
|
||||||
box-shadow: 0px 4px 20px 0px #2060ee1a;
|
|
||||||
background-color: ${({ theme }) => theme.colors.background};
|
background-color: ${({ theme }) => theme.colors.background};
|
||||||
transition: all 0.3s ease;
|
transition: all 0.3s ease;
|
||||||
`;
|
`;
|
||||||
|
Loading…
Reference in New Issue
Block a user