Files
no-copy-frontend/src/app/ui/referral-page/setting-referral-program.tsx
T

321 lines
7.6 KiB
TypeScript
Raw Normal View History

2026-02-24 17:22:01 +07:00
'use client'
import DropDownList from '@/app/components/DropDownList';
import { useTranslations } from 'next-intl';
import { ChangeEvent, useState, useActionState, useEffect } from 'react';
import BankCardInput from '@/app/ui/inputs/bank-card-inpit';
2026-02-25 18:17:08 +07:00
import { addPayoutMethod, PayoutMethodFormState, getPayoutMethods, PayoutMethods } from '@/app/actions/referralsActions';
2026-02-24 17:22:01 +07:00
import { bankCardFormSchema } from '@/app/actions/definitions';
2026-02-25 18:32:42 +07:00
import { useQuery, useQueryClient } from '@tanstack/react-query';
2026-02-26 11:49:32 +07:00
import { toast } from 'sonner';
2026-03-12 13:06:03 +07:00
import valid from 'card-validator';
interface cardInfoType {
cardType: string,
2026-03-14 10:49:07 +07:00
potentiallyValid: boolean,
cardValid?: boolean
2026-03-12 13:06:03 +07:00
}
2026-02-24 17:22:01 +07:00
2026-01-12 17:12:50 +07:00
export default function SettingReferralProgram() {
2026-02-24 17:22:01 +07:00
const [state, formAction, isPending] = useActionState(
addPayoutMethod,
undefined,
);
2026-02-25 18:17:08 +07:00
const {
data: payoutMethods,
error
} = useQuery({
queryKey: ['payoutMethods'],
queryFn: () => {
return getPayoutMethods();
},
select: (data) => {
if (data?.methods) {
return data?.methods
} else {
return []
};
}
});
2026-02-24 17:22:01 +07:00
const [formState, setFormState] = useState<PayoutMethodFormState | undefined>(undefined);
const [paymentsType, setPaymentsType] = useState('bank-card');
2026-02-25 18:17:08 +07:00
const [defaultCardNumber, setDefaultCardNumber] = useState<PayoutMethods | undefined>(undefined);
const [isEditing, setIsEditing] = useState(false);
2026-03-12 13:06:03 +07:00
const [cardInfo, setCardInfo] = useState<null | cardInfoType>(null);
const [initialCardInfo, setInitialCardInfo] = useState<null | cardInfoType>(null);
2026-02-25 18:32:42 +07:00
const queryClient = useQueryClient();
2026-02-24 17:22:01 +07:00
const t = useTranslations('Global');
useEffect(() => {
setFormState(state);
2026-02-26 11:49:32 +07:00
if (state?.success) {
toast.success('success');
2026-03-14 10:49:07 +07:00
setIsEditing(false);
2026-02-26 11:49:32 +07:00
queryClient.invalidateQueries({ queryKey: ['payoutMethods'] });
}
2026-02-24 17:22:01 +07:00
}, [state]);
2026-02-25 18:17:08 +07:00
useEffect(() => {
if (payoutMethods) {
payoutMethods.forEach(e => {
if (e.default) {
2026-03-12 13:06:03 +07:00
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
})
}
2026-02-25 18:17:08 +07:00
setDefaultCardNumber(e);
}
})
}
}, [payoutMethods])
2026-02-24 17:22:01 +07:00
async function validateField(
fieldName: string,
value: string,
) {
switch (fieldName) {
case 'bankCard':
const schema = bankCardFormSchema?.shape[fieldName];
if (!schema) {
return { success: true, data: value };
}
return schema.safeParse(value);
default:
throw new Error(`Unknown field: ${fieldName}`);
}
}
async function onChangeHandler(e: ChangeEvent<HTMLInputElement>): Promise<void> {
const fieldName = e.target.name;
const validatedField = await validateField(fieldName, e.target.value);
2026-03-12 13:06:03 +07:00
const numberValidation = valid.number(e.target.value);
if (numberValidation.card) {
setCardInfo({
cardType: numberValidation.card.type,
2026-03-14 10:49:07 +07:00
potentiallyValid: numberValidation.isPotentiallyValid,
cardValid: true
2026-03-12 13:06:03 +07:00
});
}
2026-02-24 17:22:01 +07:00
2026-03-20 12:35:52 +07:00
if (e.target.value.length === 19 && !numberValidation.isValid) {
2026-03-14 10:49:07 +07:00
setCardInfo((prev) => {
if (prev) {
return {
...prev,
cardValid: false
}
}
return null;
})
}
2026-02-24 17:22:01 +07:00
if (validatedField.success) {
setFormState(prevState => {
if (!prevState?.error || !prevState?.error[fieldName]) {
return {
previousState: prevState?.previousState,
2026-02-24 17:41:50 +07:00
error: {
server: null
}
2026-02-24 17:22:01 +07:00
};
}
const { [fieldName]: _, ...newError } = prevState.error;
return {
previousState: prevState?.previousState,
2026-02-24 17:41:50 +07:00
error: {
...newError,
server: null
}
2026-02-24 17:22:01 +07:00
};
});
}
}
2026-01-12 17:12:50 +07:00
return (
<div className="block-wrapper setting-referral-program">
<div className="section-title">
2026-02-24 17:22:01 +07:00
{t('referral-program-settings')}
2026-01-12 17:12:50 +07:00
</div>
2026-02-24 17:22:01 +07:00
<form
action={(e) => {
formAction(e);
}}
className="settings-form"
>
2026-01-24 13:19:48 +07:00
<div className="settings-form-wrapper">
2026-02-24 17:22:01 +07:00
<h4>{t('payout-settings')}</h4>
2026-01-12 17:12:50 +07:00
<div className="form-group">
2026-02-24 17:22:01 +07:00
<label>{t('preferred-payment-method')}</label>
<DropDownList
value={paymentsType}
translatedValue={
(() => {
switch (paymentsType) {
case 'on-account-balance':
return t('on-account-balance')
case 'bank-card':
return t('bank-card')
case 'bank-transfer':
return t('bank-transfer')
}
})()
}
callBack={setPaymentsType}
>
<li value="on-account-balance">
{t('on-account-balance')}
</li>
<li value="bank-card">
{t('bank-card')}
</li>
<li value="bank-transfer">
{t('bank-transfer')}
</li>
</DropDownList>
<input
type="hidden"
id="paymentsType"
name="paymentsType"
className={``}
value={paymentsType}
/>
2026-01-12 17:12:50 +07:00
</div>
2026-02-24 17:22:01 +07:00
{paymentsType === 'bank-card' && (
<div>
<h4>
{t('bank-card-number')}
</h4>
2026-03-12 13:06:03 +07:00
<div
className="relative"
>
<BankCardInput
bankCardState={defaultCardNumber?.cardNumber}
validateHandler={onChangeHandler}
disabled={!isEditing}
2026-03-14 10:49:07 +07:00
isValid={cardInfo?.cardValid}
2026-03-12 13:06:03 +07:00
/>
2026-03-14 10:49:07 +07:00
{/* */}
2026-03-12 13:06:03 +07:00
{cardInfo && (
<div
className="card-type"
>
{cardInfo.cardType}
</div>
)}
</div>
2026-03-14 10:49:07 +07:00
{cardInfo?.cardValid === false && (
<p className="text-sm text-red-500">
<span>
{t('bank-card-not-valid')}
</span>
</p>
)}
2026-02-24 17:22:01 +07:00
{formState?.error?.bankCard && (
<p className="text-sm text-red-500">
{
formState?.error?.bankCard.split('&').map((e, index) => {
return (
<span key={index}>
{t(e)}
<br />
</span>
)
})
}
</p>
)}
</div>
)}
{formState?.error?.server && (
<p className="text-sm text-red-500 mt-4">
{t(formState?.error?.server)}
</p>
)}
2026-02-25 18:17:08 +07:00
{
isEditing ? (
<div
className='flex gap-4'
>
<button
type="button"
className="btn-primary"
disabled={isPending}
onClick={() => {
2026-03-12 13:06:03 +07:00
setIsEditing(false);
setCardInfo(initialCardInfo);
2026-03-20 12:35:52 +07:00
setFormState(prevState => {
if (!prevState?.error || !prevState?.error['bankCard']) {
return {
previousState: prevState?.previousState,
error: {
server: null
}
};
}
});
2026-02-25 18:17:08 +07:00
}}
>
{t('cancel')}
</button>
<button
type="submit"
className="btn-primary"
2026-03-14 10:49:07 +07:00
disabled={isPending || !cardInfo?.cardValid}
2026-02-25 18:17:08 +07:00
>
{t("save-changes")}
{isPending && (
<div className="loading-animation">
<div className="global-spinner"></div>
</div>
)}
</button>
</div>
) : (
<button
type="button"
className="btn-primary"
disabled={isPending}
onClick={() => {
setIsEditing(true)
}}
>
Изменить
</button>
)
}
2026-01-12 17:12:50 +07:00
</div>
2026-01-24 13:19:48 +07:00
<div className="settings-form-wrapper">
2026-02-24 17:22:01 +07:00
<h4>{t('notifications')}</h4>
2026-01-12 17:12:50 +07:00
<div className="checkbox-group">
2026-02-24 17:22:01 +07:00
<input type="checkbox" name="email_notifications" defaultValue="1" />
<label>{t('email-notifications-about-new-referrals')}</label>
2026-01-12 17:12:50 +07:00
</div>
</div>
</form>
</div>
)
}