Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions src/Root.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ function Root() {

return (
<>

{isLoading ? (
<PendingUI />
) : (
Expand All @@ -34,7 +33,6 @@ function Root() {
{!isLanding && <Footer />}
</>
)}

</>
);
}
Expand Down
1 change: 1 addition & 0 deletions src/components/button/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export { default as AvatarButton } from './avatarButton';
export { default as CustomButton } from './customButton';
export { default as RadioButton } from './radioButton';
export { default as ArrowButton } from './arrowButton';
export { default as LoadMoreButton } from './loadMoreButton';
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import * as S from './chart.styles';
import * as S from './loadMoreButton.styles';

/**
* '더 보기' 버튼 컴포넌트. 더 많은 데이터를 로드할 수 있을 때 버튼을 표시하고, 로딩 중에는 비활성화 상태로 표시합니다.
Expand Down
1 change: 1 addition & 0 deletions src/components/button/loadMoreButton/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './LoadMoreButton';
52 changes: 52 additions & 0 deletions src/components/button/loadMoreButton/loadMoreButton.styles.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { css, keyframes } from '@emotion/react';
import media from '@/styles/responsive';

export const voteButtonFlow = keyframes`
0% {
background-position: 0% 50%;
}
50% {
background-position: 100% 50%;
}
100% {
background-position: 0% 50%;
}
`;

export const flexCenter = css`
display: flex;
justify-content: center;
align-items: center;
`;

export const moreButton = css`
${flexCenter};
margin-inline: auto;
margin-block: 3rem;
border-radius: 8px;
background: linear-gradient(45deg, var(--black) 0%, var(--white-alpha-20) 51%, var(--white-alpha-10) 100%);
background-position: left center; /* 기본 시작 위치 */
background-size: 400%; /* 배경 크기 확장 */
transition: all 0.1s ease;

${media({
fontSize: ['1.1rem', '1.2rem', '1.4rem', '1.6rem'],
width: ['10rem', '15rem', '20rem', '25rem'],
height: ['2.3rem', '2.7rem', '3.5rem', '4rem'],
})}

&:hover {
background-position: right center; /* hover 시 배경 이동 */
animation: ${voteButtonFlow} 4s ease infinite; /* 애니메이션 흐름 */
opacity: 0.9;
}

&:active {
transform: scale(0.97);
}

&:disabled {
cursor: not-allowed;
opacity: 0.6;
}
`;
8 changes: 4 additions & 4 deletions src/components/chart/Chart.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ import { CustomButton } from '@/components/button';
import { LoadingSpinner } from '@/components/loadingStatus';
import chartImg from '@/assets/images/chart.png';
import { fetchData } from './fetchData';
import { useScreenSize } from './useScreenSize';
import LoadMoreButton from './LoadMoreButton';
import { useScreenSize } from '@/utils/responsive';
import { LoadMoreButton } from '@/components/button';
import * as S from './chart.styles';

const Chart = ({ setModalType, selectedTab, setSelectedTab, updateCredit }) => {
const Chart = ({ setModalType, selectedTab, setSelectedTab, voteSuccessTrigger }) => {
const [chartData, setChartData] = useState([]);
const [cursor, setCursor] = useState(0);
const [hasMore, setHasMore] = useState(true);
Expand Down Expand Up @@ -52,7 +52,7 @@ const Chart = ({ setModalType, selectedTab, setSelectedTab, updateCredit }) => {
return () => {
controller.abort();
};
}, [selectedTab, screenSize, updateCredit]);
}, [selectedTab, screenSize, voteSuccessTrigger]);

const loadMore = () => {
fetchData(
Expand Down
38 changes: 5 additions & 33 deletions src/components/chart/chart.styles.js
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ export const idolList = css`
}

img {
border: 2px solid var(--black);
border: 2px solid var(--black-deep);
border-radius: 50%;
box-shadow: 0 0 0 0.1rem var(--orange);
object-fit: cover;
Expand All @@ -151,6 +151,10 @@ export const idolList = css`
marginInline: ['0.1rem', '0.2rem', '0.5rem', '0.5rem'],
})}
}

&:active {
transform: scale(1.015);
}
}
`;

Expand All @@ -177,35 +181,3 @@ export const nameStyle = css`
font-weight: 700;
color: var(--white);
`;

export const moreButton = css`
${flexCenter};
margin-inline: auto;
margin-block: 3rem;
border-radius: 8px;
background: linear-gradient(45deg, var(--black) 0%, var(--white-alpha-20) 51%, var(--white-alpha-10) 100%);
background-position: left center; /* 기본 시작 위치 */
background-size: 400%; /* 배경 크기 확장 */
transition: all 0.1s ease;

${media({
fontSize: ['1.1rem', '1.2rem', '1.4rem', '1.6rem'],
width: ['10rem', '15rem', '20rem', '25rem'],
height: ['2.3rem', '2.7rem', '3.5rem', '4rem'],
})}

&:hover {
background-position: right center; /* hover 시 배경 이동 */
animation: ${voteButtonFlow} 4s ease infinite; /* 애니메이션 흐름 */
opacity: 0.9;
}

&:active {
transform: scale(0.97);
}

&:disabled {
cursor: not-allowed;
opacity: 0.6;
}
`;
17 changes: 15 additions & 2 deletions src/components/list/ListModal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,15 @@ import {
VoteModal,
} from '@/components/modals';

const ListModal = ({ modalType, setModalType, credit, updateCredit, gender, donations }) => {
const ListModal = ({
modalType,
setModalType,
credit,
updateCredit,
setVoteSuccessTrigger,
gender,
donations,
}) => {
const onCloseModal = () => setModalType(null);

const renderModalContent = () => {
Expand All @@ -33,7 +41,12 @@ const ListModal = ({ modalType, setModalType, credit, updateCredit, gender, dona
);
case 'vote':
return (
<VoteModal gender={gender} updateCredit={updateCredit} setModalType={setModalType} />
<VoteModal
gender={gender}
updateCredit={updateCredit}
setVoteSuccessTrigger={setVoteSuccessTrigger}
setModalType={setModalType}
/>
);
default:
return null;
Expand Down
17 changes: 11 additions & 6 deletions src/components/modals/creditChargeModal/CreditChargeModal.jsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import { useState } from 'react';
import { CustomButton, RadioButton } from '@/components/button';
import { Prices } from '@/constants/creditPrice';
import { addCommas } from '@/utils/format';
import { showAlert } from '@/utils/alert';
import starTwoImg from '@/assets/images/2-star.png';
import starThreeImg from '@/assets/images/3-star.png';
import logoImg from '@/assets/images/logo.png';
import starImg from '@/assets/images/star.png';
import * as S from './creditChargeModal.styles';

Expand All @@ -21,23 +24,25 @@ const CreditChargeModal = ({ credit, updateCredit, onClose }) => {

const handleCharge = () => {
if (!selectedValue) {
alert('충전할 크레딧을 선택해주세요!');
showAlert('충전할 크레딧을 선택해주세요!', 'warning');
return;
}

const prev = credit; // ListPage에서 전달받은 credit
const newTotal = prev + Number(selectedValue);
localStorage.setItem('selectedCredit', newTotal);
updateCredit(newTotal); // 업데이트된 credit을 ListPage에 전달
alert(`크레딧 ${selectedValue} 충전 완료! 총 보유: ${newTotal}`);

showAlert(`${selectedValue} 크레딧 충전 완료!`, 'success');
// 모달 닫기 로직
onClose();
};

return (
<div css={S.modalContent}>
<h2>크레딧 충전하기</h2>
<span css={S.modalTitle}>
<img src={logoImg} alt="크레딧" />
<h2>크레딧 충전하기</h2>
</span>
<div css={S.radioButtons}>
{Prices.map((price) => {
const imgSrc = imageMap[price.id] || starImg;
Expand All @@ -50,8 +55,8 @@ const CreditChargeModal = ({ credit, updateCredit, onClose }) => {
handleSelect={() => handleRadioSelect(price.value)}
>
<div css={S.radioButtonContent}>
<img src={imgSrc} alt="크레딧" />
<span>{price.value}</span>
<img src={imgSrc} alt="크레딧" css={S.creditImg(price.id)} />
<span>{addCommas(Number(price.value))}</span>
</div>
</RadioButton>
);
Expand Down
51 changes: 27 additions & 24 deletions src/components/modals/creditChargeModal/creditChargeModal.styles.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { css, keyframes } from '@emotion/react';
import media from '@/styles/responsive';

export const voteButtonFlow = keyframes`
0% {
Expand All @@ -16,17 +15,26 @@ export const voteButtonFlow = keyframes`
export const modalContent = css`
display: flex;
flex-direction: column;
justify-content: center;
align-items: baseline;

${media({
margin: ['2.4rem 1.6rem', '2.4rem 1.6rem', '3rem 3rem', '3rem 3rem', '3rem 3rem'],
gap: ['1rem', '1rem', '2rem', '2rem', '2rem'],
})}
gap: 1.2rem;
width: 32.7rem;
height: 38rem;
padding: 2.4rem 1.6rem;
`;

export const modalTitle = css`
display: flex;
align-items: center;
margin-bottom: 0.7rem;

img {
width: 2.5rem;
height: 2.5rem;
margin-right: 0.5rem;
margin-bottom: 0.1rem;
}

h2 {
margin-block: 0.2rem 1rem;
font-size: 1.8rem;
font-size: 2rem;
}
`;

Expand All @@ -43,10 +51,8 @@ export const radioButtons = css`
`;

export const buttonStyle = css`
${media({
width: ['28rem', '29rem', '29rem', '32rem', '32rem'],
height: ['7rem', '7rem', '7rem', '8rem', '8rem'],
})}
width: 29rem;
height: 6.8rem;
border: 1px solid var(--white);
border-radius: 8px;
transition: border-color 0.3s ease;
Expand All @@ -58,11 +64,14 @@ export const buttonStyle = css`
&:has(input[type="radio"]:checked) {
border-color: var(--orange);
color: var(--gray);
transform: scale(1.015);
}

img {
width: 2.4rem;
}
`;

export const creditImg = (id) => css`
margin-inline: ${id === 1 ? '0.8rem' : id === 2 ? '0.2rem' : '0.1rem'};
width: ${id === 1 ? '2.5rem' : id === 2 ? '3.7rem' : '4rem'};
height: ${id === 1 ? '2.45rem' : id === 2 ? '2.6rem' : '3rem'};
`;

export const customButton = css`
Expand All @@ -73,11 +82,6 @@ export const customButton = css`
transition: all 0.1s ease;
animation: ${voteButtonFlow} 3s ease infinite; /* 애니메이션 흐름 */

${media({
width: ['28.7rem', '29.8rem', '29.8rem', '32.8rem', '32.8rem'],
height: ['3.8rem', '4rem', '4rem', '5rem', '5rem'],
})}

p {
font-weight: 700;
}
Expand All @@ -103,7 +107,6 @@ export const radioButtonContent = css`
padding: 0.5rem;

span {
margin: 0.2rem 0 0 0.3rem;
font-size: 2rem;
font-weight: 700;
}
Expand Down
3 changes: 2 additions & 1 deletion src/components/modals/voteModal/VoteModal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { showAlert } from '@/utils/alert';
import { requestPost } from '@/utils/api';
import { addCommas } from '@/utils/format';
import * as S from './voteModal.styles';
const VoteModal = ({ gender, updateCredit, setModalType }) => {
const VoteModal = ({ gender, updateCredit, setVoteSuccessTrigger, setModalType }) => {
const [idols, setIdols] = useState([]);
const [checkedItem, setCheckedItem] = useState();

Expand All @@ -28,6 +28,7 @@ const VoteModal = ({ gender, updateCredit, setModalType }) => {
if (response) {
localStorage.setItem('selectedCredit', Number(getCredit) - 1000);
updateCredit(Number(getCredit) - 1000);
setVoteSuccessTrigger((prev) => !prev); // 차트에 투표 수 반영하는 상태변수
showAlert('투표에 성공했습니다', 'success');
} else {
showAlert('투표에 실패했습니다', 'warning');
Expand Down
6 changes: 4 additions & 2 deletions src/pages/list/ListPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ import * as S from './listPage.styles';
const ListPage = () => {
const [modalType, setModalType] = useState(null); // 모달 타입 상태 관리
const [credit, setCredit] = useState(0); // 크레딧 상태 관리
const { idols, donations } = useLoaderData(); // 여기서 데이터 받음
const [selectedTab, setSelectedTab] = useState('females');
const [selectedIndex, setSelectedIndex] = useState(null); // 후원의 인덱스를 받아옴
const [voteSuccessTrigger, setVoteSuccessTrigger] = useState(false);
const { idols, donations } = useLoaderData(); // 여기서 데이터 받음

useEffect(() => {
const storedCredit = localStorage.getItem('selectedCredit');
Expand All @@ -36,13 +37,14 @@ const ListPage = () => {
setModalType={setModalType}
selectedTab={selectedTab}
setSelectedTab={setSelectedTab}
updateCredit={updateCredit}
voteSuccessTrigger={voteSuccessTrigger}
/>
<ListModal
modalType={modalType}
setModalType={setModalType}
credit={credit}
updateCredit={updateCredit}
setVoteSuccessTrigger={setVoteSuccessTrigger}
gender={selectedTab}
donations={selectedIndex != null ? donations.list[selectedIndex] : null}
/>
Expand Down
1 change: 1 addition & 0 deletions src/utils/responsive/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as useScreenSize } from './useScreenSize';
Loading