diff --git a/src/apis/idolListApi.js b/src/apis/monthlyChartApi.js similarity index 72% rename from src/apis/idolListApi.js rename to src/apis/monthlyChartApi.js index ce44a65..a5f7f58 100644 --- a/src/apis/idolListApi.js +++ b/src/apis/monthlyChartApi.js @@ -17,3 +17,13 @@ export async function getLists(gender, cursor = 0, pageSize) { throw error; } } + +export async function postVotes() { + try { + const res = await instance.post('/votes'); + return res.data; + } catch (error) { + console.error(error); + throw error; + } +} diff --git a/src/assets/icons/exitArrow.svg b/src/assets/icons/exitArrow.svg new file mode 100644 index 0000000..2676666 --- /dev/null +++ b/src/assets/icons/exitArrow.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/Modal.jsx b/src/components/Modal.jsx index 377d6c9..5305d40 100644 --- a/src/components/Modal.jsx +++ b/src/components/Modal.jsx @@ -1,4 +1,6 @@ import closeButton from '@/assets/icons/closeButton.svg'; +import leftTopGradient from '@/assets/images/leftTopGradient.png'; +import exitArrow from '@/assets/icons/exitArrow.svg'; import { useEffect } from 'react'; function Modal({ title, onClose, children }) { @@ -9,6 +11,34 @@ function Modal({ title, onClose, children }) { }; }, []); + if (title.includes('아이돌') && window.innerWidth <= 375) { + return ( +
+ leftTopGradient +
+
+ exitArrow + {title && ( +
+ {title} +
+ )} +
+ {children} +
+
+ ); + } + return (
diff --git a/src/components/modalContent/MonthlyChartVoteList.jsx b/src/components/modalContent/MonthlyChartVoteList.jsx new file mode 100644 index 0000000..aefc010 --- /dev/null +++ b/src/components/modalContent/MonthlyChartVoteList.jsx @@ -0,0 +1,26 @@ +import MonthlyChartItem from '@/pages/listPage/monthlyChart/MonthlyChartItem'; + +const MonthlyChartVoteList = ({ idols, selectedIdol, setSelectedIdol }) => { + return ( +
+ {idols.map((idol, idx) => ( +
setSelectedIdol(idol.id)}> + +
+
+
+
+
+ +
+
+ ))} +
+ ); +}; + +export default MonthlyChartVoteList; diff --git a/src/components/modalContent/MonthlyChartVoteModal.jsx b/src/components/modalContent/MonthlyChartVoteModal.jsx new file mode 100644 index 0000000..3b44512 --- /dev/null +++ b/src/components/modalContent/MonthlyChartVoteModal.jsx @@ -0,0 +1,67 @@ +import { useState, useEffect } from 'react'; +import MonthlyChartVoteList from '@/components/modalContent/MonthlyChartVoteList'; +import { getLists } from '@/apis/monthlyChartApi'; +import PrimaryButton from '@/components/PrimaryButton'; + +const MonthlyChartVoteModal = ({ gender }) => { + const [cursor, setCursor] = useState(0); + const [idolData, setIdolData] = useState([]); + const [loading, setLoading] = useState(false); + const [selectedIdol, setSelectedIdol] = useState(0); + + const loadIdolData = async () => { + setLoading(true); + try { + const response = await getLists(gender, cursor, 10); + if (cursor !== 0) { + setIdolData((prev) => [...prev, ...response.idols]); + } else { + setIdolData(response.idols); + } + setCursor(response.nextCursor); + } catch (error) { + console.error(error); + } finally { + setLoading(false); + } + }; + + const loadMoreData = () => { + if (cursor === null) { + alert('불러올 데이터가 없습니다.'); + } else { + loadIdolData(); + } + }; + + useEffect(() => { + setIdolData([]); + setCursor(0); + loadIdolData(); + }, [gender]); + + return ( +
+ {loading ? ( +
로딩 중입니다...
+ ) : ( + + )} +
+ + 투표하기 + +

+ 투표하는 데 1000 크레딧이 + 소모됩니다. +

+
+
+ ); +}; + +export default MonthlyChartVoteModal; diff --git a/src/pages/listPage/ListPage.jsx b/src/pages/listPage/ListPage.jsx index 51443bb..19e091b 100644 --- a/src/pages/listPage/ListPage.jsx +++ b/src/pages/listPage/ListPage.jsx @@ -11,6 +11,7 @@ import DonationModalContent from '@/components/modalContent/DonationModalContent import leftTopGradient from '@/assets/images/leftTopGradient.png'; import DonationSuccess from '@/components/modalContent/DonationSuccess'; import MonthlyChartSection from '@/pages/listPage/monthlyChart/MonthlyChartSection'; +import MonthlyChartVoteModal from '../../components/modalContent/MonthlyChartVoteModal'; function ListPage() { const [isModalOpen, setIsModalOpen] = useState(false); @@ -18,6 +19,7 @@ function ListPage() { const [credits, setCredits] = useState(getCredits()); const [selectedAmount, setSelectedAmount] = useState(null); const [selectedItem, setSelectedItem] = useState(null); + const [gender, setGender] = useState('female'); useEffect(() => { setCredits(getCredits()); @@ -53,6 +55,7 @@ function ListPage() { creditNotEnough: '', donation: '후원하기', donationSuccess: '', + vote: `이달의 ${gender === 'female' ? '여자' : '남자'} 아이돌`, }[modalStep]; return ( @@ -69,7 +72,13 @@ function ListPage() { credits={credits} /> openModal('donation', item)} /> - + { + openModal('vote'); + }} + gender={gender} + setGender={setGender} + /> {isModalOpen && ( @@ -102,6 +111,7 @@ function ListPage() { {modalStep === 'donationSuccess' && ( )} + {modalStep === 'vote' && } )}
diff --git a/src/pages/listPage/monthlyChart/MonthlyChartItem.jsx b/src/pages/listPage/monthlyChart/MonthlyChartItem.jsx index d05ffed..7f95d36 100644 --- a/src/pages/listPage/monthlyChart/MonthlyChartItem.jsx +++ b/src/pages/listPage/monthlyChart/MonthlyChartItem.jsx @@ -1,26 +1,56 @@ -import IdolCard from '@/components/IdolCard'; - -const MonthlyChartItem = ({ idol }) => { - const { profilePicture, name, rank, group, totalVotes } = idol; +const MonthlyChartItem = ({ idol, rank, layout = 'default', children }) => { + const { profilePicture, name, group, totalVotes } = idol; return (
-
-
-
- {name} + {layout === 'default' ? ( +
+
+
+
+ {name} +
+
+

{rank}

+
+

+ {group} {name} +

+
+
+
+ {totalVotes.toLocaleString()}표 +
+
+ ) : ( +
+
+
+
+ {name} +
+
+

{rank}

+
+

+ {group} {name} +

+
+ {totalVotes.toLocaleString()}표 +
+
+ {children}
-

{rank}

-

- {group} {name} -

-
-
{totalVotes}표
+ )}
); }; diff --git a/src/pages/listPage/monthlyChart/MonthlyChartList.jsx b/src/pages/listPage/monthlyChart/MonthlyChartList.jsx index 2fe7384..1816ecb 100644 --- a/src/pages/listPage/monthlyChart/MonthlyChartList.jsx +++ b/src/pages/listPage/monthlyChart/MonthlyChartList.jsx @@ -1,15 +1,11 @@ import MonthlyChartItem from '@/pages/listPage/monthlyChart/MonthlyChartItem'; const MonthlyChartList = ({ idols }) => { - if (!idols.length === 0) { - return
데이터가 존재하지 않습니다.
; - } - return (
- {idols.map((idol) => ( + {idols.map((idol, idx) => (
- +
))} diff --git a/src/pages/listPage/monthlyChart/MonthlyChartSection.jsx b/src/pages/listPage/monthlyChart/MonthlyChartSection.jsx index 4ddd23d..9a7e463 100644 --- a/src/pages/listPage/monthlyChart/MonthlyChartSection.jsx +++ b/src/pages/listPage/monthlyChart/MonthlyChartSection.jsx @@ -1,11 +1,10 @@ import { useState, useEffect } from 'react'; import chartLogo from '@/assets/icons/chartLogo.svg'; -import { getLists } from '@/apis/idolListApi'; +import { getLists } from '@/apis/monthlyChartApi'; import PrimaryButton from '@/components/PrimaryButton'; import MonthlyChartList from '@/pages/listPage/monthlyChart/MonthlyChartList'; -const MonthlyChartSection = () => { - const [gender, setGender] = useState('female'); +const MonthlyChartSection = ({ onClickVote, gender, setGender }) => { const [cursor, setCursor] = useState(0); const [idolData, setIdolData] = useState([]); const [loading, setLoading] = useState(false); @@ -63,13 +62,16 @@ const MonthlyChartSection = () => { return () => { window.removeEventListener('resize', updateChartSize); }; - }, [window.innerWidth]); + }, []); return (

이달의 차트

- + chartLogo 차트 투표하기