From 4b90d3e104b9e558c83e12f1b0b8efe64fde3f12 Mon Sep 17 00:00:00 2001 From: juha399 Date: Mon, 3 Feb 2025 19:57:14 +0900 Subject: [PATCH 1/3] mypage css layout and button --- src/assets/icons/xButton.svg | 4 + src/pages/myPage/MyPage.jsx | 161 ++++++++++++++++++++++++++++++++--- vite.config.js | 16 +++- 3 files changed, 167 insertions(+), 14 deletions(-) create mode 100644 src/assets/icons/xButton.svg diff --git a/src/assets/icons/xButton.svg b/src/assets/icons/xButton.svg new file mode 100644 index 0000000..13058a2 --- /dev/null +++ b/src/assets/icons/xButton.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/pages/myPage/MyPage.jsx b/src/pages/myPage/MyPage.jsx index 5279159..f2c1a3d 100644 --- a/src/pages/myPage/MyPage.jsx +++ b/src/pages/myPage/MyPage.jsx @@ -1,9 +1,20 @@ import React, { useState, useEffect } from 'react'; import axios from 'axios'; +import Header from '@/components/Header'; import IdolCard from '@/components/IdolCard'; +import nextIcon from '@/assets/icons/nextIcon.svg'; +import prevIcon from '@/assets/icons/prevIcon.svg'; + +const storageKey = 'favoriteIdols'; const MyPage = () => { const [idols, setIdols] = useState([]); + const [selectedIdols, setSelectedIdols] = useState([]); + const [favoriteIdols, setFavoriteIdols] = useState([]); + const [currentPage, setCurrentPage] = useState(0); + const [isClicked, setIsClicked] = useState(false); + + const idolsPerPage = 16; // 한 페이지당 아이돌 개수 useEffect(() => { const fetchIdols = async () => { @@ -11,23 +22,153 @@ const MyPage = () => { const response = await axios.get( 'https://fandom-k-api.vercel.app/13-3/idols?pageSize=30' ); - setIdols(response.data.list); // API 응답 데이터 리스트를 저장 + setIdols(response.data.list); } catch (error) { - console.error('아이돌 데이터를 불러오는 중 오류 발생:', error); //아이돌 데이터 로딩 중 에러 확인 + console.error('아이돌 데이터를 불러오는 중 오류 발생:', error); } }; - fetchIdols(); + + const storedFavorites = localStorage.getItem(storageKey); + if (storedFavorites) { + setFavoriteIdols(storedFavorites.split(',')); + } }, []); + useEffect(() => { + localStorage.setItem(storageKey, favoriteIdols.join(',')); + }, [favoriteIdols]); + + // 아이돌 클릭 핸들러 + const handleToggle = (idolId) => { + setSelectedIdols((prev) => [...prev, idolId]); + setIsClicked(true); + }; + + // 관심있는 아이돌 추가 핸들러 + const handleAddFavorites = () => { + if (!isClicked) return; + + setFavoriteIdols((prev) => { + const updatedFavorites = [...new Set([...prev, ...selectedIdols])]; // 중복 제거 + return updatedFavorites; + }); + + setSelectedIdols([]); + setIsClicked(false); // 추가 후 다시 초기화 + }; + + // 관심있는 아이돌 삭제 핸들러 + const handleRemoveFavorite = (idolId) => { + setFavoriteIdols((prev) => prev.filter((id) => id !== idolId)); + }; + + // 페이지 이동 핸들러 + const nextPage = () => { + if ((currentPage + 1) * idolsPerPage < idols.length) { + setCurrentPage((prev) => prev + 1); + } + }; + + const prevPage = () => { + if (currentPage > 0) { + setCurrentPage((prev) => prev - 1); + } + }; + return ( -
-
- {idols.length > 0 ? ( - idols.map((idol) => ) - ) : ( -

아이돌 데이터를 불러오는 중...

- )} +
+
+ +
+

+ 내가 관심있는 아이돌 +

+ + {/* 관심있는 아이돌 데이터 없을 때도 공간 유지 */} +
+ {favoriteIdols.length > 0 ? ( + favoriteIdols.map((idolId) => { + const idol = idols.find((i) => i.id === idolId); + return ( + idol && ( +
+ {idol?.name} +
+ ) + ); + }) + ) : ( +

+ 관심있는 아이돌을 추가해보세요. +

+ )} +
+ +
+ +

+ 관심 있는 아이돌을 추가해보세요. +

+ +
+ {/* 왼쪽 버튼 (1200px 바깥쪽) */} + + + {/* 오른쪽 버튼 (1200px 바깥쪽) */} + +
+ + {/* 아이돌 리스트 */} +
+ {idols + .slice(currentPage * idolsPerPage, (currentPage + 1) * idolsPerPage) + .map((idol) => ( +
handleToggle(idol.id)} + > + {idol?.name} +
+ ))} +
+ +
); diff --git a/vite.config.js b/vite.config.js index 15e5fec..dd4efa7 100644 --- a/vite.config.js +++ b/vite.config.js @@ -1,13 +1,21 @@ import { defineConfig } from 'vite'; import react from '@vitejs/plugin-react'; -import path from "path"; +import path from 'path'; -// https://vite.dev/config/ export default defineConfig({ plugins: [react()], resolve: { alias: { - "@": path.resolve(__dirname, "src"), // 수정된 부분 + '@': path.resolve(__dirname, 'src'), }, }, -}); \ No newline at end of file + server: { + proxy: { + '/api': { + target: 'https://fandom-k-api.vercel.app', + changeOrigin: true, + secure: false, + }, + }, + }, +}); From d9cebb8b73f3c586883dbf2683fb3a078f3dfb1f Mon Sep 17 00:00:00 2001 From: juha399 Date: Mon, 3 Feb 2025 21:36:32 +0900 Subject: [PATCH 2/3] fix: Resolve merge conflicts in MyPage.jsx --- src/pages/myPage/MyPage.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/myPage/MyPage.jsx b/src/pages/myPage/MyPage.jsx index f2c1a3d..7a1416a 100644 --- a/src/pages/myPage/MyPage.jsx +++ b/src/pages/myPage/MyPage.jsx @@ -4,7 +4,7 @@ import Header from '@/components/Header'; import IdolCard from '@/components/IdolCard'; import nextIcon from '@/assets/icons/nextIcon.svg'; import prevIcon from '@/assets/icons/prevIcon.svg'; - +import { Helmet } from 'react-helmet'; const storageKey = 'favoriteIdols'; const MyPage = () => { From 43787f8a72bafa2c46ddb4427c29bc62a5b6ba01 Mon Sep 17 00:00:00 2001 From: juha399 Date: Tue, 4 Feb 2025 12:12:07 +0900 Subject: [PATCH 3/3] Resolve merge conflicts --- src/apis/idolApi.js | 13 +++ src/components/IdolCard.jsx | 2 +- src/components/IdolImage.jsx | 16 ++-- src/pages/myPage/MyPage.jsx | 179 ++++++++++++++++------------------- 4 files changed, 104 insertions(+), 106 deletions(-) create mode 100644 src/apis/idolApi.js diff --git a/src/apis/idolApi.js b/src/apis/idolApi.js new file mode 100644 index 0000000..7d8a1ae --- /dev/null +++ b/src/apis/idolApi.js @@ -0,0 +1,13 @@ +import axios from 'axios'; + +export const fetchIdols = async (pageSize = 30) => { + try { + const response = await axios.get( + `https://fandom-k-api.vercel.app/13-3/idols?pageSize=${pageSize}` + ); + return response.data.list; + } catch (error) { + console.error('아이돌 데이터를 불러오는 중 오류 발생:', error); + return []; + } +}; diff --git a/src/components/IdolCard.jsx b/src/components/IdolCard.jsx index 4b4c700..3c6a24f 100644 --- a/src/components/IdolCard.jsx +++ b/src/components/IdolCard.jsx @@ -8,7 +8,7 @@ const IdolCard = ({ idol }) => { console.log('IdolCard 받은 데이터:', idol); return ( -
+
setIsSelected(!isSelected)} diff --git a/src/components/IdolImage.jsx b/src/components/IdolImage.jsx index d2c99bb..5df8c75 100644 --- a/src/components/IdolImage.jsx +++ b/src/components/IdolImage.jsx @@ -1,11 +1,10 @@ -import React, { useState, useEffect } from "react"; +import React, { useState, useEffect } from 'react'; -const ImageWithBorder = ({ borderColor = "#f96868", size = "128px" }) => { - const [imageSrc, setImageSrc] = useState(""); +const ImageWithBorder = ({ borderColor = '#f96868', size = '128px' }) => { + const [imageSrc, setImageSrc] = useState(''); useEffect(() => { - - setImageSrc("https://via.placeholder.com/150"); + setImageSrc('https://via.placeholder.com/150'); }, []); if (!imageSrc) { @@ -27,12 +26,13 @@ const ImageWithBorder = ({ borderColor = "#f96868", size = "128px" }) => { }} >
{ const [idols, setIdols] = useState([]); @@ -16,21 +17,24 @@ const MyPage = () => { const [currentPage, setCurrentPage] = useState(0); const [isClicked, setIsClicked] = useState(false); - const idolsPerPage = 16; // 한 페이지당 아이돌 개수 + const getItemsPerPage = () => { + if (typeof window !== 'undefined') { + if (window.innerWidth < 640) return 6; + if (window.innerWidth < 768) return 8; + return 16; + } + return 16; + }; useEffect(() => { - const fetchIdols = async () => { - try { - const response = await axios.get( - 'https://fandom-k-api.vercel.app/13-3/idols?pageSize=30' - ); - setIdols(response.data.list); - } catch (error) { - console.error('아이돌 데이터를 불러오는 중 오류 발생:', error); - } + const loadIdols = async () => { + const data = await fetchIdols(30); + setIdols(data); }; - fetchIdols(); + loadIdols(); + }, []); + useEffect(() => { const storedFavorites = localStorage.getItem(storageKey); if (storedFavorites) { setFavoriteIdols(storedFavorites.split(',')); @@ -41,33 +45,30 @@ const MyPage = () => { localStorage.setItem(storageKey, favoriteIdols.join(',')); }, [favoriteIdols]); - // 아이돌 클릭 핸들러 const handleToggle = (idolId) => { - setSelectedIdols((prev) => [...prev, idolId]); + setSelectedIdols((prev) => + prev.includes(idolId) + ? prev.filter((id) => id !== idolId) + : [...prev, idolId] + ); setIsClicked(true); }; - // 관심있는 아이돌 추가 핸들러 const handleAddFavorites = () => { if (!isClicked) return; - setFavoriteIdols((prev) => { - const updatedFavorites = [...new Set([...prev, ...selectedIdols])]; // 중복 제거 - return updatedFavorites; - }); - + setFavoriteIdols((prev) => [...prev, ...selectedIdols]); setSelectedIdols([]); - setIsClicked(false); // 추가 후 다시 초기화 + setIsClicked(false); }; - // 관심있는 아이돌 삭제 핸들러 const handleRemoveFavorite = (idolId) => { setFavoriteIdols((prev) => prev.filter((id) => id !== idolId)); }; - // 페이지 이동 핸들러 const nextPage = () => { - if ((currentPage + 1) * idolsPerPage < idols.length) { + const itemsPerPage = getItemsPerPage(); + if ((currentPage + 1) * itemsPerPage < idols.length) { setCurrentPage((prev) => prev + 1); } }; @@ -78,8 +79,8 @@ const MyPage = () => { } }; - return ( -
+ return ( +
@@ -87,106 +88,90 @@ const MyPage = () => { 내가 관심있는 아이돌 - {/* 관심있는 아이돌 데이터 없을 때도 공간 유지 */} -
- {favoriteIdols.length > 0 ? ( - favoriteIdols.map((idolId) => { - const idol = idols.find((i) => i.id === idolId); - return ( - idol && ( -
- {idol?.name} +
+ {favoriteIdols.length > 0 ? ( + favoriteIdols.map((idolId) => { + const idol = idols.find((i) => i.id === idolId); + return idol ? ( +
+
- ) - ); - }) - ) : ( -

- 관심있는 아이돌을 추가해보세요. -

- )} + ) : null; + }) + ) : ( +

+ 관심있는 아이돌을 추가해보세요. +

+ )} +
-
+

관심 있는 아이돌을 추가해보세요.

-
- {/* 왼쪽 버튼 (1200px 바깥쪽) */} +
- {/* 오른쪽 버튼 (1200px 바깥쪽) */} -
- {/* 아이돌 리스트 */} -
- {idols - .slice(currentPage * idolsPerPage, (currentPage + 1) * idolsPerPage) - .map((idol) => ( -
handleToggle(idol.id)} - > - {idol?.name} -
- ))} +
+ {idols + .slice( + currentPage * getItemsPerPage(), + (currentPage + 1) * getItemsPerPage() + ) + .map((idol) => ( +
handleToggle(idol.id)} + > + +
+ ))} +
- - <> - - Fandom-K - 좋아하는 아이돌을 관심 아이돌로 설정하세요 - -
-
- {idols.length > 0 ? ( - idols.map((idol) => ) - ) : ( -

아이돌 데이터를 불러오는 중...

- )} -
-
- +
); };