From c8dccb39cde4dcbb6477e8f2a08cc42de160fd32 Mon Sep 17 00:00:00 2001 From: CisThard Date: Sun, 8 Dec 2024 20:17:15 +0900 Subject: [PATCH] =?UTF-8?q?refactor:=20sprint5=20=EC=BD=94=EB=A9=98?= =?UTF-8?q?=ED=8A=B8=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/getProducts.js | 11 ++ src/components/App.css | 18 ---- src/components/App.js | 14 ++- src/components/Banner.js | 22 ++++ src/components/BestProducts.js | 14 ++- src/components/Content.js | 42 ++++++++ src/components/DropDown.js | 38 ++++--- src/components/DropdownContent.js | 27 +++++ src/components/DropdownToggle.js | 11 ++ src/components/Footer.css | 18 ---- src/components/Footer.js | 8 ++ src/components/IsImage.js | 8 -- src/components/NavBar.css | 18 ---- src/components/OnSaleProducts.js | 30 +++++- src/components/Pagination.js | 13 ++- src/components/RegistrationForm.js | 100 ++++++++++++++++++ src/components/SearchBar.js | 1 - .../{empty_heart.png => emptyHeart.png} | Bin ...age-available.png => noImageAvailable.png} | Bin src/index.css | 16 +++ src/index.js | 14 ++- src/reset.css | 2 +- src/utils/image.helper.js | 8 ++ 23 files changed, 331 insertions(+), 102 deletions(-) create mode 100644 src/components/Banner.js create mode 100644 src/components/Content.js create mode 100644 src/components/DropdownContent.js create mode 100644 src/components/DropdownToggle.js delete mode 100644 src/components/IsImage.js create mode 100644 src/components/RegistrationForm.js rename src/images/{empty_heart.png => emptyHeart.png} (100%) rename src/images/{no-image-available.png => noImageAvailable.png} (100%) create mode 100644 src/utils/image.helper.js diff --git a/src/api/getProducts.js b/src/api/getProducts.js index 7989038..f30d059 100644 --- a/src/api/getProducts.js +++ b/src/api/getProducts.js @@ -1,4 +1,8 @@ +<<<<<<< HEAD const BASE_URL = 'https://panda-market-api.vercel.app/products'; +======= +const BASE_URL = "https://backend-c2ut.onrender.com"; +>>>>>>> 0f1a9c4 (refactor: sprint5 코멘트 반영) export async function getProducts( @@ -6,16 +10,23 @@ export async function getProducts( offset = 0, limit = 10, page = 1, +<<<<<<< HEAD pageSize = 10, sort = 'recent', +======= + sort = "recent", +>>>>>>> 0f1a9c4 (refactor: sprint5 코멘트 반영) keyword = "", } ) { const query = `offset=${offset}&limit=${limit}&page=${page}&pageSize=${pageSize}&orderBy=${sort}&keyword=${keyword}`; const response = await fetch(`${BASE_URL}?${query}`); const data = await response.json(); +<<<<<<< HEAD console.log("Fetched products:", data); +======= +>>>>>>> 0f1a9c4 (refactor: sprint5 코멘트 반영) return data; } diff --git a/src/components/App.css b/src/components/App.css index fc0cba7..337ed04 100644 --- a/src/components/App.css +++ b/src/components/App.css @@ -78,21 +78,3 @@ box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2); z-index: 1; } - - -:root { - --primary-color-50: #E6F2FF; - --primary-color-100: #3692FF; - --primary-color-200: #1967D6; - --primary-color-300: #1251AA; - --error-color: #F74747; - --secondary-color-gray900: #111827; - --secondary-color-gray800: #1F2937; - --secondary-color-gray700: #374151; - --secondary-color-gray600: #4B5563; - --secondary-color-gray500: #6B7280; - --secondary-color-gray400: #9CA3AF; - --secondary-color-gray200: #E5E7EB; - --secondary-color-gray100: #F3F4F6; - --secondary-color-gray50: #F9FAFB; -} diff --git a/src/components/App.js b/src/components/App.js index 9fcfe2b..5c1014a 100644 --- a/src/components/App.js +++ b/src/components/App.js @@ -1,3 +1,4 @@ +<<<<<<< HEAD:src/components/App.js import './App.css'; import NavBar from './NavBar'; import Footer from './Footer'; @@ -7,6 +8,18 @@ import { useState } from 'react'; import SearchBar from './SearchBar'; import Pagination from './Pagination'; import DropDown from './DropDown'; +======= +import "../styles/ItemPage.css"; +import NavBar from "../components/NavBar"; +import Footer from "../components/Footer"; +import BestProducts from "../components/BestProducts"; +import { OnSaleProducts } from "../components/OnSaleProducts"; +import { useState } from "react"; +import SearchBar from "../components/SearchBar"; +import Pagination from "../components/Pagination"; +import DropDown from "../components/DropDown"; +import { NavLink } from "react-router-dom"; +>>>>>>> 0f1a9c4 (refactor: sprint5 코멘트 반영):src/pages/ItemPage.js function App() { @@ -24,7 +37,6 @@ function App() { newOption = "favorite"; setSelectedOption(newOption); } - console.log(newOption); } const handlePageChange = (newPage) => { diff --git a/src/components/Banner.js b/src/components/Banner.js new file mode 100644 index 0000000..81ea48b --- /dev/null +++ b/src/components/Banner.js @@ -0,0 +1,22 @@ +import "../styles/Banner.css"; + +export function Banner(children) { + return ( + + ); +} + +export default Banner; \ No newline at end of file diff --git a/src/components/BestProducts.js b/src/components/BestProducts.js index 582970a..c6b5a1d 100644 --- a/src/components/BestProducts.js +++ b/src/components/BestProducts.js @@ -1,15 +1,21 @@ import { useEffect, useState } from "react"; import "./BestProducts.css"; import { getProducts } from "../api/getProducts"; +<<<<<<< HEAD import "./BestProducts.css"; import "../images/empty_heart.png"; import IsImage from "./IsImage"; import "../images/no-image-available.png"; +======= +import "../images/emptyHeart.png"; +import IsImage from "../utils/image.helper"; +import "../images/noImageAvailable.png"; +>>>>>>> 0f1a9c4 (refactor: sprint5 코멘트 반영) export function BestProducts() { - const empty_heart = require("../images/empty_heart.png"); - const no_image_available = require("../images/no-image-available.png"); + const emptyHeart = require("../images/emptyHeart.png"); + const noImageAvailable = require("../images/noImageAvailable.png"); const [bestProducts, setBestProducts] = useState([]); @@ -22,7 +28,7 @@ export function BestProducts() { setBestProducts(data.list); data.list.forEach(product => { if (!IsImage(String(product.images))) { - product.images = no_image_available; + product.images = noImageAvailable; } }); }) @@ -40,7 +46,7 @@ export function BestProducts() {

{product.price.toLocaleString()}원

{product.favoriteCount}
diff --git a/src/components/Content.js b/src/components/Content.js new file mode 100644 index 0000000..cc9e96f --- /dev/null +++ b/src/components/Content.js @@ -0,0 +1,42 @@ +import "../styles/Content.css"; +import "../images/Img_home_01.png"; +import "../images/Img_home_02.png"; +import "../images/Img_home_03.png"; + +export function Content() { + + const content_image_01 = require("../images/Img_home_01.png"); + const content_image_02 = require("../images/Img_home_02.png"); + const content_image_03 = require("../images/Img_home_03.png"); + + return ( +
+
+ +
+ Hot Item +

인기 상품을
확인해 보세요

+

가장 HOT한 중고거래 물품을
판다 마켓에서 확인해 보세요

+
+
+
+
+ Search +

구매를 원하는
상품을 검색하세요

+

구매하고 싶은 물품은 검색해서
쉽게 찾아보세요

+
+ +
+
+ +
+ Register +

판매를 원하는
상품을 등록하세요

+

어떤 물건이든 판매하고 싶은 상품을
쉽게 등록하세요

+
+
+
+ ); +} + +export default Content; \ No newline at end of file diff --git a/src/components/DropDown.js b/src/components/DropDown.js index d2f2aa1..8607c7e 100644 --- a/src/components/DropDown.js +++ b/src/components/DropDown.js @@ -1,12 +1,19 @@ +<<<<<<< HEAD import React, { useState } from 'react'; import './DropDown.css'; +======= +import React, { useState } from "react"; +import "../styles/DropDown.css"; +import DropdownToggle from "./DropdownToggle"; +import DropdownContent from "./DropdownContent"; +>>>>>>> 0f1a9c4 (refactor: sprint5 코멘트 반영) -function DropDown({ options = [], onSelect }) { +function DropDown({ options = [], onSelect }) { const [isOpen, setIsOpen] = useState(false); const [selectedOption, setSelectedOption] = useState(null); const toggleDropdown = () => { - setIsOpen((prev) => !prev); + setIsOpen(!isOpen); }; const handleOptionClick = (option) => { @@ -17,24 +24,15 @@ function DropDown({ options = [], onSelect }) { return (
- - {isOpen && options.length > 0 ? ( - - ) : ( - isOpen &&

옵션이 없습니다.

- )} + +
); } diff --git a/src/components/DropdownContent.js b/src/components/DropdownContent.js new file mode 100644 index 0000000..4ef5c8a --- /dev/null +++ b/src/components/DropdownContent.js @@ -0,0 +1,27 @@ +import React from "react"; + +function DropdownContent({ isOpen, options, handleOptionClick }) { + if (!isOpen) { + return null; + } + + if (options.length > 0) { + return ( + + ); + } + + return

옵션이 없습니다.

; +} + +export default DropdownContent; diff --git a/src/components/DropdownToggle.js b/src/components/DropdownToggle.js new file mode 100644 index 0000000..7c97c97 --- /dev/null +++ b/src/components/DropdownToggle.js @@ -0,0 +1,11 @@ +import React from "react"; + +function DropdownToggle({ onToggle, selectedOption }) { + return ( + + ); +} + +export default DropdownToggle; diff --git a/src/components/Footer.css b/src/components/Footer.css index 67b88e4..009b3e2 100644 --- a/src/components/Footer.css +++ b/src/components/Footer.css @@ -1,21 +1,3 @@ -:root { - --primary-color-50: #E6F2FF; - --primary-color-100: #3692FF; - --primary-color-200: #1967D6; - --primary-color-300: #1251AA; - --error-color: #F74747; - --secondary-color-gray900: #111827; - --secondary-color-gray800: #1F2937; - --secondary-color-gray700: #374151; - --secondary-color-gray600: #4B5563; - --secondary-color-gray500: #6B7280; - --secondary-color-gray400: #9CA3AF; - --secondary-color-gray200: #E5E7EB; - --secondary-color-gray100: #F3F4F6; - --secondary-color-gray50: #F9FAFB; -} - - .footer_container { margin: 0; padding: 0; diff --git a/src/components/Footer.js b/src/components/Footer.js index 0c3caf3..4124817 100644 --- a/src/components/Footer.js +++ b/src/components/Footer.js @@ -1,8 +1,16 @@ +<<<<<<< HEAD import instagram from '../images/ic_instagram.png'; import facebook from '../images/ic_facebook.png'; import twitter from '../images/ic_twitter.png'; import youtube from '../images/ic_youtube.png'; import './Footer.css'; +======= +import instagram from "../images/ic_instagram.png"; +import facebook from "../images/ic_facebook.png"; +import twitter from "../images/ic_twitter.png"; +import youtube from "../images/ic_youtube.png"; +import "../styles/Footer.css"; +>>>>>>> 0f1a9c4 (refactor: sprint5 코멘트 반영) export function Footer() { return ( diff --git a/src/components/IsImage.js b/src/components/IsImage.js deleted file mode 100644 index 504f3c6..0000000 --- a/src/components/IsImage.js +++ /dev/null @@ -1,8 +0,0 @@ -export const is_image = ['jpg', 'jpeg', 'png', 'gif', 'bmp']; - -export function IsImage(src) { - return is_image.includes(src.split('.').pop()); -} - - -export default IsImage; \ No newline at end of file diff --git a/src/components/NavBar.css b/src/components/NavBar.css index 81a48a6..44ef5bf 100644 --- a/src/components/NavBar.css +++ b/src/components/NavBar.css @@ -1,21 +1,3 @@ -:root { - --primary-color-50: #E6F2FF; - --primary-color-100: #3692FF; - --primary-color-200: #1967D6; - --primary-color-300: #1251AA; - --error-color: #F74747; - --secondary-color-gray900: #111827; - --secondary-color-gray800: #1F2937; - --secondary-color-gray700: #374151; - --secondary-color-gray600: #4B5563; - --secondary-color-gray500: #6B7280; - --secondary-color-gray400: #9CA3AF; - --secondary-color-gray200: #E5E7EB; - --secondary-color-gray100: #F3F4F6; - --secondary-color-gray50: #F9FAFB; -} - - nav { background-color: white; color: black; diff --git a/src/components/OnSaleProducts.js b/src/components/OnSaleProducts.js index 3638fd0..5441e6a 100644 --- a/src/components/OnSaleProducts.js +++ b/src/components/OnSaleProducts.js @@ -1,14 +1,18 @@ // import Product from "./ProductList"; -import "../images/empty_heart.png"; import "../images/search.png"; +<<<<<<< HEAD import "./OnSaleProducts.css"; import "../images/empty_heart.png"; +======= +import "../styles/OnSaleProducts.css"; +import "../images/emptyHeart.png"; +>>>>>>> 0f1a9c4 (refactor: sprint5 코멘트 반영) import { useState, useEffect } from "react"; import getProducts from "../api/getProducts"; -import IsImage from "./IsImage"; +import IsImage from "../utils/image.helper"; -const no_image_available = require("../images/no-image-available.png"); -const empty_heart = require("../images/empty_heart.png"); +const noImageAvailable = require("../images/noImageAvailable.png"); +const emptyHeart = require("../images/emptyHeart.png"); export function OnSaleProducts({ sort, page, keyword }) { @@ -21,10 +25,21 @@ export function OnSaleProducts({ sort, page, keyword }) { const pageSize = input ? 10000 : 10; getProducts({ page, pageSize, sort: sortingType, keyword: input }) .then((data) => { +<<<<<<< HEAD setOnSaleProducts(data.list); data.list.forEach(product => { if (!IsImage(String(product.images))) { product.images = no_image_available; +======= + if (!data.products || !Array.isArray(data.products)) { + throw new Error("Invalid products data"); + } + setOnSaleProducts(data.products); + + data.products.forEach(product => { + if (!product.images || !IsImage(product.images)) { + product.images = noImageAvailable; +>>>>>>> 0f1a9c4 (refactor: sprint5 코멘트 반영) } }); }) @@ -53,14 +68,19 @@ export function OnSaleProducts({ sort, page, keyword }) { { filteredProducts.length > 0 ? ( filteredProducts.map((product) => ( +<<<<<<< HEAD
{product.name} +======= +
+ {product.name} +>>>>>>> 0f1a9c4 (refactor: sprint5 코멘트 반영)

{product.name}

{product.price.toLocaleString()}원

{product.favoriteCount}
diff --git a/src/components/Pagination.js b/src/components/Pagination.js index a2977de..0ebd2a1 100644 --- a/src/components/Pagination.js +++ b/src/components/Pagination.js @@ -1,5 +1,10 @@ +<<<<<<< HEAD import { useState, useEffect } from 'react'; import './Pagination.css'; +======= +import { useState, useEffect } from "react"; +import "../styles/Pagination.css"; +>>>>>>> 0f1a9c4 (refactor: sprint5 코멘트 반영) export function Pagination({ onPageChange }) { @@ -13,11 +18,9 @@ export function Pagination({ onPageChange }) { return (
- - - - - + {[1,2,3,4,5].map((i) => ( + + ))}
); } diff --git a/src/components/RegistrationForm.js b/src/components/RegistrationForm.js new file mode 100644 index 0000000..d7862d7 --- /dev/null +++ b/src/components/RegistrationForm.js @@ -0,0 +1,100 @@ +import React, { useState } from "react"; +import "../styles/RegistrationForm.css"; +import { useNavigate } from "react-router-dom"; + +export function RegistrationForm() { + + const navigate = useNavigate(); + + const handleFormSubmit = (e) => { + e.preventDefault(); + + const product_name = e.currentTarget.product_name.value.trim(); + const product_desc = e.currentTarget.product_desc.value.trim(); + const product_price = e.currentTarget.product_price.value.trim(); + const product_tags = e.currentTarget.product_tags.value.split(",").map(tag => tag.trim()); + + if (!product_name || !product_price) { + console.error("상품명과 판매가격은 필수입니다."); + return; + } + + const product_data = { + name: product_name, + description: product_desc, + price: Number(product_price), + tags: product_tags + }; + + fetch("https://backend-c2ut.onrender.com/product", { + method: "POST", + headers: { + "Content-Type": "application/json" + }, + body: JSON.stringify(product_data) + }) + .then((res) => res.json()) + .then((data) => { + if (data && data._id) { + navigate(`/products/${data._id}`); + } else { + console.error("상품 등록에 실패하였습니다.", data.message); + } + }) + .catch((error) => console.error("Error:", error)); + }; + + + return ( +
+
+
+

상품 등록하기

+ +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ ); +} + +export default RegistrationForm; diff --git a/src/components/SearchBar.js b/src/components/SearchBar.js index 2d4e6ed..f9e590f 100644 --- a/src/components/SearchBar.js +++ b/src/components/SearchBar.js @@ -13,7 +13,6 @@ export function SearchBar({ onSearch }) { } useEffect(() => { - console.log(keyword); }, [keyword]); return ( diff --git a/src/images/empty_heart.png b/src/images/emptyHeart.png similarity index 100% rename from src/images/empty_heart.png rename to src/images/emptyHeart.png diff --git a/src/images/no-image-available.png b/src/images/noImageAvailable.png similarity index 100% rename from src/images/no-image-available.png rename to src/images/noImageAvailable.png diff --git a/src/index.css b/src/index.css index c2f5d24..0e2b930 100644 --- a/src/index.css +++ b/src/index.css @@ -20,3 +20,19 @@ code { monospace; } +:root { + --primary-color-50: #E6F2FF; + --primary-color-100: #3692FF; + --primary-color-200: #1967D6; + --primary-color-300: #1251AA; + --error-color: #F74747; + --secondary-color-gray900: #111827; + --secondary-color-gray800: #1F2937; + --secondary-color-gray700: #374151; + --secondary-color-gray600: #4B5563; + --secondary-color-gray500: #6B7280; + --secondary-color-gray400: #9CA3AF; + --secondary-color-gray200: #E5E7EB; + --secondary-color-gray100: #F3F4F6; + --secondary-color-gray50: #F9FAFB; +} \ No newline at end of file diff --git a/src/index.js b/src/index.js index c14ab17..425fa24 100644 --- a/src/index.js +++ b/src/index.js @@ -1,11 +1,19 @@ -import React from 'react'; -import ReactDOM from 'react-dom/client'; +import React from "react"; +import ReactDOM from "react-dom/client"; import "./reset.css" +<<<<<<< HEAD import './index.css'; import App from './components/App'; +======= +import "./index.css"; +import MainPage from "./pages/MainPage"; +import { BrowserRouter, Routes, Route } from "react-router-dom"; +import ItemPage from "./pages/ItemPage"; +import RegistrationPage from "./pages/RegistrationPage"; +>>>>>>> 0f1a9c4 (refactor: sprint5 코멘트 반영) -const root = ReactDOM.createRoot(document.getElementById('root')); +const root = ReactDOM.createRoot(document.getElementById("root")); root.render( diff --git a/src/reset.css b/src/reset.css index e9e771a..6ceaae4 100644 --- a/src/reset.css +++ b/src/reset.css @@ -39,7 +39,7 @@ blockquote, q { } blockquote:before, blockquote:after, q:before, q:after { - content: ''; + content: ""; content: none; } table { diff --git a/src/utils/image.helper.js b/src/utils/image.helper.js new file mode 100644 index 0000000..2d5094f --- /dev/null +++ b/src/utils/image.helper.js @@ -0,0 +1,8 @@ +export const is_image = ["jpg", "jpeg", "png", "gif", "bmp"]; + +export function IsImage(src) { + return is_image.includes(src.split(".").pop()); +} + + +export default IsImage; \ No newline at end of file