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

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"@stomp/stompjs": "^7.0.0",
"axios": "^1.8.1",
"chart.js": "^4.4.8",
"framer-motion": "^12.4.11",
"lucide-react": "^0.475.0",
"react": "^19.0.0",
"react-chartjs-2": "^5.3.0",
Expand Down
2 changes: 2 additions & 0 deletions src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import { BrowserRouter as Router, Route, Routes, useNavigate } from "react-router-dom";
import { useEffect, useState } from "react";
import Home from "./pages/Home";
import Pets from "./pages/Pets";
import ErrorPage from "./error/ErrorPage";
import TestAPI from "./pages/TestAPI";
import GithubLogin from "./components/GithubLogin";
Expand All @@ -23,6 +24,7 @@ function App() {
<Routes>
<Route path="/" element={<GithubLogin />} />
<Route path="/home" element={<Home />} />
<Route path="/pets" element={<Pets />} />
<Route path="/error" element={<ErrorPage />} />
<Route path="/testapi" element={<TestAPI />} />
<Route path="/protected" element={
Expand Down
83 changes: 83 additions & 0 deletions src/pages/Pets.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/* 전체 배경 설정 */
body {
background-color: rgb(63, 178, 127);
margin: 0;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}

/* 펫이 움직이는 공간 */
.pets-wrapper {
display: flex;
justify-content: center;
align-items: center;
width: 100vw;
height: 100vh;
}

.pets-container {
position: relative;
width: 800px;
height: 500px;
background-color: white;
border-radius: 10px;
overflow: hidden;
}

/* 펫을 감싸는 div */
.pet-wrapper {
position: absolute;
width: 100px;
height: 100px;
}

/* 알이 0.8배~1배로 커졌다 작아지는 효과 */
.pet-egg {
width: 100px;
height: 100px;
animation: egg-bounce 1.5s infinite ease-in-out alternate;
}

@keyframes egg-bounce {
0% {
transform: scaleY(0.7);
}
100% {
transform: scaleY(1);
}
}

.pets-wrapper {
position: relative; /* 하위 요소들이 절대적으로 위치할 수 있도록 설정 */
}

.pets-container {
position: relative;
}

.home-button-container {
display: flex;
justify-content: center;
margin-top: 20px; /* 위쪽에 간격 추가 */
position: absolute;
bottom: 10px; /* 버튼을 하단에 고정 */
width: 100%; /* 버튼이 가로로 꽉 차도록 설정 */
}

.home-button {
padding: 10px 20px;
font-size: 16px;
cursor: pointer;
background-color: white;
border: 1px solid #ccc;
border-radius: 5px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
transition: background-color 0.3s;
color:#111111;
}

.home-button:hover {
background-color: #f0f0f0;
}
63 changes: 63 additions & 0 deletions src/pages/Pets.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { useEffect, useState } from "react";
import { motion } from "framer-motion";
import { useNavigate } from "react-router-dom"; // React Router 사용
import "./Pets.css";

const Pets = () => {
const [pets, setPets] = useState([]);
const navigate = useNavigate(); // 홈으로 돌아가기 함수

useEffect(() => {
fetch("/api/pets/getall")
.then((res) => res.json())
.then((data) => setPets(data));
}, []);

return (
<div className="pets-wrapper">
<div className="pets-container">
{pets.map((pet, index) => (
<Pet key={index} src={`/pets/${pet.grow}_${pet.type}_128.png`} />
))}
</div>

{/* 홈으로 돌아가기 버튼 추가 */}
<div className="home-button-container">
<button className="home-button" onClick={() => navigate("/home")}>
홈으로 돌아가기
</button>
</div>
</div>
);
};

const Pet = ({ src }) => {
const getRandomPosition = () => ({
x: Math.random() * 600 + 100, // 100 ~ 700 사이 랜덤 X 좌표
y: Math.random() * 400 + 100, // 100 ~ 500 사이 랜덤 Y 좌표
});

const [position, setPosition] = useState(getRandomPosition());

useEffect(() => {
const movePet = () => {
setPosition(getRandomPosition());
};

const interval = setInterval(movePet, 3000);
return () => clearInterval(interval);
}, []);

return (
<motion.div
className="pet-wrapper"
initial={{ x: position.x, y: position.y }}
animate={{ x: position.x, y: position.y }}
transition={{ duration: 1.5, ease: "easeInOut" }}
>
<img className="pet-egg" src={src} alt="Pet" />
</motion.div>
);
};

export default Pets;
28 changes: 22 additions & 6 deletions src/pages/Profile.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { useState, useEffect, useRef } from "react";
import SockJS from "sockjs-client";
import Stomp from "stompjs";
import { useNavigate } from "react-router-dom";

const Profile = ({ userInfo }) => {
const [seasonCommitCount, setSeasonCommitCount] = useState(userInfo.seasonCommitCount || 0);
Expand Down Expand Up @@ -34,6 +35,12 @@ const Profile = ({ userInfo }) => {

// 경험치 바 계산
const progress = (petExp / maxExp) * 100;
const navigate = useNavigate(); // 페이지 이동 함수

// 새 펫 받기 버튼 클릭 핸들러
const handleGetNewPet = () => {
alert("새 펫을 받는 기능은 아직 구현되지 않았습니다! 🐣");
};

// 프로필 정보 자동 새로고침 함수
const refreshProfileData = async () => {
Expand Down Expand Up @@ -75,10 +82,10 @@ const Profile = ({ userInfo }) => {
}, []);

// userInfo가 변경되면 값 업데이트
useEffect(() => {
setSeasonCommitCount((prev) => prev || userInfo.seasonCommitCount);
setPetExp((prev) => prev || userInfo.petExp);
}, [userInfo]);
useEffect(() => {
setSeasonCommitCount((prev) => prev || userInfo.seasonCommitCount);
setPetExp((prev) => prev || userInfo.petExp);
}, [userInfo]);

// 날짜 포맷팅 함수
const formatDate = (dateString) => {
Expand Down Expand Up @@ -203,7 +210,7 @@ useEffect(() => {
<div className="detail-item">
<span className="detail-icon">📅</span>
<span className="detail-text">
마지막 커밋: <span className="detail-value">{formatDate(userInfo.lastCommitted)}</span>
마지막 업데이트: <span className="detail-value">{formatDate(userInfo.lastCommitted)}</span>
</span>
</div>
</div>
Expand Down Expand Up @@ -241,8 +248,17 @@ useEffect(() => {
</div>
</div>
</div>
{/* 🆕 추가된 버튼 섹션 */}
<div className="button-section">
<button className="get-new-pet-btn" onClick={handleGetNewPet}>
🐣 새 펫 받기
</button>
<button className="view-pets-btn" onClick={() => navigate("/pets")}>
🏡 펫 보러가기
</button>
</div>
</div>
);
};

export default Profile;
export default Profile;
50 changes: 49 additions & 1 deletion src/pages/profile.css
Original file line number Diff line number Diff line change
Expand Up @@ -303,4 +303,52 @@ h2 {
box-shadow: 0 0 0 2px white;
z-index: 10;
animation: pulse 1s infinite ease-in-out;
}
}

/* 버튼 섹션 스타일 */
.button-section {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 10px;
border-radius: 10px;
}

/* 버튼 기본 스타일 */
.button-section button {
width: 140px;
height: 40px;
font-size: 16px;
font-weight: bold;
border-radius: 8px;
cursor: pointer;
transition: background-color 0.2s, transform 0.1s;
}

/* 새 펫 받기 버튼 */
.get-new-pet-btn {
background-color: #fefefe;
border: 1px solid rgb(63, 178, 127);
color: black;
}

.get-new-pet-btn:hover {
background-color: rgb(63, 178, 127);
border: 1px solid #fefefe;
transform: scale(1.05);
}

/* 펫 보러가기 버튼 */
.view-pets-btn {
background-color: rgb(63, 178, 127);
border: 1px solid #fefefe;
color: white;
}

.view-pets-btn:hover {
background-color: #fefefe;
border: 1px solid rgb(63, 178, 127);
color: rgb(63, 178, 127);
transform: scale(1.05);
}