From e362fb23ec3f915f957c0d2d9e28a2f487c29043 Mon Sep 17 00:00:00 2001 From: Hyo Bin Date: Thu, 11 Apr 2024 18:49:49 +0900 Subject: [PATCH 1/6] =?UTF-8?q?feat:#21-=EB=AA=A9=EB=A1=9D=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=20html,css=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit html,css 올리니다. --- src/components/ProductCard.jsx | 44 +++++++ src/fakeServer/db.json | 37 ++++-- src/pages/ProductsList.jsx | 67 +++++++---- src/styles/ProductCardStyle.js | 207 ++++++++++++++++++++++++++++++++ src/styles/ProductsListStyle.js | 48 ++++++++ 5 files changed, 371 insertions(+), 32 deletions(-) create mode 100644 src/components/ProductCard.jsx create mode 100644 src/styles/ProductCardStyle.js create mode 100644 src/styles/ProductsListStyle.js diff --git a/src/components/ProductCard.jsx b/src/components/ProductCard.jsx new file mode 100644 index 0000000..492d921 --- /dev/null +++ b/src/components/ProductCard.jsx @@ -0,0 +1,44 @@ +import React from "react"; +import * as S from "../styles/ProductCardStyle"; +import { useRecoilState } from "recoil"; +import { postsState } from "../recoil/RecoilWastes"; +import { LuCalendarDays } from "react-icons/lu"; +import { GoHeart } from "react-icons/go"; + +const ProductCard = () => { + const [posts, setPosts] = useRecoilState(postsState); + return ( + + {posts.map((wastes) => ( +
+
+ 임시이미지 +
+
{wastes.title}
+
+
+
{wastes.address.state}
+
{wastes.address.city}
+
+
+ +
{wastes.created_at}
+
+
+
{wastes.waste_price}원
+
+
+
+

{wastes.sell_status}

+ +
+
{wastes.likes}
+ + {/* */} +
+ ))} +
+ ); +}; + +export default ProductCard; diff --git a/src/fakeServer/db.json b/src/fakeServer/db.json index cc07d9f..4c8bfd1 100644 --- a/src/fakeServer/db.json +++ b/src/fakeServer/db.json @@ -2,24 +2,45 @@ "wastes": [ { "fileName": {}, - "title": "예시", + "title": "옷팝니다.", "wasteCategory": "의류", - "wasteStatus": "상", + "wasteStatus": "최상", "sellStatus": "거래중", - "wastePrice": "3,000", - "content": "예시입니다.", + "wastePrice": "5,000", + "content": "의류 팝니다.", "address": { - "address": "서울 서초구 안골길 4", - "zipcode": "06794", + "address": "서울 서대문구 가재울로 6", + "zipcode": "03693", "state": "서울", - "city": "서초구", - "district": "내곡동", + "city": "서대문구", + "district": "남가좌동", "detail": "" }, "likeCount": "", "viewCount": "", "created_at": "2024. 4. 11.", "id": 1 + }, + { + "fileName": {}, + "title": "주방용품 나눔해요", + "wasteCategory": "생활/주방", + "wasteStatus": "상", + "sellStatus": "거래중", + "wastePrice": "0", + "content": "나눔합니다.", + "address": { + "address": "서울 종로구 북촌로1길 9", + "zipcode": "03060", + "state": "서울", + "city": "종로구", + "district": "안국동", + "detail": "" + }, + "likeCount": "", + "viewCount": "", + "created_at": "2024. 4. 11.", + "id": 2 } ] } \ No newline at end of file diff --git a/src/pages/ProductsList.jsx b/src/pages/ProductsList.jsx index 5c4d13c..c452fa8 100644 --- a/src/pages/ProductsList.jsx +++ b/src/pages/ProductsList.jsx @@ -3,6 +3,12 @@ import { postsState } from "../recoil/RecoilWastes"; import { FaPlus } from "react-icons/fa6"; import { Link } from "react-router-dom"; import { deletePost } from "../api/WastesApi"; +import * as S from "../styles/ProductsListStyle"; +import Nav from "../components/Nav"; +import { GiHamburgerMenu } from "react-icons/gi"; +import { useState } from "react"; +import ProductCard from "../components/ProductCard"; +import { IoSearch } from "react-icons/io5"; const ProductsList = () => { const [posts, setPosts] = useRecoilState(postsState); console.log(posts); @@ -19,31 +25,44 @@ const ProductsList = () => { }; return (
-
- FreshTrash -
- -

폐기물 등록

- - -
-

입력한 내용

-
    - {posts.map((wastes) => ( -
    -
    이미지
    -
    -
    {wastes.title}
    -
    {wastes.address.zipcode}
    - {/*
    {wastes.address}
    */} -
    {wastes.waste_price}
    -
    {wastes.sell_status}
    -
    - +
-
+
+ +
+
+ + + +
+ + + +
+ + +
+ 관심순 + 조회순 + 최신순 +
+ + + + + ); }; diff --git a/src/styles/ProductCardStyle.js b/src/styles/ProductCardStyle.js new file mode 100644 index 0000000..25593cc --- /dev/null +++ b/src/styles/ProductCardStyle.js @@ -0,0 +1,207 @@ +import styled from "styled-components"; + +export const Container = styled.div` + display: grid; + grid-template-columns: 20rem 20rem 20rem; + gap: 70px; + justify-content: center; + .card { + border: 1px solid var(--grey-box); + padding: 1.5rem; + box-sizing: border-box; + .product-box { + display: flex; + flex-direction: column; + img { + margin-bottom: 2rem; + } + .title { + margin-bottom: 1.5rem; + font-size: 1.3rem; + } + .content { + display: flex; + justify-content: space-between; + margin-bottom: 1.5rem; + .adrress { + display: flex; + .state { + margin-right: 0.5rem; + } + .city { + margin-right: 3rem; + } + } + .day { + display: flex; + .created-at { + margin-left: 0.5rem; + line-height: 1rem; + } + } + } + .price { + font-size: 1.5rem; + font-weight: bold; + } + } + .card-bottom { + display: flex; + justify-content: flex-end; + align-items: center; + gap: 1.5rem; + font-size: 1.5rem; + p { + } + } + } + /* @media (max-width: 1440px) { + display: grid; + grid-template-columns: 22rem 22rem 22rem; + gap: 50px; + justify-content: center; + .card { + padding: 1.5em; + .product-box { + display: flex; + flex-direction: cloumn; + } + .sell-status { + float: right; + font-size: 1.5rem; + } + } + } */ + @media (max-width: 1280px) { + display: grid; + grid-template-columns: 19rem 19rem 19rem; + gap: 40px; + justify-content: center; + .card { + .product-box { + display: flex; + flex-direction: cloumn; + .content { + .adrress { + .state { + margin-right: 0.1rem; + } + .city { + margin-right: 3rem; + } + } + .day { + .created-at { + margin-left: 0.5rem; + line-height: 1rem; + } + } + } + .price { + font-size: 1.2rem; + font-weight: bold; + } + } + .sell-status { + font-size: 1.4rem; + } + } + } + @media (max-width: 1080px) { + display: grid; + grid-template-columns: 40rem; + gap: 70px; + .card { + border-bottom: 1px solid var(--grey-box); + .product-box { + display: flex; + flex-direction: row; + img { + margin-right: 3rem; + } + .title { + margin-bottom: 1.3rem; + font-size: 1.3rem; + } + .content { + .adrress { + .state { + font-size: 1rem; + } + .city { + font-size: 1rem; + } + } + .day { + .created-at { + font-size: 1rem; + line-height: 1rem; + } + } + } + .price { + font-size: 1.5rem; + font-weight: bold; + } + } + .sell-status { + float: right; + font-size: 1.5rem; + } + } + } + @media (max-width: 768px) { + display: grid; + grid-template-columns: 35rem; + gap: 70px; + .card { + border-bottom: 1px solid var(--grey-box); + .product-box { + display: flex; + flex-direction: row; + img { + margin-right: 3rem; + width: 200px; + } + .title { + margin-bottom: 1.3rem; + font-size: 1.3rem; + } + .content { + .adrress { + .state { + font-size: 1rem; + } + .city { + font-size: 1rem; + } + } + .day { + .created-at { + font-size: 1rem; + line-height: 1rem; + } + } + } + .price { + font-size: 1.5rem; + font-weight: bold; + } + } + .sell-status { + float: right; + font-size: 1.5rem; + } + } + } + /* @media (max-width: 1280px) { + width: 62rem; + } + @media (max-width: 1024px) { + width: 50rem; + } + @media (max-width: 768px) { + width: 40rem; + font-size: 0.88rem; + } */ +`; diff --git a/src/styles/ProductsListStyle.js b/src/styles/ProductsListStyle.js new file mode 100644 index 0000000..459588e --- /dev/null +++ b/src/styles/ProductsListStyle.js @@ -0,0 +1,48 @@ +import styled from "styled-components"; +export const Nav = styled.div` + .nav-middle { + display: flex; + padding: 1rem 1rem; + justify-content: space-between; + border-bottom: 1px solid var(--grey-box); + .nav-middle-right { + display: flex; + gap: 2rem; + align-items: center; + .search-wrapper { + position: relative; + border: 2px solid black; + border-radius: 5px; + .search-category { + width: 5rem; + height: 2.2rem; + background-color: white; + border-radius: 5px; + border: none; + } + input { + width: 15rem; + height: 2rem; + border: none; + } + .search-icon { + position: absolute; /* 아이콘을 절대적으로 위치시킵니다. */ + top: 0; + right: 0; + margin-top: 0.4rem; /* 원하는 위치로 조정하세요 */ + margin-right: 0.5rem; + } + } + } + } + .nav-bottom { + display: flex; + justify-content: flex-end; + padding: 1rem 1rem; + margin-bottom: 2rem; + span { + margin-right: 1rem; + } + } +`; +export const CardList = styled.div``; From 34d67db67c5590e6ecb3c01ef9558e94c17d7b95 Mon Sep 17 00:00:00 2001 From: Hyo Bin Date: Sat, 13 Apr 2024 16:24:16 +0900 Subject: [PATCH 2/6] =?UTF-8?q?design:#21-=EB=AA=A9=EB=A1=9D=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EB=94=94=EC=9E=90=EC=9D=B8=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/styles/ProductCardStyle.js | 56 +++-------- src/styles/ProductsListStyle.js | 165 ++++++++++++++++++++++++++++++-- 2 files changed, 171 insertions(+), 50 deletions(-) diff --git a/src/styles/ProductCardStyle.js b/src/styles/ProductCardStyle.js index 25593cc..eaca09c 100644 --- a/src/styles/ProductCardStyle.js +++ b/src/styles/ProductCardStyle.js @@ -1,14 +1,16 @@ import styled from "styled-components"; export const Container = styled.div` - display: grid; + /* display: grid; grid-template-columns: 20rem 20rem 20rem; gap: 70px; - justify-content: center; + justify-content: center; */ .card { border: 1px solid var(--grey-box); padding: 1.5rem; box-sizing: border-box; + box-shadow: 5px 5px 8px var(--grey-box); + border-radius: 10px; .product-box { display: flex; flex-direction: column; @@ -49,34 +51,22 @@ export const Container = styled.div` display: flex; justify-content: flex-end; align-items: center; - gap: 1.5rem; + gap: 1rem; font-size: 1.5rem; - p { - } - } - } - /* @media (max-width: 1440px) { - display: grid; - grid-template-columns: 22rem 22rem 22rem; - gap: 50px; - justify-content: center; - .card { - padding: 1.5em; - .product-box { + .like { display: flex; - flex-direction: cloumn; - } - .sell-status { - float: right; - font-size: 1.5rem; + + gap: 0.3rem; + button { + border: none; + background-color: white; + cursor: pointer; + } } } - } */ + } + @media (max-width: 1280px) { - display: grid; - grid-template-columns: 19rem 19rem 19rem; - gap: 40px; - justify-content: center; .card { .product-box { display: flex; @@ -108,9 +98,6 @@ export const Container = styled.div` } } @media (max-width: 1080px) { - display: grid; - grid-template-columns: 40rem; - gap: 70px; .card { border-bottom: 1px solid var(--grey-box); .product-box { @@ -151,9 +138,6 @@ export const Container = styled.div` } } @media (max-width: 768px) { - display: grid; - grid-template-columns: 35rem; - gap: 70px; .card { border-bottom: 1px solid var(--grey-box); .product-box { @@ -194,14 +178,4 @@ export const Container = styled.div` } } } - /* @media (max-width: 1280px) { - width: 62rem; - } - @media (max-width: 1024px) { - width: 50rem; - } - @media (max-width: 768px) { - width: 40rem; - font-size: 0.88rem; - } */ `; diff --git a/src/styles/ProductsListStyle.js b/src/styles/ProductsListStyle.js index 459588e..3b23641 100644 --- a/src/styles/ProductsListStyle.js +++ b/src/styles/ProductsListStyle.js @@ -5,6 +5,24 @@ export const Nav = styled.div` padding: 1rem 1rem; justify-content: space-between; border-bottom: 1px solid var(--grey-box); + box-shadow: 0px 3px 8px var(--grey-box); + .nav-left { + position: relative; + button { + display: flex; + justify-content: space-between; + align-items: center; + border: none; + border-radius: 5px; + width: 6rem; + height: 2.8rem; + background-color: var(--green-brunswick); + cursor: pointer; + p { + color: white; + } + } + } .nav-middle-right { display: flex; gap: 2rem; @@ -19,11 +37,13 @@ export const Nav = styled.div` background-color: white; border-radius: 5px; border: none; + outline: none; } input { width: 15rem; - height: 2rem; + height: 1rem; border: none; + outline: none; } .search-icon { position: absolute; /* 아이콘을 절대적으로 위치시킵니다. */ @@ -35,14 +55,141 @@ export const Nav = styled.div` } } } - .nav-bottom { - display: flex; - justify-content: flex-end; - padding: 1rem 1rem; - margin-bottom: 2rem; - span { - margin-right: 1rem; + @media (max-width: 1080px) { + .nav-middle { + display: flex; + padding: 1rem 1rem; + justify-content: space-between; + border-bottom: 1px solid var(--grey-box); + box-shadow: 0px 3px 8px var(--grey-box); + .nav-left { + position: relative; + button { + display: flex; + justify-content: center; + border: none; + border-radius: 5px; + width: 2.5rem; + height: 2.8rem; + + cursor: pointer; + p { + display: none; + } + } + } + .nav-middle-right { + display: flex; + gap: 2rem; + align-items: center; + .search-wrapper { + position: relative; + border: 2px solid black; + border-radius: 5px; + .search-category { + width: 5rem; + height: 2.2rem; + background-color: white; + border-radius: 5px; + border: none; + outline: none; + } + input { + width: 15rem; + height: 1rem; + border: none; + outline: none; + } + .search-icon { + position: absolute; /* 아이콘을 절대적으로 위치시킵니다. */ + top: 0; + right: 0; + margin-top: 0.4rem; /* 원하는 위치로 조정하세요 */ + margin-right: 0.5rem; + } + } + } } } `; -export const CardList = styled.div``; +export const Category1 = styled.div` + position: relative; + .modal { + position: absolute; + background-color: white; + box-shadow: 2px 2px 2px var(--grey-box); + border: 1px solid var(--grey-box); + /* padding: 2rem; */ + width: 15rem; + ul { + display: flex; + flex-direction: column; + align-items: center; + + li { + width: 100%; + height: 2.5rem; + cursor: pointer; + display: flex; + justify-content: center; + align-items: center; + } + li:hover { + background-color: var(--grey-light); + } + } + } +`; + +export const Header = styled.div` + display: flex; + justify-content: flex-end; + + button { + background-color: white; + border: none; + margin-top: 1rem; + margin-right: 1rem; + cursor: pointer; + + font-size: 1rem; + } +`; + +export const Pagination = styled.div` + display: flex; + justify-content: center; + align-items: center; + gap: 0.3rem; + button { + background-color: white; + border: 1px solid var(--grey-box); + width: 2.5rem; + height: 2rem; + cursor: pointer; + } +`; +export const Card = styled.div` + display: grid; + grid-template-columns: 20rem 20rem 20rem; + gap: 70px; + justify-content: center; + margin: 3rem 0; + + @media (max-width: 1280px) { + display: grid; + grid-template-columns: 19rem 19rem 19rem; + gap: 40px; + justify-content: center; + } + @media (max-width: 1080px) { + display: grid; + grid-template-columns: 40rem; + gap: 70px; + } + @media (max-width: 768px) { + display: grid; + grid-template-columns: 30rem; + gap: 70px; + } +`; From 743fa58d8a972eb2eae1c8b366ccc029cb8ccfc8 Mon Sep 17 00:00:00 2001 From: Hyo Bin Date: Sat, 13 Apr 2024 16:25:43 +0900 Subject: [PATCH 3/6] =?UTF-8?q?feat:#21-=EA=B8=B0=EB=8A=A5=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 정렬, 카테고리 별 검색, 페이지네이션 기능 구현 --- src/api/WastesApi.jsx | 5 + src/components/ProductCard.jsx | 105 ++++++++++--- src/fakeServer/db.json | 274 ++++++++++++++++++++++++++++++--- src/index.css | 4 + src/pages/ProductAdd.jsx | 4 +- src/pages/ProductsList.jsx | 193 ++++++++++++++++++++--- 6 files changed, 523 insertions(+), 62 deletions(-) diff --git a/src/api/WastesApi.jsx b/src/api/WastesApi.jsx index 176029d..707ebe1 100644 --- a/src/api/WastesApi.jsx +++ b/src/api/WastesApi.jsx @@ -28,3 +28,8 @@ export const deletePost = async (wasteId) => { throw error; // 오류를 다시 throw하여 컴포넌트에서 처리할 수 있도록 함 } }; + +export const updatePost = async (wasteId, updatedPost) => { + const response = await axios.put(`${API_URL}/wastes/${wasteId}`, updatedPost); + return response.data; +}; diff --git a/src/components/ProductCard.jsx b/src/components/ProductCard.jsx index 492d921..56717d6 100644 --- a/src/components/ProductCard.jsx +++ b/src/components/ProductCard.jsx @@ -1,43 +1,108 @@ -import React from "react"; +import React, { useState } from "react"; import * as S from "../styles/ProductCardStyle"; +import { GoHeart } from "react-icons/go"; +import { GoHeartFill } from "react-icons/go"; import { useRecoilState } from "recoil"; import { postsState } from "../recoil/RecoilWastes"; -import { LuCalendarDays } from "react-icons/lu"; -import { GoHeart } from "react-icons/go"; +import { useEffect } from "react"; +import { fetchPosts } from "../api/WastesApi"; +import { updatePost } from "../api/WastesApi"; +const ProductCard = ({ post, onDelete }) => { + const handleDeleteClick = () => { + onDelete(post.id); + }; + // const [posts, setPosts] = useRecoilState(postsState); -const ProductCard = () => { + // const handleDelete = async (postId) => { + // try { + // // API를 사용하여 제품 삭제 + // await deletePost(postId); + // // 상태에서 해당 제품을 제거합니다. + // setPosts(posts.filter((wastes) => wastes.id !== postId)); + // console.log("제품이 성공적으로 삭제되었습니다."); + // } catch (error) { + // console.error("제품 삭제 중 오류가 발생했습니다:", error); + // } + // }; + // const [hearted, setHearted] = useState(false); + // const [likeCount, setLikeCount] = useState(post.likeCount); + // const toggleHeart = () => { + // if (hearted) { + // setLikeCount(likeCount - 1); + // } else { + // setLikeCount(likeCount + 1); + // } + // setHearted(!hearted); + // }; const [posts, setPosts] = useRecoilState(postsState); + + // useEffect(() => { + // const fetchPosts = async () => { + // const data = await fetchPosts(); + // setPosts(data); + // }; + // fetchPosts(); + // }, []); + + // const handleLikeToggle = async () => { + // const updatedPost = { ...post, likeCount: post.likeCount + 1 }; + // await updatePost(post.id, updatedPost); + // const updatedPosts = posts.map((p) => + // p.id === post.id ? updatedPost : p + // ); + // setPosts(updatedPosts); + // }; + const handleLikeToggle = async () => { + const updatedPost = { ...post }; + if (updatedPost.hearted) { + updatedPost.likeCount -= 1; // 채워진 하트에서 빈 하트로 변경되면 likeCount 감소 + } else { + updatedPost.likeCount += 1; // 빈 하트에서 채워진 하트로 변경되면 likeCount 증가 + } + updatedPost.hearted = !updatedPost.hearted; // 하트 상태 업데이트 + await updatePost(post.id, updatedPost); // 서버에 업데이트 요청 + const updatedPosts = posts.map((p) => (p.id === post.id ? updatedPost : p)); + setPosts(updatedPosts); // Recoil 상태 업데이트 + }; return ( - - {posts.map((wastes) => ( -
+
+ +
임시이미지
-
{wastes.title}
+
{post.title}
-
{wastes.address.state}
-
{wastes.address.city}
+
{post.address.state}
+
{post.address.city}
- -
{wastes.created_at}
+ {/* */} + {/*
{wastes.created_at}
*/}
-
{wastes.waste_price}원
+
{post.wastePrice}원
-

{wastes.sell_status}

- +

{post.sellStatus}

+
+ +
{post.likeCount}
+
-
{wastes.likes}
- - {/* */} + {/* */} + {/* */}
- ))} -
+ +
); }; diff --git a/src/fakeServer/db.json b/src/fakeServer/db.json index 4c8bfd1..d9626fe 100644 --- a/src/fakeServer/db.json +++ b/src/fakeServer/db.json @@ -4,43 +4,279 @@ "fileName": {}, "title": "옷팝니다.", "wasteCategory": "의류", - "wasteStatus": "최상", + "wasteStatus": "중", "sellStatus": "거래중", - "wastePrice": "5,000", - "content": "의류 팝니다.", + "wastePrice": "2,000", + "content": "ㅇㅇㅇㅇㅇㅇㅇㅇㅇ", "address": { - "address": "서울 서대문구 가재울로 6", - "zipcode": "03693", - "state": "서울", - "city": "서대문구", - "district": "남가좌동", + "address": "경기 안성시 고삼면 안성대로 1980", + "zipcode": "17505", + "state": "경기", + "city": "안성시", + "district": "가유리", "detail": "" }, - "likeCount": "", - "viewCount": "", + "likeCount": 50, + "viewCount": 55, "created_at": "2024. 4. 11.", "id": 1 }, { "fileName": {}, - "title": "주방용품 나눔해요", - "wasteCategory": "생활/주방", - "wasteStatus": "상", + "title": "화장품 팝니다.", + "wasteCategory": "뷰티", + "wasteStatus": "하", + "sellStatus": "거래중", + "wastePrice": "2,500", + "content": "팔아요", + "address": { + "address": "인천 강화군 강화읍 갑룡길 35", + "zipcode": "23024", + "state": "인천", + "city": "강화군", + "district": "갑곳리", + "detail": "그랑드빌 아파트" + }, + "likeCount": 40, + "viewCount": 45, + "created_at": "2024. 4. 11.", + "id": 2 + }, + { + "fileName": {}, + "title": "핸드폰 팔아요", + "wasteCategory": "전자기기", + "wasteStatus": "최상", + "sellStatus": "거래중", + "wastePrice": "10,000", + "content": "팝니다.", + "address": { + "address": "서울 성동구 서울숲길 17", + "zipcode": "04766", + "state": "서울", + "city": "성동구", + "district": "성수동1가", + "detail": "성수파크빌" + }, + "likeCount": 60, + "viewCount": 72, + "created_at": "2024. 4. 11.", + "id": 3 + }, + { + "fileName": {}, + "title": "안녕하세요", + "wasteCategory": "뷰티", + "wasteStatus": "최하", "sellStatus": "거래중", "wastePrice": "0", "content": "나눔합니다.", "address": { - "address": "서울 종로구 북촌로1길 9", - "zipcode": "03060", + "address": "서울 종로구 북촌로 31-4", + "zipcode": "03055", "state": "서울", "city": "종로구", - "district": "안국동", + "district": "가회동", + "detail": "오픈아이즈센터" + }, + "likeCount": 60, + "viewCount": 72, + "created_at": "2024. 4. 11.", + "id": 4 + }, + { + "fileName": {}, + "title": "가구나눔", + "wasteCategory": "가구/인텔어", + "wasteStatus": "최상", + "sellStatus": "거래중", + "wastePrice": "0", + "content": "ddfweg", + "address": { + "address": "인천 부평구 갈산로 2", + "zipcode": "21316", + "state": "인천", + "city": "부평구", + "district": "갈산동", "detail": "" }, - "likeCount": "", - "viewCount": "", + "likeCount": 88, + "viewCount": 100, "created_at": "2024. 4. 11.", - "id": 2 + "id": 6, + "hearted": false + }, + { + "fileName": {}, + "title": "씨앗 팔아요", + "wasteCategory": "식물", + "wasteStatus": "최상", + "sellStatus": "거래중", + "wastePrice": "500", + "content": "ㅇㅇㅇㄴㄹㄷ", + "address": { + "address": "서울 강남구 가로수길 5", + "zipcode": "06035", + "state": "서울", + "city": "강남구", + "district": "신사동", + "detail": "" + }, + "likeCount": 100, + "viewCount": 25, + "created_at": "2024. 4. 11.", + "id": 7, + "hearted": false + }, + { + "fileName": {}, + "title": "건강식품", + "wasteCategory": "건강", + "wasteStatus": "중", + "sellStatus": "거래중", + "wastePrice": "4,000", + "content": "건강식품", + "address": { + "address": "서울 용산구 두텁바위로 5", + "zipcode": "04335", + "state": "서울", + "city": "용산구", + "district": "갈월동", + "detail": "" + }, + "likeCount": 70, + "viewCount": 55, + "created_at": "2024. 4. 12.", + "id": 8 + }, + { + "fileName": {}, + "title": "ㄹㄹㄹㄹㄹㄹㄹ", + "wasteCategory": "뷰티", + "wasteStatus": "최하", + "sellStatus": "거래중", + "wastePrice": "5,000", + "content": "뷰티", + "address": { + "address": "인천 미추홀구 남주길 5", + "zipcode": "22156", + "state": "인천", + "city": "미추홀구", + "district": "주안동", + "detail": "" + }, + "likeCount": 70, + "viewCount": 55, + "created_at": "2024. 4. 12.", + "id": 9 + }, + { + "fileName": {}, + "title": "ㅇㅇㅇㅇㅇㅇ", + "wasteCategory": "뷰티", + "wasteStatus": "하", + "sellStatus": "거래중", + "wastePrice": "0", + "content": "dddddddd", + "address": { + "address": "서울 송파구 성남대로 1541-40", + "zipcode": "05843", + "state": "서울", + "city": "송파구", + "district": "장지동", + "detail": "" + }, + "likeCount": 70, + "viewCount": 55, + "created_at": "2024. 4. 12.", + "id": 10 + }, + { + "fileName": {}, + "title": "뷰티", + "wasteCategory": "뷰티", + "wasteStatus": "최하", + "sellStatus": "거래중", + "wastePrice": "500", + "content": "뷰티템", + "address": { + "address": "서울 은평구 갈현로 181-2", + "zipcode": "03325", + "state": "서울", + "city": "은평구", + "district": "갈현동", + "detail": "" + }, + "likeCount": 70, + "viewCount": 55, + "created_at": "2024. 4. 12.", + "id": 11, + "hearted": false + }, + { + "fileName": {}, + "title": "뷰티템5개", + "wasteCategory": "뷰티", + "wasteStatus": "상", + "sellStatus": "거래중", + "wastePrice": "8,000", + "content": "5개팔아요", + "address": { + "address": "인천 남동구 간석로 1", + "zipcode": "21510", + "state": "인천", + "city": "남동구", + "district": "간석동", + "detail": "" + }, + "likeCount": 70, + "viewCount": 55, + "created_at": "2024. 4. 12.", + "id": 12, + "hearted": false + }, + { + "fileName": {}, + "title": "xx립스틱", + "wasteCategory": "뷰티", + "wasteStatus": "중", + "sellStatus": "거래중", + "wastePrice": "50,000", + "content": "새거 팝니다.", + "address": { + "address": "서울 용산구 남산공원길 101", + "zipcode": "04340", + "state": "서울", + "city": "용산구", + "district": "후암동", + "detail": "" + }, + "likeCount": 70, + "viewCount": 55, + "created_at": "2024. 4. 12.", + "id": 13 + }, + { + "fileName": {}, + "title": "사세요~", + "wasteCategory": "뷰티", + "wasteStatus": "최상", + "sellStatus": "거래중", + "wastePrice": "50,000", + "content": "가져가세요~", + "address": { + "address": "경기 부천시 소사구 부천로 1", + "zipcode": "14637", + "state": "경기", + "city": "부천시 소사구", + "district": "심곡본동", + "detail": "부천역사 쇼핑몰" + }, + "likeCount": 70, + "viewCount": 55, + "created_at": "2024. 4. 12.", + "id": 14, + "hearted": false } ] } \ No newline at end of file diff --git a/src/index.css b/src/index.css index cd7c391..5410a08 100644 --- a/src/index.css +++ b/src/index.css @@ -32,6 +32,7 @@ --grey-dgrey: #34495e; --grey-dark: #353e4a; --grey-box: #d9d9d9; + --grey-light: #f0f0f0; --white-ivory: #fbf8ef; } @@ -123,3 +124,6 @@ video { font: inherit; vertical-align: baseline; } +ul { + list-style: none; +} diff --git a/src/pages/ProductAdd.jsx b/src/pages/ProductAdd.jsx index 2ad9eda..3cf15e2 100644 --- a/src/pages/ProductAdd.jsx +++ b/src/pages/ProductAdd.jsx @@ -26,8 +26,8 @@ const ProductAdd = () => { }); const [fileName, setFileName] = useState(null); - const [likeCount, setLikeCount] = useState(""); - const [viewCount, setViewCount] = useState(""); + const [likeCount, setLikeCount] = useState(70); + const [viewCount, setViewCount] = useState(55); const navigate = useNavigate(); //찾은 주소 inptu 반영 diff --git a/src/pages/ProductsList.jsx b/src/pages/ProductsList.jsx index c452fa8..417651c 100644 --- a/src/pages/ProductsList.jsx +++ b/src/pages/ProductsList.jsx @@ -4,14 +4,41 @@ import { FaPlus } from "react-icons/fa6"; import { Link } from "react-router-dom"; import { deletePost } from "../api/WastesApi"; import * as S from "../styles/ProductsListStyle"; +import ProductCard from "../components/ProductCard"; import Nav from "../components/Nav"; -import { GiHamburgerMenu } from "react-icons/gi"; +import { RxHamburgerMenu } from "react-icons/rx"; import { useState } from "react"; -import ProductCard from "../components/ProductCard"; import { IoSearch } from "react-icons/io5"; +import { GrFormNext } from "react-icons/gr"; +import { GrFormPrevious } from "react-icons/gr"; +const ITEMS_PER_PAGE = 6; +const MAX_PAGES_DISPLAY = 6; const ProductsList = () => { const [posts, setPosts] = useRecoilState(postsState); console.log(posts); + + const [selectedCategory, setSelectedCategory] = useState("전체"); // 선택된 카테고리 + + const handleCategoryClick = (category) => { + setSelectedCategory(category); + }; + + //카테고리 별 검색 + const [showModal, setShowModal] = useState(false); + + const toggleModal = () => { + setShowModal(!showModal); + }; + + const openModal = () => { + setShowModal(true); + }; + + // 모달 닫기 함수 + const closeModal = () => { + setShowModal(false); + }; + const handleDelete = async (postId) => { try { // API를 사용하여 제품 삭제 @@ -23,26 +50,89 @@ const ProductsList = () => { console.error("제품 삭제 중 오류가 발생했습니다:", error); } }; + + const [sortedByViews, setSorted] = useState(false); + //정렬 + const handleSortByViews = () => { + const sortedPosts = [...posts].sort((a, b) => { + // 조회수가 많은 순서대로 정렬 + return b.viewCount - a.viewCount; + }); + setPosts(sortedPosts); + setSorted(true); + }; + const handleSortByLikes = () => { + const sortedPosts = [...posts].sort((a, b) => { + return b.likeCount - a.likeCount; + }); + setPosts(sortedPosts); + setSorted(true); + }; + const handleSortByCreatedAt = () => { + const sortedPosts = [...posts].sort((a, b) => { + return new Date(b.id) - new Date(a.id); + }); + setPosts(sortedPosts); + setSorted(true); + }; + + const [currentPage, setCurrentPage] = useState(1); + + const filteredPosts = + selectedCategory === "전체" + ? posts + : posts.filter((post) => post.wasteCategory === selectedCategory); + + // 전체 페이지 수 계산 + const totalPages = Math.ceil(filteredPosts.length / ITEMS_PER_PAGE); + + // 현재 페이지에 해당하는 제품들을 가져옴 + const currentProducts = filteredPosts.slice( + (currentPage - 1) * ITEMS_PER_PAGE, + currentPage * ITEMS_PER_PAGE + ); + + const getPageNumbers = () => { + const startPage = Math.max( + 1, + currentPage - Math.floor(MAX_PAGES_DISPLAY / 2) + ); + const endPage = Math.min(totalPages, startPage + MAX_PAGES_DISPLAY - 1); + return Array.from( + { length: endPage - startPage + 1 }, + (_, index) => index + startPage + ); + }; + + // 페이지를 변경하는 함수 + const handlePageChange = (page) => { + setCurrentPage(page); + }; + + // 카테고리를 변경하는 함수 + const handleCategoryChange = (category) => { + setSelectedCategory(category); + setCurrentPage(1); // 페이지를 첫 페이지로 초기화 + }; return (