Compare commits
10 Commits
9d2380f591
...
6a5088a152
Author | SHA1 | Date | |
---|---|---|---|
6a5088a152 | |||
d0716a0ad1 | |||
fda3de4d32 | |||
36f3a6cd18 | |||
4f9d9269f5 | |||
abac444027 | |||
6cb0f24ed1 | |||
3385f1457d | |||
b13fc5bd16 | |||
eb30a3c077 |
23
index.html
23
index.html
@ -1,13 +1,16 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8" />
|
<head>
|
||||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<link rel="icon" type="image/svg+xml" href="public/icon.svg" />
|
||||||
<title>Vite + React</title>
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
</head>
|
<title>히폰 - 편리한 휴대폰 중고거래</title>
|
||||||
<body>
|
</head>
|
||||||
<div id="root"></div>
|
|
||||||
<script type="module" src="/src/main.jsx"></script>
|
<body>
|
||||||
</body>
|
<div id="root"></div>
|
||||||
|
<script type="module" src="/src/main.jsx"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
1
public/icon.svg
Normal file
1
public/icon.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 38"><defs><style>.b{fill: #2060ff;}</style></defs><path class="b" d="M40,12V0H0V12H8v14H0v12H40v-12h-8V12h8Zm-18,14h-4V12h4v14Z"/></svg>
|
After Width: | Height: | Size: 193 B |
@ -31,6 +31,7 @@ export const URL = {
|
|||||||
myPage: '/user/mypage/',
|
myPage: '/user/mypage/',
|
||||||
myPost: '/mypost/',
|
myPost: '/mypost/',
|
||||||
password: '/user/password/',
|
password: '/user/password/',
|
||||||
|
deleteAccount: '/user/deleteMessage/',
|
||||||
user: '/user/',
|
user: '/user/',
|
||||||
brand: '/brand/',
|
brand: '/brand/',
|
||||||
product: '/product/',
|
product: '/product/',
|
||||||
|
@ -5,7 +5,14 @@ import { ThemeProvider } from 'styled-components';
|
|||||||
import theme from './styles/Themes.styles';
|
import theme from './styles/Themes.styles';
|
||||||
import { OverlayProvider } from '@toss/use-overlay';
|
import { OverlayProvider } from '@toss/use-overlay';
|
||||||
|
|
||||||
const queryClient = new QueryClient();
|
const queryClient = new QueryClient({
|
||||||
|
defaultOptions: {
|
||||||
|
queries: {
|
||||||
|
suspense: true,
|
||||||
|
refetchOnWindowFocus: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
return (
|
return (
|
||||||
|
@ -1 +1 @@
|
|||||||
<svg id="logo-11" width="119" height="30" viewBox="0 0 119 30" fill="none" xmlns="http://www.w3.org/2000/svg"> <path d="M2.94098 27.825H28.591V2.175H2.94098V27.825ZM23.654 12.532H18.234V7.112H23.654V12.532ZM7.87698 7.112H13.3V17.468H23.654V22.888H7.87698V7.112ZM45.294 2.175C42.7574 2.175 40.2778 2.92718 38.1688 4.33641C36.0597 5.74564 34.4159 7.74863 33.4452 10.0921C32.4745 12.4356 32.2206 15.0142 32.7154 17.502C33.2103 19.9898 34.4317 22.275 36.2253 24.0686C38.0189 25.8623 40.3041 27.0837 42.7919 27.5786C45.2798 28.0734 47.8584 27.8195 50.2019 26.8488C52.5454 25.8781 54.5483 24.2343 55.9576 22.1252C57.3668 20.0161 58.119 17.5365 58.119 15C58.115 11.5998 56.7625 8.34004 54.3582 5.93575C51.9539 3.53145 48.6942 2.17897 45.294 2.175V2.175ZM45.294 22.888C43.7339 22.888 42.2088 22.4254 40.9116 21.5586C39.6145 20.6919 38.6034 19.46 38.0064 18.0186C37.4094 16.5773 37.2532 14.9913 37.5575 13.4611C37.8619 11.931 38.6132 10.5255 39.7163 9.42234C40.8195 8.31919 42.225 7.56793 43.7551 7.26357C45.2852 6.95921 46.8712 7.11542 48.3126 7.71244C49.7539 8.30946 50.9859 9.32049 51.8526 10.6177C52.7194 11.9148 53.182 13.4399 53.182 15C53.1788 17.0911 52.3467 19.0956 50.8681 20.5742C49.3895 22.0528 47.385 22.8848 45.294 22.888V22.888ZM104.013 2.175C101.476 2.17481 98.9967 2.92681 96.8875 4.33592C94.7783 5.74503 93.1344 7.74795 92.1635 10.0914C91.1927 12.4348 90.9385 15.0136 91.4333 17.5014C91.928 19.9893 93.1494 22.2746 94.943 24.0683C96.7365 25.862 99.0217 27.0836 101.51 27.5785C103.997 28.0734 106.576 27.8195 108.92 26.8489C111.263 25.8782 113.266 24.2344 114.675 22.1253C116.085 20.0162 116.837 17.5366 116.837 15C116.833 11.6 115.481 8.34036 113.077 5.9361C110.673 3.53183 107.413 2.17924 104.013 2.175V2.175ZM104.013 22.888C102.453 22.8882 100.928 22.4257 99.6304 21.5591C98.3331 20.6925 97.3219 19.4606 96.7247 18.0193C96.1275 16.578 95.9712 14.9919 96.2754 13.4617C96.5797 11.9316 97.3309 10.526 98.434 9.4227C99.5371 8.31944 100.943 7.56808 102.473 7.26364C104.003 6.9592 105.589 7.11535 107.03 7.71235C108.472 8.30934 109.704 9.32036 110.571 10.6176C111.437 11.9148 111.9 13.4399 111.9 15C111.897 17.0909 111.065 19.0952 109.586 20.5738C108.108 22.0524 106.104 22.8846 104.013 22.888V22.888ZM74.653 2.175C72.1164 2.175 69.6368 2.92718 67.5278 4.33641C65.4187 5.74564 63.7749 7.74863 62.8042 10.0921C61.8335 12.4356 61.5796 15.0142 62.0744 17.502C62.5693 19.9898 63.7907 22.275 65.5843 24.0686C67.3779 25.8623 69.6631 27.0837 72.1509 27.5786C74.6387 28.0734 77.2174 27.8195 79.5609 26.8488C81.9044 25.8781 83.9073 24.2343 85.3166 22.1252C86.7258 20.0161 87.478 17.5365 87.478 15C87.474 11.5998 86.1215 8.34004 83.7172 5.93575C81.3129 3.53145 78.0532 2.17897 74.653 2.175ZM74.653 22.888C73.1994 22.8887 71.774 22.4878 70.5339 21.7295C69.2939 20.9712 68.2874 19.8851 67.6257 18.5909C66.9641 17.2967 66.6728 15.8448 66.7842 14.3956C66.8956 12.9463 67.4052 11.556 68.2569 10.3781C69.1086 9.20026 70.2692 8.28061 71.6105 7.72071C72.9519 7.1608 74.4219 6.98242 75.8582 7.20524C77.2946 7.42807 78.6414 8.04343 79.7501 8.98342C80.8588 9.9234 81.6862 11.1515 82.141 12.532H74.653V17.468H82.141C81.6201 19.0433 80.6165 20.4147 79.2724 21.3875C77.9284 22.3604 76.3122 22.8853 74.653 22.888V22.888Z" class="ccustom" fill="#394149"></path> </svg>
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 84 60"><defs><style>.a {fill:#0660ee;}</style></defs><path class="a" d="M84,26V14H44v12h6v14h-6v12h40v-12h-6v-14h6Zm-18,14h-4v-14h4v14Z"/><g><path class="a" d="M40,14h-12.01c-1.82,2.42-4.72,4-7.99,4s-6.16-1.58-7.99-4H0v12H3.89c-1.2,2.41-1.89,5.12-1.89,8,0,9.94,8.06,18,18,18s18-8.06,18-18c0-2.88-.69-5.59-1.89-8h3.89V14Zm-20,26c-3.31,0-6-2.69-6-6s2.69-6,6-6,6,2.69,6,6-2.69,6-6,6Z"/><circle class="a" cx="20" cy="8" r="8"/></g></svg>
|
||||||
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 487 B |
@ -1 +1 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" height="40" viewBox="0 -960 960 960" width="40"><path d="M308.283-93.667q-22.758 0-38.854-16.095-16.096-16.096-16.096-38.823v-662.83q0-22.727 16.096-38.823 16.096-16.095 38.854-16.095h343.434q22.758 0 38.854 16.095 16.096 16.096 16.096 38.823v662.83q0 22.727-16.096 38.823-16.096 16.095-38.854 16.095H308.283ZM276.5-204.333h407v-551.334h-407v551.334Z"/></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" height="40" viewBox="0 -960 960 960" width="40"><style>path{fill:#333;}</style><path d="M308.283-93.667q-22.758 0-38.854-16.095-16.096-16.096-16.096-38.823v-662.83q0-22.727 16.096-38.823 16.096-16.095 38.854-16.095h343.434q22.758 0 38.854 16.095 16.096 16.096 16.096 38.823v662.83q0 22.727-16.096 38.823-16.096 16.095-38.854 16.095H308.283ZM276.5-204.333h407v-551.334h-407v551.334Z"/></svg>
|
||||||
|
Before Width: | Height: | Size: 399 B After Width: | Height: | Size: 431 B |
@ -1,6 +1,6 @@
|
|||||||
import * as S from '../../styles/CheckBox.styles';
|
import * as S from '../../styles/CheckBox.styles';
|
||||||
|
|
||||||
function CheckBox({ name, id, text, checked, setChecked }) {
|
function CheckBox({ name, id, text, checked, setChecked, readOnly = false }) {
|
||||||
return (
|
return (
|
||||||
<S.CheckBox htmlFor={id}>
|
<S.CheckBox htmlFor={id}>
|
||||||
<S.CheckInput
|
<S.CheckInput
|
||||||
@ -8,7 +8,8 @@ function CheckBox({ name, id, text, checked, setChecked }) {
|
|||||||
name={name}
|
name={name}
|
||||||
id={id}
|
id={id}
|
||||||
checked={checked}
|
checked={checked}
|
||||||
onChange={() => setChecked(!checked)}
|
disabled={readOnly}
|
||||||
|
onChange={(e) => setChecked(e.target.checked)}
|
||||||
/>
|
/>
|
||||||
<S.Info>
|
<S.Info>
|
||||||
<S.Name>{name}</S.Name>
|
<S.Name>{name}</S.Name>
|
||||||
|
18
src/components/CloseButton/index.jsx
Normal file
18
src/components/CloseButton/index.jsx
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import * as S from '../../styles/CloseButton.styles';
|
||||||
|
|
||||||
|
function CloseButton({ onClick }) {
|
||||||
|
return (
|
||||||
|
<S.CloseButton onClick={onClick}>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 -960 960 960"
|
||||||
|
width="24"
|
||||||
|
>
|
||||||
|
<path d="m256-200-56-56 224-224-224-224 56-56 224 224 224-224 56 56-224 224 224 224-56 56-224-224-224 224Z" />
|
||||||
|
</svg>
|
||||||
|
</S.CloseButton>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CloseButton;
|
14
src/components/CustomDialog/Transition.jsx
Normal file
14
src/components/CustomDialog/Transition.jsx
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { Slide } from '@mui/material';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const Transition = React.forwardRef(function Transition(props, ref) {
|
||||||
|
return (
|
||||||
|
<Slide
|
||||||
|
direction="up"
|
||||||
|
ref={ref}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default Transition;
|
46
src/components/CustomDialog/index.jsx
Normal file
46
src/components/CustomDialog/index.jsx
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import * as S from '../../styles/CustomDialog.styles';
|
||||||
|
import { Dialog } from '@mui/material';
|
||||||
|
import Transition from './Transition';
|
||||||
|
import CloseButton from '../CloseButton';
|
||||||
|
|
||||||
|
function CustomDialog({
|
||||||
|
children,
|
||||||
|
open,
|
||||||
|
onClose,
|
||||||
|
hasCloseButton = true,
|
||||||
|
title,
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<Dialog
|
||||||
|
open={open}
|
||||||
|
onClose={onClose}
|
||||||
|
TransitionComponent={Transition}
|
||||||
|
PaperProps={{
|
||||||
|
style: {
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
gap: '24px',
|
||||||
|
padding: '24px',
|
||||||
|
borderRadius: '20px',
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
slotProps={{
|
||||||
|
backdrop: {
|
||||||
|
style: {
|
||||||
|
backdropFilter: 'blur(5px)',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{hasCloseButton && (
|
||||||
|
<S.DialogHeader>
|
||||||
|
<S.DialogTitle>{title}</S.DialogTitle>
|
||||||
|
<CloseButton onClick={onClose} />
|
||||||
|
</S.DialogHeader>
|
||||||
|
)}
|
||||||
|
{children}
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CustomDialog;
|
@ -80,7 +80,7 @@ const ProductPriceGraph = ({ id }) => {
|
|||||||
></Line>
|
></Line>
|
||||||
</S.GraphArea>
|
</S.GraphArea>
|
||||||
<S.Text>
|
<S.Text>
|
||||||
최근 6개월간 최저 <span>{minPriceString}원</span>
|
최근 6개월간 최저가 <span>{minPriceString}원</span>
|
||||||
</S.Text>
|
</S.Text>
|
||||||
</S.ProductPriceGraph>
|
</S.ProductPriceGraph>
|
||||||
);
|
);
|
||||||
|
76
src/components/ItemIssues/index.jsx
Normal file
76
src/components/ItemIssues/index.jsx
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
import { useCallback } from 'react';
|
||||||
|
import * as S from '../../styles/ItemIssues.styles';
|
||||||
|
import CheckBox from '../CheckBox';
|
||||||
|
|
||||||
|
function ItemIssues({ readOnly, itemIssues, setItemIssues }) {
|
||||||
|
const setChecked = useCallback(
|
||||||
|
(key) =>
|
||||||
|
readOnly
|
||||||
|
? () => {}
|
||||||
|
: (value) => setItemIssues((prev) => ({ ...prev, [key]: value })),
|
||||||
|
[readOnly, setItemIssues]
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<S.Grid>
|
||||||
|
<CheckBox
|
||||||
|
id="화면 깨짐"
|
||||||
|
name="화면 깨짐"
|
||||||
|
text="화면이 깨져있어요"
|
||||||
|
checked={itemIssues.display}
|
||||||
|
setChecked={setChecked('display')}
|
||||||
|
readOnly={readOnly}
|
||||||
|
/>
|
||||||
|
<CheckBox
|
||||||
|
id="뒷면/옆면 파손"
|
||||||
|
name="뒷면/옆면 파손"
|
||||||
|
text="화면이 아닌 부분에 파손이 있어요"
|
||||||
|
checked={itemIssues.frame}
|
||||||
|
setChecked={setChecked('frame')}
|
||||||
|
readOnly={readOnly}
|
||||||
|
/>
|
||||||
|
<CheckBox
|
||||||
|
id="버튼 고장"
|
||||||
|
name="버튼 고장"
|
||||||
|
text="고장난 버튼이 있어요"
|
||||||
|
checked={itemIssues.button}
|
||||||
|
setChecked={setChecked('button')}
|
||||||
|
readOnly={readOnly}
|
||||||
|
/>
|
||||||
|
<CheckBox
|
||||||
|
id="생체 인식 고장"
|
||||||
|
name="생체 인식 고장"
|
||||||
|
text="지문이나 얼굴인식이 작동하지 않아요"
|
||||||
|
checked={itemIssues.biometric}
|
||||||
|
setChecked={setChecked('biometric')}
|
||||||
|
readOnly={readOnly}
|
||||||
|
/>
|
||||||
|
<CheckBox
|
||||||
|
id="카메라 고장"
|
||||||
|
name="카메라 고장"
|
||||||
|
text="카메라가 작동하지 않아요"
|
||||||
|
checked={itemIssues.camera}
|
||||||
|
setChecked={setChecked('camera')}
|
||||||
|
readOnly={readOnly}
|
||||||
|
/>
|
||||||
|
<CheckBox
|
||||||
|
id="스피커 고장"
|
||||||
|
name="스피커 고장"
|
||||||
|
text="스피커가 작동하지 않아요"
|
||||||
|
checked={itemIssues.speaker}
|
||||||
|
setChecked={setChecked('speaker')}
|
||||||
|
readOnly={readOnly}
|
||||||
|
/>
|
||||||
|
<CheckBox
|
||||||
|
id="기타 고장"
|
||||||
|
name="기타 고장"
|
||||||
|
text="그 밖에 고장난 부분이 있어요"
|
||||||
|
checked={itemIssues.others}
|
||||||
|
setChecked={setChecked('others')}
|
||||||
|
readOnly={readOnly}
|
||||||
|
/>
|
||||||
|
</S.Grid>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ItemIssues;
|
@ -16,7 +16,7 @@ function Header() {
|
|||||||
<S.Nav>
|
<S.Nav>
|
||||||
<div className="left">
|
<div className="left">
|
||||||
<S.NavItem to={URL.product}>기기 목록</S.NavItem>
|
<S.NavItem to={URL.product}>기기 목록</S.NavItem>
|
||||||
<S.NavItem to={URL.post}>최신글</S.NavItem>
|
<S.NavItem to={URL.post}>판매글</S.NavItem>
|
||||||
</div>
|
</div>
|
||||||
<AuthMenu />
|
<AuthMenu />
|
||||||
</S.Nav>
|
</S.Nav>
|
||||||
|
@ -2,11 +2,12 @@ import { API, URL, getCookie } from '../../API';
|
|||||||
import * as S from '../../styles/Form.styles';
|
import * as S from '../../styles/Form.styles';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import CSRFToken from '../CSRFToken/CSRFToken';
|
import CSRFToken from '../CSRFToken/CSRFToken';
|
||||||
import { Navigate, useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
import useIsLogged from '../../hooks/network/isLogged';
|
import useIsLogged from '../../hooks/network/isLogged';
|
||||||
|
|
||||||
const LoginForm = () => {
|
const LoginForm = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
const csrfToken = getCookie('csrftoken');
|
||||||
const setIsLogged = useIsLogged((state) => state.setIsLogged);
|
const setIsLogged = useIsLogged((state) => state.setIsLogged);
|
||||||
const [username, setUsername] = useState('');
|
const [username, setUsername] = useState('');
|
||||||
const [password, setPassword] = useState('');
|
const [password, setPassword] = useState('');
|
||||||
@ -15,6 +16,7 @@ const LoginForm = () => {
|
|||||||
API.post(URL.login, {
|
API.post(URL.login, {
|
||||||
username,
|
username,
|
||||||
password,
|
password,
|
||||||
|
csrfmiddlewaretoken: csrfToken,
|
||||||
})
|
})
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res.status === 200) {
|
if (res.status === 200) {
|
||||||
|
@ -8,7 +8,7 @@ const getBrands = async () => {
|
|||||||
|
|
||||||
export const useBrands = () => {
|
export const useBrands = () => {
|
||||||
const data = useQuery('brands', () => getBrands(), {
|
const data = useQuery('brands', () => getBrands(), {
|
||||||
suspense: true,
|
staleTime: 1000 * 60 * 60 * 24,
|
||||||
});
|
});
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
@ -21,7 +21,7 @@ const getBrand = async (id) => {
|
|||||||
|
|
||||||
export const useBrand = (id) => {
|
export const useBrand = (id) => {
|
||||||
const data = useQuery(['products', id], () => getBrand(id), {
|
const data = useQuery(['products', id], () => getBrand(id), {
|
||||||
suspense: true,
|
staleTime: 1000 * 60 * 60 * 24,
|
||||||
});
|
});
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
@ -33,9 +33,7 @@ const getBrandProduct = async (id) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const useBrandProduct = (id) => {
|
export const useBrandProduct = (id) => {
|
||||||
const data = useQuery(['brandproduct', id], () => getBrandProduct(id), {
|
const data = useQuery(['brandproduct', id], () => getBrandProduct(id));
|
||||||
suspense: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
@ -8,7 +8,7 @@ const getGraph = async (id) => {
|
|||||||
|
|
||||||
export const useGraph = (id) => {
|
export const useGraph = (id) => {
|
||||||
const data = useQuery(['post', id], () => getGraph(id), {
|
const data = useQuery(['post', id], () => getGraph(id), {
|
||||||
suspense: true,
|
staleTime: 1000 * 60 * 60 * 24,
|
||||||
});
|
});
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { API, URL } from '../../API';
|
import { API, URL } from '../../API';
|
||||||
import { useQuery } from 'react-query';
|
import { useMutation, useQuery } from 'react-query';
|
||||||
|
|
||||||
const getPosts = async ({ my, page }) => {
|
const getPosts = async ({ my, page }) => {
|
||||||
const pageString = page ? `?page=${page}` : '';
|
const pageString = page ? `?page=${page}` : '';
|
||||||
@ -11,9 +11,7 @@ const getPosts = async ({ my, page }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const usePosts = ({ my, page }) => {
|
export const usePosts = ({ my, page }) => {
|
||||||
const data = useQuery(['post', my, page], () => getPosts({ my, page }), {
|
const data = useQuery(['post', my, page], () => getPosts({ my, page }));
|
||||||
suspense: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
@ -24,9 +22,21 @@ const getPost = async (id) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const usePost = (id) => {
|
export const usePost = (id) => {
|
||||||
const data = useQuery(['post', id], () => getPost(id), {
|
const data = useQuery(['post', id], () => getPost(id));
|
||||||
suspense: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const addPost = async (data) => {
|
||||||
|
const { data: res } = await API.post(URL.post, data, {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'multipart/form-data',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useAddPost = (formData) => {
|
||||||
|
const data = useMutation(() => addPost(formData));
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
@ -8,7 +8,7 @@ const getProducts = async () => {
|
|||||||
|
|
||||||
export const useProducts = () => {
|
export const useProducts = () => {
|
||||||
const data = useQuery('products', () => getProducts(), {
|
const data = useQuery('products', () => getProducts(), {
|
||||||
suspense: true,
|
staleTime: 1000 * 60 * 60 * 24,
|
||||||
});
|
});
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
@ -21,7 +21,7 @@ const getProduct = async (id) => {
|
|||||||
|
|
||||||
export const useProduct = (id) => {
|
export const useProduct = (id) => {
|
||||||
const data = useQuery(['product', id], () => getProduct(id), {
|
const data = useQuery(['product', id], () => getProduct(id), {
|
||||||
suspense: true,
|
staleTime: 1000 * 60 * 60 * 24,
|
||||||
});
|
});
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
@ -37,9 +37,6 @@ export const useProductPost = ({ id, page }) => {
|
|||||||
const data = useQuery(
|
const data = useQuery(
|
||||||
['productpost', id],
|
['productpost', id],
|
||||||
() => getProductPost({ id, page }),
|
() => getProductPost({ id, page }),
|
||||||
{
|
|
||||||
suspense: true,
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
return data;
|
return data;
|
||||||
|
@ -11,20 +11,11 @@ import ProductPriceGraph from '../../components/Graph/ProductPriceGraph';
|
|||||||
import { SubTitle } from '../../styles/Common.styles';
|
import { SubTitle } from '../../styles/Common.styles';
|
||||||
import ButtonArea from '../../components/ButtonArea';
|
import ButtonArea from '../../components/ButtonArea';
|
||||||
import Button from '../../components/Button';
|
import Button from '../../components/Button';
|
||||||
import Spacer from '../../components/Spacer';
|
|
||||||
import { useOverlay } from '@toss/use-overlay';
|
import { useOverlay } from '@toss/use-overlay';
|
||||||
import { Dialog, Slide } from '@mui/material';
|
import { Suspense } from 'react';
|
||||||
import React, { Suspense } from 'react';
|
import CustomDialog from '../../components/CustomDialog';
|
||||||
|
import ItemIssues from '../../components/ItemIssues';
|
||||||
const Transition = React.forwardRef(function Transition(props, ref) {
|
import Spacer from '../../components/Spacer';
|
||||||
return (
|
|
||||||
<Slide
|
|
||||||
direction="up"
|
|
||||||
ref={ref}
|
|
||||||
{...props}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
const PostDetail = () => {
|
const PostDetail = () => {
|
||||||
const overlay = useOverlay();
|
const overlay = useOverlay();
|
||||||
@ -32,29 +23,15 @@ const PostDetail = () => {
|
|||||||
const { data } = usePost(id);
|
const { data } = usePost(id);
|
||||||
const openOverlay = () => {
|
const openOverlay = () => {
|
||||||
overlay.open(({ isOpen, close }) => (
|
overlay.open(({ isOpen, close }) => (
|
||||||
<Dialog
|
<CustomDialog
|
||||||
open={isOpen}
|
open={isOpen}
|
||||||
onClose={close}
|
onClose={close}
|
||||||
TransitionComponent={Transition}
|
title={'최근 거래 가격'}
|
||||||
PaperProps={{
|
|
||||||
style: {
|
|
||||||
padding: '8px 24px 24px 24px',
|
|
||||||
borderRadius: '20px',
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
slotProps={{
|
|
||||||
backdrop: {
|
|
||||||
style: {
|
|
||||||
backdropFilter: 'blur(5px)',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<SubTitle>최근 거래 가격</SubTitle>
|
|
||||||
<Suspense fallback={<></>}>
|
<Suspense fallback={<></>}>
|
||||||
<ProductPriceGraph id={data.product.id} />
|
<ProductPriceGraph id={data.product.id} />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</Dialog>
|
</CustomDialog>
|
||||||
));
|
));
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -70,12 +47,17 @@ const PostDetail = () => {
|
|||||||
<PanelLayout>
|
<PanelLayout>
|
||||||
<LeftPanel>
|
<LeftPanel>
|
||||||
<S.Text>{data.text}</S.Text>
|
<S.Text>{data.text}</S.Text>
|
||||||
|
<ItemIssues
|
||||||
|
itemIssues={data.item_issues}
|
||||||
|
readOnly
|
||||||
|
/>
|
||||||
</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>
|
||||||
|
<Spacer height={48} />
|
||||||
<ButtonArea>
|
<ButtonArea>
|
||||||
<S.Price>가격 {data.price.toLocaleString()}원</S.Price>
|
<S.Price>가격 {data.price.toLocaleString()}원</S.Price>
|
||||||
<Button onClick={openOverlay}>최근 가격 보기</Button>
|
<Button onClick={openOverlay}>최근 가격 보기</Button>
|
||||||
|
@ -16,7 +16,7 @@ const Posts = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<S.Posts>
|
<S.Posts>
|
||||||
<Title>최신글</Title>
|
<Title>판매글</Title>
|
||||||
<PostList
|
<PostList
|
||||||
data={data}
|
data={data}
|
||||||
page={page}
|
page={page}
|
||||||
|
32
src/pages/WriteSteps/ItemIssuesStep.jsx
Normal file
32
src/pages/WriteSteps/ItemIssuesStep.jsx
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import Button from '../../components/Button';
|
||||||
|
import ButtonArea from '../../components/ButtonArea';
|
||||||
|
import ItemIssues from '../../components/ItemIssues';
|
||||||
|
import * as S from '../../styles/WriteSteps.styles';
|
||||||
|
|
||||||
|
function ItemIssuesStep({
|
||||||
|
gotoNextStep,
|
||||||
|
gotoPrevStep,
|
||||||
|
itemIssues,
|
||||||
|
setItemIssues,
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<S.Step>
|
||||||
|
<S.Title>휴대폰 상태에 대해 알려주세요</S.Title>
|
||||||
|
<ItemIssues
|
||||||
|
itemIssues={itemIssues}
|
||||||
|
setItemIssues={setItemIssues}
|
||||||
|
/>
|
||||||
|
<ButtonArea>
|
||||||
|
<Button
|
||||||
|
secondary
|
||||||
|
onClick={gotoPrevStep}
|
||||||
|
>
|
||||||
|
이전
|
||||||
|
</Button>
|
||||||
|
<Button onClick={gotoNextStep}>다음</Button>
|
||||||
|
</ButtonArea>
|
||||||
|
</S.Step>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ItemIssuesStep;
|
@ -1,3 +1,4 @@
|
|||||||
|
import { useState } from 'react';
|
||||||
import Button from '../../components/Button';
|
import Button from '../../components/Button';
|
||||||
import ButtonArea from '../../components/ButtonArea';
|
import ButtonArea from '../../components/ButtonArea';
|
||||||
import ImageInput from '../../components/ImageInput';
|
import ImageInput from '../../components/ImageInput';
|
||||||
@ -12,8 +13,11 @@ function PhotoStep({ gotoNextStep, gotoPrevStep, photos, setPhotos }) {
|
|||||||
Array.from(files).forEach((file) =>
|
Array.from(files).forEach((file) =>
|
||||||
newPhotos.push(URL.createObjectURL(file))
|
newPhotos.push(URL.createObjectURL(file))
|
||||||
);
|
);
|
||||||
setPhotos(newPhotos);
|
setPhotos(files);
|
||||||
|
setPreviewPhotos(newPhotos);
|
||||||
};
|
};
|
||||||
|
const [previewPhotos, setPreviewPhotos] = useState([]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<S.Step>
|
<S.Step>
|
||||||
<S.Title>휴대폰 사진을 올려주세요</S.Title>
|
<S.Title>휴대폰 사진을 올려주세요</S.Title>
|
||||||
@ -23,7 +27,7 @@ function PhotoStep({ gotoNextStep, gotoPrevStep, photos, setPhotos }) {
|
|||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
/>
|
/>
|
||||||
<Spacer height={32} />
|
<Spacer height={32} />
|
||||||
{photos.length > 0 && <ImageViewer images={photos} />}
|
{photos.length > 0 && <ImageViewer images={previewPhotos} />}
|
||||||
<ButtonArea>
|
<ButtonArea>
|
||||||
<Button
|
<Button
|
||||||
secondary
|
secondary
|
||||||
|
50
src/pages/WriteSteps/PostCreateStep.jsx
Normal file
50
src/pages/WriteSteps/PostCreateStep.jsx
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import { useEffect } from 'react';
|
||||||
|
import { getCookie } from '../../API';
|
||||||
|
import { useAddPost } from '../../hooks/network/post';
|
||||||
|
import * as S from '../../styles/WriteSteps.styles';
|
||||||
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
import { Center } from '../../styles/Center.styles';
|
||||||
|
|
||||||
|
function PostCreateStep({ formData }) {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const csrfToken = getCookie('csrftoken');
|
||||||
|
const formDataWithCSRF = new FormData();
|
||||||
|
|
||||||
|
formDataWithCSRF.append('product', formData.product);
|
||||||
|
formDataWithCSRF.append('price', formData.price);
|
||||||
|
formDataWithCSRF.append('text', formData.text);
|
||||||
|
formDataWithCSRF.append('csrfmiddlewaretoken', csrfToken);
|
||||||
|
Object.keys(formData.item_issues).forEach((itemIssueKey) => {
|
||||||
|
formDataWithCSRF.append(
|
||||||
|
`item_issues.${itemIssueKey}`,
|
||||||
|
formData.item_issues[itemIssueKey]
|
||||||
|
);
|
||||||
|
});
|
||||||
|
Array.from(formData.photos).forEach((photo) => {
|
||||||
|
formDataWithCSRF.append('photos', photo);
|
||||||
|
});
|
||||||
|
|
||||||
|
const { mutate, isSuccess } = useAddPost(formDataWithCSRF);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
mutate();
|
||||||
|
}, [mutate]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isSuccess) {
|
||||||
|
setTimeout(() => {
|
||||||
|
navigate('/', { replace: true });
|
||||||
|
}, 3000);
|
||||||
|
}
|
||||||
|
}, [isSuccess, navigate]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<S.Step>
|
||||||
|
<Center>
|
||||||
|
<S.Title>작성한 판매글을 올렸어요</S.Title>
|
||||||
|
</Center>
|
||||||
|
</S.Step>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PostCreateStep;
|
@ -9,14 +9,14 @@ import * as S from '../../styles/WriteSteps.styles';
|
|||||||
function PriceStep({ modelId, gotoNextStep, gotoPrevStep, price, setPrice }) {
|
function PriceStep({ modelId, gotoNextStep, gotoPrevStep, price, setPrice }) {
|
||||||
return (
|
return (
|
||||||
<S.Step>
|
<S.Step>
|
||||||
<S.Title>얼마에 팔고 싶나요?</S.Title>
|
<S.Title>판매할 가격을 입력해 주세요</S.Title>
|
||||||
<CurrencyInput
|
<CurrencyInput
|
||||||
value={price}
|
value={price}
|
||||||
setValue={setPrice}
|
setValue={setPrice}
|
||||||
autofocus
|
autofocus
|
||||||
/>
|
/>
|
||||||
<Spacer height={20} />
|
<Spacer height={20} />
|
||||||
<SubTitle>최근 판매 가격</SubTitle>
|
<SubTitle>최근 판매 가격은 다음과 같아요</SubTitle>
|
||||||
<ProductPriceGraph id={modelId} />
|
<ProductPriceGraph id={modelId} />
|
||||||
<ButtonArea>
|
<ButtonArea>
|
||||||
<Button
|
<Button
|
||||||
|
@ -1,70 +0,0 @@
|
|||||||
import Button from '../../components/Button';
|
|
||||||
import ButtonArea from '../../components/ButtonArea';
|
|
||||||
import CheckBox from '../../components/CheckBox';
|
|
||||||
import * as S from '../../styles/WriteSteps.styles';
|
|
||||||
|
|
||||||
function StatusStep({ gotoNextStep, gotoPrevStep, status, setStatus }) {
|
|
||||||
return (
|
|
||||||
<S.Step>
|
|
||||||
<S.Title>휴대폰 상태에 대해 알려주세요</S.Title>
|
|
||||||
<S.Grid>
|
|
||||||
<CheckBox
|
|
||||||
id="화면 깨짐"
|
|
||||||
name="화면 깨짐"
|
|
||||||
text="화면이 깨져있어요"
|
|
||||||
value={status.display}
|
|
||||||
setChecked={(value) =>
|
|
||||||
setStatus((prev) => ({ ...prev, display: value }))
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<CheckBox
|
|
||||||
id="뒷면/옆면 파손"
|
|
||||||
name="뒷면/옆면 파손"
|
|
||||||
text="화면이 아닌 부분에 파손이 있어요"
|
|
||||||
value={status.back}
|
|
||||||
setChecked={(value) =>
|
|
||||||
setStatus((prev) => ({ ...prev, back: value }))
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<CheckBox
|
|
||||||
id="버튼 고장"
|
|
||||||
name="버튼 고장"
|
|
||||||
text="고장난 버튼이 있어요"
|
|
||||||
value={status.button}
|
|
||||||
setChecked={(value) =>
|
|
||||||
setStatus((prev) => ({ ...prev, button: value }))
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<CheckBox
|
|
||||||
id="생체 인식 고장"
|
|
||||||
name="생체 인식 고장"
|
|
||||||
text="지문이나 얼굴인식이 작동하지 않아요"
|
|
||||||
value={status.biometrics}
|
|
||||||
setChecked={(value) =>
|
|
||||||
setStatus((prev) => ({ ...prev, biometrics: value }))
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<CheckBox
|
|
||||||
id="기능 고장"
|
|
||||||
name="기능 고장"
|
|
||||||
text="그 밖에 작동하지 않는 기능이 있어요"
|
|
||||||
value={status.others}
|
|
||||||
setChecked={(value) =>
|
|
||||||
setStatus((prev) => ({ ...prev, others: value }))
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</S.Grid>
|
|
||||||
<ButtonArea>
|
|
||||||
<Button
|
|
||||||
secondary
|
|
||||||
onClick={gotoPrevStep}
|
|
||||||
>
|
|
||||||
이전
|
|
||||||
</Button>
|
|
||||||
<Button onClick={gotoNextStep}>다음</Button>
|
|
||||||
</ButtonArea>
|
|
||||||
</S.Step>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default StatusStep;
|
|
14
src/pages/WriteSteps/WaitingCreation.jsx
Normal file
14
src/pages/WriteSteps/WaitingCreation.jsx
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { Center } from '../../styles/Center.styles';
|
||||||
|
import * as S from '../../styles/WriteSteps.styles';
|
||||||
|
|
||||||
|
function WaitingCreation() {
|
||||||
|
return (
|
||||||
|
<S.Step>
|
||||||
|
<Center>
|
||||||
|
<S.Title>작성한 판매글을 올리는 중이에요</S.Title>
|
||||||
|
</Center>
|
||||||
|
</S.Step>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default WaitingCreation;
|
@ -1,30 +1,48 @@
|
|||||||
import { useState } from 'react';
|
import { Suspense, useState } from 'react';
|
||||||
import ProductSelectStep from './ProductSelectStep';
|
import ProductSelectStep from './ProductSelectStep';
|
||||||
import StatusStep from './StatusStep';
|
import ItemIssuesStep from './ItemIssuesStep';
|
||||||
import PhotoStep from './PhotoStep';
|
import PhotoStep from './PhotoStep';
|
||||||
import PriceStep from './PriceStep';
|
import PriceStep from './PriceStep';
|
||||||
import TextStep from './TextStep';
|
import TextStep from './TextStep';
|
||||||
|
import PostCreateStep from './PostCreateStep';
|
||||||
|
import WaitingCreation from './WaitingCreation';
|
||||||
|
|
||||||
function WriteSteps() {
|
function WriteSteps() {
|
||||||
const [formData, setFormData] = useState({
|
const [formData, setFormData] = useState({
|
||||||
product: '',
|
product: '',
|
||||||
status: {
|
item_issues: {
|
||||||
display: false,
|
display: false,
|
||||||
back: false,
|
frame: false,
|
||||||
button: false,
|
button: false,
|
||||||
biometrics: false,
|
biometric: false,
|
||||||
|
camera: false,
|
||||||
|
speaker: false,
|
||||||
others: false,
|
others: false,
|
||||||
},
|
},
|
||||||
price: '',
|
price: '',
|
||||||
photos: [],
|
photos: [],
|
||||||
text: '',
|
text: '',
|
||||||
});
|
});
|
||||||
const STEP_INFO = ['product', 'status', 'price', 'photo', 'text', 'step6'];
|
const STEP_INFO = [
|
||||||
|
'product',
|
||||||
|
'itemIssues',
|
||||||
|
'price',
|
||||||
|
'photo',
|
||||||
|
'text',
|
||||||
|
'postCreate',
|
||||||
|
];
|
||||||
const setProduct = (product) => {
|
const setProduct = (product) => {
|
||||||
setFormData((prev) => ({ ...prev, product }));
|
setFormData((prev) => ({ ...prev, product }));
|
||||||
};
|
};
|
||||||
const setStatus = (status) => {
|
const setItemIssues = (itemIssues) => {
|
||||||
setFormData((prev) => ({ ...prev, status }));
|
if (typeof itemIssues === 'function') {
|
||||||
|
setFormData((prev) => ({
|
||||||
|
...prev,
|
||||||
|
item_issues: itemIssues(prev.item_issues),
|
||||||
|
}));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setFormData((prev) => ({ ...prev, itemIssues }));
|
||||||
};
|
};
|
||||||
const setPrice = (price) => {
|
const setPrice = (price) => {
|
||||||
setFormData((prev) => ({ ...prev, price }));
|
setFormData((prev) => ({ ...prev, price }));
|
||||||
@ -41,15 +59,15 @@ function WriteSteps() {
|
|||||||
<>
|
<>
|
||||||
{step === 'product' && (
|
{step === 'product' && (
|
||||||
<ProductSelectStep
|
<ProductSelectStep
|
||||||
gotoNextStep={() => setStep('status')}
|
gotoNextStep={() => setStep('itemIssues')}
|
||||||
product={formData.product}
|
product={formData.product}
|
||||||
setProduct={setProduct}
|
setProduct={setProduct}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{step === 'status' && (
|
{step === 'itemIssues' && (
|
||||||
<StatusStep
|
<ItemIssuesStep
|
||||||
status={formData.status}
|
itemIssues={formData.item_issues}
|
||||||
setStatus={setStatus}
|
setItemIssues={setItemIssues}
|
||||||
gotoNextStep={() => setStep('price')}
|
gotoNextStep={() => setStep('price')}
|
||||||
gotoPrevStep={() => setStep('product')}
|
gotoPrevStep={() => setStep('product')}
|
||||||
/>
|
/>
|
||||||
@ -60,7 +78,7 @@ function WriteSteps() {
|
|||||||
price={formData.price}
|
price={formData.price}
|
||||||
setPrice={setPrice}
|
setPrice={setPrice}
|
||||||
gotoNextStep={() => setStep('photo')}
|
gotoNextStep={() => setStep('photo')}
|
||||||
gotoPrevStep={() => setStep('status')}
|
gotoPrevStep={() => setStep('itemIssues')}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{step === 'photo' && (
|
{step === 'photo' && (
|
||||||
@ -75,10 +93,15 @@ function WriteSteps() {
|
|||||||
<TextStep
|
<TextStep
|
||||||
text={formData.text}
|
text={formData.text}
|
||||||
setText={setText}
|
setText={setText}
|
||||||
gotoNextStep={() => setStep('step6')}
|
gotoNextStep={() => setStep('postCreate')}
|
||||||
gotoPrevStep={() => setStep('photo')}
|
gotoPrevStep={() => setStep('photo')}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{step === 'postCreate' && (
|
||||||
|
<Suspense fallback={<WaitingCreation />}>
|
||||||
|
<PostCreateStep formData={formData} />
|
||||||
|
</Suspense>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
8
src/styles/Center.styles.js
Normal file
8
src/styles/Center.styles.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
export const Center = styled.div`
|
||||||
|
flex:1;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
`;
|
20
src/styles/CloseButton.styles.js
Normal file
20
src/styles/CloseButton.styles.js
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
export const CloseButton = styled.button`
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
border: none;
|
||||||
|
background-color: transparent;
|
||||||
|
cursor: pointer;
|
||||||
|
outline: none;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
& > svg {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
fill: ${({ theme }) => theme.colors.gray700};
|
||||||
|
}
|
||||||
|
`;
|
@ -44,6 +44,7 @@ export const Input = styled.input`
|
|||||||
export const DynamicList = styled.div`
|
export const DynamicList = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 20px;
|
gap: 20px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
|
||||||
@media screen and (max-width: 768px) {
|
@media screen and (max-width: 768px) {
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
17
src/styles/CustomDialog.styles.js
Normal file
17
src/styles/CustomDialog.styles.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
export const DialogHeader = styled.header`
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const DialogTitle = styled.h2`
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 700;
|
||||||
|
padding: 0 8px;
|
||||||
|
margin: 0;
|
||||||
|
user-select: none;
|
||||||
|
box-sizing: border-box;
|
||||||
|
color: ${({ theme }) => theme.colors.gray900};
|
||||||
|
`;
|
11
src/styles/ItemIssues.styles.js
Normal file
11
src/styles/ItemIssues.styles.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
export const Grid = styled.div`
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
gap: 20px;
|
||||||
|
|
||||||
|
@media screen and (max-width: 768px) {
|
||||||
|
grid-template-columns: repeat(1, 1fr);
|
||||||
|
}
|
||||||
|
`;
|
@ -14,7 +14,7 @@ const colors = {
|
|||||||
error: '#e0245e',
|
error: '#e0245e',
|
||||||
background: '#fff',
|
background: '#fff',
|
||||||
grayBackground: '#f2f4f6',
|
grayBackground: '#f2f4f6',
|
||||||
primary: '#2060ee',
|
primary: '#0660ee',
|
||||||
};
|
};
|
||||||
|
|
||||||
const boxShadow = css`
|
const boxShadow = css`
|
||||||
|
@ -33,6 +33,8 @@ export const Select = styled.select`
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
color: ${({ theme }) => theme.colors.gray900};
|
color: ${({ theme }) => theme.colors.gray900};
|
||||||
|
outline-color: ${({ theme }) => theme.colors.primary};
|
||||||
|
border-color: transparent;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user