From 1ce8735981441e93d8ab78671e4f55c9819c0c5b Mon Sep 17 00:00:00 2001 From: MyungJiwoo <1206jiwoo@gmail.com> Date: Sun, 30 Jun 2024 04:52:09 +0900 Subject: [PATCH] =?UTF-8?q?[#23]=20=EB=82=98=EB=A8=B8=EC=A7=80=20api=20?= =?UTF-8?q?=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/App.tsx | 20 ++- src/components/Review.tsx | 44 ++--- src/index.tsx | 6 +- src/pages/EditReview.tsx | 254 +++++++++++++++++++++++++++++ src/pages/KakaoLogin.tsx | 29 ++-- src/pages/KakaoRedirectHandler.tsx | 19 ++- src/pages/LoginPage.tsx | 58 +++---- src/pages/MyPage.tsx | 42 +++-- src/pages/PostReview.tsx | 148 +++++++++++------ src/pages/ReadReview.tsx | 59 +++++-- src/pages/ReviewWriting.tsx | 120 ++++++++++++++ src/props/ReviewProps.tsx | 10 ++ src/styles/LoginPageStyled.tsx | 30 +++- src/styles/MyPageStyled.tsx | 13 ++ src/styles/PostReviewStyled.tsx | 6 + src/styles/ReviewStyled.tsx | 13 ++ 16 files changed, 710 insertions(+), 161 deletions(-) create mode 100644 src/pages/EditReview.tsx create mode 100644 src/pages/ReviewWriting.tsx create mode 100644 src/props/ReviewProps.tsx diff --git a/src/App.tsx b/src/App.tsx index eea0eec..c191e16 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,4 +1,4 @@ -import React, { useEffect } from "react"; +import React, { useState, useEffect } from "react"; import { BrowserRouter as Router, Route, Routes } from "react-router-dom"; import AOS from "aos"; import "aos/dist/aos.css"; @@ -15,9 +15,18 @@ import KakaoRedirectHandler from "./pages/KakaoRedirectHandler"; import MyPage from "./pages/MyPage"; import PostReview from "./pages/PostReview"; import ReadReview from "./pages/ReadReview"; +import EditReview from "./pages/EditReview"; const App: React.FC = () => { + const [isLoggedIn, setIsLoggedIn] = useState(false); + useEffect(() => { + // 로컬 스토리지에서 토큰 확인 + const token = localStorage.getItem("accessToken"); + if (token) { + setIsLoggedIn(true); + } + AOS.init(); // AOS 초기화 }, []); @@ -33,16 +42,21 @@ const App: React.FC = () => { /> } /> } /> - } /> + {/* } /> */} } /> } /> } /> - } /> + } /> + } /> } /> + : } + /> ); diff --git a/src/components/Review.tsx b/src/components/Review.tsx index 3f313f4..1a687d0 100644 --- a/src/components/Review.tsx +++ b/src/components/Review.tsx @@ -1,36 +1,42 @@ -import { Link } from "react-router-dom"; -import { Review } from "../styles/ReviewStyled"; +import { Review, StyledLink } from "../styles/ReviewStyled"; +import { ReviewProps } from "../props/ReviewProps"; -type ReviewProps = { - reviewId: string; - title: string; -}; - -const ReviewTheme = (reviewId: any) => { +const ReviewTheme: React.FC = ({ + id, + themeName, + isSuccess, + numberOfPeople, + numberOfHintsUsed, + remainingTime, + totalThemeTime, + content, +}) => { return ( - + <>
-

제목

-
-
-

매장

+

{themeName}

-

리뷰

+

+ {content.length > 35 + ? content.slice(0, 35) + "..." + : content} +

-

#탈출 성공

-

#4명

-

#힌트 3번

-

#남은 시간 10분

+

#{isSuccess ? "성공" : "실패"}

+

#{numberOfPeople}명과 함께

+

#{numberOfHintsUsed}개의 힌트

+

#테마 시간 {totalThemeTime}분

+

#남은 시간 {remainingTime}초

- +
); }; diff --git a/src/index.tsx b/src/index.tsx index 1264558..989e1aa 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -10,9 +10,9 @@ const root = ReactDOM.createRoot( root.render( // strictMode에서는 두번 렌더링 될 수 있으나, 프로덕션빌드 모드에서는 그렇지 않을것 - - - + // + + // ); // If you want to start measuring performance in your app, pass a function diff --git a/src/pages/EditReview.tsx b/src/pages/EditReview.tsx new file mode 100644 index 0000000..8f0d838 --- /dev/null +++ b/src/pages/EditReview.tsx @@ -0,0 +1,254 @@ +import React, { useEffect, useState, KeyboardEvent, ChangeEvent } from "react"; +import { Container, StartBtn } from "../styles/PostReviewStyled"; +import axios from "axios"; +import { useNavigate } from "react-router-dom"; +import { useParams } from "react-router-dom"; + +// interface reviewInfo { +// themeName: String; +// isSuccess: Boolean; +// numberOfPeople: Number; +// numberOfHintsUsed: Number; +// remainingTime_min: Number; +// remainingTime_sec: Number; +// totalThemeTime: Number; +// content: String; +// } + +const EditReview = () => { + const navigate = useNavigate(); + const onChange = (e: any) => { + console.log(e.target.value); + setFormData(e.target.value); + }; + const { reviewId } = useParams<{ reviewId: string }>(); + + const [formData, setFormData] = useState({ + themeName: "", + isSuccess: true, + numberOfPeople: 0, + numberOfHintsUsed: 0, + remainingTime_min: 0, + remainingTime_sec: 0, + remainingTime: 0, + totalThemeTime: 0, + content: "", + }); + + const onInputHandler = ( + e: ChangeEvent< + HTMLTextAreaElement | HTMLInputElement | HTMLSelectElement + > + ) => { + const { name, value, type } = e.target; + const inputValue = + type === "checkbox" && "checked" in e.target + ? e.target.checked + : value; + + setFormData((prevState) => { + let updatedState = { + ...prevState, + [name]: type === "number" ? Number(inputValue) : inputValue, + }; + + // remainingTime_min과 remainingTime_sec의 값을 기반으로 remainingTime을 업데이트 + if (name === "remainingTime_min" || name === "remainingTime_sec") { + const remainingTime_min = + name === "remainingTime_min" + ? Number(inputValue) + : prevState.remainingTime_min; + const remainingTime_sec = + name === "remainingTime_sec" + ? Number(inputValue) + : prevState.remainingTime_sec; + updatedState = { + ...updatedState, + remainingTime: remainingTime_min * 60 + remainingTime_sec, + }; + } + + if (name === "isSuccess") { + updatedState = { + ...updatedState, + isSuccess: value === "true", + }; + } + + return updatedState; + }); + }; + + const handleSubmit = (event: React.FormEvent) => { + event.preventDefault(); + + // formData에서 remainingTime_min와 remainingTime_sec를 제외한 새로운 객체 생성 + const { remainingTime_min, remainingTime_sec, ...data } = formData; + + console.log(data); + const token = localStorage.getItem("accessToken"); + if (token) { + axios + .patch( + `https://api.labyrinth30-edu.link/reviews/${reviewId}`, + data, + { + headers: { + Authorization: `Bearer ${token}`, + }, + } + ) + .then((response) => { + console.log("Response:", response.data); + navigate(`/mypage`); + }) + .catch((error) => { + console.error("Error:", error); + }); + } else { + console.error("No access token found"); + } + }; + + useEffect(() => { + const fetchPostData = async () => { + const token = localStorage.getItem("accessToken"); + + try { + const response = await axios.get( + `https://api.labyrinth30-edu.link/reviews/${reviewId}`, + { + headers: { + Authorization: `Bearer ${token}`, + }, + } + ); + console.log(response); + const postData = response.data; + + const remainingTime_min = Math.floor( + postData.remainingTime / 60 + ); + const remainingTime_sec = postData.remainingTime % 60; + + setFormData({ + themeName: postData.themeName, + isSuccess: postData.isSuccess, + numberOfPeople: postData.numberOfPeople, + numberOfHintsUsed: postData.numberOfHintsUsed, + remainingTime_min: remainingTime_min, + remainingTime_sec: remainingTime_sec, + remainingTime: postData.remainingTime, + totalThemeTime: postData.totalThemeTime, + content: postData.content, + }); + } catch (error) { + console.error("Error fetching post data:", error); + } + }; + + fetchPostData(); + }, [reviewId]); + + return ( + +
+
+ +
+

저장

+
+
+
+ +
+

테마 이름

+ +
+
+

성공 여부

+ +
+
+

인원 수

+ +
+
+

힌트 사용

+ +
+ +
+

테마 시간

+ +
+
+

남은 시간

+ + +
+ +
+

한 줄 리뷰

+ +
+
+
+ ); +}; +export default EditReview; diff --git a/src/pages/KakaoLogin.tsx b/src/pages/KakaoLogin.tsx index 45659e2..eb88a2d 100644 --- a/src/pages/KakaoLogin.tsx +++ b/src/pages/KakaoLogin.tsx @@ -1,30 +1,27 @@ import React, { useEffect } from "react"; -import loginBtn from '../image/kakao_login_img.png' +import loginBtn from "../image/kakao_login_img.png"; import axios from "axios"; import { useNavigate } from "react-router-dom"; const KakaoLogin: React.FC = () => { - const loginWithKakao = () => { - if (window.Kakao && window.Kakao.Auth) { - window.Kakao.Auth.authorize({ - redirectUri: "http://localhost:3000/auth/kakao/redirect", // 설정한 리디렉션 URI 입력 - }); + window.Kakao.Auth.authorize({ + redirectUri: "http://localhost:3000/auth/kakao/redirect", // 설정한 리디렉션 URI 입력 + }); } else { - console.error("Kakao SDK가 초기화되지 않았습니다."); + console.error("Kakao SDK가 초기화되지 않았습니다."); } - }; - - return ( + }; + + return (
- - 카카오 로그인 버튼 - -

+ + 카카오 로그인 버튼 + +

- ); + ); }; export default KakaoLogin; - diff --git a/src/pages/KakaoRedirectHandler.tsx b/src/pages/KakaoRedirectHandler.tsx index a85c0ea..1f8c289 100644 --- a/src/pages/KakaoRedirectHandler.tsx +++ b/src/pages/KakaoRedirectHandler.tsx @@ -9,19 +9,24 @@ const KakaoRedirectHandler = () => { console.log(code); const login = async () => { try { - console.log('code', code); - + console.log("로그인시작!!!!!!!!----------"); const response = await axios.post( "https://api.labyrinth30-edu.link/auth/kakao/redirect", { - code + code, } ); + console.log("로그인해볼게요---------------"); + console.log(response); localStorage.setItem("accessToken", response.data.accessToken); - navigate("/mypage"); + localStorage.setItem("username", response.data.nickname); + console.log(response); + + navigate("/mypage"); + window.location.reload(); } catch (error) { - console.log('123', error); + console.log(error); } }; @@ -30,8 +35,6 @@ const KakaoRedirectHandler = () => { login(); } }, []); - return ( -
로그인 중..
- ); + return
로그인 중..
; }; export default KakaoRedirectHandler; diff --git a/src/pages/LoginPage.tsx b/src/pages/LoginPage.tsx index a91c712..e39fd6a 100644 --- a/src/pages/LoginPage.tsx +++ b/src/pages/LoginPage.tsx @@ -3,38 +3,38 @@ import KakaoLogin from "./KakaoLogin"; // Kakao 로그인 버튼 컴포넌트 import { Container } from "../styles/LoginPageStyled"; const LoginPage: React.FC = () => { + useEffect(() => { + const initializeKakao = () => { + console.log("Kakao SDK 초기화 시도"); // 로그 추가 + if (window.Kakao && !window.Kakao.isInitialized()) { + window.Kakao.init("458c6e177d255cd6277b97d9a180e2b6"); // 여기에 Kakao 앱의 JavaScript 키 입력 + console.log("Kakao SDK 초기화 완료"); // 초기화 완료 로그 + } else if (window.Kakao) { + console.log("Kakao SDK 이미 초기화됨"); // 이미 초기화된 경우 로그 + } else { + console.error("Kakao 객체를 찾을 수 없음"); // Kakao 객체가 없는 경우 로그 + } + }; - useEffect(() => { - const initializeKakao = () => { - console.log("Kakao SDK 초기화 시도"); // 로그 추가 - if (window.Kakao && !window.Kakao.isInitialized()) { - window.Kakao.init("458c6e177d255cd6277b97d9a180e2b6"); // 여기에 Kakao 앱의 JavaScript 키 입력 - console.log("Kakao SDK 초기화 완료"); // 초기화 완료 로그 - } else if (window.Kakao) { - console.log("Kakao SDK 이미 초기화됨"); // 이미 초기화된 경우 로그 - } else { - console.error("Kakao 객체를 찾을 수 없음"); // Kakao 객체가 없는 경우 로그 - } - }; + const script = document.createElement("script"); + script.src = "https://developers.kakao.com/sdk/js/kakao.min.js"; + script.async = true; + script.onload = initializeKakao; + document.head.appendChild(script); - const script = document.createElement('script'); - script.src = "https://developers.kakao.com/sdk/js/kakao.min.js"; - script.async = true; - script.onload = initializeKakao; - document.head.appendChild(script); + return () => { + document.head.removeChild(script); + }; + }, []); - return () => { - document.head.removeChild(script); - }; - }, []); - - return ( - -
- -
-
- ); + return ( + +
+

잠깐! 로그인 후 이용해주세요.

+ +
+
+ ); }; export default LoginPage; diff --git a/src/pages/MyPage.tsx b/src/pages/MyPage.tsx index 788f4e6..01005b5 100644 --- a/src/pages/MyPage.tsx +++ b/src/pages/MyPage.tsx @@ -1,34 +1,37 @@ import React, { useEffect, useState, KeyboardEvent, ChangeEvent } from "react"; import Review from "../components/Review"; -import { Container, StartBtn } from "../styles/MyPageStyled"; +import { Container, StartBtn, StyledLink } from "../styles/MyPageStyled"; import Pagination from "../components/BasicPagination"; import axios from "axios"; import { Link } from "react-router-dom"; +import { ReviewProps } from "../props/ReviewProps"; const MyPage = () => { const [take, setTake] = useState(1); // 총 페이지 수 const [page, setPage] = useState(1); // 현재 페이지 - const [reviews, setReviews] = useState(null); + // const [reviews, setReviews] = useState(null); const [accessToken, setAccessToken] = useState(null); + const [reviewList, setReviewList] = useState([]); useEffect(() => { - const token = localStorage.getItem('accessToken'); + const token = localStorage.getItem("accessToken"); if (token) { setAccessToken(token); } - console.log(token); }, []); useEffect(() => { const reviewList = async () => { - const token = localStorage.getItem('accessToken'); + const token = localStorage.getItem("accessToken"); try { - const response = await axios.get("https://api.labyrinth30-edu.link/reviews", + const response = await axios.get( + "https://api.labyrinth30-edu.link/reviews", { - headers: {'Authorization': `Bearer ${token}`} + headers: { Authorization: `Bearer ${token}` }, } ); - setReviews(response.data); + console.log(response.data); + setReviewList(response.data); } catch (error) { console.log(error); } @@ -48,22 +51,29 @@ const MyPage = () => {
당신을 위한 방,
-
username님의 방탈출 일지
+
나만의 방탈출 일지
- +

리뷰 작성

- +
- - - - - + {reviewList?.map((review) => ( + + ))}
diff --git a/src/pages/PostReview.tsx b/src/pages/PostReview.tsx index c23ffdb..841fd47 100644 --- a/src/pages/PostReview.tsx +++ b/src/pages/PostReview.tsx @@ -8,84 +8,102 @@ interface reviewInfo { isSuccess: Boolean; numberOfPeople: Number; numberOfHintsUsed: Number; - remainingTime: Number; + remainingTime_min: Number; + remainingTime_sec: Number; totalThemeTime: Number; content: String; } const PostReview = () => { - // const onInputHandler = ( - // _e: ChangeEvent - // ) => { - // // const value = e.target.value.trim(); // 입력값 앞뒤 공백 제거 - // // if (e.target.name === "title") { - // // setTitle(value); - // // } else if (e.target.name === "content") { - // // setContent(value); - // // setInputCount(value.length); - // // } else if (e.target.name === "tags") { - // // const tagArray = value - // // .split("#") - // // .map((tag) => tag.trim()) - // // .filter((tag) => tag !== ""); - // // setTags(tagArray); - // // } - // }; - const navigate = useNavigate(); const onChange = (e: any) => { console.log(e.target.value); setFormData(e.target.value); - } + }; const [formData, setFormData] = useState({ themeName: "", isSuccess: true, numberOfPeople: 0, numberOfHintsUsed: 0, + remainingTime_min: 0, + remainingTime_sec: 0, remainingTime: 0, totalThemeTime: 0, content: "", }); - const onInputHandler = (e: ChangeEvent) => { + const onInputHandler = ( + e: ChangeEvent< + HTMLTextAreaElement | HTMLInputElement | HTMLSelectElement + > + ) => { const { name, value, type } = e.target; - const inputValue = type === "checkbox" && "checked" in e.target ? e.target.checked : value; - setFormData((prevState) => ({ - ...prevState, - [name]: type === "number" ? Number(inputValue) : inputValue, - })); + const inputValue = + type === "checkbox" && "checked" in e.target + ? e.target.checked + : value; + + setFormData((prevState) => { + let updatedState = { + ...prevState, + [name]: type === "number" ? Number(inputValue) : inputValue, + }; + + // remainingTime_min과 remainingTime_sec의 값을 기반으로 remainingTime을 업데이트 + if (name === "remainingTime_min" || name === "remainingTime_sec") { + const remainingTime_min = + name === "remainingTime_min" + ? Number(inputValue) + : prevState.remainingTime_min; + const remainingTime_sec = + name === "remainingTime_sec" + ? Number(inputValue) + : prevState.remainingTime_sec; + updatedState = { + ...updatedState, + remainingTime: remainingTime_min * 60 + remainingTime_sec, + }; + } + + if (name === "isSuccess") { + updatedState = { + ...updatedState, + isSuccess: value === "true", + }; + } + + return updatedState; + }); }; const handleSubmit = (event: React.FormEvent) => { - event.preventDefault(); - - const data = { - ...formData, - }; - console.log(formData); - const token = localStorage.getItem('accessToken'); + + // formData에서 remainingTime_min와 remainingTime_sec를 제외한 새로운 객체 생성 + const { remainingTime_min, remainingTime_sec, ...data } = formData; + + console.log(data); + const token = localStorage.getItem("accessToken"); if (token) { - axios.post('https://api.labyrinth30-edu.link/reviews', data, { - headers: { - 'Authorization': `Bearer ${token}`, - 'Content-Type': 'application/json' - } - }) - .then(response => { - console.log('Response:', response.data); - navigate('/reviews/${reviewId}'); + axios + .post("https://api.labyrinth30-edu.link/reviews", data, { + headers: { + Authorization: `Bearer ${token}`, + "Content-Type": "application/json", + }, + }) + .then((response) => { + console.log("Response:", response.data); + navigate(`/mypage`); }) - .catch(error => { - console.error('Error:', error); + .catch((error) => { + console.error("Error:", error); }); } else { - console.error('No access token found'); + console.error("No access token found"); } - }; - return ( @@ -109,9 +127,13 @@ const PostReview = () => {

성공 여부

- + +
@@ -134,15 +156,35 @@ const PostReview = () => { className="postTagsBox" >
+ +
+

테마 시간

+ +

남은 시간

+
+

한 줄 리뷰