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;