diff --git a/src/Friends/Friends.tsx b/src/Friends/Friends.tsx
new file mode 100644
index 0000000..d71964d
--- /dev/null
+++ b/src/Friends/Friends.tsx
@@ -0,0 +1,31 @@
+import React from "react";
+import { Box, Heading, ChakraProvider } from "@chakra-ui/react";
+
+const Friends: React.FC = () => {
+ return (
+
+
+
+
+ 친구 관리 페이지입니다.
+
+
+
+
+ );
+};
+
+export default Friends;
diff --git a/src/Main/components/NavBar.tsx b/src/Main/components/NavBar.tsx
index a199c1c..77b0bfa 100644
--- a/src/Main/components/NavBar.tsx
+++ b/src/Main/components/NavBar.tsx
@@ -9,6 +9,7 @@ import {
IconButton,
} from "@chakra-ui/react";
import { HamburgerIcon } from "@chakra-ui/icons";
+import { useNavigate } from "react-router-dom"; // React Router의 useNavigate 가져오기
import api from "../../api/interceptor";
interface NavBarProps {
@@ -23,6 +24,7 @@ const NavBar = ({
handleLogout,
}: NavBarProps): JSX.Element => {
const [userName, setUserName] = useState("");
+ const navigate = useNavigate(); // useNavigate 훅 초기화
useEffect(() => {
const fetchUserName = async () => {
@@ -91,9 +93,13 @@ const NavBar = ({
icon={}
variant="outline"
/>
-
+
{/* 사용자 이름 표시 */}
+
+
diff --git a/src/Main/components/RecommendedContents.tsx b/src/Main/components/RecommendedContents.tsx
index 1feaf43..6810ecf 100644
--- a/src/Main/components/RecommendedContents.tsx
+++ b/src/Main/components/RecommendedContents.tsx
@@ -55,15 +55,12 @@ function RecommendedContents(): JSX.Element {
const fetchAllContents = async () => {
setIsLoading(true);
try {
- // 많이 시청한 콘텐츠 TOP 10 가져오기
const watchResponse = await api.get("/api/watch/top");
setWatchTop(watchResponse.data || []);
- // 좋아요한 콘텐츠 TOP 10 가져오기
const likeResponse = await api.get("/api/like/top");
setLikeTop(likeResponse.data || []);
- // 추천 콘텐츠 가져오기
const recommendResponse = await api.get("/api/recommend/10");
const fetchedCategories = Object.keys(categoryLabels).map(
(category) => ({
@@ -98,11 +95,11 @@ function RecommendedContents(): JSX.Element {
const renderContentGrid = (
items: { content: Content; count?: number }[],
label: string,
- countLabel?: string
+ countLabel?: "likeCount" | "watchCount"
) => (
-
+
포스터가 없습니다.
@@ -190,9 +191,11 @@ function RecommendedContents(): JSX.Element {
>
{content.title}
- {countLabel && (
+ {countLabel && count !== undefined && (
- {`${countLabel}: ${count}`}
+ {countLabel === "likeCount"
+ ? `${count}명이 좋아해요💕`
+ : `${count}명이 시청했어요✨`}
)}
@@ -200,7 +203,11 @@ function RecommendedContents(): JSX.Element {
) : (
-
+
충분한 데이터가 쌓이지 않았습니다.
)}
@@ -237,7 +244,7 @@ function RecommendedContents(): JSX.Element {
count: item.watchCount,
})),
"🔥 서비스 이용자들이 많이 시청한 콘텐츠 TOP 10",
- "시청 횟수"
+ "watchCount"
)}
{/* 좋아요한 콘텐츠 TOP 10 */}
@@ -247,7 +254,7 @@ function RecommendedContents(): JSX.Element {
count: item.likeCount,
})),
"❤️ 서비스 이용자들이 좋아한 콘텐츠 TOP 10",
- "좋아요 수"
+ "likeCount"
)}
{/* 기존 추천 콘텐츠 */}
diff --git a/src/MyPage/MyPage.tsx b/src/MyPage/MyPage.tsx
new file mode 100644
index 0000000..44b1369
--- /dev/null
+++ b/src/MyPage/MyPage.tsx
@@ -0,0 +1,397 @@
+import { useState, useEffect } from "react";
+import {
+ Box,
+ Grid,
+ Text,
+ Button,
+ IconButton,
+ Image,
+ Spinner,
+ Flex,
+ useToast,
+ Divider,
+} from "@chakra-ui/react";
+import { ArrowBackIcon, CloseIcon } from "@chakra-ui/icons";
+import { useNavigate } from "react-router-dom";
+import api from "../api/interceptor";
+import DetailModal from "../Main/components/DetailModal";
+
+interface Content {
+ id: number;
+ showId: string;
+ type: string;
+ title: string;
+ director: string;
+ cast: string;
+ country: string;
+ dateAdded: string;
+ releaseYear: string;
+ rating: string;
+ duration: string;
+ listedIn: string;
+ description: string;
+ posterPath?: string;
+}
+
+interface WatchRecord {
+ watchedDateTime: string;
+ content: Content;
+}
+
+function MyContentPage(): JSX.Element {
+ const [contents, setContents] = useState([]);
+ const [watchRecords, setWatchRecords] = useState([]);
+ const [isLoading, setIsLoading] = useState(false);
+ const [selectedContent, setSelectedContent] = useState(null);
+ const [isModalOpen, setIsModalOpen] = useState(false);
+ const [currentTab, setCurrentTab] = useState<"like" | "dislike" | "watch">(
+ "like"
+ );
+
+ const navigate = useNavigate();
+ const toast = useToast();
+
+ const fetchContents = async (type: "like" | "dislike" | "watch") => {
+ setIsLoading(true);
+ try {
+ let response;
+ if (type === "like") {
+ response = await api.get("/api/like/true");
+ setContents(
+ response.data.map((item: { content: Content }) => item.content)
+ );
+ } else if (type === "dislike") {
+ response = await api.get("/api/like/false");
+ setContents(
+ response.data.map((item: { content: Content }) => item.content)
+ );
+ } else {
+ response = await api.get("/api/watch");
+ setWatchRecords(response.data || []);
+ }
+ } catch (error) {
+ toast({
+ title: "데이터를 불러오는 중 오류 발생",
+ description: "데이터를 가져오는 데 실패했습니다.",
+ status: "error",
+ duration: 3000,
+ isClosable: true,
+ position: "top", // Toast 위치 설정
+ });
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ const deleteContent = async (id: number) => {
+ try {
+ if (currentTab === "like" || currentTab === "dislike") {
+ await api.delete(`/api/like/${id}`);
+ } else {
+ await api.delete(`/api/watch/${id}`);
+ }
+ toast({
+ title: "삭제 성공",
+ description: "콘텐츠가 성공적으로 삭제되었습니다.",
+ status: "success",
+ duration: 3000,
+ isClosable: true,
+ position: "top", // Toast 위치 설정
+ });
+ fetchContents(currentTab);
+ } catch (error) {
+ toast({
+ title: "삭제 실패",
+ description: "콘텐츠 삭제 중 오류가 발생했습니다.",
+ status: "error",
+ duration: 3000,
+ isClosable: true,
+ position: "top", // Toast 위치 설정
+ });
+ }
+ };
+
+ const handleCardClick = (content: Content) => {
+ setSelectedContent(content);
+ setIsModalOpen(true);
+ };
+
+ const closeModal = () => {
+ setSelectedContent(null);
+ setIsModalOpen(false);
+ };
+
+ const groupByDate = (records: WatchRecord[]) => {
+ const grouped: { [key: string]: Content[] } = {};
+ records.forEach((record) => {
+ const date = new Date(record.watchedDateTime).toLocaleDateString();
+ if (!grouped[date]) {
+ grouped[date] = [];
+ }
+ grouped[date].push(record.content);
+ });
+ return grouped;
+ };
+
+ const renderWatchRecords = () => {
+ const groupedRecords = groupByDate(watchRecords);
+ return Object.entries(groupedRecords).map(([date, contents]) => (
+
+
+ {date}
+
+
+ {contents.map((content) => (
+ handleCardClick(content)}
+ >
+ {content.posterPath ? (
+
+ ) : (
+
+
+ 포스터 없음
+
+
+ )}
+
+ {content.title}
+
+ }
+ position="absolute"
+ top="0.5rem"
+ right="0.5rem"
+ size="sm"
+ color="red.500"
+ bg="none" // 배경 제거
+ _hover={{ bg: "none", color: "red.700" }} // Hover 스타일
+ onClick={(e) => {
+ e.stopPropagation();
+ deleteContent(content.id);
+ }}
+ />
+
+ ))}
+
+
+
+ ));
+ };
+
+ useEffect(() => {
+ fetchContents("like");
+ }, []);
+
+ return (
+
+
+ }
+ position="absolute" // 절대 위치 지정
+ top="0" // 화면의 위쪽 경계에 맞춤
+ left="0" // 화면의 왼쪽 경계에 맞춤
+ mr={"1rem"} // 우측 마진 설정
+ onClick={() => navigate("/main")}
+ />
+
+
+
+
+
+
+
+
+ {isLoading ? (
+
+
+
+ ) : currentTab === "watch" ? (
+ renderWatchRecords()
+ ) : (
+
+ {contents.map((content) => (
+ handleCardClick(content)}
+ >
+ {content.posterPath ? (
+
+ ) : (
+
+
+ 포스터 없음
+
+
+ )}
+
+ {content.title}
+
+ }
+ position="absolute"
+ top="0.1rem"
+ right="0.1rem"
+ size="sm"
+ color="red.500"
+ bg="none" // 배경 제거
+ _hover={{ bg: "none", color: "red.700" }} // Hover 스타일
+ onClick={(e) => {
+ e.stopPropagation();
+ deleteContent(content.id);
+ }}
+ />
+
+ ))}
+
+ )}
+
+
+ {selectedContent && (
+
+ )}
+
+ );
+}
+
+export default MyContentPage;
diff --git a/src/routes/Routes.tsx b/src/routes/Routes.tsx
index d107a5e..9be8715 100644
--- a/src/routes/Routes.tsx
+++ b/src/routes/Routes.tsx
@@ -2,6 +2,8 @@ import { createBrowserRouter, RouterProvider } from "react-router-dom"; // 라
import MainPage from "../../src/Main/MainPage"; // 메인 페이지 컴포넌트 가져오기
import StartPage from "../Start/StartPage";
import RedirectPage from "../Start/Redirection";
+import MyPage from "../MyPage/MyPage";
+import Friends from "../Friends/Friends";
import { RouterPath } from "./path"; // 경로 상수 가져오기
// 라우터 정의
@@ -18,6 +20,14 @@ const router = createBrowserRouter([
path: RouterPath.main, // 메인 페이지 경로
element: , // 메인 페이지를 직접 렌더링
},
+ {
+ path: RouterPath.mypage, // 마이페이지 경로
+ element: , // 마이페이지를 직접 렌더링
+ },
+ {
+ path: RouterPath.friends, // 친구 페이지 경로
+ element: , // 친구 페이지를 직접 렌더링
+ },
]);
// 라우터를 렌더링하는 컴포넌트
diff --git a/src/routes/path.ts b/src/routes/path.ts
index d629d24..c32e2a3 100644
--- a/src/routes/path.ts
+++ b/src/routes/path.ts
@@ -2,4 +2,6 @@ export const RouterPath = {
root: "/",
main: "/main", // 메인 페이지
rediretcion: "/redirection", // 카카오 로그인 리다이렉션 페이지
+ mypage: "/mypage", // 마이페이지
+ friends: "/friends", // 친구 페이지
};