From 88d4e679080086b28482728b1721c24e8dede17f Mon Sep 17 00:00:00 2001 From: jihye5081 Date: Sat, 8 Feb 2025 02:14:44 +0900 Subject: [PATCH] feat: Implement Vote Button --- src/apis/monthlyChartApi.js | 9 ++- src/components/Modal.jsx | 22 ++++-- .../modalContent/MonthlyChartVoteList.jsx | 26 ++++++- .../modalContent/MonthlyChartVoteModal.jsx | 73 ++++++++++++++++--- src/index.css | 11 +++ src/pages/listPage/ListPage.jsx | 4 +- src/utils/creditStorage.js | 1 + 7 files changed, 124 insertions(+), 22 deletions(-) diff --git a/src/apis/monthlyChartApi.js b/src/apis/monthlyChartApi.js index a5f7f58..041eca4 100644 --- a/src/apis/monthlyChartApi.js +++ b/src/apis/monthlyChartApi.js @@ -18,9 +18,14 @@ export async function getLists(gender, cursor = 0, pageSize) { } } -export async function postVotes() { +export async function postVotes(idolId) { try { - const res = await instance.post('/votes'); + const loadData = { idolId }; + const res = await instance.post('/votes', loadData, { + headers: { + 'Content-Type': 'application/json', + }, + }); return res.data; } catch (error) { console.error(error); diff --git a/src/components/Modal.jsx b/src/components/Modal.jsx index 5305d40..6ec390b 100644 --- a/src/components/Modal.jsx +++ b/src/components/Modal.jsx @@ -1,9 +1,11 @@ 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'; +import { useEffect, useState } from 'react'; function Modal({ title, onClose, children }) { + const [isMobile, setIsMobile] = useState(false); + useEffect(() => { document.body.style.overflow = 'hidden'; // 모달창 열려 있으면 뒤의 배경 스크롤 막기 return () => { @@ -11,15 +13,25 @@ function Modal({ title, onClose, children }) { }; }, []); - if (title.includes('아이돌') && window.innerWidth <= 375) { + useEffect(() => { + const handleResize = () => { + setIsMobile(window.innerWidth < 768); + }; + + window.addEventListener('resize', handleResize); + handleResize(); + return () => window.removeEventListener('resize', handleResize); + }, []); + + if (title.includes('아이돌') && isMobile) { return ( -
+
leftTopGradient -
+
+
{ +const MonthlyChartVoteList = ({ + idols, + selectedIdol, + setSelectedIdol, + children, +}) => { + const [isMobile, setIsMobile] = useState(false); + + useEffect(() => { + const handleResize = () => { + setIsMobile(window.innerWidth < 768); + }; + + window.addEventListener('resize', handleResize); + handleResize(); + return () => window.removeEventListener('resize', handleResize); + }, []); + return ( -
+
{idols.map((idol, idx) => (
setSelectedIdol(idol.id)}> @@ -19,6 +40,7 @@ const MonthlyChartVoteList = ({ idols, selectedIdol, setSelectedIdol }) => {
))} + {children}
); }; diff --git a/src/components/modalContent/MonthlyChartVoteModal.jsx b/src/components/modalContent/MonthlyChartVoteModal.jsx index 3b44512..47573aa 100644 --- a/src/components/modalContent/MonthlyChartVoteModal.jsx +++ b/src/components/modalContent/MonthlyChartVoteModal.jsx @@ -1,13 +1,16 @@ -import { useState, useEffect } from 'react'; +import { useState, useEffect, useRef } from 'react'; import MonthlyChartVoteList from '@/components/modalContent/MonthlyChartVoteList'; -import { getLists } from '@/apis/monthlyChartApi'; +import { getLists, postVotes } from '@/apis/monthlyChartApi'; import PrimaryButton from '@/components/PrimaryButton'; +import { spendCredits } from '../../utils/creditStorage'; -const MonthlyChartVoteModal = ({ gender }) => { +const MonthlyChartVoteModal = ({ gender, onClickVoteCredit, closeModal }) => { const [cursor, setCursor] = useState(0); const [idolData, setIdolData] = useState([]); const [loading, setLoading] = useState(false); const [selectedIdol, setSelectedIdol] = useState(0); + const [isMobile, setIsMobile] = useState(false); + const observerRef = useRef(); const loadIdolData = async () => { setLoading(true); @@ -26,11 +29,21 @@ const MonthlyChartVoteModal = ({ gender }) => { } }; - const loadMoreData = () => { - if (cursor === null) { - alert('불러올 데이터가 없습니다.'); + const handleVoteClick = async () => { + const result = spendCredits(1000); + if (result === 'NOT-ENOUGH') { + alert('앗! 투표하기 위한 크레딧이 부족해요'); } else { - loadIdolData(); + try { + const voteResult = await postVotes(selectedIdol); + alert('투표 완료!'); + + if (onClickVoteCredit) onClickVoteCredit(); + closeModal(); + } catch (error) { + console.error(error); + alert('투표에 실패했습니다. 다시 시도해 주세요.'); + } } }; @@ -40,8 +53,33 @@ const MonthlyChartVoteModal = ({ gender }) => { loadIdolData(); }, [gender]); + useEffect(() => { + const handleResize = () => { + setIsMobile(window.innerWidth < 768); + }; + + window.addEventListener('resize', handleResize); + handleResize(); + return () => window.removeEventListener('resize', handleResize); + }, []); + + useEffect(() => { + if (cursor === null) return; + + const observer = new IntersectionObserver( + (entries) => { + if (entries[0].isIntersecting) loadIdolData(); + }, + { threshold: 0.2 } + ); + if (observerRef.current) observer.observe(observerRef.current); + return () => observer.disconnect(); + }, [cursor]); + return ( -
+
{loading ? (
로딩 중입니다...
) : ( @@ -49,13 +87,24 @@ const MonthlyChartVoteModal = ({ gender }) => { idols={idolData} selectedIdol={selectedIdol} setSelectedIdol={setSelectedIdol} - /> + > +
+ )} -
- +
+ 투표하기 -

+

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

diff --git a/src/index.css b/src/index.css index 25fd95e..b52abc0 100644 --- a/src/index.css +++ b/src/index.css @@ -6,3 +6,14 @@ html, body { @apply bg-midnightBlack; } + +@layer utilities { + .scrollbar-hidden { + scrollbar-width: none; /* Firefox */ + -ms-overflow-style: none; /* IE 10+ */ + } + + .scrollbar-hidden::-webkit-scrollbar { + display: none; /* Chrome, Safari, Edge */ + } +} diff --git a/src/pages/listPage/ListPage.jsx b/src/pages/listPage/ListPage.jsx index a0fa469..16fa895 100644 --- a/src/pages/listPage/ListPage.jsx +++ b/src/pages/listPage/ListPage.jsx @@ -111,7 +111,9 @@ function ListPage() { {modalStep === 'donationSuccess' && ( )} - {modalStep === 'vote' && } + {modalStep === 'vote' && ( + + )} )}
diff --git a/src/utils/creditStorage.js b/src/utils/creditStorage.js index f8ffc47..8209b56 100644 --- a/src/utils/creditStorage.js +++ b/src/utils/creditStorage.js @@ -33,5 +33,6 @@ export const spendCredits = (amount) => { const newCredits = Math.max(currentCredits - amount, 0); localStorage.setItem('credits', newCredits); + return 'SUCCESS'; } };