add bank card validation
This commit is contained in:
Generated
+18
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "no-copy-frontend",
|
"name": "no-copy-frontend",
|
||||||
"version": "0.49.0",
|
"version": "0.58.0",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "no-copy-frontend",
|
"name": "no-copy-frontend",
|
||||||
"version": "0.49.0",
|
"version": "0.58.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@a2seven/yoo-checkout": "^1.1.4",
|
"@a2seven/yoo-checkout": "^1.1.4",
|
||||||
"@react-input/mask": "^2.0.4",
|
"@react-input/mask": "^2.0.4",
|
||||||
@@ -15,6 +15,7 @@
|
|||||||
"@tanstack/react-query": "^5.90.11",
|
"@tanstack/react-query": "^5.90.11",
|
||||||
"@tanstack/react-table": "^8.21.3",
|
"@tanstack/react-table": "^8.21.3",
|
||||||
"@vkid/sdk": "^2.6.2",
|
"@vkid/sdk": "^2.6.2",
|
||||||
|
"card-validator": "^10.0.4",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"jose": "^6.1.2",
|
"jose": "^6.1.2",
|
||||||
"next": "^16.0.7",
|
"next": "^16.0.7",
|
||||||
@@ -3416,6 +3417,15 @@
|
|||||||
],
|
],
|
||||||
"license": "CC-BY-4.0"
|
"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": {
|
"node_modules/chalk": {
|
||||||
"version": "4.1.2",
|
"version": "4.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
|
||||||
@@ -3504,6 +3514,12 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"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": {
|
"node_modules/cross-spawn": {
|
||||||
"version": "7.0.6",
|
"version": "7.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
||||||
|
|||||||
+2
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "no-copy-frontend",
|
"name": "no-copy-frontend",
|
||||||
"version": "0.58.0",
|
"version": "0.59.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev -p 2999",
|
"dev": "next dev -p 2999",
|
||||||
@@ -16,6 +16,7 @@
|
|||||||
"@tanstack/react-query": "^5.90.11",
|
"@tanstack/react-query": "^5.90.11",
|
||||||
"@tanstack/react-table": "^8.21.3",
|
"@tanstack/react-table": "^8.21.3",
|
||||||
"@vkid/sdk": "^2.6.2",
|
"@vkid/sdk": "^2.6.2",
|
||||||
|
"card-validator": "^10.0.4",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"jose": "^6.1.2",
|
"jose": "^6.1.2",
|
||||||
"next": "^16.0.7",
|
"next": "^16.0.7",
|
||||||
|
|||||||
@@ -112,6 +112,15 @@
|
|||||||
&.password {
|
&.password {
|
||||||
padding-right: 32px;
|
padding-right: 32px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.valid-card {
|
||||||
|
border: 2px solid #2f9e44;
|
||||||
|
background: #ecf8ee;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-options {
|
.form-options {
|
||||||
|
|||||||
@@ -1486,6 +1486,16 @@
|
|||||||
transition: all 0.3s;
|
transition: all 0.3s;
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.card-type {
|
||||||
|
position: absolute;
|
||||||
|
right: 12px;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,16 +3,16 @@
|
|||||||
import { useState, ChangeEvent, useEffect } from 'react';
|
import { useState, ChangeEvent, useEffect } from 'react';
|
||||||
import { InputMask, format } from '@react-input/mask';
|
import { InputMask, format } from '@react-input/mask';
|
||||||
import styles from '@/app/styles/module/login.module.scss';
|
import styles from '@/app/styles/module/login.module.scss';
|
||||||
import { PayoutMethods } from '@/app/actions/referralsActions';
|
|
||||||
|
|
||||||
interface BankCardProps {
|
interface BankCardProps {
|
||||||
bankCardState: string | undefined;
|
bankCardState: string | undefined;
|
||||||
validateHandler: (e: ChangeEvent<HTMLInputElement>) => void;
|
validateHandler: (e: ChangeEvent<HTMLInputElement>) => void;
|
||||||
clearCondition?: string | undefined | null;
|
clearCondition?: string | undefined | null;
|
||||||
disabled: boolean;
|
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<string | undefined>('');
|
const [bankCard, setBankCard] = useState<string | undefined>('');
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -37,7 +37,7 @@ export default function BankCardInput({ bankCardState, validateHandler, disabled
|
|||||||
type="phone"
|
type="phone"
|
||||||
id="bankCard"
|
id="bankCard"
|
||||||
name="bankCard"
|
name="bankCard"
|
||||||
className={`${styles['form-input']}`}
|
className={`${styles['form-input']} ${isValid ? styles['valid-card'] : ''}`}
|
||||||
mask="____ ____ ____ ____"
|
mask="____ ____ ____ ____"
|
||||||
replacement={{ _: /\d/ }}
|
replacement={{ _: /\d/ }}
|
||||||
value={bankCard}
|
value={bankCard}
|
||||||
|
|||||||
@@ -10,24 +10,6 @@ export default function SecurePayments() {
|
|||||||
<h3>
|
<h3>
|
||||||
Безопасные платежи
|
Безопасные платежи
|
||||||
</h3>
|
</h3>
|
||||||
<div
|
|
||||||
className='flex gap-4'
|
|
||||||
>
|
|
||||||
<button
|
|
||||||
className='btn btn-primary'
|
|
||||||
onClick={async () => {
|
|
||||||
const response = await referralRefill();
|
|
||||||
console.log(response);
|
|
||||||
if (response?.account === 'refill') {
|
|
||||||
toast.success('refill');
|
|
||||||
} else {
|
|
||||||
toast.error(`error refill: ${response?.error?.message_desc}`);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
referralRefill
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div className="secure-payments-grid">
|
<div className="secure-payments-grid">
|
||||||
<div className="secure-payments-card">
|
<div className="secure-payments-card">
|
||||||
<div className="secure-payments-card-header">
|
<div className="secure-payments-card-header">
|
||||||
|
|||||||
@@ -8,6 +8,12 @@ import { addPayoutMethod, PayoutMethodFormState, getPayoutMethods, PayoutMethods
|
|||||||
import { bankCardFormSchema } from '@/app/actions/definitions';
|
import { bankCardFormSchema } from '@/app/actions/definitions';
|
||||||
import { useQuery, useQueryClient } from '@tanstack/react-query';
|
import { useQuery, useQueryClient } from '@tanstack/react-query';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
|
import valid from 'card-validator';
|
||||||
|
|
||||||
|
interface cardInfoType {
|
||||||
|
cardType: string,
|
||||||
|
potentiallyValid: boolean
|
||||||
|
}
|
||||||
|
|
||||||
export default function SettingReferralProgram() {
|
export default function SettingReferralProgram() {
|
||||||
const [state, formAction, isPending] = useActionState(
|
const [state, formAction, isPending] = useActionState(
|
||||||
@@ -36,6 +42,8 @@ export default function SettingReferralProgram() {
|
|||||||
const [paymentsType, setPaymentsType] = useState('bank-card');
|
const [paymentsType, setPaymentsType] = useState('bank-card');
|
||||||
const [defaultCardNumber, setDefaultCardNumber] = useState<PayoutMethods | undefined>(undefined);
|
const [defaultCardNumber, setDefaultCardNumber] = useState<PayoutMethods | undefined>(undefined);
|
||||||
const [isEditing, setIsEditing] = useState(false);
|
const [isEditing, setIsEditing] = useState(false);
|
||||||
|
const [cardInfo, setCardInfo] = useState<null | cardInfoType>(null);
|
||||||
|
const [initialCardInfo, setInitialCardInfo] = useState<null | cardInfoType>(null);
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const t = useTranslations('Global');
|
const t = useTranslations('Global');
|
||||||
|
|
||||||
@@ -52,6 +60,17 @@ export default function SettingReferralProgram() {
|
|||||||
if (payoutMethods) {
|
if (payoutMethods) {
|
||||||
payoutMethods.forEach(e => {
|
payoutMethods.forEach(e => {
|
||||||
if (e.default) {
|
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);
|
setDefaultCardNumber(e);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -78,8 +97,15 @@ export default function SettingReferralProgram() {
|
|||||||
|
|
||||||
async function onChangeHandler(e: ChangeEvent<HTMLInputElement>): Promise<void> {
|
async function onChangeHandler(e: ChangeEvent<HTMLInputElement>): Promise<void> {
|
||||||
const fieldName = e.target.name;
|
const fieldName = e.target.name;
|
||||||
|
|
||||||
const validatedField = await validateField(fieldName, e.target.value);
|
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) {
|
if (validatedField.success) {
|
||||||
setFormState(prevState => {
|
setFormState(prevState => {
|
||||||
@@ -162,11 +188,23 @@ export default function SettingReferralProgram() {
|
|||||||
<h4>
|
<h4>
|
||||||
{t('bank-card-number')}
|
{t('bank-card-number')}
|
||||||
</h4>
|
</h4>
|
||||||
|
<div
|
||||||
|
className="relative"
|
||||||
|
>
|
||||||
<BankCardInput
|
<BankCardInput
|
||||||
bankCardState={defaultCardNumber?.cardNumber}
|
bankCardState={defaultCardNumber?.cardNumber}
|
||||||
validateHandler={onChangeHandler}
|
validateHandler={onChangeHandler}
|
||||||
disabled={!isEditing}
|
disabled={!isEditing}
|
||||||
|
isValid={cardInfo?.potentiallyValid}
|
||||||
/>
|
/>
|
||||||
|
{cardInfo && (
|
||||||
|
<div
|
||||||
|
className="card-type"
|
||||||
|
>
|
||||||
|
{cardInfo.cardType}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
{formState?.error?.bankCard && (
|
{formState?.error?.bankCard && (
|
||||||
<p className="text-sm text-red-500">
|
<p className="text-sm text-red-500">
|
||||||
{
|
{
|
||||||
@@ -199,7 +237,8 @@ export default function SettingReferralProgram() {
|
|||||||
className="btn-primary"
|
className="btn-primary"
|
||||||
disabled={isPending}
|
disabled={isPending}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setIsEditing(false)
|
setIsEditing(false);
|
||||||
|
setCardInfo(initialCardInfo);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{t('cancel')}
|
{t('cancel')}
|
||||||
@@ -208,7 +247,7 @@ export default function SettingReferralProgram() {
|
|||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
className="btn-primary"
|
className="btn-primary"
|
||||||
disabled={isPending}
|
disabled={isPending || !cardInfo?.potentiallyValid}
|
||||||
>
|
>
|
||||||
{t("save-changes")}
|
{t("save-changes")}
|
||||||
{isPending && (
|
{isPending && (
|
||||||
|
|||||||
@@ -225,6 +225,7 @@
|
|||||||
"start-upload": "Start upload",
|
"start-upload": "Start upload",
|
||||||
"completed": "Completed",
|
"completed": "Completed",
|
||||||
"pending": "Pending",
|
"pending": "Pending",
|
||||||
|
"canceled": "Canceled",
|
||||||
"all-content": "All content",
|
"all-content": "All content",
|
||||||
"monitoring": "Monitoring",
|
"monitoring": "Monitoring",
|
||||||
"efficiency": "Efficiency",
|
"efficiency": "Efficiency",
|
||||||
|
|||||||
@@ -225,6 +225,7 @@
|
|||||||
"start-upload": "Начать загрузку",
|
"start-upload": "Начать загрузку",
|
||||||
"completed": "Завершено",
|
"completed": "Завершено",
|
||||||
"pending": "В ожидании",
|
"pending": "В ожидании",
|
||||||
|
"canceled": "Отменено",
|
||||||
"all-content": "Весь контент",
|
"all-content": "Весь контент",
|
||||||
"monitoring": "Мониторинг",
|
"monitoring": "Мониторинг",
|
||||||
"efficiency": "Эффективность",
|
"efficiency": "Эффективность",
|
||||||
|
|||||||
Reference in New Issue
Block a user