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
8 changes: 8 additions & 0 deletions src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { setActiveTab } from './store/slices/appSlice';
import { fetchMyPageData } from './store/slices/userSlice';
import {
BrowserRouter,
Routes,
Expand Down Expand Up @@ -57,6 +58,13 @@ export default function App() {
const dispatch = useDispatch();
// removed top-level navigate; navigation is handled inside AppContent via react-router

useEffect(() => {
const token = localStorage.getItem('token');
if (token) {
dispatch(fetchMyPageData());
}
}, [dispatch]);

if (appState === 'splash') return <SplashScreen />;
if (appState === 'onboarding') return <OnboardingScreen />;

Expand Down
8 changes: 5 additions & 3 deletions src/components/screens/EditProfileScreen.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -219,9 +219,11 @@ export default function EditProfileScreen({ onBack }) {
</div>

{/* μƒνƒœ λ©”μ‹œμ§€ */}
{nicknameValid && nickname === originNickname ? (
<span style={{ color: "#d33b3b" }}>ν˜„μž¬ λ‹‰λ„€μž„μž…λ‹ˆλ‹€</span>
) : nicknameValid && nickAvailable === true ? (
{
// nicknameValid && nickname === originNickname ? (
// <span style={{ color: "#d33b3b" }}>ν˜„μž¬ λ‹‰λ„€μž„μž…λ‹ˆλ‹€</span>
// ) :
nicknameValid && nickAvailable === true ? (
<span style={{ color: "#3fa14a" }}>μ‚¬μš© κ°€λŠ₯ν•œ λ‹‰λ„€μž„μž…λ‹ˆλ‹€</span>
) : nicknameValid && nickAvailable === false ? (
<span style={{ color: "#d33b3b" }}>이미 μ‘΄μž¬ν•˜λŠ” λ‹‰λ„€μž„μž…λ‹ˆλ‹€</span>
Expand Down
30 changes: 11 additions & 19 deletions src/components/screens/HomeScreen.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useEffect, useState, useRef } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { setActiveTab } from '../../store/slices/appSlice';
import { fetchPointInfo } from '../../store/slices/userSlice';
import { fetchPointInfo, fetchMyPageData } from '../../store/slices/userSlice';
import EcoNewsList from '../screens/EcoNewsList';
import { TrophyIcon } from '@heroicons/react/24/solid';
import { useMemo } from 'react';
Expand Down Expand Up @@ -66,7 +66,7 @@ export default function HomeScreen({ onNavigate }) {
}, [currentLocation, isLocationLoading, fetchCurrentLocation]);

useEffect(() => {
const onFocus = () => dispatch(fetchPointInfo());
const onFocus = () => dispatch( fetchMyPageData());
window.addEventListener('focus', onFocus);
return () => window.removeEventListener('focus', onFocus);
}, [dispatch]);
Expand Down Expand Up @@ -310,18 +310,8 @@ export default function HomeScreen({ onNavigate }) {
<div className='bg-gradient-to-br from-[#4CAF50] to-[#8BC34A] rounded-3xl p-6 text-white shadow-xl border-0'>
{/* μ‚¬μš©μž 이름 ν‘œμ‹œ */}
<div className='flex items-center gap-2 mb-4'>
{profile.avatar && (
<img
src={profile.avatar}
alt='ν”„λ‘œν•„'
className='w-10 h-10 rounded-full'
/>
)}
<p className='text-white/90 text-sm'>
{profile.nickname || profile.name}λ‹˜μ˜ κ·Έλ¦°
ν™œλ™
</p>
</div>
{profile.avatar && ( <img src={profile.avatar} alt='ν”„λ‘œν•„' className='w-10 h-10 rounded-full' /> )}
<p className='text-white font-medium text-base sm:text-lg tracking-wide'>{profile.nickname || profile.name}λ‹˜μ˜ κ·Έλ¦° ν™œλ™</p> </div>

<div className='flex items-center justify-between mb-4'>
<div>
Expand Down Expand Up @@ -365,11 +355,13 @@ export default function HomeScreen({ onNavigate }) {
</div>

<button
onClick={() => navigate('cert')}
className='w-full bg-white text-[#4CAF50] py-3 rounded-[20px] text-center transition-transform hover:scale-105'
>
ν™œλ™ μΈμ¦ν•˜κ³  포인트 λ°›κΈ°
</button>
onClick={() => navigate('cert')}
className='w-full bg-white text-[#4CAF50] py-3 rounded-[20px] text-center font-semibold
shadow-md border border-[#4CAF50]/20 transition-transform duration-200
hover:scale-[1.01] hover:shadow-lg active:scale-[0.99]'
>
ν™œλ™ μΈμ¦ν•˜κ³  포인트 λ°›κΈ°
</button>
</div>
</div>
)}
Expand Down
6 changes: 4 additions & 2 deletions src/components/screens/LoginSignupScreen.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import api from '../../api/axios';
// ↓ redux μ•ˆ μ“°λ©΄ 이 λΆ€λΆ„ μ œκ±°ν•΄λ„λ¨
import { useDispatch } from 'react-redux';
import { updateProfile } from '../../store/slices/userSlice';
import { useNavigate } from 'react-router-dom';
import kakaoBtn from '../../assets/kakao_login_medium_wide.png';
import HomeScreen from './HomeScreen';

Expand All @@ -20,6 +21,7 @@ const kakaoLogin = () => {
}/oauth2/authorization/kakao`;
};


// μ „μ—­ μŠ€νƒ€μΌ κ·ΈλŒ€λ‘œ μœ μ§€
const styles = `
:root { --brand: ${themeColor}; }
Expand Down Expand Up @@ -198,6 +200,7 @@ export default function LoginSignupScreen() {

/* ------------------ 둜그인 ------------------ */
function LoginForm({ setUserInfo, setModal }) {
const navigate = useNavigate();
const [email, setEmail] = useState('');
const [tEmail, setTEmail] = useState(false);
const [password, setPassword] = useState('');
Expand All @@ -217,8 +220,7 @@ function LoginForm({ setUserInfo, setModal }) {
headers: { Authorization: `Bearer ${res.data.data.accessToken}` },
});
setUserInfo(info.data.data);
setModal({ message: '둜그인 성곡!', type: 'success' });
setTimeout(() => (window.location.href = '/'), 800);
setTimeout(() => navigate('/'));
} catch {
setModal({
message: '이메일 λ˜λŠ” λΉ„λ°€λ²ˆν˜Έλ₯Ό ν™•μΈν•΄μ£Όμ„Έμš”.',
Expand Down
31 changes: 21 additions & 10 deletions src/store/slices/userSlice.js
Original file line number Diff line number Diff line change
Expand Up @@ -349,16 +349,27 @@ const userSlice = createSlice({
state.loading = true;
state.error = null;
})
.addCase(fetchPointInfo.fulfilled, (state, action) => {
state.loading = false;
state.isLoggedIn = true;

state.stats = {
point: action.payload.point,
totalPoint: action.payload.totalPoint || 0,
carbonReduction: action.payload.carbon_save,
};
})
.addCase(fetchPointInfo.fulfilled, (state, action) => {
state.loading = false;
state.isLoggedIn = true;

state.stats = {
point: action.payload.point,
totalPoint: action.payload.totalPoint || 0,
carbonReduction: action.payload.carbon_save,
};

if (action.payload.member) {
const member = action.payload.member;
state.profile = {
memberId: member.memberId,
name: member.nickname,
email: member.email,
avatar: member.imageUrl,
nickname: member.nickname,
};
}
})
.addCase(fetchPointInfo.rejected, (state, action) => {
state.loading = false;
state.error = action.payload;
Expand Down