Skip to content
Open
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
159 changes: 159 additions & 0 deletions front/src/pages/MyPage/MainContent.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
import React from 'react';
import styled from 'styled-components';

interface Props {
userInfo: { name: string };
fundingCount: number;
point: number;
onHandleClick: (label: string) => void; // ✅ 이름 변경
}

const MainContent: React.FC<Props> = ({ userInfo, fundingCount, point, onHandleClick }) => {
return (
<Main>
<Greeting>
<h2>{userInfo.name}님 안녕하세요.</h2>
<InviteBox>당신의 아이디어, 펀딩으로 연결하세요!</InviteBox>
<StatGrid>
{['펀딩+', '스토어', '지지서명', '알림신청', '포인트', '쿠폰'].map((label) => {
let value: React.ReactNode;

if (label === '지지서명' || label === '알림신청') {
value = <button onClick={() => onHandleClick(label)}>보기</button>;
} else if (label === '포인트') {
value = <strong>{point.toLocaleString()}P</strong>;
} else if (label === '펀딩+') {
value = <strong>{fundingCount}</strong>;
} else if (label === '스토어') {
value = <strong>0</strong>;
} else if (label === '쿠폰') {
value = <strong>2장</strong>;
}

return (
<StatItem key={label}>
<span>{label}</span>
{value}
</StatItem>
);
})}
</StatGrid>
</Greeting>

<SectionTitle>최근 본 프로젝트 👀</SectionTitle>
<ProductList>
{[...Array(5)].map((_, i) => (
<ProductCardNormal key={i}>
<img
src="https://shop-phinf.pstatic.net/20220615_163/1655256234926pHmSR_JPEG/56392121446286841_1599012163.jpg?type=m510"
alt={`상품${i + 1}`}
/>
<div className="discount">28,000원</div>
</ProductCardNormal>
))}
</ProductList>
</Main>
);
};

export default MainContent;

/* ---------------------- Styled Components ---------------------- */
const Main = styled.main`
flex: 1;
min-width: 0; // ✅ flex-child overflow 방지
padding: 40px 15px;
background: #fff;
overflow-x: hidden;
`;

const Greeting = styled.div`
margin-bottom: 30px;

h2 {
font-size: 22px;
font-weight: bold;
margin-bottom: 12px;
}
`;

const InviteBox = styled.div`
background: #A66CFF;
padding: 16px;
border-radius: 10px;
font-weight: 500;
margin-bottom: 20px;
color: #fff;
`;

const StatGrid = styled.div`
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 12px;
`;

const StatItem = styled.div`
background: #fff;
border: 1px solid #ddd;
padding: 14px;
border-radius: 10px;
text-align: center;
font-size: 14px;

span {
display: block;
margin-bottom: 6px;
color: #666;
}

button,
strong {
background: none;
border: none;
font-weight: bold;
font-size: 15px;
color: #333;
cursor: pointer;
}
`;

const SectionTitle = styled.div`
font-weight: bold;
font-size: 18px;
margin-bottom: 14px;
`;

const ProductList = styled.div`
display: grid;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); /* ✅ 간격 여유 */
gap: 24px; /* ✅ 사진 간격 넓게 */
padding-bottom: 20px;
width: 100%;
max-width: 100%;
`;

const ProductCardNormal = styled.div`
background: #fff;
border: 1px solid #eee;
border-radius: 12px;
text-align: center;
padding: 12px;
transition: transform 0.2s;

img {
width: 100%;
height: 180px;
object-fit: cover;
border-radius: 8px;
}

.discount {
font-weight: bold;
margin-top: 10px;
font-size: 14px;
}

&:hover {
transform: scale(1.02);
}
`;
217 changes: 217 additions & 0 deletions front/src/pages/MyPage/MyPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
import React, { useState, useEffect, ChangeEvent } from 'react';
import styled from 'styled-components';
import Swal from 'sweetalert2';
import { api } from '../../AxiosInstance';

// 쪼갠 컴포넌트들
import Sidebar from './Sidebar';
import SettingsOverlay from './SettingsOverlay';
import RecentOverlay from './RecentOverlay';
import PointOverlay from './PointOverlay';
import MainContent from './MainContent';

const MyPage = () => {
const baseUrl = process.env.REACT_APP_API_BASE_URL;
const [fundingCount, setFundingCount] = useState<number>(0);

const [homePhone, setHomePhone] = useState({ area: '02', number: '' });
const [showRecentView, setShowRecentView] = useState(false);
const [showSettingsOverlay, setShowSettingsOverlay] = useState(false);
const [showPointOverlay, setShowPointOverlay] = useState(false);

const [profileImage, setProfileImage] = useState<string | null>(null);
const [tempProfileImage, setTempProfileImage] = useState<string | null>(null);

const [point, setPoint] = useState(0);
const [activeTab, setActiveTab] = useState<'서포터' | '메이커'>('서포터');

const [userInfo, setUserInfo] = useState({
name: '김찬영',
nickname: '넥스트레벨',
phone: '010-6672-6024',
email: '[email protected]',
password: '비밀번호 변경하기',
passwordcf: '비밀번호 확인',
});
const [tempUserInfo, setTempUserInfo] = useState(userInfo);

// ✅ editFields 추가
const [editFields, setEditFields] = useState<{ [key: string]: boolean }>({});

// 📌 최근 본 데이터 (dummy)
const [products] = useState([
{ id: 1, name: '청소기', price: '28,000원', image: 'https://via.placeholder.com/200', tags: ['가전', '음식'] },
{ id: 2, name: '햄버거', price: '8,000원', image: 'https://via.placeholder.com/200', tags: ['음식'] },
]);

const [selectedFilter, setSelectedFilter] = useState("전체");
const allTags = Array.from(new Set(products.flatMap(p => p.tags)));

// 📌 공통 닫기
const closeAll = () => {
setShowRecentView(false);
setShowSettingsOverlay(false);
setShowPointOverlay(false);
};

// 📌 클릭 핸들러
const handleClick = (label: string) => {
closeAll();
if (label === '최근본') setShowRecentView(true);
else if (label === '내 정보 설정') setShowSettingsOverlay(true);
else if (label === '포인트 충전') {
setShowPointOverlay(true);
api.get('/social/user/my-point').then((res) => setPoint(res.data.data.point));
} else {
Swal.fire({
icon: 'info',
title: `${label} 기능은 준비 중입니다.`,
confirmButtonColor: '#a66cff',
});
}
};

// 📌 Toss 결제 팝업 열기
const openPaymentWindow = (amount: number) => {
const width = 700;
const height = 900;
const left = window.screenX + (window.outerWidth - width) / 2;
const top = window.screenY + (window.outerHeight - height) / 2;

const url = `/popup-payment?amount=${amount}`;

window.open(
url,
'toss_payment_popup',
`width=${width},height=${height},left=${left},top=${top},resizable=no,scrollbars=no`
);

const messageListener = (event: MessageEvent) => {
if (event.origin !== window.location.origin) return;

if (event.data === 'payment-success') {
api.get('/social/user/my-point').then((res) => {
setPoint(res.data.data.point);
});

window.removeEventListener('message', messageListener);
}
};

window.addEventListener('message', messageListener);
};

// ✅ 핸들러 추가
const handleInputChange = (e: ChangeEvent<HTMLInputElement>, field: string) => {
setTempUserInfo((prev) => ({ ...prev, [field]: e.target.value }));
};

const handleHomePhoneChange = (e: ChangeEvent<HTMLInputElement | HTMLSelectElement>) => {
const { name, value } = e.target;
setHomePhone((prev) => ({ ...prev, [name]: value }));
};

const handleEditClick = (field: string) => {
setEditFields((prev) => ({ ...prev, [field]: true }));
};

const handleImageChange = (e: ChangeEvent<HTMLInputElement>) => {
if (e.target.files && e.target.files[0]) {
const file = e.target.files[0];
setTempProfileImage(URL.createObjectURL(file));
}
};

const handleResetClick = () => {
setTempUserInfo(userInfo);
setTempProfileImage(profileImage);
setEditFields({});
};

// 📌 API - 펀딩 카운트
useEffect(() => {
api.post('/public/project/all', { tag: [], page: 0, myPageWhere: 'PROJECT' })
.then(res => setFundingCount(res.data.data.totalCount))
.catch(e => console.log(e));
}, []);

return (
<Container>
<Sidebar
activeTab={activeTab}
setActiveTab={setActiveTab}
userInfo={userInfo}
profileImage={profileImage}
onOpenSettings={() => setShowSettingsOverlay(true)}
onOpenRecent={() => setShowRecentView(true)}
onOpenPoint={() => setShowPointOverlay(true)}
/>

<MainContent
userInfo={userInfo}
fundingCount={fundingCount}
point={point}
onHandleClick={handleClick}
/>

{showSettingsOverlay && (
<SettingsOverlay
userInfo={userInfo}
tempUserInfo={tempUserInfo}
setUserInfo={setUserInfo}
profileImage={profileImage}
tempProfileImage={tempProfileImage}
setTempUserInfo={setTempUserInfo}
setTempProfileImage={setTempProfileImage}
setProfileImage={setProfileImage}
homePhone={homePhone}
setHomePhone={setHomePhone}
editFields={editFields} // ✅ 추가
setEditFields={setEditFields} // ✅ 추가
onReset={handleResetClick} // ✅ 추가
onInputChange={handleInputChange} // ✅ 추가
onHomePhoneChange={handleHomePhoneChange}// ✅ 추가
onEditClick={handleEditClick} // ✅ 추가
onImageChange={handleImageChange} // ✅ 추가
onClose={closeAll}
/>
)}

{showRecentView && (
<RecentOverlay
onClose={closeAll}
products={products}
selectedFilter={selectedFilter}
setSelectedFilter={setSelectedFilter}
allTags={allTags}
userInfo={userInfo}
tempUserInfo={tempUserInfo}
profileImage={profileImage}
tempProfileImage={tempProfileImage}
/>
)}

{showPointOverlay && (
<PointOverlay
point={point}
onClose={closeAll}
openPaymentWindow={openPaymentWindow}
/>
)}
</Container>
);
};

export default MyPage;

/* ---------------------- Styled Components ---------------------- */
const Container = styled.div`
display: flex;
padding: 15px;
box-sizing: border-box;
font-family: 'Pretendard', sans-serif;
width: 100%;
max-width: 100vw;
min-height: 100vh;
overflow-x: hidden;
`;
Loading