From 5950b989744675862089aab3c44475c31489f4c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B0=95=EC=A7=80=EB=AF=BC?= Date: Sun, 22 Dec 2024 23:34:20 +0900 Subject: [PATCH] =?UTF-8?q?Feat:=20=EB=B9=84=EB=B0=80=EB=B2=88=ED=98=B8=20?= =?UTF-8?q?=EC=9E=AC=EB=B0=9C=EA=B8=89=20=EB=AA=A8=EB=8B=AC=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 비밀번호 찾기 클릭 시 모달 띄워주기 - 가입 이메일 입력 후 발송 성공 시 alert창 띄워주기 - 그 외 경고 메세지 출력 Related to: #26 --- src/assets/css/user/Login.module.css | 18 +++ .../css/user/PasswordResetModal.module.css | 110 ++++++++++++++++++ src/components/user/LoginForm.jsx | 14 ++- src/components/user/PasswordResetModal.jsx | 83 +++++++++++++ src/pages/recipe/Recipe.jsx | 1 - src/pages/user/Login.jsx | 10 +- src/sources/api/UserAPI.js | 12 +- 7 files changed, 243 insertions(+), 5 deletions(-) create mode 100644 src/assets/css/user/PasswordResetModal.module.css create mode 100644 src/components/user/PasswordResetModal.jsx diff --git a/src/assets/css/user/Login.module.css b/src/assets/css/user/Login.module.css index a99b8eb..ad37196 100644 --- a/src/assets/css/user/Login.module.css +++ b/src/assets/css/user/Login.module.css @@ -32,6 +32,24 @@ padding: 0 16px; } +.passwordReset { + text-align: right; + margin-top: 5px; +} + +.resetButton { + background: none; + border: none; + color: #666; + font-size: 15px; + padding: 0; + cursor: pointer; +} + +.resetButton:hover { + text-decoration: underline; +} + .input:focus { border-color: #3FA2F6; background: #fff; diff --git a/src/assets/css/user/PasswordResetModal.module.css b/src/assets/css/user/PasswordResetModal.module.css new file mode 100644 index 0000000..351ea9a --- /dev/null +++ b/src/assets/css/user/PasswordResetModal.module.css @@ -0,0 +1,110 @@ +.modalBackdrop { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, 0.5); + display: flex; + justify-content: center; + align-items: center; + z-index: 1050; +} + +.modal { + background: white; + border-radius: 8px; + width: 450px; +} + +.modalHeader { + padding: 16px 20px; + border-bottom: 1px solid #eee; + display: flex; + justify-content: space-between; + align-items: center; +} + +.modalHeader h4 { + font-size: 20px; + font-weight: 600; +} + +.closeButton { + background: none; + border: none; + font-size: 24px; + cursor: pointer; + color: #666; + padding: 0; +} + +.closeButton:hover { + color: #333; +} + +.modalBody { + padding: 20px; +} + +.description { + text-align: center; + color: #666; + font-size: 16px; + margin-bottom: 20px; + line-height: 1.6; +} + +.formGroup { + margin-bottom: 16px; +} + +.formGroup label { + display: block; + font-size: 15px; + margin-bottom: 8px; +} + +.input { + width: 100%; + height: 48px; + padding: 0 16px; + border: 1px solid #DDE2E5; + border-radius: 5px; + background: #F8F9FA; + font-size: 14px; + box-sizing: border-box; +} + +.input:focus { + outline: none; + border-color: #3FA2F6; + background: #fff; +} + +.error { + color: #FF3B3B; + font-size: 15px; + margin-top: 8px; +} + +.submitButton { + width: 100%; + height: 48px; + background: #3FA2F6; + border: none; + border-radius: 5px; + color: white; + font-size: 16px; + margin-top: 16px; + cursor: pointer; +} + +.submitButton:hover { + background: #3691E0; +} + +.submitButton:disabled { + background: #A5D3FB; + cursor: not-allowed; +} \ No newline at end of file diff --git a/src/components/user/LoginForm.jsx b/src/components/user/LoginForm.jsx index c831eb5..018b08e 100644 --- a/src/components/user/LoginForm.jsx +++ b/src/components/user/LoginForm.jsx @@ -1,10 +1,10 @@ import { useState } from 'react' -import { Form, Button } from 'react-bootstrap' +import { Form } from 'react-bootstrap' import { userApi } from '../../sources/api/UserAPI' import { useNavigate } from 'react-router-dom' import styles from '../../assets/css/user/Login.module.css' -const LoginForm = () => { +const LoginForm = ({ onPasswordReset }) => { const navigate = useNavigate() const [formData, setFormData] = useState({ userId: '', @@ -79,6 +79,16 @@ const LoginForm = () => { > {isSubmitting ? '로그인 중...' : '로그인'} + +
+ +
{error &&
{error}
} diff --git a/src/components/user/PasswordResetModal.jsx b/src/components/user/PasswordResetModal.jsx new file mode 100644 index 0000000..47aabea --- /dev/null +++ b/src/components/user/PasswordResetModal.jsx @@ -0,0 +1,83 @@ +import { useState, useEffect } from "react" +import { userApi } from "../../sources/api/UserAPI" +import styles from '../../assets/css/user/PasswordResetModal.module.css' + +const PasswordResetModal = ({show, onHide}) => { + const [email, setEmail] = useState('') + const [error, setError] = useState('') + const [isSubmitting, setIsSubmitting] = useState(false) + + useEffect(() => { + if (!show) { + setEmail('') + setError('') + setIsSubmitting(false) + } + }, [show]) + + const handleSubmit = async (e) => { + e.preventDefault() + setIsSubmitting(true) + setError('') + + try { + await userApi.resetPassword({ userEmail: email }) + alert('입력하신 이메일로 임시 비밀번호가 발송되었습니다.') + onHide() + } catch (error) { + setError('비밀번호 재발급 중 오류가 발생했습니다.') + } finally { + setIsSubmitting(false) + } + } + + if (!show) return null + + return ( +
+
e.stopPropagation()}> +
+

비밀번호 찾기

+ +
+
+

+ 가입하신 이메일 주소를 입력하시면
+ 임시 비밀번호를 발송해 드립니다. +

+
+
+ + { + setEmail(e.target.value) + setError('') + }} + disabled={isSubmitting} + className={styles.input} + /> + {error &&
{error}
} +
+ +
+
+
+
+ ) +} + +export default PasswordResetModal \ No newline at end of file diff --git a/src/pages/recipe/Recipe.jsx b/src/pages/recipe/Recipe.jsx index b3298b6..96b9f3a 100644 --- a/src/pages/recipe/Recipe.jsx +++ b/src/pages/recipe/Recipe.jsx @@ -2,7 +2,6 @@ import { useEffect, useState,useCallback } from "react" import { getRecipeList } from "../../sources/api/recipeAPI.jsx"; import {Link, Route, useNavigate} from "react-router-dom"; import { AddRecipe} from "./addRecipe.jsx"; -import {Route, useNavigate} from "react-router-dom"; import RecipeItem from "../../components/recipe/RecipeItem.jsx" import "../../assets/css/recipe/recipe.css" diff --git a/src/pages/user/Login.jsx b/src/pages/user/Login.jsx index 09cd76c..e0f7ea7 100644 --- a/src/pages/user/Login.jsx +++ b/src/pages/user/Login.jsx @@ -2,12 +2,20 @@ import LoginForm from "../../components/user/LoginForm" import { Container } from "react-bootstrap" import { Link } from "react-router-dom" import styles from "../../assets/css/user/Login.module.css" +import { useState } from "react" +import PasswordResetModal from "../../components/user/PasswordResetModal" const Login = () => { + const [showResetModal, setShowResetModal] = useState(false) + return (

로그인

- + setShowResetModal(true)} /> + setShowResetModal(false)} + />

아직 ReciPick의 회원이 아니신가요? diff --git a/src/sources/api/UserAPI.js b/src/sources/api/UserAPI.js index 4225d5a..f141e66 100644 --- a/src/sources/api/UserAPI.js +++ b/src/sources/api/UserAPI.js @@ -30,5 +30,15 @@ export const userApi = { } catch (error) { throw error } - } + }, + + // 비밀번호 재발급 + resetPassword: async (data) => { + try { + const response = await axios.post(`${BASE_URL}/password/reset`, data) + return response.data + } catch (error) { + throw error + } + }, } \ No newline at end of file