From 19b91a532d8da13b3e3cb07cfa79dea0a09a59d0 Mon Sep 17 00:00:00 2001 From: JinHo Kim <81083461+jinhokim98@users.noreply.github.com> Date: Thu, 26 Sep 2024 15:22:03 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EA=B3=84=EC=A2=8C=EB=B2=88=ED=98=B8=20?= =?UTF-8?q?=EC=9E=85=EB=A0=A5=20=EC=A0=9C=ED=95=9C=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?(#634)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 계좌번호 입력 유효성 기능 추가 * feat: 계좌번호 유효성을 검증하고 input을 컨트롤하는 기능 추가 * style: console.log 제거 * feat: 계좌번호 정규표현식 수정 --------- Co-authored-by: Soyeon Choe <77609591+soi-ha@users.noreply.github.com> --- client/src/constants/errorMessage.ts | 1 + client/src/constants/regExp.ts | 1 + client/src/constants/rule.ts | 2 + client/src/hooks/useAccount.ts | 42 ++++++++++++++++--- client/src/pages/AccountPage/Account.tsx | 19 ++++++--- .../utils/validate/validateAccountNumber.ts | 23 ++++++++++ 6 files changed, 78 insertions(+), 10 deletions(-) create mode 100644 client/src/utils/validate/validateAccountNumber.ts diff --git a/client/src/constants/errorMessage.ts b/client/src/constants/errorMessage.ts index b33c12d5a..af59ee50d 100644 --- a/client/src/constants/errorMessage.ts +++ b/client/src/constants/errorMessage.ts @@ -45,6 +45,7 @@ export const ERROR_MESSAGE = { preventEmpty: '값은 비어있을 수 없어요', invalidInput: '올바르지 않은 입력이에요.', emptyBank: '계좌번호가 입력되지 않아서\n토스 송금 기능을 사용할 수 없어요', + invalidAccountNumber: '계좌번호는 8자에서 30자 사이로 입력 가능해요', }; export const UNKNOWN_ERROR = 'UNKNOWN_ERROR'; diff --git a/client/src/constants/regExp.ts b/client/src/constants/regExp.ts index d235c1f11..8a32f3755 100644 --- a/client/src/constants/regExp.ts +++ b/client/src/constants/regExp.ts @@ -3,6 +3,7 @@ const REGEXP = { eventUrl: /\/event\/([a-zA-Z0-9-]+)\//, billTitle: /^([ㄱ-ㅎ가-힣a-zA-Z0-9ㆍᆢ]\s?)*$/, memberName: /^([ㄱ-ㅎ가-힣a-zA-Zㆍᆢ]\s?)*$/, + accountNumber: /^\d+([\s\-]\d+)*[\s\-]?$/, }; export default REGEXP; diff --git a/client/src/constants/rule.ts b/client/src/constants/rule.ts index 4b78031b9..f311dedea 100644 --- a/client/src/constants/rule.ts +++ b/client/src/constants/rule.ts @@ -3,6 +3,8 @@ const RULE = { maxEventPasswordLength: 4, maxMemberNameLength: 4, maxPrice: 10000000, + minAccountNumberLength: 8, + maxAccountNumberLength: 30, }; export default RULE; diff --git a/client/src/hooks/useAccount.ts b/client/src/hooks/useAccount.ts index 288432800..0c6ed13b3 100644 --- a/client/src/hooks/useAccount.ts +++ b/client/src/hooks/useAccount.ts @@ -2,15 +2,20 @@ import type {Event} from 'types/serviceType'; import {useEffect, useState} from 'react'; +import validateAccountNumber from '@utils/validate/validateAccountNumber'; + +import RULE from '@constants/rule'; + import useRequestPatchEvent from './queries/event/useRequestPatchEvent'; import useRequestGetEvent from './queries/event/useRequestGetEvent'; const useAccount = () => { const {bankName, accountNumber} = useRequestGetEvent(); - const [bankNameState, setBankName] = useState(bankName); const [accountNumberState, setAccountNumber] = useState(accountNumber); + const [accountNumberErrorMessage, setAccountNumberErrorMessage] = useState(null); const [canSubmit, setCanSubmit] = useState(false); + const [isPasting, setIsPasting] = useState(false); useEffect(() => { setBankName(bankName); @@ -24,7 +29,32 @@ const useAccount = () => { }; const handleAccount = (event: React.ChangeEvent) => { - setAccountNumber(event.target.value); + if (isPasting) return; + + const newValue = event.target.value; + const {isValid, errorMessage} = validateAccountNumber(newValue); + setAccountNumberErrorMessage(errorMessage); + + const isValidMinLength = newValue.length >= RULE.minAccountNumberLength; + + if (isValid) { + setAccountNumber(event.target.value); + } else if (!isValid && !isValidMinLength) { + setAccountNumber(event.target.value.replace(/[^0-9\s\-]/g, '').trim()); + } + }; + + const handleAccountOnPaste = (event: React.ClipboardEvent) => { + setIsPasting(true); + + const value = `${accountNumberState}${event.clipboardData.getData('text')}`; + const newValue = value.replace(/[^0-9\s\-]/g, '').trim(); + const {isValid, errorMessage} = validateAccountNumber(newValue); + + setAccountNumberErrorMessage(errorMessage); + if (isValid) setAccountNumber(newValue); + + setTimeout(() => setIsPasting(false), 0); }; const getChangedField = () => { @@ -49,15 +79,17 @@ const useAccount = () => { const existEmptyField = bankNameState.trim() === '' || accountNumberState.trim() === ''; const isChanged = bankName !== bankNameState || accountNumber !== accountNumberState; - setCanSubmit(!existEmptyField && isChanged); - }, [bankNameState, accountNumberState]); - + setCanSubmit(!existEmptyField && isChanged && accountNumberErrorMessage === null); + }, [bankName, accountNumber, bankNameState, accountNumberState, accountNumberErrorMessage]); + return { bankName: bankNameState, accountNumber: accountNumberState, + accountNumberErrorMessage, canSubmit, selectBank, handleAccount, + handleAccountOnPaste, enrollAccount, }; }; diff --git a/client/src/pages/AccountPage/Account.tsx b/client/src/pages/AccountPage/Account.tsx index 29e389fe6..0490ead31 100644 --- a/client/src/pages/AccountPage/Account.tsx +++ b/client/src/pages/AccountPage/Account.tsx @@ -15,7 +15,16 @@ const Account = () => { const [isBottomSheetOpen, setIsBottomSheetOpen] = useState(false); - const {bankName, accountNumber, canSubmit, selectBank, handleAccount, enrollAccount} = useAccount(); + const { + bankName, + accountNumber, + accountNumberErrorMessage, + canSubmit, + selectBank, + handleAccount, + handleAccountOnPaste, + enrollAccount, + } = useAccount(); const enrollAccountAndNavigateAdmin = async () => { await enrollAccount(); @@ -40,19 +49,19 @@ const Account = () => { errorText={null} autoFocus={false} isAlwaysOnLabel - isAlwaysOnInputBorder readOnly onClick={() => setIsBottomSheetOpen(true)} /> {isBottomSheetOpen && ( { + const isValidateType = () => { + return REGEXP.accountNumber.test(accountNumber); + }; + + const isValidateLength = () => { + return accountNumber.length >= RULE.minAccountNumberLength && accountNumber.length <= RULE.maxAccountNumberLength; + }; + + if (isValidateType() && isValidateLength()) { + return {isValid: true, errorMessage: null}; + } + + return {isValid: false, errorMessage: ERROR_MESSAGE.invalidAccountNumber}; +}; + +export default validateAccountNumber;