Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const MypageProfileForm = () => {
)}
{/* TODO(수현): 디자인 토큰 변경 요청 해놓은 상태로 등록 시 추후 변경 */}
<button
className="absolute left-[52px] top-[52px] h-[28px] w-[28px] rounded-full bg-[#f5f5f5] flex-center"
className="absolute left-[52px] top-[52px] h-[28px] w-[28px] rounded-full bg-fill-neutral-strong-default flex-center"
aria-label="프로필 이미지 변경 버튼"
>
<Icon name="CameraBorder" size={16} />
Expand Down
17 changes: 9 additions & 8 deletions src/app/(route)/sign-up/_components/SignUpField/SignUpField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { useFormContext, useWatch } from "react-hook-form";
import { useSignUpBtnClick } from "../../_hooks/useSignUpBtnClick";
import { useEffect } from "react";
import SignUpItem from "../SignUpItem/SignUpItem";
import { useNicknameCheck } from "../../_hooks/useNicknameCheck";

const SignUpField = ({ onNext }: { onNext: () => void }) => {
const {
Expand All @@ -21,24 +20,26 @@ const SignUpField = ({ onNext }: { onNext: () => void }) => {
void trigger("passwordConfirm");
}, [password, trigger]);

const { isEmailDisabled, isEmailAuthDisabled, isEmailAuthVerified, handlerToClick } =
useSignUpBtnClick();

const { isNicknameVerified } = useNicknameCheck();
const {
isEmailDisabled,
isEmailAuthDisabled,
isEmailAuthVerified,
handlerToClick,
isNicknameVerified,
} = useSignUpBtnClick();

const handleDisabled = (name: string) => {
if (name === "emailAuth") return isEmailAuthDisabled;
else if (name === "email") return isEmailDisabled;
};

const handleSuccess = (name: string) => {
const handleVerified = (name: string) => {
if (name === "emailAuth") return isEmailAuthVerified;
else if (name === "nickname") return isNicknameVerified;
else return false;
};

const isNextEnabled = isValid && isEmailAuthVerified && isNicknameVerified;

return (
<>
<DetailHeader title="회원가입" />
Expand All @@ -48,7 +49,7 @@ const SignUpField = ({ onNext }: { onNext: () => void }) => {
key={item.name}
disabled={handleDisabled(item.name)}
btnOnClick={() => handlerToClick(item.name)}
isVerified={handleSuccess(item.name)}
isVerified={handleVerified(item.name)}
{...item}
/>
))}
Expand Down
18 changes: 8 additions & 10 deletions src/app/(route)/sign-up/_components/SignUpItem/SignUpItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ const inputValidationRules = {
},
nickname: {
required: true,
minLength: {
value: 2,
message: "2~10자 사이의 닉네임을 입력해 주세요.",
},
maxLength: {
value: 10,
message: "2~10자 사이의 닉네임을 입력해 주세요.",
Expand All @@ -50,23 +54,17 @@ const SignUpItem = ({ children, isVerified, ...props }: SignUpItemProps) => {
rules: props.validation,
});

const isSuccess = isDirty && !error && field.value;
const isFieldSuccess = isDirty && !error && !!field.value;

const handleSuccess = (name: string) => {
if (name === "emailAuth") return isVerified;
else return isSuccess;
};
const isSuccessState =
props.name === "emailAuth" || props.name === "nickname" ? isVerified : isFieldSuccess;

const inputValidation = (name: string) =>
inputValidationRules[name as keyof typeof inputValidationRules];

return (
<div className="h-[96px]">
<InputText
validation={inputValidation(props.name)}
isSuccess={handleSuccess(props.name)}
{...props}
>
<InputText validation={inputValidation(props.name)} isSuccess={isSuccessState} {...props}>
{children}
</InputText>
</div>
Expand Down
4 changes: 4 additions & 0 deletions src/app/(route)/sign-up/_constants/SIGNUP_ERROR_MESSAGE.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,8 @@ export const NICKNAME_ERROR_MESSAGE = {

export const EMAIL_CHECK_CODE_MESSAGE = {
_INVALID_CREDENTIALS: { message: "인증번호가 일치하지 않아요.", status: "warning" },
"AUTH400-EMAIL_VERIFY_FAILED": {
message: "인증번호가 만료되었거나 일치하지 않아요.",
status: "warning",
},
} as const;
1 change: 1 addition & 0 deletions src/app/(route)/sign-up/_constants/SIGNUP_INPUT_CONFIG.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,6 @@ export const SIGNUP_INPUT_CONFIG = [
rule: "2~10자, 특수문자/금칙어 제한",
children: "중복 확인",
maxLength: 10,
successMessage: "사용할 수 있는 닉네임입니다.",
},
] as const;
30 changes: 16 additions & 14 deletions src/app/(route)/sign-up/_hooks/useNicknameCheck.ts
Original file line number Diff line number Diff line change
@@ -1,36 +1,38 @@
import { useEffect, useState } from "react";
import { useApiCheckNickname } from "@/api/fetch/auth";
import { useFormContext } from "react-hook-form";
import { useToast } from "@/context/ToastContext";
import { NICKNAME_ERROR_MESSAGE } from "../_constants/SIGNUP_ERROR_MESSAGE";
import { useApiCheckNickname } from "@/api/fetch/auth";

export const useNicknameCheck = () => {
const { addToast } = useToast();
const { getValues } = useFormContext();

const [nicknameValue, setNicknameValue] = useState("");
const [isNicknameVerified, setIsNicknameVerified] = useState(false);
const { data, error, isSuccess, isError } = useApiCheckNickname(nicknameValue);

const handlerNickname = () => {
const { data, isSuccess, isError } = useApiCheckNickname(nicknameValue);

const handlerToClickNickname = (name: string) => {
const nickname = getValues(name);

setIsNicknameVerified(false);
setNicknameValue(nickname);
};

useEffect(() => {
if (!data) return;

if (isSuccess) {
addToast("사용할 수 있는 닉네임입니다.", "success");
setIsNicknameVerified(true);
addToast("사용할 수 있는 닉네임이에요.", "success");
} else {
setIsNicknameVerified(false);

const target = NICKNAME_ERROR_MESSAGE[data.code as keyof typeof NICKNAME_ERROR_MESSAGE];
addToast(target.message, target.status);
}
};

useEffect(() => {
handlerNickname();
}, [nicknameValue, data, error, isSuccess, isError]);

const handlerToClickNickname = (name: string) => {
const nickname = getValues(name);
setNicknameValue(nickname);
};
}, [data, isSuccess, isError, addToast]);

return {
handlerToClickNickname,
Expand Down
9 changes: 6 additions & 3 deletions src/app/(route)/sign-up/_hooks/useSignUpBtnClick.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { useFormContext } from "react-hook-form";
import { useToast } from "@/context/ToastContext";
import { useMemo, useState } from "react";
import { useApiCheckCode, useApiSendEmail } from "@/api/fetch/auth";
import { useErrorToast } from "@/hooks";
import { useNicknameCheck } from "./useNicknameCheck";
import { EMAIL_ERROR_MESSAGE, EMAIL_CHECK_CODE_MESSAGE } from "../_constants/SIGNUP_ERROR_MESSAGE";
import { throttle } from "lodash";
import { useApiCheckCode, useApiSendEmail } from "@/api/fetch/auth";

export const useSignUpBtnClick = () => {
const { getValues, trigger } = useFormContext();
Expand All @@ -22,7 +22,7 @@ export const useSignUpBtnClick = () => {

const { mutate: EmailMutate } = useApiSendEmail();
const { mutate: CodeMutate } = useApiCheckCode();
const { handlerToClickNickname } = useNicknameCheck();
const { handlerToClickNickname, isNicknameVerified } = useNicknameCheck();

const handlerToClick = useMemo(
() =>
Expand Down Expand Up @@ -59,7 +59,9 @@ export const useSignUpBtnClick = () => {
setIsEmailDisabled(true);
setIsEmailAuthVerified(true);
},
onError: (error) => handlerApiError(EMAIL_CHECK_CODE_MESSAGE, error.code),
onError: (error) => {
handlerApiError(EMAIL_CHECK_CODE_MESSAGE, error.code);
},
}
);
} else if (name === "nickname") {
Expand Down Expand Up @@ -87,5 +89,6 @@ export const useSignUpBtnClick = () => {
isEmailAuthDisabled,
isEmailDisabled,
isEmailAuthVerified,
isNicknameVerified,
};
};