diff --git a/apis/auth.ts b/apis/auth.ts new file mode 100644 index 0000000..227d875 --- /dev/null +++ b/apis/auth.ts @@ -0,0 +1,81 @@ +import client from "./client"; + +export const SignIn = async (userData: { + loginId: string; + password: string; +}) => { + try { + const response = await client.post( + "/users/login", + { + loginId: userData.loginId, + password: userData.password, + }, + { + headers: { + "Content-Type": "application/json", + }, + }, + ); + return response.data; + } catch (error) { + if (error.response) { + // 200 이외 + console.error("서버 응답 오류:", error.response.data); + } else if (error.request) { + // 요청이 전송되었으나 응답을 받지 못한 경우 + console.error("응답 없음:", error.request); + } else { + // 요청을 설정하는 도중에 발생한 오류 + console.error("요청 설정 오류:", error.message); + } + throw error; + } +}; + +export interface userProps { + loginId: string; + password: string; + nickname: string; + identifier: string; + centerIdx: number; +} + +export const SignUp = async (userData: userProps) => { + try { + const response = await client.post("/users/signup", { + loginId: userData.loginId, + password: userData.password, + nickname: userData.nickname, + identifier: userData.identifier, + centerIdx: userData.centerIdx, + }); + return response.data; + } catch (error) { + if (error.response) { + console.error(); + } + throw error; + } +}; + +export type centerProps = { + centerIdx: number; + name: string; +}; + +export interface getCenterListBody { + ceterList: centerProps[]; +} + +export const getCenterList = async () => { + try { + const response = await client.get("/users/signupView"); + return response.data.result.ceterList; + } catch (error) { + if (error.response) { + console.error(error.response); + } + throw error; + } +}; diff --git a/apis/auth.tsx b/apis/auth.tsx deleted file mode 100644 index d88d664..0000000 --- a/apis/auth.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import client from "./client"; - -export const SignIn = async (userData: { - loginId: string; - password: string; -}) => { - try { - const response = await client.post( - "/users/login", - { - loginId: userData.loginId, - password: userData.password, - }, - { - headers: { - "Content-Type": "application/json", - }, - }, - ); - return response.data; - } catch (error) { - if (error.response) { - // 200 이외 - console.error("서버 응답 오류:", error.response.data); - } else if (error.request) { - // 요청이 전송되었으나 응답을 받지 못한 경우 - console.error("응답 없음:", error.request); - } else { - // 요청을 설정하는 도중에 발생한 오류 - console.error("요청 설정 오류:", error.message); - } - throw error; - } -}; diff --git a/apis/hooks/auth.ts b/apis/hooks/auth.ts new file mode 100644 index 0000000..165ebfb --- /dev/null +++ b/apis/hooks/auth.ts @@ -0,0 +1,31 @@ +import { useQuery, useMutation } from "@tanstack/react-query"; +import { SignUp, getCenterList, userProps } from "../auth"; +import { useRouter } from "next/router"; + +function useGetCenterList() { + const { data } = useQuery({ + queryKey: ["getCenterList"], + queryFn: getCenterList, + }); + return { data }; +} + +function useSignUp(userData: userProps) { + const router = useRouter(); + + const { mutate } = useMutation({ + mutationKey: ["signUp"], + mutationFn: () => SignUp(userData), + onSuccess: () => { + router.push("/login"); + }, + onError: (error: Error) => { + window.alert("다시 회원가입해주세요."); + router.push("/main"); + }, + }); + + return { mutate }; +} + +export { useGetCenterList, useSignUp }; diff --git a/components/auth/AuthInput.tsx b/components/auth/AuthInput.tsx new file mode 100644 index 0000000..95cbe99 --- /dev/null +++ b/components/auth/AuthInput.tsx @@ -0,0 +1,56 @@ +import { ChangeEvent, HTMLInputTypeAttribute, forwardRef } from "react"; + +export interface TextInputProps { + className?: string; + value: string; + isError?: boolean; + errorText?: string; + placeholder?: string; + type?: HTMLInputTypeAttribute; + name: string; + onChange: (e: ChangeEvent) => void; + maxLength?: number; +} + +const AuthInput = forwardRef( + ( + { + className = "", + value, + isError, + placeholder = "", + errorText, + type = "text", + name, + onChange, + maxLength = 10, + }, + ref, + ) => { + return ( +
+
+ +
+ {isError && ( +
{errorText}
+ )} +
+ ); + }, +); + +export default AuthInput; diff --git a/components/calendar/CalendarModal.tsx b/components/calendar/CalendarModal.tsx index 2a34b95..93916ab 100644 --- a/components/calendar/CalendarModal.tsx +++ b/components/calendar/CalendarModal.tsx @@ -72,7 +72,7 @@ interface TextProps { addExpBack?: string; } -function TextLine({ +export function TextLine({ title, className, children, diff --git a/pages/login.tsx b/pages/login.tsx index 3857aa3..06e84e4 100644 --- a/pages/login.tsx +++ b/pages/login.tsx @@ -1,14 +1,16 @@ import HeadFunction from "@/components/HeadFunction"; -import { useState, useEffect, useCallback, useRef } from "react"; +import { useState, useCallback, useRef } from "react"; import { NextPage } from "next"; import { useRouter } from "next/router"; import Button from "@/components/Button"; import LogoLetterIcon from "@/public/svgs/LogoLetter.svg"; import { useMutation } from "@tanstack/react-query"; -import { ResponseBody, setTokenFromLocalStorage } from "@/apis/client"; -import { SignIn } from "@/apis/auth"; +import { setTokenFromLocalStorage } from "@/apis/client"; + import { atom, useAtom } from "jotai"; import { centerNameAtom, isAdminAtom } from "@/utils/atom"; +import AuthInput from "@/components/auth/AuthInput"; +import { SignIn } from "@/apis/auth"; interface userProps { loginId: string; @@ -62,7 +64,7 @@ const Login: NextPage = () => { alert("로그인에 성공하였습니다"); }, onError: (error) => { - alert("로그인에 실패하였습니다"); + alert("아이디나 비밀번호가 틀렸습니다."); }, }); @@ -75,30 +77,27 @@ const Login: NextPage = () => { >
- -
- diff --git a/pages/main.tsx b/pages/main.tsx index 689ed86..07200b4 100644 --- a/pages/main.tsx +++ b/pages/main.tsx @@ -7,17 +7,17 @@ const OnBoardingMain: NextPage = () => { const router = useRouter(); return ( -
- +
+
diff --git a/pages/signup.tsx b/pages/signup.tsx index 754f122..c77d845 100644 --- a/pages/signup.tsx +++ b/pages/signup.tsx @@ -1,48 +1,90 @@ +import { centerProps, userProps } from "@/apis/auth"; +import { useGetCenterList, useSignUp } from "@/apis/hooks/auth"; import Button from "@/components/Button"; import HeadFunction from "@/components/HeadFunction"; +import AuthInput from "@/components/auth/AuthInput"; +import { TextLine } from "@/components/calendar/CalendarModal"; import { NextPage } from "next"; +import { useState, useCallback, useRef } from "react"; import { useRouter } from "next/router"; -import { useState, useEffect, useCallback, useRef } from "react"; +import "react-toastify/dist/ReactToastify.css"; +import CheckIcon from "@/public/svgs/Check.svg"; +import FlexBox from "@/components/Flexbox"; +import { usePostNicknameCheck } from "@/apis/hooks/mypage"; +import { InputError } from "./mypage/password"; const SignUp: NextPage = () => { - const router = useRouter(); + //input용 - const [idValue, setIDValue] = useState(""); - const [pwValue, setPWValue] = useState(""); - const inputRef = useRef(null); + const [userInfo, setUserInfo] = useState({ + loginId: "", + password: "", + nickname: "", + identifier: "", + centerIdx: 0, + }); - //input 함수 - //onChange - const onChangeID = (e: React.ChangeEvent) => { - setIDValue(e.target.value); - }; + //inputRef설정 함수 + const idInputRef = useRef(null); + const pwInputRef = useRef(null); + const nicknameInputRef = useRef(null); + const idfInputRef = useRef(null); + const cidInputRef = useRef(null); - const onChangePW = (e: React.ChangeEvent) => { - setPWValue(e.target.value); + //onChange + const onChange = (e: React.ChangeEvent) => { + setUserInfo({ ...userInfo, [e.target.name]: e.target.value }); }; - //inputRef설정 함수 - const handleInputClick = (e: React.MouseEvent) => { - e.preventDefault(); - e.stopPropagation(); - inputRef.current?.focus(); - }; //onSubmit const onSubmit = useCallback( (e: React.FormEvent) => { e.preventDefault(); - - //입력한 값이 없을 때 alert 추가 - if (idValue.trim() == "") { - alert("아이디를 입력해주세요."); - } else if (pwValue.trim() == "") { - // createChatting(inputValue); - alert("비밀번호를 입력해주세요."); - } else { - //api - } + signUpMutate(); }, - [idValue, pwValue], + [userInfo], + ); + + //api 호출 + const { data } = useGetCenterList(); + const { mutate: signUpMutate } = useSignUp(userInfo); + + //중복 확인 + const [nameError, setNameError] = useState({ + status: false, + text: "", + }); + + const onClickCheckBtn = () => { + if (checkNicknameValidity()) nameCheckMutate(); + }; + + const [tempName, setTempName] = useState(""); + const checkNicknameValidity = () => { + setTempName(userInfo.nickname); + if (userInfo.nickname.length > 8) { + setNameError({ + status: true, + text: "닉네임 최대 길이는 8자입니다.", + }); + return false; + } + + const regex = /^[가-힣a-zA-Z0-9\s]*$/; + if (!regex.test(userInfo.nickname)) { + setNameError({ + status: true, + text: "닉네임은 영어, 한글, 숫자로만 구성할 수 있습니다.", + }); + return false; + } + + return true; + }; + + const { mutate: nameCheckMutate } = usePostNicknameCheck( + userInfo.nickname, + setNameError, ); return ( @@ -50,31 +92,100 @@ const SignUp: NextPage = () => {
- + + + + + + - + + + + + + + + + + + + + + + {data && ( + + )}
- diff --git a/pages/success.tsx b/pages/success.tsx new file mode 100644 index 0000000..9ca251a --- /dev/null +++ b/pages/success.tsx @@ -0,0 +1,37 @@ +import { NextPage } from "next"; +import lottie from "lottie-web"; +import { useEffect, useRef } from "react"; +import FlexBox from "@/components/Flexbox"; +import Button from "@/components/Button"; +import { useRouter } from "next/router"; + +const SignupSuccess: NextPage = () => { + const router = useRouter(); + const lottieRef = useRef(); + + useEffect(() => { + lottie.loadAnimation({ + container: lottieRef.current, + renderer: "svg", + loop: false, + autoplay: true, + animationData: require("@/public/lotties/Check.json"), + }); + }); + + return ( + +
+
회원가입을 완료하였습니다!
+
+
+ + ); +}; + +export default SignupSuccess;