diff --git a/src/pages/AddItem.jsx b/src/pages/AddItem.jsx
new file mode 100644
index 000000000..268fd9273
--- /dev/null
+++ b/src/pages/AddItem.jsx
@@ -0,0 +1,81 @@
+import Description from "components/addItem/Description";
+import ImgFileInput from "components/addItem/ImgFileInput";
+import PriceInput from "components/addItem/PriceInput";
+import TextInput from "components/addItem/TextInput";
+import TagInput from "components/addItem/TagInput";
+import Header from "components/common/Header";
+import PrimaryButton from "components/common/PrimaryButton";
+import React, { useEffect, useState } from "react";
+
+const INITIAL_INPUT = {
+ images: [],
+ name: "",
+ description: "",
+ price: 0,
+ tags: [],
+};
+
+function AddItem() {
+ const [userInput, setUserInput] = useState(INITIAL_INPUT);
+ const { name, description, price, tags } = userInput;
+ const [isFormValid, setIsFormValid] = useState(false);
+ const handleSubmit = (e) => {
+ e.preventDefault();
+ if (isFormValid) {
+ // 추후 POST API 연동 예정
+ console.log(e);
+ }
+ };
+
+ useEffect(() => {
+ const isValid =
+ name !== "" && description !== "" && price !== 0 && tags.length > 0;
+ setIsFormValid(isValid);
+ }, [name, description, price, tags.length]);
+ return (
+ <>
+
+
+ >
+ );
+}
+
+export default AddItem;
diff --git a/src/pages/Home.jsx b/src/pages/Home.jsx
index 915e714ff..562c52501 100644
--- a/src/pages/Home.jsx
+++ b/src/pages/Home.jsx
@@ -1,7 +1,7 @@
import React from "react";
import { NavLink } from "react-router-dom";
-import Header from "components/Header";
-import Footer from "components/Footer";
+import Header from "components/common/Header";
+import Footer from "components/common/Footer";
function Home() {
return (
diff --git a/src/pages/Items.jsx b/src/pages/Items.jsx
index 7f4869d99..78451f03d 100644
--- a/src/pages/Items.jsx
+++ b/src/pages/Items.jsx
@@ -1,7 +1,7 @@
import React from "react";
-import Header from "components/Header";
-import BestProduct from "components/BestProductList";
-import ProductList from "components/ProductList";
+import Header from "components/common/Header";
+import BestProduct from "components/items/BestProductList";
+import ProductList from "components/items/ProductList";
function Items() {
return (
diff --git a/src/router/index.js b/src/router/index.js
index 5621718ac..514594c32 100644
--- a/src/router/index.js
+++ b/src/router/index.js
@@ -2,6 +2,7 @@ import Login from "pages/Login";
import Home from "../pages/Home";
import Signup from "pages/Signup";
import Items from "pages/Items";
+import AddItem from "pages/AddItem";
const routes = [
{
@@ -20,6 +21,10 @@ const routes = [
path: "/items",
element:
,
},
+ {
+ path: "/additem",
+ element:
,
+ },
];
export default routes;
diff --git a/src/style/css/style.css b/src/style/css/style.css
index 4998a6e51..80d8504f7 100644
--- a/src/style/css/style.css
+++ b/src/style/css/style.css
@@ -762,7 +762,6 @@ footer .footer-wrap .sns a img {
z-index: 1;
}
.sort-area .options {
- display: none;
overflow: hidden;
position: absolute;
top: calc(100% + 0.8rem);
@@ -771,9 +770,6 @@ footer .footer-wrap .sns a img {
border: 0.1rem solid var(--gray200);
border-radius: 1.2rem;
}
-.sort-area .options[open] {
- display: block;
-}
.sort-area .options button {
display: block;
width: 100%;
@@ -808,12 +804,17 @@ footer .footer-wrap .sns a img {
.btn-primary {
border: 0;
border-radius: 0.8rem;
+ padding: 0.8rem 2.3rem;
background-color: var(--primary100);
font-size: 1.6rem;
font-weight: var(--semiBold);
line-height: 2.6rem;
color: var(--gray100);
}
+.btn-primary:disabled {
+ cursor: not-allowed;
+ background-color: var(--gray400);
+}
.pagination {
display: flex;
@@ -847,6 +848,130 @@ footer .footer-wrap .sns a img {
transform: rotate(180deg);
}
+.form-input-wrap {
+ display: flex;
+ flex-direction: column;
+ gap: 1.6rem 0;
+ /* 상품 이미지 */
+ /* 상품 태그 */
+}
+.form-input-wrap label {
+ font-size: 1.8rem;
+ font-weight: var(--bold);
+ line-height: 2.6rem;
+ color: var(--gray800);
+}
+.form-input-wrap input:not([type=file]),
+.form-input-wrap textarea {
+ border: 0;
+ border-radius: 1.2rem;
+ padding: 1.6rem 2.4rem;
+ background-color: var(--gray100);
+ font-size: 1.6rem;
+ line-height: 2.6rem;
+ color: var(--gray800);
+}
+.form-input-wrap input:not([type=file])::-moz-placeholder, .form-input-wrap textarea::-moz-placeholder {
+ color: var(--gray400);
+}
+.form-input-wrap input:not([type=file])::placeholder,
+.form-input-wrap textarea::placeholder {
+ color: var(--gray400);
+}
+.form-input-wrap input:not([type=file]):focus,
+.form-input-wrap textarea:focus {
+ outline: 0.1rem solid var(--primary100);
+}
+.form-input-wrap textarea {
+ height: 28.2rem;
+ resize: none;
+}
+.form-input-wrap .upload-area {
+ display: flex;
+ gap: 0 2.4rem;
+}
+.form-input-wrap .upload-area .btn-upload-img,
+.form-input-wrap .upload-area .thumbnail {
+ width: 28.2rem;
+ aspect-ratio: 1/1;
+ border-radius: 1.2rem;
+}
+.form-input-wrap .upload-area .btn-upload-img {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ gap: 1.2rem 0;
+ padding: 0;
+ border: 0;
+ font-size: 1.6rem;
+ line-height: 2.6rem;
+ color: var(--gray400);
+}
+.form-input-wrap .upload-area .btn-upload-img img {
+ width: 4.8rem;
+ height: 4.8rem;
+}
+.form-input-wrap .upload-area .thumbnail {
+ overflow: hidden;
+ position: relative;
+ border: 0.1rem solid var(--gray50);
+}
+.form-input-wrap .upload-area .thumbnail img {
+ width: 100%;
+ height: 100%;
+}
+.form-input-wrap .upload-area .thumbnail .btn-delete-thumbnail {
+ position: absolute;
+ top: 1.4rem;
+ right: 1.3rem;
+ width: 2.2rem;
+ height: 2.4rem;
+ border: 0;
+ background: url(/public/images/icons/ic_delete.svg) no-repeat center/cover;
+}
+.form-input-wrap .error-msg {
+ font-size: 1.6rem;
+ line-height: 2.6rem;
+ color: var(--error);
+}
+.form-input-wrap .tag-area {
+ display: flex;
+ gap: 1.2rem;
+ margin-top: 1.4rem;
+}
+.form-input-wrap .tag-area .tag {
+ position: relative;
+ padding: 0.5rem 4.2rem 0.5rem 1.6rem;
+ border-radius: 2.6rem;
+ background-color: var(--gray100);
+ font-size: 1.6rem;
+ line-height: 2.6rem;
+ color: var(--gray800);
+}
+.form-input-wrap .tag-area .tag .btn-delete-tag {
+ position: absolute;
+ top: 0.6rem;
+ right: 1.2rem;
+ width: 2.2rem;
+ height: 2.4rem;
+ border: 0;
+ background: url(/public/images/icons/ic_delete.svg) no-repeat center/cover;
+}
+
+/* BreakPoint Tablet */
+@media (max-width: 1199px) {
+ .form-input-wrap {
+ /* 상품 이미지 */
+ }
+ .form-input-wrap .upload-area {
+ gap: 0 1rem;
+ }
+ .form-input-wrap .upload-area .btn-upload-img,
+ .form-input-wrap .upload-area .thumbnail {
+ width: 16.8rem;
+ }
+}
/* Main & Section */
.section-layout {
display: flex;
@@ -1318,9 +1443,6 @@ footer .footer-wrap .sns a img {
.page-items section#section_all .top .filter-area .input-area input:focus {
outline: 0.1rem solid var(--primary100);
}
-.page-items section#section_all .top .filter-area .btn-register {
- padding: 0.8rem 2.3rem;
-}
.page-items section .products-wrap {
display: grid;
gap: 4rem 2.4rem;
@@ -1398,5 +1520,40 @@ footer .footer-wrap .sns a img {
grid-template-columns: repeat(2, 1fr);
}
}
+.form-wrap {
+ max-width: 120rem;
+ width: 100%;
+ margin: 0 auto;
+ padding: 2.9rem 0 6.9rem;
+}
+.form-wrap form {
+ display: flex;
+ flex-direction: column;
+ gap: 3.2rem 0;
+}
+.form-wrap form .title-wrap {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ margin-bottom: 2.4rem;
+}
+.form-wrap form .title-wrap h2 {
+ font-size: 2rem;
+ font-weight: var(--bold);
+ line-height: 3.2rem;
+}
+
+/* BreakPoint Tablet */
+@media (max-width: 1199px) {
+ .form-wrap {
+ max-width: 69.6rem;
+ }
+}
+/* BreakPoint Mobile */
+@media (max-width: 767px) {
+ .form-wrap {
+ max-width: 34.6rem;
+ }
+}
/* BreakPoint Tablet */
/* BreakPoint Mobile */
\ No newline at end of file
diff --git a/src/style/scss/components/_dropdown.scss b/src/style/scss/components/_dropdown.scss
index 3eb71e1ce..3665f0b76 100644
--- a/src/style/scss/components/_dropdown.scss
+++ b/src/style/scss/components/_dropdown.scss
@@ -30,7 +30,6 @@
}
}
.options {
- display: none;
overflow: hidden;
position: absolute;
top: calc(100% + 0.8rem);
@@ -38,9 +37,6 @@
width: 13rem;
border: 0.1rem solid var(--gray200);
border-radius: 1.2rem;
- &[open] {
- display: block;
- }
button {
display: block;
width: 100%;
diff --git a/src/style/scss/components/_index.scss b/src/style/scss/components/_index.scss
index 92d3ced85..2f3ef3a9d 100644
--- a/src/style/scss/components/_index.scss
+++ b/src/style/scss/components/_index.scss
@@ -4,3 +4,4 @@
@forward "dropdown";
@forward "primaryButton";
@forward "pagination";
+@forward "inputs";
diff --git a/src/style/scss/components/_inputs.scss b/src/style/scss/components/_inputs.scss
new file mode 100644
index 000000000..4e19d0514
--- /dev/null
+++ b/src/style/scss/components/_inputs.scss
@@ -0,0 +1,121 @@
+.form-input-wrap {
+ display: flex;
+ flex-direction: column;
+ gap: 1.6rem 0;
+ label {
+ font-size: 1.8rem;
+ font-weight: var(--bold);
+ line-height: 2.6rem;
+ color: var(--gray800);
+ }
+ input:not([type="file"]),
+ textarea {
+ border: 0;
+ border-radius: 1.2rem;
+ padding: 1.6rem 2.4rem;
+ background-color: var(--gray100);
+ font-size: 1.6rem;
+ line-height: 2.6rem;
+ color: var(--gray800);
+ &::placeholder {
+ color: var(--gray400);
+ }
+ &:focus {
+ outline: 0.1rem solid var(--primary100);
+ }
+ }
+ textarea {
+ height: 28.2rem;
+ resize: none;
+ }
+ /* 상품 이미지 */
+ .upload-area {
+ display: flex;
+ gap: 0 2.4rem;
+ .btn-upload-img,
+ .thumbnail {
+ width: 28.2rem;
+ aspect-ratio: 1 / 1;
+ border-radius: 1.2rem;
+ }
+ .btn-upload-img {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ gap: 1.2rem 0;
+ padding: 0;
+ border: 0;
+ font-size: 1.6rem;
+ line-height: 2.6rem;
+ color: var(--gray400);
+ img {
+ width: 4.8rem;
+ height: 4.8rem;
+ }
+ }
+ .thumbnail {
+ overflow: hidden;
+ position: relative;
+ border: 0.1rem solid var(--gray50);
+ img {
+ width: 100%;
+ height: 100%;
+ }
+ .btn-delete-thumbnail {
+ position: absolute;
+ top: 1.4rem;
+ right: 1.3rem;
+ width: 2.2rem;
+ height: 2.4rem;
+ border: 0;
+ background: url(/public/images/icons/ic_delete.svg) no-repeat center /
+ cover;
+ }
+ }
+ }
+ .error-msg {
+ font-size: 1.6rem;
+ line-height: 2.6rem;
+ color: var(--error);
+ }
+ /* 상품 태그 */
+ .tag-area {
+ display: flex;
+ gap: 1.2rem;
+ margin-top: 1.4rem;
+ .tag {
+ position: relative;
+ padding: 0.5rem 4.2rem 0.5rem 1.6rem;
+ border-radius: 2.6rem;
+ background-color: var(--gray100);
+ font-size: 1.6rem;
+ line-height: 2.6rem;
+ color: var(--gray800);
+ .btn-delete-tag {
+ position: absolute;
+ top: 0.6rem;
+ right: 1.2rem;
+ width: 2.2rem;
+ height: 2.4rem;
+ border: 0;
+ background: url(/public/images/icons/ic_delete.svg) no-repeat center /
+ cover;
+ }
+ }
+ }
+}
+
+/* BreakPoint Tablet */
+@media (max-width: 1199px) {
+ .form-input-wrap {
+ /* 상품 이미지 */
+ .upload-area {
+ gap: 0 1rem;
+ .btn-upload-img,
+ .thumbnail {
+ width: 16.8rem;
+ }
+ }
+ }
+}
diff --git a/src/style/scss/components/_primaryButton.scss b/src/style/scss/components/_primaryButton.scss
index 958d8535c..dacb00977 100644
--- a/src/style/scss/components/_primaryButton.scss
+++ b/src/style/scss/components/_primaryButton.scss
@@ -1,9 +1,14 @@
.btn-primary {
border: 0;
border-radius: 0.8rem;
+ padding: 0.8rem 2.3rem;
background-color: var(--primary100);
font-size: 1.6rem;
font-weight: var(--semiBold);
line-height: 2.6rem;
color: var(--gray100);
+ &:disabled {
+ cursor: not-allowed;
+ background-color: var(--gray400);
+ }
}
diff --git a/src/style/scss/pages/_addItem.scss b/src/style/scss/pages/_addItem.scss
new file mode 100644
index 000000000..01c49497b
--- /dev/null
+++ b/src/style/scss/pages/_addItem.scss
@@ -0,0 +1,36 @@
+.form-wrap {
+ max-width: 120rem;
+ width: 100%;
+ margin: 0 auto;
+ padding: 2.9rem 0 6.9rem;
+ form {
+ display: flex;
+ flex-direction: column;
+ gap: 3.2rem 0;
+ .title-wrap {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ margin-bottom: 2.4rem;
+ h2 {
+ font-size: 2rem;
+ font-weight: var(--bold);
+ line-height: 3.2rem;
+ }
+ }
+ }
+}
+
+/* BreakPoint Tablet */
+@media (max-width: 1199px) {
+ .form-wrap {
+ max-width: 69.6rem;
+ }
+}
+
+/* BreakPoint Mobile */
+@media (max-width: 767px) {
+ .form-wrap {
+ max-width: 34.6rem;
+ }
+}
diff --git a/src/style/scss/pages/_index.scss b/src/style/scss/pages/_index.scss
index 262513497..2e67fef98 100644
--- a/src/style/scss/pages/_index.scss
+++ b/src/style/scss/pages/_index.scss
@@ -1,3 +1,4 @@
@forward "home";
@forward "auth";
@forward "items";
+@forward "addItem";
diff --git a/src/style/scss/pages/_items.scss b/src/style/scss/pages/_items.scss
index d5afa47cb..bc1c26364 100644
--- a/src/style/scss/pages/_items.scss
+++ b/src/style/scss/pages/_items.scss
@@ -60,9 +60,6 @@
}
}
}
- .btn-register {
- padding: 0.8rem 2.3rem;
- }
}
}
}
diff --git a/src/utils/api.js b/src/utils/api.js
index a607f2c00..6412e3ceb 100644
--- a/src/utils/api.js
+++ b/src/utils/api.js
@@ -8,12 +8,9 @@ export const fetchProducts = async ({
} = {}) => {
try {
const queryParams = new URLSearchParams({ page, pageSize, orderBy });
- if (keyword) queryParams.set("keyword", encodeURIComponent(keyword));
+ if (keyword) queryParams.set("keyword", keyword);
- const response = await fetch(
- // `${SERVER_URL}?page=${page}&pageSize=${pageSize}&orderBy=${order}`
- `${SERVER_URL}?${queryParams}`
- );
+ const response = await fetch(`${SERVER_URL}?${queryParams}`);
if (!response.ok) {
throw new Error("데이터 불러오기 실패");
}
diff --git a/src/utils/checkDevice.js b/src/utils/checkDevice.js
index 0eea0c4a4..6c0369e71 100644
--- a/src/utils/checkDevice.js
+++ b/src/utils/checkDevice.js
@@ -1,30 +1,12 @@
-/**
- * 화면 사이즈에 맞는 디바이스를 리턴
- * @param {*} width 화면 width
- * @returns 'pc' | 'tablet' | 'mobile'
- */
-const getBreakpoint = (width) => {
- if (width >= 1200) {
- return "pc";
- } else if (width >= 768) {
- return "tablet";
- } else {
- return "mobile";
- }
-};
-
/**
* 각 디바이스마다 화면에 표시할 product 수 리턴
- * @param {*} device 'pc' | 'tablet' | 'mobile'
+ * @param {*} width 화면 width
* @returns
*/
-const updateProductsPerPage = (device) => {
- const productsPerpage = {
- pc: 10,
- tablet: 6,
- mobile: 4,
- };
- return productsPerpage[device] || 10;
+const updateProductsPerPage = (width) => {
+ if (width >= 1200) return 10;
+ else if (width >= 768) return 6;
+ else return 4;
};
-export { getBreakpoint, updateProductsPerPage };
+export { updateProductsPerPage };
diff --git a/src/utils/formatPrice.js b/src/utils/formatPrice.js
index 0571f43ce..bc5a5da6d 100644
--- a/src/utils/formatPrice.js
+++ b/src/utils/formatPrice.js
@@ -7,4 +7,26 @@ const formatPriceToKRW = (price) => {
return price.toLocaleString("ko-KR") + "원";
};
-export default formatPriceToKRW;
+/**
+ * Input의 입력을 금액 형식으로 리턴
+ * ex) 1000 -> 1,000
+ * @param {*} price
+ */
+const formatPrice = (price) => {
+ if (!price) return "";
+ return price.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
+};
+
+/**
+ * 금액 형식의(ex: 1,000) 데이터를 API 규격에 맞게(1000) 리턴
+ * @param {*} price
+ * @returns
+ */
+const formatToPrice = (price) => {
+ const removedString = price.replace(/\D/g, ""); // 숫자를 제외한 입력 삭제
+ const formated = removedString.replace(/^0+/, ""); // 0으로 시작하는 경우 제거
+
+ return formated;
+};
+
+export { formatPriceToKRW, formatPrice, formatToPrice };