diff --git a/front/src/pages/MyPage/MainContent.tsx b/front/src/pages/MyPage/MainContent.tsx new file mode 100644 index 0000000..b9ece17 --- /dev/null +++ b/front/src/pages/MyPage/MainContent.tsx @@ -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 = ({ userInfo, fundingCount, point, onHandleClick }) => { + return ( +
+ +

{userInfo.name}님 안녕하세요.

+ 당신의 아이디어, 펀딩으로 연결하세요! + + {['펀딩+', '스토어', '지지서명', '알림신청', '포인트', '쿠폰'].map((label) => { + let value: React.ReactNode; + + if (label === '지지서명' || label === '알림신청') { + value = ; + } else if (label === '포인트') { + value = {point.toLocaleString()}P; + } else if (label === '펀딩+') { + value = {fundingCount}; + } else if (label === '스토어') { + value = 0; + } else if (label === '쿠폰') { + value = 2장; + } + + return ( + + {label} + {value} + + ); + })} + +
+ + 최근 본 프로젝트 👀 + + {[...Array(5)].map((_, i) => ( + + {`상품${i +
28,000원
+
+ ))} +
+
+ ); +}; + +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); + } +`; diff --git a/front/src/pages/MyPage/MyPage.tsx b/front/src/pages/MyPage/MyPage.tsx new file mode 100644 index 0000000..2a9049f --- /dev/null +++ b/front/src/pages/MyPage/MyPage.tsx @@ -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(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(null); + const [tempProfileImage, setTempProfileImage] = useState(null); + + const [point, setPoint] = useState(0); + const [activeTab, setActiveTab] = useState<'서포터' | '메이커'>('서포터'); + + const [userInfo, setUserInfo] = useState({ + name: '김찬영', + nickname: '넥스트레벨', + phone: '010-6672-6024', + email: 'kcy021216@gmail.com', + 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, field: string) => { + setTempUserInfo((prev) => ({ ...prev, [field]: e.target.value })); + }; + + const handleHomePhoneChange = (e: ChangeEvent) => { + const { name, value } = e.target; + setHomePhone((prev) => ({ ...prev, [name]: value })); + }; + + const handleEditClick = (field: string) => { + setEditFields((prev) => ({ ...prev, [field]: true })); + }; + + const handleImageChange = (e: ChangeEvent) => { + 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 ( + + setShowSettingsOverlay(true)} + onOpenRecent={() => setShowRecentView(true)} + onOpenPoint={() => setShowPointOverlay(true)} + /> + + + + {showSettingsOverlay && ( + + )} + + {showRecentView && ( + + )} + + {showPointOverlay && ( + + )} + + ); +}; + +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; +`; diff --git a/front/src/pages/MyPage/PointOverlay.tsx b/front/src/pages/MyPage/PointOverlay.tsx new file mode 100644 index 0000000..9e56f6a --- /dev/null +++ b/front/src/pages/MyPage/PointOverlay.tsx @@ -0,0 +1,108 @@ +import React from 'react'; +import styled from 'styled-components'; + +interface Props { + point: number; + onClose: () => void; + openPaymentWindow: (amount: number) => void; +} + +const PointOverlay: React.FC = ({ point, onClose, openPaymentWindow }) => { + return ( + + +

포인트 충전

+ × +
+ + + 현재 보유 포인트: {point.toLocaleString()}P + + +

충전하실 금액을 선택하세요

+ + {[1000, 5000, 10000, 20000].map((amount) => ( + openPaymentWindow(amount)}> + {amount.toLocaleString()}P + + ))} + +
+
+
+ ); +}; + +export default PointOverlay; + +/* ---------------------- Styled Components ---------------------- */ +const Overlay = styled.div` + position: fixed; + top: 50%; + left: 50%; + width: 500px; + transform: translate(-50%, -50%); + background: #fff; + padding: 30px; + border-radius: 16px; + box-shadow: 0 12px 40px rgba(0, 0, 0, 0.25); + z-index: 1000; +`; + +const OverlayHeader = styled.div` + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 20px; + + h2 { + font-size: 22px; + font-weight: bold; + } +`; + +const CloseButton = styled.button` + font-size: 24px; + border: none; + background: none; + cursor: pointer; +`; + +const OverlayContent = styled.div` + margin-top: 20px; +`; + +const PointAmount = styled.div` + font-size: 18px; + font-weight: bold; + margin-bottom: 30px; +`; + +const ChargeBox = styled.div` + background: #f9f9f9; + border-radius: 10px; + padding: 20px; + text-align: center; +`; + +const ChargeOptions = styled.div` + margin-top: 15px; + display: flex; + justify-content: center; + gap: 12px; +`; + +const ChargeBtn = styled.button` + padding: 10px 16px; + border-radius: 8px; + background: #A66CFF; + color: white; + border: none; + cursor: pointer; + font-weight: bold; + font-size: 14px; + + &:hover { + background: #8e4ae0; + } +`; diff --git a/front/src/pages/MyPage/RecentOverlay.tsx b/front/src/pages/MyPage/RecentOverlay.tsx new file mode 100644 index 0000000..c8d3ab5 --- /dev/null +++ b/front/src/pages/MyPage/RecentOverlay.tsx @@ -0,0 +1,258 @@ +import React from 'react'; +import styled, { keyframes } from 'styled-components'; +import Swal from 'sweetalert2'; + +interface Product { + id: number; + name: string; + price: string; + image: string; + tags: string[]; +} + +interface Props { + onClose: () => void; + products: Product[]; + selectedFilter: string; + setSelectedFilter: (v: string) => void; + allTags: string[]; + userInfo: any; + tempUserInfo: any; + profileImage: string | null; + tempProfileImage: string | null; +} + +const RecentOverlay: React.FC = ({ + onClose, + products, + selectedFilter, + setSelectedFilter, + allTags, + userInfo, + tempUserInfo, + profileImage, + tempProfileImage, +}) => { + const filteredProducts = + selectedFilter === '전체' + ? products + : products.filter((p) => p.tags.includes(selectedFilter)); + + return ( + + +

나의 활동

+ { + const hasChanges = + JSON.stringify(userInfo) !== JSON.stringify(tempUserInfo) || + profileImage !== tempProfileImage; + + if (hasChanges) { + Swal.fire({ + icon: 'warning', + title: '변경 사항이 저장되지 않았습니다', + text: '입력한 내용이 저장되지 않은 채 창이 닫힙니다.', + confirmButtonColor: '#a66cff', + }); + } + onClose(); + }} + > + × + +
+ + + + + 최근 본 + + + {['전체', ...allTags].map((cat) => ( + setSelectedFilter(cat)} + > + {cat} + + ))} + + + + 전체 {filteredProducts.length}개 + + {filteredProducts.map((item) => ( + + {item.name} + + + {item.price} + +

{item.name}

+ + {item.tags.map((tag, i) => ( + #{tag} + ))} + +
+
+ ))} +
+
+
+ ); +}; + +export default RecentOverlay; + +/* ---------------------- Styled Components ---------------------- */ +const fadeIn = keyframes` + from { opacity: 0; transform: translate(-50%, -55%); } + to { opacity: 1; transform: translate(-50%, -50%); } +`; + +const Overlay = styled.div` + position: fixed; + top: 50%; + left: 50%; + width: 800px; + height: 800px; + transform: translate(-50%, -50%); + background: #fff; + z-index: 999; + padding: 30px; + border-radius: 20px; + box-shadow: 0 10px 40px rgba(0, 0, 0, 0.2); + display: flex; + flex-direction: column; + overflow: hidden; + animation: ${fadeIn} 0.3s ease-out; +`; + +const OverlayHeader = styled.div` + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 20px; + + h2 { + font-size: 24px; + font-weight: bold; + } +`; + +const CloseButton = styled.button` + font-size: 24px; + border: none; + background: none; + cursor: pointer; +`; + +const ScrollableContent = styled.div` + flex: 1; + overflow-y: auto; + padding-right: 6px; +`; + +const Tabs = styled.div` + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 10px; +`; + +const TabGroup = styled.div` + display: flex; + gap: 20px; +`; + +const Tab = styled.div` + font-size: 16px; + color: #999; + cursor: pointer; +`; + +const ActiveTab = styled(Tab)` + font-weight: bold; + color: #000; + border-bottom: 2px solid #000; +`; + +const FilterGroup = styled.div` + display: flex; + gap: 10px; +`; + +const FilterBtn = styled.button<{ active: boolean }>` + border: none; + padding: 6px 14px; + border-radius: 20px; + background: ${({ active }) => (active ? '#000' : '#f0f0f0')}; + color: ${({ active }) => (active ? '#fff' : '#000')}; + cursor: pointer; + font-weight: 500; +`; + +const ItemCount = styled.div` + font-size: 14px; + color: #666; + margin-bottom: 20px; +`; + +const ProductColumn = styled.div` + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 20px; +`; + +const ProductCardOverlay = styled.div` + background: #fff; + border: 1px solid #ddd; + border-radius: 12px; + overflow: hidden; + display: flex; + flex-direction: column; + padding: 0px; + + img { + width: 100%; + height: 200px; + object-fit: cover; + } +`; + +const CardContent = styled.div` + padding: 10px; + + p { + font-size: 13px; + color: #333; + margin-top: 6px; + } +`; + +const Price = styled.div` + font-size: 14px; + color: #a66cff; + + span { + font-weight: bold; + } +`; + +const HashtagList = styled.div` + margin-top: 6px; + display: flex; + flex-wrap: wrap; + gap: 6px; +`; + +const Hashtag = styled.span` + font-size: 12px; + color: #666; + background: #f0f0f0; + padding: 4px 8px; + border-radius: 12px; +`; diff --git a/front/src/pages/MyPage/SettingsOverlay.tsx b/front/src/pages/MyPage/SettingsOverlay.tsx new file mode 100644 index 0000000..b40cf47 --- /dev/null +++ b/front/src/pages/MyPage/SettingsOverlay.tsx @@ -0,0 +1,344 @@ +import React, { ChangeEvent } from 'react'; +import styled from 'styled-components'; +import Swal from 'sweetalert2'; + +interface Props { + userInfo: any; + tempUserInfo: any; + profileImage: string | null; + tempProfileImage: string | null; + editFields: { [key: string]: boolean }; + homePhone: { area: string; number: string }; + setHomePhone: React.Dispatch>; // ✅ 추가 + onClose: () => void; + onReset: () => void; + onInputChange: (e: ChangeEvent, field: string) => void; + onHomePhoneChange: (e: ChangeEvent) => void; + onEditClick: (field: string) => void; + onImageChange: (e: ChangeEvent) => void; + setEditFields: React.Dispatch>; + setUserInfo: React.Dispatch>; + setTempUserInfo: React.Dispatch>; + setProfileImage: React.Dispatch>; + setTempProfileImage: React.Dispatch>; +} + +const SettingsOverlay: React.FC = ({ + userInfo, + tempUserInfo, + profileImage, + tempProfileImage, + editFields, + homePhone, + setHomePhone, // ✅ props 추가 + onClose, + onReset, + onInputChange, + onHomePhoneChange, + onEditClick, + onImageChange, + setEditFields, + setUserInfo, + setTempUserInfo, + setProfileImage, + setTempProfileImage, +}) => { + return ( + + +

내 정보 설정

+ × +
+ + +
+ + + + +
+ + 이미지변경 + +
+
+ + {[ + { label: '이름', field: 'name' }, + { label: '닉네임', field: 'nickname' }, + { label: '전화번호', field: 'phone' }, + { label: '이메일 주소', field: 'email' }, + { label: '비밀번호', field: 'password' }, + { label: '비밀번호 확인', field: 'passwordcf' }, + ].map(({ label, field }) => ( + + + + {editFields[field] ? ( + onInputChange(e, field)} + /> + ) : ( + tempUserInfo[field] + )} + + {editFields[field] ? ( + { + setEditFields((prev) => ({ ...prev, [field]: false })); + }} + > + 변경완료 + + ) : ( + onEditClick(field)}>변경 + )} + + ))} + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + 초기화 + + { + const { name, nickname, phone, email } = tempUserInfo; + + if (!name.trim() || !nickname.trim() || !phone.trim() || !email.trim()) { + await Swal.fire({ + icon: 'error', + title: '필수 항목을 입력해주세요.', + text: '이름, 닉네임, 전화번호, 이메일은 필수입니다.', + confirmButtonColor: '#a66cff', + }); + return; + } + + const result = await Swal.fire({ + title: '변경사항을 저장할까요?', + text: '입력한 정보가 저장됩니다.', + icon: 'question', + showCancelButton: true, + confirmButtonText: '저장', + cancelButtonText: '취소', + confirmButtonColor: '#A66CFF', + cancelButtonColor: '#ddd', + }); + + if (result.isConfirmed) { + setUserInfo(tempUserInfo); // ✅ 변경 적용 + setProfileImage(tempProfileImage); // ✅ 프로필 반영 + + await Swal.fire({ + icon: 'success', + title: '정보가 변경되었습니다!', + showConfirmButton: false, + timer: 1500, + }); + + onClose(); + } + }} + > + 완료 + + +
+ ); +}; + +export default SettingsOverlay; + +/* ---------------------- Styled Components ---------------------- */ +const Overlay = styled.div` + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 520px; + max-width: 90vw; + height: auto; + max-height: 90vh; + overflow-y: auto; + background: #fff; + padding: 24px; + border-radius: 20px; + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2); +`; + +const ScrollableContent = styled.div` + flex: 1; + overflow-y: auto; + padding-right: 6px; +`; + +const OverlayHeader = styled.div` + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 20px; + + h2 { + font-size: 24px; + font-weight: bold; + } +`; + +const CloseButton = styled.button` + font-size: 24px; + border: none; + background: none; + cursor: pointer; +`; + +const InfoItem = styled.div` + display: flex; + align-items: center; + padding: 16px 0; + border-bottom: 1px solid #eee; + + &:last-child { + border-bottom: none; + } +`; + +const Label = styled.div` + width: 180px; + font-weight: bold; + font-size: 15px; + color: #333; +`; + +const Content = styled.div` + flex: 1; + font-size: 14px; + color: #666; + + input { + width: 100%; + padding: 6px 10px; + font-size: 14px; + border: 1px solid #ccc; + border-radius: 6px; + } +`; + +const ChangeBtn = styled.button` + padding: 6px 14px; + border-radius: 20px; + border: 1px solid #ccc; + background: white; + font-size: 13px; + cursor: pointer; + + &:hover { + background: #f3f3f3; + } +`; + +const RequiredMark = styled.span` + color: #a66cff; + font-size: 16px; + margin-left: 4px; +`; + +const FlexRow = styled.div` + display: flex; + gap: 8px; +`; + +const AreaSelect = styled.select` + padding: 6px 10px; + border-radius: 6px; + border: 1px solid #ccc; + font-size: 14px; +`; + +const HomePhoneInput = styled.input` + flex: 1; + padding: 6px 10px; + border-radius: 6px; + border: 1px solid #ccc; + font-size: 14px; +`; + +const HiddenFileInput = styled.input` + display: none; +`; + +const ImageInputLabel = styled.label` + display: inline-block; +`; + +const AvatarImg = styled.img` + width: 105px; + height: 105px; + border-radius: 50%; + object-fit: cover; + pointer-events: none; +`; + +const OverlayFooter = styled.div` + display: flex; + justify-content: flex-end; + gap: 10px; + margin-top: 20px; + padding-top: 20px; + border-top: 1px solid #eee; +`; diff --git a/front/src/pages/MyPage/Sidebar.tsx b/front/src/pages/MyPage/Sidebar.tsx new file mode 100644 index 0000000..ba45b23 --- /dev/null +++ b/front/src/pages/MyPage/Sidebar.tsx @@ -0,0 +1,143 @@ +import React from 'react'; +import styled from 'styled-components'; + +interface SidebarProps { + activeTab: '서포터' | '메이커'; + setActiveTab: (tab: '서포터' | '메이커') => void; + userInfo: { name: string }; + profileImage: string | null; + onOpenSettings: () => void; + onOpenRecent: () => void; + onOpenPoint: () => void; +} + +const Sidebar: React.FC = ({ + activeTab, + setActiveTab, + userInfo, + profileImage, + onOpenSettings, + onOpenRecent, + onOpenPoint, +}) => { + return ( + + {/* 탭 버튼 */} + + setActiveTab('서포터')}> + 서포터 + + setActiveTab('메이커')}> + 메이커 + + + + {/* 프로필 영역 */} + + + {userInfo.name} + 내 정보 설정 + + + {/* 탭에 따라 다른 메뉴 */} + {activeTab === '서포터' ? ( + + 최근본 + 포인트 충전 + 좋아요 + 팔로잉 + 펀딩 목록 + + ) : ( + + 내 프로젝트 + 정산 관리 + 문의 답변 + + )} + + ); +}; + +export default Sidebar; + +const Container = styled.aside` + width: 260px; + background: #fff; + padding: 30px 20px; + border-right: 1px solid #eee; +`; + +const TopTab = styled.div` + display: flex; + width: 100%; + margin-bottom: 30px; +`; + +const TabButton = styled.button<{ active?: boolean }>` + flex: 1; + padding: 10px 0; + border: none; + border-radius: 20px; + background: ${({ active }) => (active ? '#a66cff' : '#f0f0f0')}; + color: ${({ active }) => (active ? '#fff' : '#999')}; + font-weight: bold; + cursor: pointer; +`; + +const ProfileBox = styled.div` + text-align: center; + margin: 30px 0; +`; + +const AvatarImg = styled.img` + width: 105px; + height: 105px; + border-radius: 50%; + object-fit: cover; +`; + +const Name = styled.div` + font-weight: bold; + margin: 10px 0; + font-size: 16px; +`; + +const SettingsBtn = styled.button` + padding: 6px 14px; + border-radius: 20px; + border: 1px solid #ccc; + background: white; + font-size: 13px; + cursor: pointer; + + &:hover { + background: #f3f3f3; + } +`; + +const ActivityMenu = styled.div` + display: flex; + flex-direction: column; + gap: 10px; +`; + +const MenuButton = styled.button` + background: none; + border: none; + text-align: left; + font-size: 15px; + padding: 8px; + cursor: pointer; + border-radius: 6px; + + &:hover { + background: #ecebf5; + } +`;