From 9c312fe8847513db61637256d4208929308c4799 Mon Sep 17 00:00:00 2001 From: Dahyeon Jin Date: Sun, 12 Jan 2025 13:33:08 +0900 Subject: [PATCH 01/13] =?UTF-8?q?style:=20gray=5F900(#222222)=20color?= =?UTF-8?q?=EB=A5=BC=20#1B1B1B=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tailwind.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tailwind.config.ts b/tailwind.config.ts index 823da95..9c85088 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -10,7 +10,7 @@ const colors = { black: '#111111', white: '#FFFFFF', gray: { - 900: '#222222', + 900: '#1B1B1B', 800: '#2B2B2B', 700: '#616161', 600: '#757575', From a0415a1489093b5f028ffa542fc74db3bc49e4d0 Mon Sep 17 00:00:00 2001 From: Dahyeon Jin Date: Sun, 12 Jan 2025 13:44:24 +0900 Subject: [PATCH 02/13] =?UTF-8?q?style:=20=EB=AA=A8=EB=8B=AC,=20=ED=8C=9D?= =?UTF-8?q?=EC=97=85,=20=EC=95=8C=EB=A6=BC=EC=B0=BD=EC=9D=98=20=EA=B3=B5?= =?UTF-8?q?=ED=86=B5=20=EB=B0=B0=EA=B2=BD=EC=83=89=20`background.overlay`?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tailwind.config.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/tailwind.config.ts b/tailwind.config.ts index 9c85088..54b436d 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -25,6 +25,7 @@ const colors = { subtitle: '#F5F5F5', background: { base: '#101010', + overlay: '#232225', }, }; From 64e476872ef6189f274dd75f873013e822726440 Mon Sep 17 00:00:00 2001 From: Dahyeon Jin Date: Sun, 12 Jan 2025 13:51:11 +0900 Subject: [PATCH 03/13] =?UTF-8?q?remove:=20=EA=B8=80=EB=A1=9C=EB=B2=8C?= =?UTF-8?q?=EB=A1=9C=20=EC=A7=80=EC=A0=95=EB=90=9C=20spacing=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Icon/iconSizes.ts | 6 +++--- tailwind.config.ts | 6 ------ 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/src/components/Icon/iconSizes.ts b/src/components/Icon/iconSizes.ts index 9a5a38f..f682012 100644 --- a/src/components/Icon/iconSizes.ts +++ b/src/components/Icon/iconSizes.ts @@ -1,10 +1,10 @@ export type IconSize = 'xl' | 'l' | 'm' | 's'; const iconSizes: Record = { - xl: 'w-15 h-15', - l: 'w-7.5 h-7.5', + xl: 'w-[60px] h-[60px]', + l: 'w-[30px] h-[30px]', m: 'w-6 h-6', - s: 'w-4.5 h-4.5', + s: 'w-[18px] h-[18px]', }; export default iconSizes; diff --git a/tailwind.config.ts b/tailwind.config.ts index 54b436d..a1fc01a 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -43,12 +43,6 @@ export default { sans: ['var(--font-montserrat)'], }, colors, - spacing: { - '4.5': '18px', - '7.5': '30px', - '15': '60px', - '78.25': '313px', - }, }, screens: { mobile: '677px', From 418d76a416afa83a98be4710883c2b87f7123786 Mon Sep 17 00:00:00 2001 From: Dahyeon Jin Date: Sun, 12 Jan 2025 14:06:07 +0900 Subject: [PATCH 04/13] =?UTF-8?q?style:=20MoreVertical=20=EC=95=84?= =?UTF-8?q?=EC=9D=B4=EC=BD=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Icon/icons/MoreVertical.tsx | 21 +++++++++++++++++++++ src/components/Icon/iconsNames.ts | 2 ++ 2 files changed, 23 insertions(+) create mode 100644 src/components/Icon/icons/MoreVertical.tsx diff --git a/src/components/Icon/icons/MoreVertical.tsx b/src/components/Icon/icons/MoreVertical.tsx new file mode 100644 index 0000000..0b0f673 --- /dev/null +++ b/src/components/Icon/icons/MoreVertical.tsx @@ -0,0 +1,21 @@ +import { IconProps } from '@/types'; + +const MoreVertical = ({ className }: IconProps) => { + return ( + + + + ); +}; + +export default MoreVertical; diff --git a/src/components/Icon/iconsNames.ts b/src/components/Icon/iconsNames.ts index f1ccc7c..a128287 100644 --- a/src/components/Icon/iconsNames.ts +++ b/src/components/Icon/iconsNames.ts @@ -32,6 +32,7 @@ import Lock from './icons/Lock'; import Menu from './icons/Menu'; import Message from './icons/Message'; import MoreHorizontal from './icons/MoreHorizontal'; +import MoreVertical from './icons/MoreVertical'; import Notification from './icons/Notification'; import Pause from './icons/Pause'; import Person from './icons/Person'; @@ -81,6 +82,7 @@ export const iconsNames: Record< Menu, Message, MoreHorizontal, + MoreVertical, Notification, Pause, Person, From bf7de5d6fe5cf88afdc6d8d48d34c6b02b8ebf9b Mon Sep 17 00:00:00 2001 From: Dahyeon Jin Date: Sun, 12 Jan 2025 18:22:11 +0900 Subject: [PATCH 05/13] =?UTF-8?q?rename:=20EmailInput=EC=9D=84=20BasicInpu?= =?UTF-8?q?t=EC=9C=BC=EB=A1=9C=20=EC=9D=B4=EB=A6=84=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/auth/(authWithLayout)/sign-in/page.tsx | 4 ++-- src/app/auth/(authWithLayout)/sign-up/page.tsx | 4 ++-- src/components/Input/{EmailInput.tsx => BasicInput.tsx} | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) rename src/components/Input/{EmailInput.tsx => BasicInput.tsx} (95%) diff --git a/src/app/auth/(authWithLayout)/sign-in/page.tsx b/src/app/auth/(authWithLayout)/sign-in/page.tsx index 8b3369c..cbcb040 100644 --- a/src/app/auth/(authWithLayout)/sign-in/page.tsx +++ b/src/app/auth/(authWithLayout)/sign-in/page.tsx @@ -7,7 +7,7 @@ import { object, string } from 'zod'; import usePostSignIn from '@/apis/auth/signIn'; import SquareButtonL from '@/components/Button/SquareButtonL'; -import EmailInput from '@/components/Input/EmailInput'; +import BasicInput from '@/components/Input/BasicInput'; import PasswordInput from '@/components/Input/PasswordInput'; import SocialSignInSection from '@/components/SocialSignInSection'; import ROUTE from '@/constants/routes'; @@ -47,7 +47,7 @@ const SignInPage = () => { className='w-full flex flex-col gap-[60px]' >
- +
{ className='w-full flex flex-col gap-[60px]' >
- +
diff --git a/src/components/Input/EmailInput.tsx b/src/components/Input/BasicInput.tsx similarity index 95% rename from src/components/Input/EmailInput.tsx rename to src/components/Input/BasicInput.tsx index f3822d3..2dabdba 100644 --- a/src/components/Input/EmailInput.tsx +++ b/src/components/Input/BasicInput.tsx @@ -7,11 +7,11 @@ import { useFormContext } from 'react-hook-form'; import Icon from '../Icon/Icon'; -interface EmailInputProps { +interface BasicInputProps { mode: 'sign-up' | 'sign-in'; } -const EmailInput = ({ mode }: EmailInputProps) => { +const BasicInput = ({ mode }: BasicInputProps) => { const [isEmailValidating, setIsEmailValidating] = useState(false); const { @@ -96,4 +96,4 @@ const EmailInput = ({ mode }: EmailInputProps) => { ); }; -export default EmailInput; +export default BasicInput; From 08ebfdde4562d5f976cb74b724d94dc7176711cf Mon Sep 17 00:00:00 2001 From: Dahyeon Jin Date: Sun, 12 Jan 2025 18:28:16 +0900 Subject: [PATCH 06/13] =?UTF-8?q?chore:=20tertiary=EB=A1=9C=20=EC=A0=95?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/auth/(authWithLayout)/sign-up/page.tsx | 2 +- src/components/Button/OvalButton.tsx | 6 +++--- src/components/Button/SquareButtonL.tsx | 8 ++++---- src/components/artwork/ArtworkFilter.tsx | 2 +- src/types/buttons/BaseButtonProps.ts | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/app/auth/(authWithLayout)/sign-up/page.tsx b/src/app/auth/(authWithLayout)/sign-up/page.tsx index d110152..dbc0078 100644 --- a/src/app/auth/(authWithLayout)/sign-up/page.tsx +++ b/src/app/auth/(authWithLayout)/sign-up/page.tsx @@ -105,7 +105,7 @@ const SignUpPage = () => {

회원가입

diff --git a/src/components/Button/OvalButton.tsx b/src/components/Button/OvalButton.tsx index a6ade1a..6e0f4d2 100644 --- a/src/components/Button/OvalButton.tsx +++ b/src/components/Button/OvalButton.tsx @@ -11,19 +11,19 @@ const OvalButton = ({ const bgColorClasses = { primary: 'bg-main', secondary: buttonSize === 'm' ? '' : 'bg-gray-200', - tertiaty: buttonSize === 'm' ? 'bg-gray-800' : 'bg-gray-700', + tertiary: buttonSize === 'm' ? 'bg-gray-800' : 'bg-gray-700', }; const hoverBgColorClasses = { primary: 'hover:bg-[#885DFF]', secondary: buttonSize === 'm' ? '' : 'hover:bg-gray-300', - tertiaty: '', + tertiary: '', }; const textColorClasses = { primary: 'text-white', secondary: buttonSize === 'm' ? 'text-transparent' : 'text-gray-600', - tertiaty: buttonSize === 'm' ? 'text-white' : 'text-gray-300', + tertiary: buttonSize === 'm' ? 'text-white' : 'text-gray-300', }; const buttonSizeClasses = { diff --git a/src/components/Button/SquareButtonL.tsx b/src/components/Button/SquareButtonL.tsx index cbe452d..3263cc3 100644 --- a/src/components/Button/SquareButtonL.tsx +++ b/src/components/Button/SquareButtonL.tsx @@ -1,7 +1,7 @@ import { SquareButtonLProps } from '@/types'; const SquareButtonL = ({ - variant = 'tertiaty', + variant = 'tertiary', children, onClick, @@ -15,19 +15,19 @@ const SquareButtonL = ({ const bgColorClasses = { primary: 'bg-main', secondary: 'bg-gray-200', - tertiaty: 'bg-gray-800', + tertiary: 'bg-gray-800', }; const hoverBgColorClasses = { primary: 'hover:bg-[#885DFF]', secondary: 'hover:bg-gray-300', - tertiaty: 'hover:bg-gray-700', + tertiary: 'hover:bg-gray-700', }; const textColorClasses = { primary: 'text-white', secondary: 'text-gray-500', - tertiaty: 'text-gray-300', + tertiary: 'text-gray-300', }; return ( diff --git a/src/components/artwork/ArtworkFilter.tsx b/src/components/artwork/ArtworkFilter.tsx index bad23ef..fd383b4 100644 --- a/src/components/artwork/ArtworkFilter.tsx +++ b/src/components/artwork/ArtworkFilter.tsx @@ -60,7 +60,7 @@ const ArtworkFilter = ({ variant={ selectedArtworkField === artworkField.value ? 'primary' - : 'tertiaty' + : 'tertiary' } buttonSize='m' onClick={() => handleArtworkFieldClick(artworkField.value)} diff --git a/src/types/buttons/BaseButtonProps.ts b/src/types/buttons/BaseButtonProps.ts index 49b1408..1e17489 100644 --- a/src/types/buttons/BaseButtonProps.ts +++ b/src/types/buttons/BaseButtonProps.ts @@ -1,7 +1,7 @@ import { ReactNode } from 'react'; export interface BaseButtonProps { - variant: 'primary' | 'secondary' | 'tertiaty'; + variant: 'primary' | 'secondary' | 'tertiary'; children: ReactNode; onClick?: () => void; From 66e9fc0f950a0854ebcd30a89401d707a9ffc0fd Mon Sep 17 00:00:00 2001 From: Dahyeon Jin Date: Mon, 13 Jan 2025 11:08:24 +0900 Subject: [PATCH 07/13] =?UTF-8?q?feat:=20BasicInput=EC=97=90=20=EB=B9=84?= =?UTF-8?q?=EC=A6=88=EB=8B=88=EC=8A=A4=20=EB=A1=9C=EC=A7=81=20=EA=B1=B7?= =?UTF-8?q?=EC=96=B4=EB=82=B4=EC=96=B4=20=EC=9E=AC=EC=82=AC=EC=9A=A9=20?= =?UTF-8?q?=EA=B0=80=EB=8A=A5=ED=95=9C=20=EC=BB=B4=ED=8F=AC=EB=84=8C?= =?UTF-8?q?=ED=8A=B8=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/(authWithLayout)/sign-in/page.tsx | 73 ++++++++- .../auth/(authWithLayout)/sign-up/page.tsx | 102 ++++++++++++- src/components/Input/BasicInput.tsx | 142 +++++++++++------- src/components/Input/NicknameInput.tsx | 103 ------------- src/components/Input/PasswordInput.tsx | 119 --------------- 5 files changed, 253 insertions(+), 286 deletions(-) delete mode 100644 src/components/Input/NicknameInput.tsx delete mode 100644 src/components/Input/PasswordInput.tsx diff --git a/src/app/auth/(authWithLayout)/sign-in/page.tsx b/src/app/auth/(authWithLayout)/sign-in/page.tsx index cbcb040..0721575 100644 --- a/src/app/auth/(authWithLayout)/sign-in/page.tsx +++ b/src/app/auth/(authWithLayout)/sign-in/page.tsx @@ -1,14 +1,15 @@ 'use client'; import { zodResolver } from '@hookform/resolvers/zod'; +import { debounce } from 'lodash'; import Link from 'next/link'; +import { useState } from 'react'; import { FormProvider, useForm } from 'react-hook-form'; import { object, string } from 'zod'; import usePostSignIn from '@/apis/auth/signIn'; import SquareButtonL from '@/components/Button/SquareButtonL'; import BasicInput from '@/components/Input/BasicInput'; -import PasswordInput from '@/components/Input/PasswordInput'; import SocialSignInSection from '@/components/SocialSignInSection'; import ROUTE from '@/constants/routes'; import { SignInFormType } from '@/types/auth'; @@ -19,6 +20,9 @@ const signInValidationSchema = object({ }); const SignInPage = () => { + const [isEmailValidating, setIsEmailValidating] = useState(false); + const [isPasswordValidating, setIsPasswordValidating] = useState(false); + const { mutate: mutateSignIn } = usePostSignIn(); const formHandlerMethods = useForm({ @@ -30,7 +34,33 @@ const SignInPage = () => { resolver: zodResolver(signInValidationSchema), }); - const { isValid: isFormDataValid } = formHandlerMethods.formState; + const { + register, + resetField, + watch, + setValue, + trigger, + formState: { errors, isValid: isFormDataValid }, + } = formHandlerMethods; + + const email = watch('email'); + const password = watch('password'); + + const handleEmailInputOnChange = debounce(async (e) => { + setValue('email', e.target.value); + setIsEmailValidating(true); + await trigger('email'); + setIsEmailValidating(false); + }, 300); + + const clearEmailField = () => resetField('email'); + + const handlePasswordInputOnChange = debounce(async (e) => { + setValue('password', e.target.value); + setIsPasswordValidating(true); + await trigger('password'); + setIsPasswordValidating(false); + }, 300); const onValidForm = (signInData: SignInFormType) => { mutateSignIn(signInData); @@ -47,13 +77,46 @@ const SignInPage = () => { className='w-full flex flex-col gap-[60px]' >
- - + {/* TODO: 검증할 요소 더 있으면 BasicInput의 errorMessage, + successMessage에 추가 */} + +
로그인 diff --git a/src/app/auth/(authWithLayout)/sign-up/page.tsx b/src/app/auth/(authWithLayout)/sign-up/page.tsx index dbc0078..4018d00 100644 --- a/src/app/auth/(authWithLayout)/sign-up/page.tsx +++ b/src/app/auth/(authWithLayout)/sign-up/page.tsx @@ -1,7 +1,9 @@ 'use client'; import { zodResolver } from '@hookform/resolvers/zod'; +import { debounce } from 'lodash'; import Link from 'next/link'; +import { useState } from 'react'; import { FormProvider, useForm } from 'react-hook-form'; import { object, string, ZodIssueCode } from 'zod'; @@ -10,8 +12,6 @@ import getValidateEmail from '@/apis/auth/validateEmail'; import getValidateNickname from '@/apis/auth/validateNickname'; import SquareButtonL from '@/components/Button/SquareButtonL'; import BasicInput from '@/components/Input/BasicInput'; -import NicknameInput from '@/components/Input/NicknameInput'; -import PasswordInput from '@/components/Input/PasswordInput'; import { NICKNAME_VALIDATE_ERROR_MESSAGE, SIGNIN_ERROR_MESSAGE, @@ -72,7 +72,12 @@ const signUpValidationSchema = object({ }); const SignUpPage = () => { + const [isEmailValidating, setIsEmailValidating] = useState(false); + const [isPasswordValidating, setIsPasswordValidating] = useState(false); + const [isNicknameValidating, setIsNicknameValidating] = useState(false); + const { mutate: signUpMutate } = usePostSignUp(); + const formHandlerMethods = useForm({ defaultValues: { email: '', @@ -83,12 +88,46 @@ const SignUpPage = () => { resolver: zodResolver(signUpValidationSchema), }); + const { + register, + setValue, + resetField, + trigger, + watch, + formState: { errors, isValid: isFormDataValid }, + } = formHandlerMethods; + + const email = watch('email'); + const password = watch('password'); + const nickname = watch('nickname'); + + const handleEmailInputOnChange = debounce(async (e) => { + setValue('email', e.target.value); + setIsEmailValidating(true); + await trigger('email'); + setIsEmailValidating(false); + }, 300); + + const clearEmailField = () => resetField('email'); + + const handlePasswordInputOnChange = debounce(async (e) => { + setValue('password', e.target.value); + setIsPasswordValidating(true); + await trigger('password'); + setIsPasswordValidating(false); + }, 300); + + const handleNicknameInputOnChange = debounce(async (e) => { + setValue('nickname', e.target.value); + setIsNicknameValidating(true); + await trigger('nickname'); + setIsNicknameValidating(false); + }, 300); + const onValidForm = (formData: SignUpFormType) => { signUpMutate(formData); }; - const { isValid: isFormDataValid } = formHandlerMethods.formState; - return (

회원가입

@@ -98,9 +137,58 @@ const SignUpPage = () => { className='w-full flex flex-col gap-[60px]' >
- - - + {/* TODO: 검증할 요소 더 있으면 BasicInput의 errorMessage, + successMessage에 추가 */} + + +
{ - const [isEmailValidating, setIsEmailValidating] = useState(false); + type?: string; + label?: string; + placeholder?: string; - const { - register, - resetField, - watch, - setValue, - trigger, - formState: { errors }, - } = useFormContext(); + value: string; + onChange: (e: ChangeEvent) => void; - const email = watch('email'); + showClear?: boolean; + onClear?: () => void; + showEyeIcon?: boolean; + showTextLength?: boolean; + isInvalid?: boolean; + minLength?: number; + maxLength?: number; + errorMessage?: string; + successMessage?: string; +} - const clearEmailField = () => resetField('email'); +const BasicInput = ({ + type, + label, + placeholder, + value, + onChange, + showClear = false, + onClear, + showEyeIcon = false, + showTextLength = false, + isInvalid = false, + minLength, + maxLength, + errorMessage, + successMessage, +}: BasicInputProps) => { + const [isPasswordVisible, setIsPasswordVisible] = useState(false); - const handleEmailInputOnChange = debounce(async (e) => { - setValue('email', e.target.value); + const togglePasswordVisibility = () => + setIsPasswordVisible(!isPasswordVisible); - setIsEmailValidating(true); - await trigger('email'); - setIsEmailValidating(false); - }, 300); + const currentTextLength = value.length; + const textLengthColor = + currentTextLength === 0 + ? 'text-gray-700' + : maxLength && currentTextLength > maxLength + ? 'text-system-error' + : 'text-white'; return (
+ {showEyeIcon && ( + + )} + + {showTextLength && maxLength && ( +
+ + {currentTextLength} + + /{maxLength} +
+ )} + + } classNames={{ label: 'custom-label', input: 'placeholder:text-gray-700', inputWrapper: ['bg-gray-900', 'rounded-md'], }} - onClear={clearEmailField} - onChange={handleEmailInputOnChange} /> -
- {errors.email ? ( + {isInvalid && errorMessage ? ( <> -

- {errors.email.message as string} -

+

{errorMessage}

) : ( - mode === 'sign-up' && - (email && isEmailValidating ? ( + successMessage && ( <> -

이메일 검증 중...

+

{successMessage}

- ) : ( - !!email && ( - <> - -

- 사용가능한 이메일입니다. -

- - ) - )) + ) )}
diff --git a/src/components/Input/NicknameInput.tsx b/src/components/Input/NicknameInput.tsx deleted file mode 100644 index 813f59c..0000000 --- a/src/components/Input/NicknameInput.tsx +++ /dev/null @@ -1,103 +0,0 @@ -'use client'; - -import { Input } from '@nextui-org/react'; -import { debounce } from 'lodash'; -import { useState } from 'react'; -import { useFormContext } from 'react-hook-form'; - -import Icon from '../Icon/Icon'; - -const MAX_NICKNAME_LENGTH = 10; - -const NicknameInput = () => { - const [isNicknameValidating, setIsNicknameValidating] = useState(false); - - const { - register, - watch, - setValue, - trigger, - formState: { errors }, - } = useFormContext(); - - const nickname = watch('nickname'); - - const currentNicknameLength = nickname.length; - const nicknameLengthColor = - nickname.length === 0 - ? 'text-gray-700' - : currentNicknameLength > MAX_NICKNAME_LENGTH - ? 'text-system-error' - : 'text-white'; - - const handleNicknameInputOnChange = debounce(async (e) => { - setValue('nickname', e.target.value); - - setIsNicknameValidating(true); - await trigger('nickname'); - setIsNicknameValidating(false); - }, 300); - - return ( -
- - - {currentNicknameLength} - - - /{MAX_NICKNAME_LENGTH} - -
- } - /> -
- {errors.nickname ? ( - <> - -

- {errors.nickname.message as string} -

- - ) : nickname && isNicknameValidating ? ( - <> - -

이메일 검증 중...

- - ) : ( - !!nickname && ( - <> - -

- 사용가능한 닉네임입니다. -

- - ) - )} -
-
- ); -}; - -export default NicknameInput; diff --git a/src/components/Input/PasswordInput.tsx b/src/components/Input/PasswordInput.tsx deleted file mode 100644 index 868af71..0000000 --- a/src/components/Input/PasswordInput.tsx +++ /dev/null @@ -1,119 +0,0 @@ -'use client'; - -import { Input } from '@nextui-org/react'; -import { debounce } from 'lodash'; -import { useState } from 'react'; -import { useFormContext } from 'react-hook-form'; - -import Icon from '../Icon/Icon'; - -interface PasswordnputProps { - mode: 'sign-up' | 'sign-in'; -} - -const PasswordInput = ({ mode }: PasswordnputProps) => { - const [isPasswordValidating, setIsPasswordValidating] = useState(false); - const [isPasswordVisible, setIsPasswordVisible] = useState(false); - - const { - register, - watch, - setValue, - trigger, - formState: { errors }, - } = useFormContext(); - - const password = watch('password'); - - const togglePasswordVisibility = () => - setIsPasswordVisible(!isPasswordVisible); - - const handlePasswordInputOnChange = debounce(async (e) => { - setValue('password', e.target.value); - - setIsPasswordValidating(true); - await trigger('password'); - setIsPasswordValidating(false); - }, 300); - - return ( -
- - {isPasswordVisible ? ( - - ) : ( - - )} - - } - /> -
- {errors.password ? ( - <> - -

- {errors.password.message as string} -

- - ) : ( - mode === 'sign-up' && - (password && isPasswordValidating ? ( - <> - -

패스워드 검증 중...

- - ) : ( - !!password && ( - <> - -

- 사용가능한 비밀번호입니다. -

- - ) - )) - )} -
-
- ); -}; - -export default PasswordInput; From 0df63094fb0cc5555a86fa50d3ddc5a35c6574a0 Mon Sep 17 00:00:00 2001 From: Dahyeon Jin Date: Mon, 13 Jan 2025 17:56:44 +0900 Subject: [PATCH 08/13] =?UTF-8?q?chore:=20fillRule,=20clipRule=EB=A1=9C=20?= =?UTF-8?q?=EC=86=8D=EC=84=B1=EB=AA=85=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Icon/icons/MoreVertical.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Icon/icons/MoreVertical.tsx b/src/components/Icon/icons/MoreVertical.tsx index 0b0f673..c446afd 100644 --- a/src/components/Icon/icons/MoreVertical.tsx +++ b/src/components/Icon/icons/MoreVertical.tsx @@ -9,8 +9,8 @@ const MoreVertical = ({ className }: IconProps) => { xmlns='http://www.w3.org/2000/svg' > From de3007d594cbed04fcedc2f4494438397c62a1d4 Mon Sep 17 00:00:00 2001 From: Dahyeon Jin Date: Tue, 14 Jan 2025 02:48:07 +0900 Subject: [PATCH 09/13] =?UTF-8?q?style:=20Input=20=EA=B3=B5=EC=9A=A9=20?= =?UTF-8?q?=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=88=98=EC=A0=95?= =?UTF-8?q?=EC=82=AC=ED=95=AD=20=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Input/BasicInput.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/Input/BasicInput.tsx b/src/components/Input/BasicInput.tsx index 0aa2098..9c491bb 100644 --- a/src/components/Input/BasicInput.tsx +++ b/src/components/Input/BasicInput.tsx @@ -102,12 +102,12 @@ const BasicInput = ({ } classNames={{ - label: 'custom-label', + label: ['custom-label', 'top-5', '!text-gray-400'], input: 'placeholder:text-gray-700', - inputWrapper: ['bg-gray-900', 'rounded-md'], + inputWrapper: ['bg-gray-900', 'rounded-md', 'h-15'], }} /> -
+
{isInvalid && errorMessage ? ( <> Date: Tue, 14 Jan 2025 20:38:25 +0900 Subject: [PATCH 10/13] =?UTF-8?q?chore:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8,=20?= =?UTF-8?q?=ED=9A=8C=EC=9B=90=EA=B0=80=EC=9E=85=20Input=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81=EC=9D=84=20?= =?UTF-8?q?=EC=9C=84=ED=95=9C=20=EB=A1=A4=EB=B0=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../auth/(authWithLayout)/sign-in/page.tsx | 77 ++---------- .../auth/(authWithLayout)/sign-up/page.tsx | 108 ++-------------- src/components/Input/EmailInput.tsx | 99 +++++++++++++++ src/components/Input/NicknameInput.tsx | 103 +++++++++++++++ src/components/Input/PasswordInput.tsx | 119 ++++++++++++++++++ 5 files changed, 338 insertions(+), 168 deletions(-) create mode 100644 src/components/Input/EmailInput.tsx create mode 100644 src/components/Input/NicknameInput.tsx create mode 100644 src/components/Input/PasswordInput.tsx diff --git a/src/app/auth/(authWithLayout)/sign-in/page.tsx b/src/app/auth/(authWithLayout)/sign-in/page.tsx index 0721575..8607c62 100644 --- a/src/app/auth/(authWithLayout)/sign-in/page.tsx +++ b/src/app/auth/(authWithLayout)/sign-in/page.tsx @@ -1,17 +1,16 @@ 'use client'; import { zodResolver } from '@hookform/resolvers/zod'; -import { debounce } from 'lodash'; import Link from 'next/link'; -import { useState } from 'react'; import { FormProvider, useForm } from 'react-hook-form'; import { object, string } from 'zod'; -import usePostSignIn from '@/apis/auth/signIn'; import SquareButtonL from '@/components/Button/SquareButtonL'; -import BasicInput from '@/components/Input/BasicInput'; +import EmailInput from '@/components/Input/EmailInput'; +import PasswordInput from '@/components/Input/PasswordInput'; import SocialSignInSection from '@/components/SocialSignInSection'; import ROUTE from '@/constants/routes'; +import usePostSignIn from '@/hooks/serverStateHooks/usePostSignIn'; import { SignInFormType } from '@/types/auth'; const signInValidationSchema = object({ @@ -20,9 +19,6 @@ const signInValidationSchema = object({ }); const SignInPage = () => { - const [isEmailValidating, setIsEmailValidating] = useState(false); - const [isPasswordValidating, setIsPasswordValidating] = useState(false); - const { mutate: mutateSignIn } = usePostSignIn(); const formHandlerMethods = useForm({ @@ -34,33 +30,7 @@ const SignInPage = () => { resolver: zodResolver(signInValidationSchema), }); - const { - register, - resetField, - watch, - setValue, - trigger, - formState: { errors, isValid: isFormDataValid }, - } = formHandlerMethods; - - const email = watch('email'); - const password = watch('password'); - - const handleEmailInputOnChange = debounce(async (e) => { - setValue('email', e.target.value); - setIsEmailValidating(true); - await trigger('email'); - setIsEmailValidating(false); - }, 300); - - const clearEmailField = () => resetField('email'); - - const handlePasswordInputOnChange = debounce(async (e) => { - setValue('password', e.target.value); - setIsPasswordValidating(true); - await trigger('password'); - setIsPasswordValidating(false); - }, 300); + const { isValid: isFormDataValid } = formHandlerMethods.formState; const onValidForm = (signInData: SignInFormType) => { mutateSignIn(signInData); @@ -77,46 +47,13 @@ const SignInPage = () => { className='w-full flex flex-col gap-[60px]' >
- {/* TODO: 검증할 요소 더 있으면 BasicInput의 errorMessage, - successMessage에 추가 */} - - + +
로그인 diff --git a/src/app/auth/(authWithLayout)/sign-up/page.tsx b/src/app/auth/(authWithLayout)/sign-up/page.tsx index 4018d00..8030364 100644 --- a/src/app/auth/(authWithLayout)/sign-up/page.tsx +++ b/src/app/auth/(authWithLayout)/sign-up/page.tsx @@ -1,22 +1,22 @@ 'use client'; import { zodResolver } from '@hookform/resolvers/zod'; -import { debounce } from 'lodash'; import Link from 'next/link'; -import { useState } from 'react'; import { FormProvider, useForm } from 'react-hook-form'; import { object, string, ZodIssueCode } from 'zod'; -import usePostSignUp from '@/apis/auth/signUp'; import getValidateEmail from '@/apis/auth/validateEmail'; import getValidateNickname from '@/apis/auth/validateNickname'; import SquareButtonL from '@/components/Button/SquareButtonL'; -import BasicInput from '@/components/Input/BasicInput'; +import EmailInput from '@/components/Input/EmailInput'; +import NicknameInput from '@/components/Input/NicknameInput'; +import PasswordInput from '@/components/Input/PasswordInput'; import { NICKNAME_VALIDATE_ERROR_MESSAGE, SIGNIN_ERROR_MESSAGE, } from '@/constants/errorMessage'; import ROUTE from '@/constants/routes'; +import usePostSignUp from '@/hooks/serverStateHooks/usePostSignUp'; import { SignUpFormType } from '@/types/auth'; const PASSWORD_REGEX = @@ -72,12 +72,7 @@ const signUpValidationSchema = object({ }); const SignUpPage = () => { - const [isEmailValidating, setIsEmailValidating] = useState(false); - const [isPasswordValidating, setIsPasswordValidating] = useState(false); - const [isNicknameValidating, setIsNicknameValidating] = useState(false); - const { mutate: signUpMutate } = usePostSignUp(); - const formHandlerMethods = useForm({ defaultValues: { email: '', @@ -88,46 +83,12 @@ const SignUpPage = () => { resolver: zodResolver(signUpValidationSchema), }); - const { - register, - setValue, - resetField, - trigger, - watch, - formState: { errors, isValid: isFormDataValid }, - } = formHandlerMethods; - - const email = watch('email'); - const password = watch('password'); - const nickname = watch('nickname'); - - const handleEmailInputOnChange = debounce(async (e) => { - setValue('email', e.target.value); - setIsEmailValidating(true); - await trigger('email'); - setIsEmailValidating(false); - }, 300); - - const clearEmailField = () => resetField('email'); - - const handlePasswordInputOnChange = debounce(async (e) => { - setValue('password', e.target.value); - setIsPasswordValidating(true); - await trigger('password'); - setIsPasswordValidating(false); - }, 300); - - const handleNicknameInputOnChange = debounce(async (e) => { - setValue('nickname', e.target.value); - setIsNicknameValidating(true); - await trigger('nickname'); - setIsNicknameValidating(false); - }, 300); - const onValidForm = (formData: SignUpFormType) => { signUpMutate(formData); }; + const { isValid: isFormDataValid } = formHandlerMethods.formState; + return (

회원가입

@@ -137,63 +98,14 @@ const SignUpPage = () => { className='w-full flex flex-col gap-[60px]' >
- {/* TODO: 검증할 요소 더 있으면 BasicInput의 errorMessage, - successMessage에 추가 */} - - - + + +

회원가입

diff --git a/src/components/Input/EmailInput.tsx b/src/components/Input/EmailInput.tsx new file mode 100644 index 0000000..f3822d3 --- /dev/null +++ b/src/components/Input/EmailInput.tsx @@ -0,0 +1,99 @@ +'use client'; + +import { Input } from '@nextui-org/react'; +import { debounce } from 'lodash'; +import { useState } from 'react'; +import { useFormContext } from 'react-hook-form'; + +import Icon from '../Icon/Icon'; + +interface EmailInputProps { + mode: 'sign-up' | 'sign-in'; +} + +const EmailInput = ({ mode }: EmailInputProps) => { + const [isEmailValidating, setIsEmailValidating] = useState(false); + + const { + register, + resetField, + watch, + setValue, + trigger, + formState: { errors }, + } = useFormContext(); + + const email = watch('email'); + + const clearEmailField = () => resetField('email'); + + const handleEmailInputOnChange = debounce(async (e) => { + setValue('email', e.target.value); + + setIsEmailValidating(true); + await trigger('email'); + setIsEmailValidating(false); + }, 300); + + return ( +
+ + +
+ {errors.email ? ( + <> + +

+ {errors.email.message as string} +

+ + ) : ( + mode === 'sign-up' && + (email && isEmailValidating ? ( + <> + +

이메일 검증 중...

+ + ) : ( + !!email && ( + <> + +

+ 사용가능한 이메일입니다. +

+ + ) + )) + )} +
+
+ ); +}; + +export default EmailInput; diff --git a/src/components/Input/NicknameInput.tsx b/src/components/Input/NicknameInput.tsx new file mode 100644 index 0000000..813f59c --- /dev/null +++ b/src/components/Input/NicknameInput.tsx @@ -0,0 +1,103 @@ +'use client'; + +import { Input } from '@nextui-org/react'; +import { debounce } from 'lodash'; +import { useState } from 'react'; +import { useFormContext } from 'react-hook-form'; + +import Icon from '../Icon/Icon'; + +const MAX_NICKNAME_LENGTH = 10; + +const NicknameInput = () => { + const [isNicknameValidating, setIsNicknameValidating] = useState(false); + + const { + register, + watch, + setValue, + trigger, + formState: { errors }, + } = useFormContext(); + + const nickname = watch('nickname'); + + const currentNicknameLength = nickname.length; + const nicknameLengthColor = + nickname.length === 0 + ? 'text-gray-700' + : currentNicknameLength > MAX_NICKNAME_LENGTH + ? 'text-system-error' + : 'text-white'; + + const handleNicknameInputOnChange = debounce(async (e) => { + setValue('nickname', e.target.value); + + setIsNicknameValidating(true); + await trigger('nickname'); + setIsNicknameValidating(false); + }, 300); + + return ( +
+ + + {currentNicknameLength} + + + /{MAX_NICKNAME_LENGTH} + +
+ } + /> +
+ {errors.nickname ? ( + <> + +

+ {errors.nickname.message as string} +

+ + ) : nickname && isNicknameValidating ? ( + <> + +

이메일 검증 중...

+ + ) : ( + !!nickname && ( + <> + +

+ 사용가능한 닉네임입니다. +

+ + ) + )} +
+
+ ); +}; + +export default NicknameInput; diff --git a/src/components/Input/PasswordInput.tsx b/src/components/Input/PasswordInput.tsx new file mode 100644 index 0000000..868af71 --- /dev/null +++ b/src/components/Input/PasswordInput.tsx @@ -0,0 +1,119 @@ +'use client'; + +import { Input } from '@nextui-org/react'; +import { debounce } from 'lodash'; +import { useState } from 'react'; +import { useFormContext } from 'react-hook-form'; + +import Icon from '../Icon/Icon'; + +interface PasswordnputProps { + mode: 'sign-up' | 'sign-in'; +} + +const PasswordInput = ({ mode }: PasswordnputProps) => { + const [isPasswordValidating, setIsPasswordValidating] = useState(false); + const [isPasswordVisible, setIsPasswordVisible] = useState(false); + + const { + register, + watch, + setValue, + trigger, + formState: { errors }, + } = useFormContext(); + + const password = watch('password'); + + const togglePasswordVisibility = () => + setIsPasswordVisible(!isPasswordVisible); + + const handlePasswordInputOnChange = debounce(async (e) => { + setValue('password', e.target.value); + + setIsPasswordValidating(true); + await trigger('password'); + setIsPasswordValidating(false); + }, 300); + + return ( +
+ + {isPasswordVisible ? ( + + ) : ( + + )} + + } + /> +
+ {errors.password ? ( + <> + +

+ {errors.password.message as string} +

+ + ) : ( + mode === 'sign-up' && + (password && isPasswordValidating ? ( + <> + +

패스워드 검증 중...

+ + ) : ( + !!password && ( + <> + +

+ 사용가능한 비밀번호입니다. +

+ + ) + )) + )} +
+
+ ); +}; + +export default PasswordInput; From 1f11412c7d272ee0eb1dbde1ec1ffc2a4314eee2 Mon Sep 17 00:00:00 2001 From: Dahyeon Jin Date: Tue, 14 Jan 2025 21:22:20 +0900 Subject: [PATCH 11/13] =?UTF-8?q?style:=20FilterDropdown=EC=9D=98=20hover?= =?UTF-8?q?=20=EC=83=89=EC=83=81=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/FilterDropdown/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/FilterDropdown/index.tsx b/src/components/FilterDropdown/index.tsx index 2ee36a0..b555b95 100644 --- a/src/components/FilterDropdown/index.tsx +++ b/src/components/FilterDropdown/index.tsx @@ -62,7 +62,7 @@ const FilterDropdown = ({ className={`block w-[149px] h-[44px] px-[23px] py-[10px] text-gray-400 cursor-pointer ${option === selected && isDropdownOpen ? 'text-white' : 'text-gray-400'} - hover:bg-gray-800`} + hover:bg-background-overlay`} > {option} From 1d645e8541d6cf36e8e3e35a2c3b37c43bea355b Mon Sep 17 00:00:00 2001 From: Dahyeon Jin Date: Tue, 14 Jan 2025 21:28:31 +0900 Subject: [PATCH 12/13] =?UTF-8?q?chore:=20variant=20=EB=B3=80=EC=88=98?= =?UTF-8?q?=EB=AA=85=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/auth/(authWithLayout)/sign-in/page.tsx | 2 +- src/app/auth/(authWithLayout)/sign-up/page.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/auth/(authWithLayout)/sign-in/page.tsx b/src/app/auth/(authWithLayout)/sign-in/page.tsx index 8607c62..a64a154 100644 --- a/src/app/auth/(authWithLayout)/sign-in/page.tsx +++ b/src/app/auth/(authWithLayout)/sign-in/page.tsx @@ -53,7 +53,7 @@ const SignInPage = () => { 로그인 diff --git a/src/app/auth/(authWithLayout)/sign-up/page.tsx b/src/app/auth/(authWithLayout)/sign-up/page.tsx index 8030364..194c228 100644 --- a/src/app/auth/(authWithLayout)/sign-up/page.tsx +++ b/src/app/auth/(authWithLayout)/sign-up/page.tsx @@ -105,7 +105,7 @@ const SignUpPage = () => {

회원가입

From 2a4406facd0c18f6d2073fb65489a624eb2acd97 Mon Sep 17 00:00:00 2001 From: Dahyeon Jin Date: Tue, 14 Jan 2025 22:11:06 +0900 Subject: [PATCH 13/13] =?UTF-8?q?refactor:=20=EC=95=84=EC=9D=B4=EC=BD=98?= =?UTF-8?q?=20=ED=86=A0=EA=B8=80=20=EB=A1=9C=EC=A7=81=20=EB=A6=AC=ED=8C=A9?= =?UTF-8?q?=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Input/BasicInput.tsx | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/src/components/Input/BasicInput.tsx b/src/components/Input/BasicInput.tsx index 9c491bb..f81f424 100644 --- a/src/components/Input/BasicInput.tsx +++ b/src/components/Input/BasicInput.tsx @@ -75,19 +75,11 @@ const BasicInput = ({ onClick={togglePasswordVisibility} disabled={value === ''} > - {isPasswordVisible ? ( - - ) : ( - - )} + )}