This commit is contained in:
ondal
2025-02-13 15:47:22 +09:00
commit bb7ce2d988
53 changed files with 19663 additions and 0 deletions
+73
View File
@@ -0,0 +1,73 @@
// src/pages/LoginPage.js
import React, { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { Box, Typography, Paper } from '@mui/material';
import { useAuth } from '../contexts/AuthContext';
import LoginForm from '../components/auth/LoginForm';
const LoginPage = () => {
const navigate = useNavigate();
const { currentUser, login } = useAuth();
const [error, setError] = useState('');
useEffect(() => {
if (currentUser) {
navigate('/');
}
}, [currentUser, navigate]);
const handleLogin = async (userId, password) => {
try {
setError('');
const success = await login(userId, password);
if (success) {
navigate('/');
} else {
setError('아이디 또는 비밀번호가 올바르지 않습니다.');
}
} catch (err) {
setError('로그인 중 오류가 발생했습니다. 다시 시도해주세요.');
}
};
return (
<Box
sx={{
minHeight: '100vh',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
p: 2
}}
>
<Paper
elevation={3}
sx={{
width: '100%',
maxWidth: 400,
p: 4,
display: 'flex',
flexDirection: 'column',
alignItems: 'center'
}}
>
<Typography
component="h1"
variant="h4"
gutterBottom
sx={{ mb: 4 }}
>
마이구독
</Typography>
<LoginForm
onSubmit={handleLogin}
error={error}
/>
</Paper>
</Box>
);
};
export default LoginPage;
+73
View File
@@ -0,0 +1,73 @@
// src/pages/MainPage.js
import React, { useEffect, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { Box } from '@mui/material';
import { useAuth } from '../contexts/AuthContext';
import TotalFee from '../components/main/TotalFee';
import MySubscriptions from '../components/main/MySubscriptions';
import RecommendCategory from '../components/main/RecommendCategory';
import LoadingSpinner from '../components/common/LoadingSpinner';
import ErrorMessage from '../components/common/ErrorMessage';
import { mySubscriptionApi, recommendApi, handleApiResponse } from '../services/api';
const MainPage = () => {
const navigate = useNavigate();
const { currentUser, authInitialized } = useAuth();
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const [totalFee, setTotalFee] = useState(null);
const [subscriptions, setSubscriptions] = useState([]);
const [recommendedCategory, setRecommendedCategory] = useState(null);
useEffect(() => {
// 로그인 체크
if (authInitialized && !currentUser) { // loading 상태 체크 추가
navigate('/login');
return;
}
if(authInitialized) {
const fetchData = async () => {
try {
setLoading(true);
setError(null);
const [totalFeeResponse, subscriptionsResponse, recommendResponse] = await Promise.all([
mySubscriptionApi.getTotalFee(currentUser.userId),
mySubscriptionApi.getMySubscriptions(currentUser.userId),
recommendApi.getRecommendedCategory(currentUser.userId)
]);
setTotalFee(handleApiResponse(totalFeeResponse));
setSubscriptions(handleApiResponse(subscriptionsResponse));
setRecommendedCategory(handleApiResponse(recommendResponse));
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
fetchData();
}
}, [currentUser, navigate, authInitialized]);
if (loading) return <LoadingSpinner />;
if (error) return <ErrorMessage message={error} />;
return (
<Box>
<TotalFee
totalFee={totalFee?.totalFee}
feeLevel={totalFee?.feeLevel}
/>
<MySubscriptions subscriptions={subscriptions} />
{recommendedCategory && (
<RecommendCategory {...recommendedCategory} />
)}
</Box>
);
};
export default MainPage;
+87
View File
@@ -0,0 +1,87 @@
// src/pages/SubscriptionDetailPage.js
import React, { useEffect, useState } from 'react';
import { useParams, useNavigate } from 'react-router-dom';
import { useAuth } from '../contexts/AuthContext';
import SubscriptionDetail from '../components/subscriptions/SubscriptionDetail';
import LoadingSpinner from '../components/common/LoadingSpinner';
import ErrorMessage from '../components/common/ErrorMessage';
import { mySubscriptionApi, handleApiResponse } from '../services/api';
const SubscriptionDetailPage = () => {
const { id } = useParams();
const navigate = useNavigate();
const { currentUser, authInitialized } = useAuth();
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const [subscription, setSubscription] = useState(null);
const [isSubscribed, setIsSubscribed] = useState(false);
useEffect(() => {
if (authInitialized && !currentUser) { // login여부가 완료되고 현재 유저 정보가 없을때 로그인 페이지로 보냄
navigate('/login');
return;
}
if(authInitialized) {
const fetchData = async () => {
try {
setLoading(true);
setError(null);
// 구독 상세 정보와 사용자의 구독 목록을 동시에 조회
const [detailResponse, mySubsResponse] = await Promise.all([
mySubscriptionApi.getSubscriptionDetail(id),
mySubscriptionApi.getMySubscriptions(currentUser.userId)
]);
const detailData = handleApiResponse(detailResponse);
const mySubsData = handleApiResponse(mySubsResponse);
setSubscription(detailData);
// 현재 서비스가 사용자의 구독 목록에 있는지 확인
setIsSubscribed(mySubsData.some(sub => sub.id === parseInt(id)));
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
fetchData();
}
}, [currentUser, navigate, id, authInitialized]);
const handleSubscribe = async () => {
try {
await mySubscriptionApi.subscribe(id, currentUser.userId);
navigate('/');
} catch (err) {
setError(err.message);
}
};
const handleCancel = async () => {
try {
await mySubscriptionApi.cancelSubscription(id);
navigate('/');
} catch (err) {
setError(err.message);
}
};
if (loading) return <LoadingSpinner />;
if (error) return <ErrorMessage message={error} />;
if (!subscription) return <ErrorMessage message="구독 서비스를 찾을 수 없습니다." />;
return (
<SubscriptionDetail
{...subscription}
isSubscribed={isSubscribed}
onSubscribe={handleSubscribe}
onCancel={handleCancel}
/>
);
};
export default SubscriptionDetailPage;
+94
View File
@@ -0,0 +1,94 @@
// src/pages/SubscriptionListPage.js
import React, { useEffect, useState } from 'react';
import { useNavigate, useSearchParams } from 'react-router-dom';
import { useAuth } from '../contexts/AuthContext';
import { Box } from '@mui/material';
import CategoryList from '../components/subscriptions/CategoryList';
import SubscriptionList from '../components/subscriptions/SubscriptionList';
import LoadingSpinner from '../components/common/LoadingSpinner';
import ErrorMessage from '../components/common/ErrorMessage';
import { mySubscriptionApi, handleApiResponse } from '../services/api';
const SubscriptionListPage = () => {
const navigate = useNavigate();
const { currentUser, authInitialized } = useAuth();
const [searchParams, setSearchParams] = useSearchParams();
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const [categories, setCategories] = useState([]);
const [services, setServices] = useState([]);
const selectedCategory = searchParams.get('category') || '';
useEffect(() => {
if (authInitialized && !currentUser) { // login여부가 완료되고 현재 유저 정보가 없을때 로그인 페이지로 보냄
navigate('/login');
return;
}
if(authInitialized) {
const fetchCategories = async () => {
try {
setLoading(true);
setError(null);
const response = await mySubscriptionApi.getCategories();
const categoryList = handleApiResponse(response);
setCategories(categoryList);
// 선택된 카테고리가 없으면 첫 번째 카테고리 선택
if (!selectedCategory && categoryList.length > 0) {
setSearchParams({ category: categoryList[0].categoryId });
}
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
fetchCategories();
}
}, [currentUser, navigate, selectedCategory, setSearchParams, authInitialized]);
useEffect(() => {
if(authInitialized) {
const fetchServices = async () => {
if (!selectedCategory) return;
try {
setLoading(true);
setError(null);
const response = await mySubscriptionApi.getServicesByCategory(selectedCategory);
setServices(handleApiResponse(response));
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
fetchServices();
}
}, [selectedCategory, authInitialized]);
const handleCategoryChange = (categoryId) => {
setSearchParams({ category: categoryId });
};
if (loading) return <LoadingSpinner />;
if (error) return <ErrorMessage message={error} />;
return (
<Box>
<CategoryList
categories={categories}
selectedCategory={selectedCategory}
onCategoryChange={handleCategoryChange}
/>
<SubscriptionList services={services} />
</Box>
);
};
export default SubscriptionListPage;