From f9e82e11c8d995d40d7939c1c61eb96e81f9474d Mon Sep 17 00:00:00 2001 From: taejun0 Date: Sat, 2 Nov 2024 21:57:43 +0900 Subject: [PATCH] =?UTF-8?q?feature:=20api=20=EC=97=B0=EA=B2=B0=20=EB=B0=8F?= =?UTF-8?q?=20=EB=A1=9C=EA=B7=B8=EC=9D=B8,=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=ED=9A=8C=EB=A9=B4=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Letters/styled.js | 16 ++--- src/constant/routeConstants.js | 4 +- src/hooks/useLetterPost.js | 53 +++++++++++++++ src/hooks/useLogin.js | 37 +++++++++++ src/hooks/useSignup.js | 46 +++++++++++++ src/layouts/DefaultLayout.jsx | 2 +- src/pages/LetterMakePage/LetterMakePage.jsx | 38 +++-------- src/pages/LetterMakePage/styled.js | 2 +- src/pages/LoginPage/LoginPage.jsx | 45 +++++++++++-- src/pages/LoginPage/styled.js | 50 +++++++++++--- src/pages/homepage/HomePage.jsx | 2 +- src/pages/introPage/IntroPage.jsx | 15 +++++ src/pages/introPage/styled.js | 33 ++++++++++ src/pages/signupPage/SignupPage.jsx | 72 +++++++++++++++++++++ src/pages/signupPage/styled.js | 61 +++++++++++++++++ src/routes/router.jsx | 12 +++- src/services/AuthService.js | 0 src/services/letterService.js | 15 +++++ src/services/loginService.js | 15 +++++ src/services/signupService.js | 15 +++++ 20 files changed, 477 insertions(+), 56 deletions(-) create mode 100644 src/hooks/useLetterPost.js create mode 100644 src/hooks/useLogin.js create mode 100644 src/hooks/useSignup.js create mode 100644 src/pages/introPage/IntroPage.jsx create mode 100644 src/pages/introPage/styled.js create mode 100644 src/pages/signupPage/SignupPage.jsx create mode 100644 src/pages/signupPage/styled.js delete mode 100644 src/services/AuthService.js create mode 100644 src/services/letterService.js create mode 100644 src/services/loginService.js create mode 100644 src/services/signupService.js diff --git a/src/components/Letters/styled.js b/src/components/Letters/styled.js index f84947a..16c6d85 100644 --- a/src/components/Letters/styled.js +++ b/src/components/Letters/styled.js @@ -63,6 +63,14 @@ export const Section = styled.div` gap: 20px; `; +export const Rowing = styled.div` + display: flex; + flex-direction: row; + width: 60%; + gap: 4px; + justify-content: space-around; +`; + export const MainText = styled.div` color: ${({theme}) => theme.colors.black}; @@ -84,14 +92,6 @@ export const Buttons = styled.div` cursor: pointer; `; -export const Rowing = styled.div` - display: flex; - flex-direction: row; - width: 60%; - gap: 4px; - justify-content: space-around; -`; - export const CustomDatePicker = styled(DatePicker)` color: ${({theme}) => theme.colors.black}; diff --git a/src/constant/routeConstants.js b/src/constant/routeConstants.js index 23f1cdc..a282393 100644 --- a/src/constant/routeConstants.js +++ b/src/constant/routeConstants.js @@ -1,6 +1,8 @@ // src/constants/routeConstants.js export const ROUTE_PATHS = { - LOGIN: "/", + INTRO: "/", + LOGIN: "/login", + SIGNUP: "/signup", HOME: "/homepage", ABOUT: "/about", LETTER: "/letter", diff --git a/src/hooks/useLetterPost.js b/src/hooks/useLetterPost.js new file mode 100644 index 0000000..7d3bca4 --- /dev/null +++ b/src/hooks/useLetterPost.js @@ -0,0 +1,53 @@ +import { useState } from "react"; +import { useNavigate } from "react-router-dom"; +import { toast } from "react-toastify"; +import { letterService } from "@services/letterService"; + +export const useLetterPost = () => { + const [letterContent, setLetterContent] = useState(""); + const [receiverInfo, setReceiverInfo] = useState({ + year: "", + monthDay: "", + time: "", + phoneNumber: "", + }); + + const navigate = useNavigate(); + + const handleSubmit = async () => { + if ( + !letterContent || + !receiverInfo.year || + !receiverInfo.monthDay || + !receiverInfo.time || + !receiverInfo.phoneNumber + ) { + toast.error("모든 필수 정보를 입력해 주세요!"); + return; + } + + const sendAt = `${receiverInfo.year}-${receiverInfo.monthDay}T${receiverInfo.time}`; + const letterData = { + letter: letterContent, + receivePhone: receiverInfo.phoneNumber, + sendAt: sendAt, + }; + + try { + const response = await letterService(letterData); + toast.success("편지가 성공적으로 제출되었습니다!"); + navigate("/inventory"); + } catch (error) { + toast.error("편지 제출에 실패했습니다. 다시 시도해 주세요."); + console.error("Letter submission error:", error); + } + }; + + return { + letterContent, + setLetterContent, + receiverInfo, + setReceiverInfo, + handleSubmit, + }; +}; diff --git a/src/hooks/useLogin.js b/src/hooks/useLogin.js new file mode 100644 index 0000000..8f59eee --- /dev/null +++ b/src/hooks/useLogin.js @@ -0,0 +1,37 @@ +import { useState } from "react"; +import { loginService } from "@services/loginService"; +import { useNavigate } from "react-router-dom"; + +export const useLogin = () => { + const [loginData, setLoginData] = useState({ + phone: "", + nickname: "", + password: "", + }); + + const navigate = useNavigate(); + + const handleChange = (e) => { + const { name, value } = e.target; + setLoginData((prevData) => ({ + ...prevData, + [name]: value, + })); + }; + + const handleSubmit = async () => { + try { + const response = await loginService(loginData); + console.log("Login successful:", response); + navigate("/homepage"); + } catch (error) { + console.error("Login error:", error); + } + }; + + return { + loginData, + handleChange, + handleSubmit, + }; +}; diff --git a/src/hooks/useSignup.js b/src/hooks/useSignup.js new file mode 100644 index 0000000..99400cb --- /dev/null +++ b/src/hooks/useSignup.js @@ -0,0 +1,46 @@ +import { useState } from "react"; +import { signupService } from "@services/signupService"; +import { useNavigate } from "react-router-dom"; + +export const useSignup = () => { + const [signupData, setSignupData] = useState({ + nickname: "", + password: "", + confirmPassword: "", + phone: "", + }); + + const navigate = useNavigate(); + + const handleChange = (e) => { + const { name, value } = e.target; + setSignupData((prevData) => ({ + ...prevData, + [name]: value, + })); + }; + + const handleSubmit = async () => { + if (signupData.password !== signupData.confirmPassword) { + alert("비밀번호가 일치하지 않습니다."); + return; + } + + const { nickname, password, phone } = signupData; + const signupPayload = { nickname, password, phone }; + + try { + const response = await signupService(signupPayload); + console.log("회원 가입 성공:", response); + navigate("/homepage"); + } catch (error) { + console.error("회원 가입 에러:", error); + } + }; + + return { + signupData, + handleChange, + handleSubmit, + }; +}; diff --git a/src/layouts/DefaultLayout.jsx b/src/layouts/DefaultLayout.jsx index a134798..faa3cd8 100644 --- a/src/layouts/DefaultLayout.jsx +++ b/src/layouts/DefaultLayout.jsx @@ -5,7 +5,7 @@ import { Footer } from "@components/layout/footer/Footer"; const DefaultLayout = () => { const location = useLocation(); - const isRoot = location.pathname === "/"; + const isRoot = location.pathname === "/" || location.pathname === "/login" || location.pathname === "/signup"; return ( <> diff --git a/src/pages/LetterMakePage/LetterMakePage.jsx b/src/pages/LetterMakePage/LetterMakePage.jsx index 01c9e31..36b914d 100644 --- a/src/pages/LetterMakePage/LetterMakePage.jsx +++ b/src/pages/LetterMakePage/LetterMakePage.jsx @@ -1,48 +1,30 @@ import React, { useState } from "react"; import * as S from "./styled"; -import { useNavigate } from "react-router-dom"; import { LetterOne } from "@components/Letters/LetterOne"; import { LetterTwo } from "@components/Letters/LetterTwo"; import ToastOne from "@components/common/Toasts/ToastOne"; -import { toast } from "react-toastify"; +import { useLetterPost } from "@hooks/useLetterPost"; export const LetterMakePage = () => { - const [currentStep, setCurrentStep] = useState(0); - const [letterContent, setLetterContent] = useState(""); // 편지 내용 상태 - const [receiverInfo, setReceiverInfo] = useState({ - year: "", - monthDay: "", - time: "", - phoneNumber: "", - }); + const [currentStep, setCurrentStep] = useState(1); + const { + letterContent, + setLetterContent, + receiverInfo, + setReceiverInfo, + handleSubmit, + } = useLetterPost(); const [selectedYear, setSelectedYear] = useState(null); const [selectedDate, setSelectedDate] = useState(null); const [selectedTime, setSelectedTime] = useState(null); - const navigate = useNavigate(); - const handleNextStep = () => setCurrentStep((prev) => prev + 1); const handlePrevStep = () => setCurrentStep((prev) => prev - 1); - const handleSubmit = () => { - if (!letterContent || !receiverInfo.year || !receiverInfo.monthDay || !receiverInfo.time || !receiverInfo.phoneNumber) { - toast.error("모든 필수 정보를 입력해 주세요!"); // 토스트 알림 표시 - return; - } - - const combinedDateTime = `${receiverInfo.year}-${receiverInfo.monthDay}T${receiverInfo.time}`; - console.log("Submitting letter data:", letterContent); - console.log("Receiver Info:", { - ...receiverInfo, - dateTime: combinedDateTime, - }); - navigate("/inventory"); - }; - return ( - setCurrentStep(1)}>편지 작성하기 + 편지 작성하기 {currentStep === 1 && ( { + const { loginData, handleChange, handleSubmit } = useLogin(); + const navigate = useNavigate(); + return ( - 내일의 편지 - 로그인 + + 본인의 별명을 입력해 주세요. + + + + + + 본인의 비밀번호를 입력해 주세요. + + + + + + navigate(-1)}>돌아가기 + 완료 + - ) -} + ); +}; -export default LoginPage; \ No newline at end of file +export default LoginPage; diff --git a/src/pages/LoginPage/styled.js b/src/pages/LoginPage/styled.js index 98834b9..6148a13 100644 --- a/src/pages/LoginPage/styled.js +++ b/src/pages/LoginPage/styled.js @@ -10,22 +10,52 @@ export const Wrapper = styled.div` gap: 30px; `; +export const Section = styled.div` + display: flex; + flex-direction: column; + width: 100%; + justify-content: center; + align-items: center; + gap: 20px; +`; + export const MainText = styled.div` - display: flex; + color: ${({theme}) => theme.colors.black}; font-family: ${({theme}) => theme.fonts.GowunDodum["font-family"]}; - color: ${({theme}) => theme.colors.black}; - font-size: 40px; - font-weight: 400; + font-size: 18px; font-style: normal; + font-weight: 300; + line-height: 1.4; `; -export const Login = styled.div` +export const Rowing = styled.div` display: flex; + flex-direction: row; + width: 60%; + gap: 4px; + justify-content: space-around; +`; + + +export const Buttons = styled.div` + display: flex; + position: absolute; + width: 100%; + bottom: 0; + padding: 15px; + justify-content: space-between; + + cursor: pointer; +`; + +export const Button = styled.button` + cursor: pointer; - font-family: ${({theme}) => theme.fonts.GowunDodum["font-family"]}; color: ${({theme}) => theme.colors.black}; - font-size: 30px; - font-weight: 400; - font-style: normal -`; \ No newline at end of file + + font-family: ${({theme}) => theme.fonts.GowunDodum["font-family"]}; + font-size: 14px; + font-style: normal; + font-weight: 700; +`; diff --git a/src/pages/homepage/HomePage.jsx b/src/pages/homepage/HomePage.jsx index 741f5e5..8dbd4e2 100644 --- a/src/pages/homepage/HomePage.jsx +++ b/src/pages/homepage/HomePage.jsx @@ -3,7 +3,7 @@ import * as S from "./styled"; export const HomePage = () => { return ( - 타임캡슐 -- 설명하기 + ) } diff --git a/src/pages/introPage/IntroPage.jsx b/src/pages/introPage/IntroPage.jsx new file mode 100644 index 0000000..83cd979 --- /dev/null +++ b/src/pages/introPage/IntroPage.jsx @@ -0,0 +1,15 @@ +import * as S from "./styled"; +import { useNavigate } from "react-router-dom"; + +export const IntroPage = () => { + const navigate = useNavigate(); + return ( + + 내일의 편지 + navigate("/login")}>로그인 + navigate("/signup")}>회원가입 + + ) +} + +export default IntroPage; \ No newline at end of file diff --git a/src/pages/introPage/styled.js b/src/pages/introPage/styled.js new file mode 100644 index 0000000..79ec881 --- /dev/null +++ b/src/pages/introPage/styled.js @@ -0,0 +1,33 @@ +import styled from "styled-components"; + +export const Wrapper = styled.div` + display: flex; + position: relative; + height: 100vh; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 50px; +`; + +export const MainText = styled.div` + display: flex; + + font-family: ${({theme}) => theme.fonts.GowunDodum["font-family"]}; + color: ${({theme}) => theme.colors.black}; + font-size: 40px; + font-weight: 400; + font-style: normal; +`; + +export const Login = styled.div` + display: flex; + + cursor: pointer; + + font-family: ${({theme}) => theme.fonts.GowunDodum["font-family"]}; + color: ${({theme}) => theme.colors.black}; + font-size: 20px; + font-weight: 400; + font-style: normal +`; \ No newline at end of file diff --git a/src/pages/signupPage/SignupPage.jsx b/src/pages/signupPage/SignupPage.jsx new file mode 100644 index 0000000..5f270a1 --- /dev/null +++ b/src/pages/signupPage/SignupPage.jsx @@ -0,0 +1,72 @@ +import React from "react"; +import * as S from "./styled"; +import { useNavigate } from "react-router-dom"; +import { useSignup } from "@hooks/useSignup"; + +export const SignupPage = () => { + const { signupData, handleChange, handleSubmit } = useSignup(); + const navigate = useNavigate(); + + return ( + + + 사용할 별명을 입력해 주세요. + + + + + + 사용할 비밀번호를 입력해 주세요. + + + + + + 비밀번호를 다시 입력해 주세요. + + + + + + 핸드폰 번호 (선택사항) + + + + + + navigate(-1)}>돌아가기 + 완료 + + + ); +}; + +export default SignupPage; diff --git a/src/pages/signupPage/styled.js b/src/pages/signupPage/styled.js new file mode 100644 index 0000000..6148a13 --- /dev/null +++ b/src/pages/signupPage/styled.js @@ -0,0 +1,61 @@ +import styled from "styled-components"; + +export const Wrapper = styled.div` + display: flex; + position: relative; + height: 100vh; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 30px; +`; + +export const Section = styled.div` + display: flex; + flex-direction: column; + width: 100%; + justify-content: center; + align-items: center; + gap: 20px; +`; + +export const MainText = styled.div` + color: ${({theme}) => theme.colors.black}; + + font-family: ${({theme}) => theme.fonts.GowunDodum["font-family"]}; + font-size: 18px; + font-style: normal; + font-weight: 300; + line-height: 1.4; +`; + +export const Rowing = styled.div` + display: flex; + flex-direction: row; + width: 60%; + gap: 4px; + justify-content: space-around; +`; + + +export const Buttons = styled.div` + display: flex; + position: absolute; + width: 100%; + bottom: 0; + padding: 15px; + justify-content: space-between; + + cursor: pointer; +`; + +export const Button = styled.button` + cursor: pointer; + + color: ${({theme}) => theme.colors.black}; + + font-family: ${({theme}) => theme.fonts.GowunDodum["font-family"]}; + font-size: 14px; + font-style: normal; + font-weight: 700; +`; diff --git a/src/routes/router.jsx b/src/routes/router.jsx index 30edc2d..9af0c7b 100644 --- a/src/routes/router.jsx +++ b/src/routes/router.jsx @@ -8,16 +8,26 @@ import { NotFoundLayout } from "@layouts/NotFoundLayout"; import { LetterMakePage } from "@pages/LetterMakePage/LetterMakePage"; import { LetterInventoryPage } from "@pages/LetterInventoryPage/LetterInventoryPage"; import { LoginPage } from "@pages/LoginPage/LoginPage"; +import { IntroPage } from "@pages/introPage/IntroPage"; +import { SignupPage } from "@pages/signupPage/SignupPage"; export const router = createBrowserRouter([ { - path: ROUTE_PATHS.LOGIN, + path: ROUTE_PATHS.INTRO, element: , children: [ + { + path: ROUTE_PATHS.INTRO, + element: + }, { path: ROUTE_PATHS.LOGIN, element: , }, + { + path: ROUTE_PATHS.SIGNUP, + element: , + }, { path: ROUTE_PATHS.HOME, element: , diff --git a/src/services/AuthService.js b/src/services/AuthService.js deleted file mode 100644 index e69de29..0000000 diff --git a/src/services/letterService.js b/src/services/letterService.js new file mode 100644 index 0000000..9916a62 --- /dev/null +++ b/src/services/letterService.js @@ -0,0 +1,15 @@ +import { instance } from "./instance"; + +export const letterService = async (letterData) => { + try { + const response = await instance.post("/letter/add", letterData, { + headers: { + "Content-Type": "application/json", + }, + }); + return response.data; + } catch (error) { + console.error("Failed to submit letter", error); + throw error; + } +}; diff --git a/src/services/loginService.js b/src/services/loginService.js new file mode 100644 index 0000000..121a5cc --- /dev/null +++ b/src/services/loginService.js @@ -0,0 +1,15 @@ +import { instance } from "./instance"; + +export const loginService = async (loginData) => { + try{ + const response = await instance.post("/login", loginData, { + headers: { + "Content-Type": "application/json", + }, + }); + return response.data; + } catch (error) { + console.error("Failed to Login", error); + throw error; + } +}; \ No newline at end of file diff --git a/src/services/signupService.js b/src/services/signupService.js new file mode 100644 index 0000000..e3f2d77 --- /dev/null +++ b/src/services/signupService.js @@ -0,0 +1,15 @@ +import { instance } from "./instance"; + +export const signupService = async (signupData) => { + try { + const response = await instance.post("/signup", signupData, { + headers: { + "Content-Type": "application/json", + }, + }); + return response.data; + } catch (error) { + console.error("회원 가입 실패", error); + throw error; + } +};