diff --git a/package-lock.json b/package-lock.json index 5669f0c..6561c66 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "@fortawesome/react-fontawesome": "^0.2.2", "@mui/icons-material": "^6.1.4", "@mui/material": "^6.1.4", + "@tanstack/react-query": "^5.59.16", "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", @@ -4074,6 +4075,30 @@ "tslib": "^2.6.2" } }, + "node_modules/@tanstack/query-core": { + "version": "5.59.16", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.59.16.tgz", + "integrity": "sha512-crHn+G3ltqb5JG0oUv6q+PMz1m1YkjpASrXTU+sYWW9pLk0t2GybUHNRqYPZWhxgjPaVGC4yp92gSFEJgYEsPw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-query": { + "version": "5.59.16", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.59.16.tgz", + "integrity": "sha512-MuyWheG47h6ERd4PKQ6V8gDyBu3ThNG22e1fRVwvq6ap3EqsFhyuxCAwhNP/03m/mLg+DAb0upgbPaX6VB+CkQ==", + "dependencies": { + "@tanstack/query-core": "5.59.16" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^18 || ^19" + } + }, "node_modules/@testing-library/dom": { "version": "10.4.0", "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz", @@ -23809,6 +23834,19 @@ "tslib": "^2.6.2" } }, + "@tanstack/query-core": { + "version": "5.59.16", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.59.16.tgz", + "integrity": "sha512-crHn+G3ltqb5JG0oUv6q+PMz1m1YkjpASrXTU+sYWW9pLk0t2GybUHNRqYPZWhxgjPaVGC4yp92gSFEJgYEsPw==" + }, + "@tanstack/react-query": { + "version": "5.59.16", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.59.16.tgz", + "integrity": "sha512-MuyWheG47h6ERd4PKQ6V8gDyBu3ThNG22e1fRVwvq6ap3EqsFhyuxCAwhNP/03m/mLg+DAb0upgbPaX6VB+CkQ==", + "requires": { + "@tanstack/query-core": "5.59.16" + } + }, "@testing-library/dom": { "version": "10.4.0", "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz", diff --git a/package.json b/package.json index 34f481d..0835a85 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "@fortawesome/react-fontawesome": "^0.2.2", "@mui/icons-material": "^6.1.4", "@mui/material": "^6.1.4", + "@tanstack/react-query": "^5.59.16", "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", diff --git a/src/App.js b/src/App.js index ca5a0aa..28848f8 100644 --- a/src/App.js +++ b/src/App.js @@ -1,9 +1,14 @@ -import './App.css'; -import Routers from './Routers'; +import "./App.css"; +import Routers from "./Routers"; +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; + +const queryClient = new QueryClient(); function App() { return ( - + + + ); } diff --git a/src/api/main.js b/src/api/main.js index 90a332c..f11b91b 100644 --- a/src/api/main.js +++ b/src/api/main.js @@ -28,3 +28,12 @@ export const getTILDetail = async ({ tilId }) => { export const getChallengeDetail = async ({ challengeId }) => { return await client.get(`/api/challenges/${challengeId}`); }; + +// 유저 정보 조회 +export const getUserInfo = async (userToken) => { + return await client.get(`/api/member`, { + headers: { + Authorization: `Bearer ${userToken}`, + }, + }); +}; diff --git a/src/components/common/Header.jsx b/src/components/common/Header.jsx index 4413e11..aeb6125 100644 --- a/src/components/common/Header.jsx +++ b/src/components/common/Header.jsx @@ -9,7 +9,8 @@ import { } from "@fortawesome/free-solid-svg-icons"; import { useAuth } from "../../hooks/useAuth"; import { useNavigate } from "react-router-dom"; -import { removeCookie } from "../../api/cookie"; +import { getCookie, removeCookie } from "../../api/cookie"; +import { useGetUserInfo } from "../../hooks/useGetUserInfo"; const Header = () => { const { isLoggedIn } = useAuth(); @@ -17,6 +18,7 @@ const Header = () => { const [modal, setModal] = React.useState(false); const [anchorEl, setAnchorEl] = React.useState(null); const menuOpen = Boolean(anchorEl); + const { data, refetch } = useGetUserInfo(getCookie("accessToken")); // 프로필 메뉴 클릭 const handleMenuClick = (event) => { @@ -50,6 +52,7 @@ const Header = () => { handleMenuClose(); removeCookie("accessToken"); navigate("/"); + refetch(); }; return ( @@ -69,10 +72,10 @@ const Header = () => { - {isLoggedIn ? ( + {data ? (
profile diff --git a/src/hooks/useGetUserInfo.jsx b/src/hooks/useGetUserInfo.jsx new file mode 100644 index 0000000..96e1db8 --- /dev/null +++ b/src/hooks/useGetUserInfo.jsx @@ -0,0 +1,22 @@ +import { useQuery } from "@tanstack/react-query"; +import { getUserInfo } from "../api/main"; + +export const useGetUserInfo = (token) => { + const queryKey = ["userInfo", token]; + + const queryFn = async () => { + if (token) { + const response = await getUserInfo(token); + return response.data; + } + throw new Error("Missing parameters"); + }; + + const { isLoading, isError, data, error, isSuccess, refetch } = useQuery({ + queryKey, + queryFn, + enabled: !!token, + }); + + return { isLoading, isError, data, error, isSuccess, refetch }; +}; diff --git a/src/pages/MainPage.jsx b/src/pages/MainPage.jsx index fe94c4b..b810a86 100644 --- a/src/pages/MainPage.jsx +++ b/src/pages/MainPage.jsx @@ -19,10 +19,10 @@ const MainPage = () => { }; useEffect(() => { - setCookie( - "accessToken", - "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzb2NpYWwiOiJrYWthbyIsImVtYWlsIjoieW1qMDcxNjhAbmF2ZXIuY29tIiwicHJvZmlsZUltYWdlIjoiaHR0cDovL2sua2FrYW9jZG4ubmV0L2RuL2RmQVd6Zy9idHNKbjdtWWZmby9jN2FncWtvY1lONDVmN3hIQjIxc3ZLL2ltZ182NDB4NjQwLmpwZyIsInJvbGUiOiJNRU1CRVIiLCJuYW1lIjoi7J207Zqo7JuQIiwiaWF0IjoxNzI5NzUxMDg1LCJleHAiOjE3Mjk3ODcwODV9.hvJm84rtzssOWMjVbF3SCjH4rkFRqCIVikIxQc_36_s" - ); + // setCookie( + // "accessToken", + // "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzb2NpYWwiOiJrYWthbyIsImVtYWlsIjoieW1qMDcxNjhAbmF2ZXIuY29tIiwicHJvZmlsZUltYWdlIjoiaHR0cDovL2sua2FrYW9jZG4ubmV0L2RuL2RmQVd6Zy9idHNKbjdtWWZmby9jN2FncWtvY1lONDVmN3hIQjIxc3ZLL2ltZ182NDB4NjQwLmpwZyIsInJvbGUiOiJNRU1CRVIiLCJuYW1lIjoi7J207Zqo7JuQIiwiaWF0IjoxNzI5NzUxMDg1LCJleHAiOjE3Mjk3ODcwODV9.hvJm84rtzssOWMjVbF3SCjH4rkFRqCIVikIxQc_36_s" + // ); getTIL({ pageNumber: 0 }).then((res) => setPosts(res.data.content)); getChallenges({ pageSize: 0 }).then((res) => setChallenges(res.data.content) diff --git a/src/pages/MyPage.jsx b/src/pages/MyPage.jsx index e69de29..b730398 100644 --- a/src/pages/MyPage.jsx +++ b/src/pages/MyPage.jsx @@ -0,0 +1,175 @@ +import React, { useEffect, useState } from "react"; +import PageContainer from "../components/common/PageContainer"; +import ProfileSection from "../components/mypage/ProfileSection"; +import SubTabBar from "../components/mypage/SubTabBar"; +import MyCard from "../components/mypage/MyCard"; +import SimpleBarChart from "../components/mypage/SimpleBarChart"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; +import { + faCalendar, + faHeart, + faPenToSquare, + faThumbsDown, + faThumbsUp, +} from "@fortawesome/free-solid-svg-icons"; +import CountCard from "../components/mypage/CountCard"; +import { Button, Chip } from "@mui/material"; +import characterImg from "../images/main-character.png"; +import Header from "../components/common/Header"; +import { getLikeTILs, getMyTILs, getStatistics } from "../api/mypage"; +import { useNavigate } from "react-router-dom"; +import { useGetUserInfo } from "../hooks/useGetUserInfo"; +import { getCookie } from "../api/cookie"; + +const MyPage = () => { + const navigate = useNavigate(); + const token = getCookie("accessToken"); + const { data } = useGetUserInfo(token); + const [selectedTab, setSelectedTab] = useState("write"); + const [myPosts, setMyPosts] = useState([]); + const [likedPosts, setLikedPosts] = useState([]); + const [statistics, setStatistics] = useState(null); + + const handleChange = (event, newValue) => { + setSelectedTab(newValue); + }; + + useEffect(() => { + getMyTILs({ pageSize: 0 }).then((res) => setMyPosts(res.data.content)); + getLikeTILs({ pageSize: 0 }).then((res) => setLikedPosts(res.data.content)); + getStatistics().then((res) => setStatistics(res.data)); + }, []); + + return ( + <> +
+ + +
+ + {selectedTab === "write" && ( +
+ {myPosts?.map((item) => ( + navigate(`/posts/${item.tilId}`)} + /> + ))} +
+ )} + {selectedTab === "like" && ( +
+ {likedPosts?.map((item) => ( + navigate(`/posts/${item.tilId}`)} + /> + ))} +
+ )} + {selectedTab === "statistic" && ( +
+
+ + + +
+
+
+
+ +
+
+
AI 분석
+
+ {statistics?.analysisResult} +
+
+ } + label={statistics?.mostValue} + sx={{ + padding: "8px", + }} + /> + } + label={statistics?.leastValue} + sx={{ + padding: "8px", + }} + /> +
+
+
+
+
+ +
+ + {/* 개인 맞춤형 문제 추천 */} +
+ character +
+
+ {data.name}님은 {statistics?.leastValue} 알고리즘 보완이 + 필요해요! +
+
+ {statistics?.leastValue} 핵심 문제를 추천해드릴게요! 문제를 + 통해 코딩마스터가 되어보아요^^ +
+
+
+
+ 오늘 추천 문제는 백준의 {statistics?.aiRecommendDto.title} + 입니다. +
+ +
+
+
+ )} +
+ + ); +}; + +export default MyPage;