diff --git a/package-lock.json b/package-lock.json index ab65428..bbbb650 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "no-copy-frontend", - "version": "0.49.0", + "version": "0.58.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "no-copy-frontend", - "version": "0.49.0", + "version": "0.58.0", "dependencies": { "@a2seven/yoo-checkout": "^1.1.4", "@react-input/mask": "^2.0.4", @@ -15,6 +15,7 @@ "@tanstack/react-query": "^5.90.11", "@tanstack/react-table": "^8.21.3", "@vkid/sdk": "^2.6.2", + "card-validator": "^10.0.4", "clsx": "^2.1.1", "jose": "^6.1.2", "next": "^16.0.7", @@ -3416,6 +3417,15 @@ ], "license": "CC-BY-4.0" }, + "node_modules/card-validator": { + "version": "10.0.4", + "resolved": "https://registry.npmjs.org/card-validator/-/card-validator-10.0.4.tgz", + "integrity": "sha512-1Q/tDCSvM5iL3psPo6j8KF/VoI63WA7Y4Zthy1E7l/H4Iul2e2X+1S0vp33n3OMRjLgGU2+gLaK2NsGtwArJkQ==", + "license": "MIT", + "dependencies": { + "credit-card-type": "^10.1.0" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -3504,6 +3514,12 @@ "dev": true, "license": "MIT" }, + "node_modules/credit-card-type": { + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/credit-card-type/-/credit-card-type-10.1.2.tgz", + "integrity": "sha512-fP9vlyeYSHfKbIdE6IOR63YivTXMy0ZqyEkBhHxuAbvpltepe+zJmQ34A2Va7zJ8J7ZaB5IMAdD+1YJOmsajJA==", + "license": "MIT" + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", diff --git a/package.json b/package.json index ab0c9a8..13fd357 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "no-copy-frontend", - "version": "0.58.0", + "version": "0.59.0", "private": true, "scripts": { "dev": "next dev -p 2999", @@ -16,6 +16,7 @@ "@tanstack/react-query": "^5.90.11", "@tanstack/react-table": "^8.21.3", "@vkid/sdk": "^2.6.2", + "card-validator": "^10.0.4", "clsx": "^2.1.1", "jose": "^6.1.2", "next": "^16.0.7", diff --git a/src/app/styles/module/login.module.scss b/src/app/styles/module/login.module.scss index e812a17..462ba55 100644 --- a/src/app/styles/module/login.module.scss +++ b/src/app/styles/module/login.module.scss @@ -112,6 +112,15 @@ &.password { padding-right: 32px; } + + &:disabled { + opacity: 0.6; + } + + &.valid-card { + border: 2px solid #2f9e44; + background: #ecf8ee; + } } .form-options { diff --git a/src/app/styles/pages-styles.scss b/src/app/styles/pages-styles.scss index eb4a00b..e621be0 100644 --- a/src/app/styles/pages-styles.scss +++ b/src/app/styles/pages-styles.scss @@ -1486,6 +1486,16 @@ transition: all 0.3s; margin-top: 20px; } + + .card-type { + position: absolute; + right: 12px; + top: 50%; + transform: translateY(-50%); + text-transform: uppercase; + font-size: 12px; + font-weight: 600; + } } } diff --git a/src/app/ui/inputs/bank-card-inpit.tsx b/src/app/ui/inputs/bank-card-inpit.tsx index 1f14f72..91f3dc4 100644 --- a/src/app/ui/inputs/bank-card-inpit.tsx +++ b/src/app/ui/inputs/bank-card-inpit.tsx @@ -3,16 +3,16 @@ import { useState, ChangeEvent, useEffect } from 'react'; import { InputMask, format } from '@react-input/mask'; import styles from '@/app/styles/module/login.module.scss'; -import { PayoutMethods } from '@/app/actions/referralsActions'; interface BankCardProps { bankCardState: string | undefined; validateHandler: (e: ChangeEvent) => void; clearCondition?: string | undefined | null; disabled: boolean; + isValid: boolean | undefined; } -export default function BankCardInput({ bankCardState, validateHandler, disabled }: BankCardProps) { +export default function BankCardInput({ bankCardState, validateHandler, disabled, isValid }: BankCardProps) { const [bankCard, setBankCard] = useState(''); useEffect(() => { @@ -37,7 +37,7 @@ export default function BankCardInput({ bankCardState, validateHandler, disabled type="phone" id="bankCard" name="bankCard" - className={`${styles['form-input']}`} + className={`${styles['form-input']} ${isValid ? styles['valid-card'] : ''}`} mask="____ ____ ____ ____" replacement={{ _: /\d/ }} value={bankCard} diff --git a/src/app/ui/payment/secure-payments.tsx b/src/app/ui/payment/secure-payments.tsx index 2bd2eaa..7fbc9df 100644 --- a/src/app/ui/payment/secure-payments.tsx +++ b/src/app/ui/payment/secure-payments.tsx @@ -10,24 +10,6 @@ export default function SecurePayments() {

Безопасные платежи

-
- -
diff --git a/src/app/ui/referral-page/setting-referral-program.tsx b/src/app/ui/referral-page/setting-referral-program.tsx index 693af10..22ae97e 100644 --- a/src/app/ui/referral-page/setting-referral-program.tsx +++ b/src/app/ui/referral-page/setting-referral-program.tsx @@ -8,6 +8,12 @@ import { addPayoutMethod, PayoutMethodFormState, getPayoutMethods, PayoutMethods import { bankCardFormSchema } from '@/app/actions/definitions'; import { useQuery, useQueryClient } from '@tanstack/react-query'; import { toast } from 'sonner'; +import valid from 'card-validator'; + +interface cardInfoType { + cardType: string, + potentiallyValid: boolean +} export default function SettingReferralProgram() { const [state, formAction, isPending] = useActionState( @@ -36,6 +42,8 @@ export default function SettingReferralProgram() { const [paymentsType, setPaymentsType] = useState('bank-card'); const [defaultCardNumber, setDefaultCardNumber] = useState(undefined); const [isEditing, setIsEditing] = useState(false); + const [cardInfo, setCardInfo] = useState(null); + const [initialCardInfo, setInitialCardInfo] = useState(null); const queryClient = useQueryClient(); const t = useTranslations('Global'); @@ -52,6 +60,17 @@ export default function SettingReferralProgram() { if (payoutMethods) { payoutMethods.forEach(e => { if (e.default) { + const numberValidation = valid.number(e.cardNumber); + if (numberValidation.card) { + setCardInfo({ + cardType: numberValidation.card.type, + potentiallyValid: numberValidation.isPotentiallyValid + }); + setInitialCardInfo({ + cardType: numberValidation.card.type, + potentiallyValid: numberValidation.isPotentiallyValid + }) + } setDefaultCardNumber(e); } }) @@ -78,8 +97,15 @@ export default function SettingReferralProgram() { async function onChangeHandler(e: ChangeEvent): Promise { const fieldName = e.target.name; - const validatedField = await validateField(fieldName, e.target.value); + const numberValidation = valid.number(e.target.value); + + if (numberValidation.card) { + setCardInfo({ + cardType: numberValidation.card.type, + potentiallyValid: numberValidation.isPotentiallyValid + }); + } if (validatedField.success) { setFormState(prevState => { @@ -162,11 +188,23 @@ export default function SettingReferralProgram() {

{t('bank-card-number')}

- +
+ + {cardInfo && ( +
+ {cardInfo.cardType} +
+ )} +
{formState?.error?.bankCard && (

{ @@ -199,7 +237,8 @@ export default function SettingReferralProgram() { className="btn-primary" disabled={isPending} onClick={() => { - setIsEditing(false) + setIsEditing(false); + setCardInfo(initialCardInfo); }} > {t('cancel')} @@ -208,7 +247,7 @@ export default function SettingReferralProgram() {