diff --git a/frontend/src/apis/firebase/auth.ts b/frontend/src/apis/firebase/auth.ts index e583fb1..78ca45d 100644 --- a/frontend/src/apis/firebase/auth.ts +++ b/frontend/src/apis/firebase/auth.ts @@ -12,8 +12,10 @@ import { doc, getDoc, getDocs, + query, setDoc, updateDoc, + where, } from "firebase/firestore"; interface UserTypeforSignup { @@ -69,25 +71,24 @@ const updateUserData = async (key: string, value: string | number) => { const signUpWithCredential = async (user: UserTypeforSignup & TUser) => { const { email, password, ...rest } = user; - await createUserWithEmailAndPassword(auth, email, password) - .then((credential) => { + await createUserWithEmailAndPassword(auth, email, password).then( + (credential) => { setDoc(doc(db, "users", credential.user.uid), { ...rest, + email: email, imgUrl: "", sizeType: null, sneakerSize: 0, }); - }) - .catch((e) => alert(e)); + } + ); }; const signInWithCredential = async (user: { email: string; password: string; }) => { - await signInWithEmailAndPassword(auth, user.email, user.password) - .then() - .catch((e) => alert(e.message)); + await signInWithEmailAndPassword(auth, user.email, user.password); }; const signInWithGoogle = async () => { @@ -96,6 +97,7 @@ const signInWithGoogle = async () => { const isNew = await signInWithPopup(auth, provider) .then((credential) => { setDoc(doc(db, "users", credential.user.uid), { + email: credential.user.email, username: credential.user.displayName, gender: null, birthDate: "", @@ -120,6 +122,14 @@ const logOut = async () => { .catch((e) => alert(e.message)); }; +const availableAccount = async (email: string) => { + const docRef = collection(db, "users"); + const q = query(docRef, where("email", "==", email)); + const querySnapshot = await getDocs(q); + + return querySnapshot.empty; +}; + export { getUserData, updateUserData, @@ -127,4 +137,5 @@ export { signInWithCredential, signInWithGoogle, logOut, + availableAccount, }; diff --git a/frontend/src/components/Chat/ChatLoading.tsx b/frontend/src/components/Chat/ChatLoading.tsx index 087b3f5..09482b0 100644 --- a/frontend/src/components/Chat/ChatLoading.tsx +++ b/frontend/src/components/Chat/ChatLoading.tsx @@ -1,10 +1,10 @@ const ChatLoading = () => { return ( -
+
-
-
-
+
+
+
); diff --git a/frontend/src/components/common/html/DropDown.tsx b/frontend/src/components/common/html/DropDown.tsx index e8e92d9..d27336e 100644 --- a/frontend/src/components/common/html/DropDown.tsx +++ b/frontend/src/components/common/html/DropDown.tsx @@ -41,7 +41,6 @@ const DropDown = forwardRef((props, ref) => { setIsOpen(false); if (onChange) { onChange(value); - console.log(value); } }; diff --git a/frontend/src/components/common/html/Input.tsx b/frontend/src/components/common/html/Input.tsx index d98f055..86b6ae9 100644 --- a/frontend/src/components/common/html/Input.tsx +++ b/frontend/src/components/common/html/Input.tsx @@ -1,17 +1,21 @@ import { ComponentPropsWithoutRef, forwardRef } from "react"; import { twMerge } from "tailwind-merge"; -type InputProps = ComponentPropsWithoutRef<"input">; +type InputProps = ComponentPropsWithoutRef<"input"> & { isErrored?: boolean }; const Input = forwardRef((props, ref) => { const { className, ...rest } = props; return ( <> + /> ); }); diff --git a/frontend/src/components/login/Login.tsx b/frontend/src/components/login/Login.tsx index 72cc280..ef03017 100644 --- a/frontend/src/components/login/Login.tsx +++ b/frontend/src/components/login/Login.tsx @@ -45,8 +45,8 @@ const Login = () => { [name]: value, }); - if (name === "email") setEmailError(""); - if (name === "password") setPasswordError(""); + setEmailError(""); + setPasswordError(""); }; const submitHandle = async (e: React.FormEvent) => { @@ -87,7 +87,15 @@ const Login = () => { //값 확인용 if (isLoginValid) { - await signInWithCredential(loginData); + await signInWithCredential(loginData) + .then(() => { + closeAll(); + navigate("/"); + }) + .catch(() => { + setEmailError("이메일을 확인해주세요."); + setPasswordError("비밀번호를 확인해주세요."); + }); // zustand로 관리하는 user가 업데이트가 바로 안이루어져서, // 임시 방편으로 updateUserInfo 가 userData를 반환하게끔 하고 // 반환값을 사용하도록 하자 @@ -96,8 +104,6 @@ const Login = () => { uid: string; username: string; }; - closeAll(); - navigate("/"); // 여기서 맞춤상품 api 호출 처리 try { @@ -137,6 +143,7 @@ const Login = () => { { { const location = useLocation(); diff --git a/frontend/src/components/signup/SignUp.tsx b/frontend/src/components/signup/SignUp.tsx index 78e7add..7b53068 100644 --- a/frontend/src/components/signup/SignUp.tsx +++ b/frontend/src/components/signup/SignUp.tsx @@ -1,5 +1,5 @@ -import SignUpRequired from "./SignUpRequired"; -import SignUpAdditional from "./SignUpAdditional"; +import SignUpRequired from "@components/signUp/SignUpRequired"; +import SignUpAdditional from "@components/signUp/SignUpAdditional"; import userStore from "@store/auth.store.ts"; import { redirect } from "react-router-dom"; diff --git a/frontend/src/components/signup/SignUpRequired.tsx b/frontend/src/components/signup/SignUpRequired.tsx index 5a40306..873831d 100644 --- a/frontend/src/components/signup/SignUpRequired.tsx +++ b/frontend/src/components/signup/SignUpRequired.tsx @@ -3,7 +3,11 @@ import InputField from "@common/InputField"; import Input from "@common/html/Input"; import BottomButton from "@common/BottomButton"; import DropDown, { DropDownRef } from "@common/html/DropDown"; -import { signUpWithCredential, updateUserData } from "@apis/firebase/auth"; +import { + availableAccount, + signUpWithCredential, + updateUserData, +} from "@apis/firebase/auth"; import { useInput } from "@hooks/useInput"; import userStore from "@store/auth.store"; import { auth } from "@/firebase"; @@ -39,9 +43,6 @@ const SignUpRequired = () => { const [userNameRef, focusUserName, handleUserNamePress] = useFocus(); const [genderRef, focusGender] = useFocus(); - const [birthYearRef, focusBirthYear] = useFocus(); - const [birthMonthRef, focusBirthMonth] = useFocus(); - const [birthDayRef, focusBirthDay] = useFocus(); const genderOptions: { value: string; label: string }[] = [ { value: "남성", label: "남성" }, @@ -55,7 +56,7 @@ const SignUpRequired = () => { const generateYearOptions = (startYear: number, endYear: number) => { const options: { value: string; label: string }[] = []; - for (let year = startYear; year <= endYear; year++) { + for (let year = endYear; year >= startYear; year--) { options.push({ value: year.toString(), label: year.toString() }); } return options; @@ -96,78 +97,73 @@ const SignUpRequired = () => { if (name === "username") setnameError(""); }; - const submitHandle = (e: React.FormEvent) => { - e.preventDefault(); - + const checkValidate = () => { + let isvalid = true; const validateEmail = (email: string) => { const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; if (!emailRegex.test(email)) { focusEmail(); // 이메일 필드 자동 포커스 setEmailError("이메일 형식을 확인해주세요"); - isSignUpRequiredValid = false; - return false; - } else { - setEmailError(""); - return true; } }; - let isSignUpRequiredValid = true; - if (!signUpRequired.email) { //아이디 비어있을 때 - if (isSignUpRequiredValid) focusEmail(); // 이메일 필드 자동 포커스 - + focusEmail(); // 이메일 필드 자동 포커스 setEmailError("아이디를 입력하세요"); - isSignUpRequiredValid = false; - } else if (validateEmail(signUpRequired.email)) { - isSignUpRequiredValid = true; - } + isvalid = false; + } else if (emailError.length > 0) { + focusEmail(); + isvalid = false; + } else validateEmail(signUpRequired.email); if (!signUpRequired.password) { //비밀번호 비어있을 때 - if (isSignUpRequiredValid) focusPassword(); // 비밀번호 필드 자동 포커스 - + if (isvalid) focusPassword(); // 비밀번호 필드 자동 포커스 + isvalid = false; setPasswordError("비밀번호를 입력하세요"); - isSignUpRequiredValid = false; } if (!signUpRequired.username) { - if (isSignUpRequiredValid) focusUserName(); // 이름 필드 자동 포커스 - + if (isvalid) focusUserName(); // 이름 필드 자동 포커스 + isvalid = false; setnameError("이름을 입력하세요"); - isSignUpRequiredValid = false; } if (!signUpRequired.gender) { - if (isSignUpRequiredValid) focusGender(); // 성별 필드 자동 포커스 - + if (isvalid) focusGender(); // 성별 필드 자동 포커스 + isvalid = false; setGenderError("성별을 입력하세요"); - isSignUpRequiredValid = false; } if (!signUpRequired.birthDate) { if (!birthYear) { - focusBirthYear(); // 년도 필드에 포커스 + // focusBirthYear(); // 년도 필드에 포커스 setBirthDateError("년도를 선택하세요"); - isSignUpRequiredValid = false; } else if (!birthMonth) { - focusBirthMonth(); // 월 필드에 포커스 + // focusBirthMonth(); // 월 필드에 포커스 setBirthDateError("월을 선택하세요"); - isSignUpRequiredValid = false; } else if (!birthDay) { - focusBirthDay(); // 일 필드에 포커스 + // focusBirthDay(); // 일 필드에 포커스 setBirthDateError("일을 선택하세요"); - isSignUpRequiredValid = false; } } + }; + + const submitHandle = (e: React.FormEvent) => { + e.preventDefault(); - //값 확인용 - if (isSignUpRequiredValid) { + if ( + emailError.length === 0 && + passwordError.length === 0 && + genderError.length === 0 && + birthDateError.length === 0 && + nameError.length === 0 + ) { if (user) { updateUserData("gender", signUpRequired.gender); updateUserData("birthDate", signUpRequired.birthDate); - + console.log(166); updateUserInfo(); } else signUpWithCredential({ @@ -177,7 +173,18 @@ const SignUpRequired = () => { gender: signUpRequired.gender === "남성" ? "male" : "female", username: signUpRequired.username, birthDate: signUpRequired.birthDate, - }).then(updateUserInfo); + }) + .then(() => { + updateUserInfo(); + console.log(179); + }) + .catch((data) => { + if (data.code === "auth/email-already-in-use") + setEmailError("이미 사용중인 이메일입니다"); + else if (data.code === "auth/weak-password") { + setPasswordError("비밀번호는 6자 이상이어야합니다"); + } + }); } }; @@ -200,11 +207,19 @@ const SignUpRequired = () => { type="email" name="email" placeholder={user?.email || "이메일을 입력해주세요"} - className="h-[48px] w-full rounded-[4px] px-4 py-[14px]" + className={`h-[48px] w-full rounded-[4px] px-4 py-[14px]`} + isErrored={!!emailError} value={user?.email ? "" : signUpRequired.email} onChange={handleInputChange} disabled={user?.email ? true : false} onKeyDown={(e) => handleEmailPress(e, focusPassword)} // 다음 패스워드 포커스 + onBlur={async (e) => { + const isAvailable = await availableAccount(e.target.value); + + if (!isAvailable) { + setEmailError("이미 사용중인 이메일입니다"); + } + }} /> {/*패스워드 입력 필드*/} @@ -213,6 +228,7 @@ const SignUpRequired = () => { ref={passwordRef} type="password" name="password" + isErrored={!!passwordError} placeholder={ user?.email ? "소셜 로그인 회원입니다." @@ -231,6 +247,7 @@ const SignUpRequired = () => { ref={userNameRef} type="text" name="username" + isErrored={!!nameError} placeholder={user?.displayName || "이름을 입력해주세요"} className="h-[48px] w-full rounded-[4px] px-4 py-[14px]" value={user?.displayName ? "" : signUpRequired.username} @@ -253,7 +270,6 @@ const SignUpRequired = () => {
{ }} /> { }} /> {
{/*회원가입 다음 페이지로 이동 버튼*/} - +
diff --git a/frontend/src/styles/tailwind.css b/frontend/src/styles/tailwind.css index 2881ba1..7994839 100644 --- a/frontend/src/styles/tailwind.css +++ b/frontend/src/styles/tailwind.css @@ -8,6 +8,9 @@ html { font-family: "Pretendard Variable", system-ui, sans-serif; } + input{ + outline: none; + } } /* Custom CSS */ diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts index 53cab5c..4af401e 100644 --- a/frontend/vite.config.ts +++ b/frontend/vite.config.ts @@ -4,7 +4,11 @@ import tsconfigPaths from "vite-tsconfig-paths"; import svgr from "vite-plugin-svgr"; // https://vitejs.dev/config/ -export default defineConfig({ +export default defineConfig(({ mode }) => ({ plugins: [react(), tsconfigPaths(), svgr()], base: "/stepup_front", -}); + esbuild: { + drop: ["debugger"], + pure: mode === "production" ? ["console.log"] : [], + }, +}));