@akinon/pz-masterpass 1.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.prettierrc +13 -0
- package/CHANGELOG.md +7 -0
- package/README.md +122 -0
- package/assets/img/mp_amex.jpg +0 -0
- package/assets/img/mp_mastercard.png +0 -0
- package/assets/img/mp_masterpass-logo.png +0 -0
- package/assets/img/mp_other.png +0 -0
- package/assets/img/mp_troy.png +0 -0
- package/assets/img/mp_visa.png +0 -0
- package/assets/mfs-client.min.js +3 -0
- package/assets/zepto.min.js +2 -0
- package/package.json +18 -0
- package/src/hooks/use-cards.ts +51 -0
- package/src/hooks/use-countdown.ts +28 -0
- package/src/hooks/use-delete-card.tsx +88 -0
- package/src/index.d.ts +3 -0
- package/src/index.ts +17 -0
- package/src/masterpass-provider.tsx +141 -0
- package/src/redux/reducer.ts +126 -0
- package/src/types/index.ts +111 -0
- package/src/utils/check-masterpass.ts +66 -0
- package/src/utils/get-masterpass-cards.ts +32 -0
- package/src/utils/index.ts +177 -0
- package/src/utils/init.ts +17 -0
- package/src/views/card-list/index.tsx +166 -0
- package/src/views/card-registration/index.tsx +250 -0
- package/src/views/countdown-timer/countdown-timer.tsx +43 -0
- package/src/views/delete-confirmation-modal/index.tsx +64 -0
- package/src/views/info-modal/index.tsx +102 -0
- package/src/views/link-modal/index.tsx +135 -0
- package/src/views/otp-modal/index.tsx +148 -0
- package/src/views/otp-modal/otp-form.tsx +86 -0
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { LoaderSpinner } from 'components';
|
|
4
|
+
import { MasterpassStatus } from '../../types';
|
|
5
|
+
import { getCreditCardType } from '../../utils';
|
|
6
|
+
|
|
7
|
+
import amex from '../../../assets/img/mp_amex.jpg';
|
|
8
|
+
import mastercard from '../../../assets/img/mp_mastercard.png';
|
|
9
|
+
import masterpassLogo from '../../../assets/img/mp_masterpass-logo.png';
|
|
10
|
+
import other from '../../../assets/img/mp_other.png';
|
|
11
|
+
import troy from '../../../assets/img/mp_troy.png';
|
|
12
|
+
import visa from '../../../assets/img/mp_visa.png';
|
|
13
|
+
|
|
14
|
+
import { useAppDispatch, useAppSelector } from '@akinon/next/redux/hooks';
|
|
15
|
+
import { useCards } from '../../hooks/use-cards';
|
|
16
|
+
import { useDeleteCard } from '../../hooks/use-delete-card';
|
|
17
|
+
import { useSetBinNumberMutation } from '@akinon/next/data/client/checkout';
|
|
18
|
+
import { Button, Image } from '@akinon/next/components';
|
|
19
|
+
import { twMerge } from 'tailwind-merge';
|
|
20
|
+
import { setIsDirectPurchase, setSelectedCard } from '../../redux/reducer';
|
|
21
|
+
import { setInstallmentOptions } from '@akinon/next/redux/reducers/checkout';
|
|
22
|
+
|
|
23
|
+
const cardImages = {
|
|
24
|
+
amex,
|
|
25
|
+
mastercard,
|
|
26
|
+
troy,
|
|
27
|
+
visa,
|
|
28
|
+
other
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const defaultTranslations = {
|
|
32
|
+
title: 'Select a card to pay with',
|
|
33
|
+
pay_with_new_card: 'Pay with a new card',
|
|
34
|
+
retryFetchCards: 'Retry Fetching Cards'
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export interface MasterpassCardListProps {
|
|
38
|
+
className?: string;
|
|
39
|
+
translations?: typeof defaultTranslations;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export const MasterpassCardList = ({
|
|
43
|
+
className,
|
|
44
|
+
translations
|
|
45
|
+
}: MasterpassCardListProps) => {
|
|
46
|
+
const { preOrder } = useAppSelector((state) => state.checkout);
|
|
47
|
+
const { accountStatus, isDirectPurchase, selectedCard } = useAppSelector(
|
|
48
|
+
(state) => state.masterpass
|
|
49
|
+
);
|
|
50
|
+
const [setMasterpassBinNumber] = useSetBinNumberMutation();
|
|
51
|
+
const { cards, loading, error, refreshCards } = useCards({ skipData: false });
|
|
52
|
+
const { DeleteButton } = useDeleteCard();
|
|
53
|
+
const dispatch = useAppDispatch();
|
|
54
|
+
|
|
55
|
+
const switchPaymentButton = (
|
|
56
|
+
<a
|
|
57
|
+
href="#"
|
|
58
|
+
className="text-xs underline"
|
|
59
|
+
onClick={(e) => {
|
|
60
|
+
e.preventDefault();
|
|
61
|
+
dispatch(setIsDirectPurchase(true));
|
|
62
|
+
dispatch(setInstallmentOptions([]));
|
|
63
|
+
}}
|
|
64
|
+
>
|
|
65
|
+
{translations?.pay_with_new_card ?? defaultTranslations.pay_with_new_card}
|
|
66
|
+
</a>
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
if (accountStatus !== MasterpassStatus.ListCards) {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (loading) {
|
|
74
|
+
return (
|
|
75
|
+
<div className="p-5">
|
|
76
|
+
<LoaderSpinner className="w-8 h-8" />
|
|
77
|
+
</div>
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (error && !isDirectPurchase) {
|
|
82
|
+
return (
|
|
83
|
+
<div className="flex flex-col items-center">
|
|
84
|
+
<div className="p-5">{error}</div>
|
|
85
|
+
<Button
|
|
86
|
+
className="w-48 mb-5"
|
|
87
|
+
onClick={() => {
|
|
88
|
+
refreshCards();
|
|
89
|
+
}}
|
|
90
|
+
>
|
|
91
|
+
{translations?.retryFetchCards ?? defaultTranslations.retryFetchCards}
|
|
92
|
+
</Button>
|
|
93
|
+
|
|
94
|
+
{switchPaymentButton}
|
|
95
|
+
</div>
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (
|
|
100
|
+
preOrder.payment_option?.payment_type !== 'masterpass' ||
|
|
101
|
+
!cards?.length ||
|
|
102
|
+
isDirectPurchase
|
|
103
|
+
) {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return (
|
|
108
|
+
<div className={twMerge('w-full', className)}>
|
|
109
|
+
<Image
|
|
110
|
+
className="mb-4"
|
|
111
|
+
width={140}
|
|
112
|
+
height={25}
|
|
113
|
+
src={masterpassLogo.src}
|
|
114
|
+
alt="Masterpass Logo"
|
|
115
|
+
/>
|
|
116
|
+
<p className="text-xs">
|
|
117
|
+
{translations?.title ?? defaultTranslations.title}
|
|
118
|
+
</p>
|
|
119
|
+
<ul className="mt-4 text-xs">
|
|
120
|
+
{cards?.map((card) => {
|
|
121
|
+
return (
|
|
122
|
+
<li
|
|
123
|
+
key={card.UniqueId}
|
|
124
|
+
className="p-4 mb-2 border-2 border-gray-200 flex justify-between items-center space-x-2 cursor-pointer"
|
|
125
|
+
onClick={async () => {
|
|
126
|
+
await setMasterpassBinNumber(
|
|
127
|
+
card.Value1.substring(0, 6)
|
|
128
|
+
).unwrap();
|
|
129
|
+
|
|
130
|
+
dispatch(setSelectedCard(card));
|
|
131
|
+
}}
|
|
132
|
+
>
|
|
133
|
+
<input
|
|
134
|
+
name="masterpass-card"
|
|
135
|
+
type="radio"
|
|
136
|
+
checked={selectedCard?.UniqueId === card.UniqueId}
|
|
137
|
+
value={card.Name}
|
|
138
|
+
id={card.UniqueId}
|
|
139
|
+
className="mr-2"
|
|
140
|
+
onChange={(e) => e.preventDefault()}
|
|
141
|
+
/>
|
|
142
|
+
<label
|
|
143
|
+
htmlFor={card.UniqueId}
|
|
144
|
+
className="flex flex-col w-full cursor-pointer md:flex-row md:items-center md:justify-between"
|
|
145
|
+
>
|
|
146
|
+
<p className="w-full lg:w-1/3">{card.Name}</p>
|
|
147
|
+
<p className="w-full text-[10px] lg:w-1/3">{card.Value1}</p>
|
|
148
|
+
<Image
|
|
149
|
+
className="w-8 h-6 object-contain flex items-center justify-center mr-4"
|
|
150
|
+
width={50}
|
|
151
|
+
height={50}
|
|
152
|
+
src={cardImages[getCreditCardType(card.Value1)].src}
|
|
153
|
+
alt={card.Name}
|
|
154
|
+
/>
|
|
155
|
+
</label>
|
|
156
|
+
|
|
157
|
+
<DeleteButton cardAliasName={card.Name} />
|
|
158
|
+
</li>
|
|
159
|
+
);
|
|
160
|
+
})}
|
|
161
|
+
</ul>
|
|
162
|
+
|
|
163
|
+
{switchPaymentButton}
|
|
164
|
+
</div>
|
|
165
|
+
);
|
|
166
|
+
};
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import { useAppDispatch, useAppSelector } from '@akinon/next/redux/hooks';
|
|
2
|
+
import { Button, Checkbox, Icon, Input, LoaderSpinner } from 'components';
|
|
3
|
+
import React, { useEffect, useState } from 'react';
|
|
4
|
+
import { RootState } from 'redux/store';
|
|
5
|
+
import masterpassLogo from '../../../assets/img/mp_masterpass-logo.png';
|
|
6
|
+
import { formCreator } from '../../utils';
|
|
7
|
+
import { InfoModal } from '../info-modal';
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
setAccountStatus,
|
|
11
|
+
setIsDirectPurchase,
|
|
12
|
+
setOtpModalVisible,
|
|
13
|
+
setOtpResponse
|
|
14
|
+
} from '../../redux/reducer';
|
|
15
|
+
import { MasterpassStatus } from '../../types';
|
|
16
|
+
import { useCards } from '../../hooks/use-cards';
|
|
17
|
+
import { Image } from '@akinon/next/components/image';
|
|
18
|
+
import { twMerge } from 'tailwind-merge';
|
|
19
|
+
import { setInstallmentOptions } from '@akinon/next/redux/reducers/checkout';
|
|
20
|
+
|
|
21
|
+
const defaultTranslations = {
|
|
22
|
+
enter_card_name: 'Enter card name',
|
|
23
|
+
continue: 'Continue',
|
|
24
|
+
pay_with_my_masterpass_card: 'Pay with my Masterpass card',
|
|
25
|
+
terms_and_conditions: 'Masterpass terms and conditions',
|
|
26
|
+
card_registration_consent:
|
|
27
|
+
'I want to store my card information in the Mastercard infrastructure and use it again in my next purchase.'
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
export const MasterpassCardRegistration = ({
|
|
31
|
+
getValues,
|
|
32
|
+
className,
|
|
33
|
+
translations
|
|
34
|
+
}: {
|
|
35
|
+
getValues: () => Record<string, string | number | boolean>;
|
|
36
|
+
className?: string;
|
|
37
|
+
translations?: typeof defaultTranslations;
|
|
38
|
+
}) => {
|
|
39
|
+
const { preOrder } = useAppSelector((state: RootState) => state.checkout);
|
|
40
|
+
const { msisdn, token, language, otp, isDirectPurchase } = useAppSelector(
|
|
41
|
+
(state) => state.masterpass
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
const [isInfoModalOpen, setIsInfoModalOpen] = useState(false);
|
|
45
|
+
const [isAgreementChecked, setIsAgreementChecked] = useState(false);
|
|
46
|
+
const [accountAliasName, setAccountAliasName] = useState('');
|
|
47
|
+
const [formError, setFormError] = useState<string | null>(null);
|
|
48
|
+
const [isBusy, setIsBusy] = useState(false);
|
|
49
|
+
const { refreshCards } = useCards();
|
|
50
|
+
|
|
51
|
+
const dispatch = useAppDispatch();
|
|
52
|
+
|
|
53
|
+
const registerCard = async () => {
|
|
54
|
+
const formValues = getValues();
|
|
55
|
+
setFormError(null);
|
|
56
|
+
|
|
57
|
+
if (isBusy) return;
|
|
58
|
+
|
|
59
|
+
if (!accountAliasName) {
|
|
60
|
+
setFormError(
|
|
61
|
+
translations?.enter_card_name ?? defaultTranslations.enter_card_name
|
|
62
|
+
);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const cardNumber = String(formValues['card_number']).replace(/\s/g, '');
|
|
67
|
+
const cardCvv = String(formValues['card_cvv']);
|
|
68
|
+
const cardYear = String(formValues['card_year']);
|
|
69
|
+
const cardMonth = String(formValues['card_month']);
|
|
70
|
+
const expirationMonth = `0${cardMonth}`.slice(-2);
|
|
71
|
+
const expirationYear = cardYear?.slice(2, 4);
|
|
72
|
+
|
|
73
|
+
const fields = [
|
|
74
|
+
{ name: 'userId', value: msisdn },
|
|
75
|
+
{ name: 'msisdn', value: msisdn },
|
|
76
|
+
{
|
|
77
|
+
name: 'token',
|
|
78
|
+
value: token
|
|
79
|
+
},
|
|
80
|
+
{ name: 'sendSmsLanguage', value: language },
|
|
81
|
+
{ name: 'accountAliasName', value: accountAliasName },
|
|
82
|
+
{ name: 'rtaPan', value: cardNumber },
|
|
83
|
+
{ name: 'expiryDate', value: `${expirationYear}${expirationMonth}` },
|
|
84
|
+
{ name: 'cvc', value: cardCvv },
|
|
85
|
+
{ name: 'sendSms', value: 'N' },
|
|
86
|
+
{ name: 'actionType', value: 'A' },
|
|
87
|
+
{ name: 'clientIp', value: '' },
|
|
88
|
+
{ name: 'delinkReason', value: '' },
|
|
89
|
+
{ name: 'eActionType', value: 'A' },
|
|
90
|
+
{ name: 'cardTypeFlag', value: '05' },
|
|
91
|
+
{ name: 'cpinFlag', value: 'Y' },
|
|
92
|
+
{ name: 'defaultAccount', value: 'Y' },
|
|
93
|
+
{ name: 'mmrpConfig', value: '110010' },
|
|
94
|
+
{ name: 'identityVerificationFlag', value: 'N' },
|
|
95
|
+
{ name: 'mobileAccountConfig', value: 'MWA' },
|
|
96
|
+
{ name: 'timeZone', value: '+01' },
|
|
97
|
+
{ name: 'uiChannelType', value: '6' },
|
|
98
|
+
{ name: 'clientType', value: '1' }
|
|
99
|
+
];
|
|
100
|
+
|
|
101
|
+
const form = formCreator({
|
|
102
|
+
id: 'register-masterpass-form',
|
|
103
|
+
fields
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
form
|
|
107
|
+
.querySelectorAll<HTMLInputElement>('[name="msisdn"], [name="userId"]')
|
|
108
|
+
.forEach((input) => {
|
|
109
|
+
input.value = `9${preOrder.user_phone_number}`;
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
setIsBusy(true);
|
|
113
|
+
|
|
114
|
+
window.MFS.register($(form), async (statusCode, response) => {
|
|
115
|
+
setIsBusy(false);
|
|
116
|
+
|
|
117
|
+
const showOtpModal = ['5001', '5008'].includes(response.responseCode);
|
|
118
|
+
|
|
119
|
+
dispatch(setOtpModalVisible(showOtpModal));
|
|
120
|
+
|
|
121
|
+
if (showOtpModal) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (response.responseCode === '0000' || response.responseCode === '') {
|
|
126
|
+
setIsBusy(false);
|
|
127
|
+
setIsAgreementChecked(false);
|
|
128
|
+
setAccountAliasName('');
|
|
129
|
+
dispatch(setAccountStatus(MasterpassStatus.ListCards));
|
|
130
|
+
refreshCards();
|
|
131
|
+
dispatch(setOtpModalVisible(false));
|
|
132
|
+
} else {
|
|
133
|
+
setFormError(response.responseDescription);
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
useEffect(() => {
|
|
139
|
+
if (
|
|
140
|
+
otp.response?.responseCode === '0000' ||
|
|
141
|
+
otp.response?.responseCode === '3838' ||
|
|
142
|
+
otp.response?.responseCode === ''
|
|
143
|
+
) {
|
|
144
|
+
dispatch(setAccountStatus(MasterpassStatus.ListCards));
|
|
145
|
+
dispatch(setOtpResponse(undefined));
|
|
146
|
+
setAccountAliasName('');
|
|
147
|
+
refreshCards();
|
|
148
|
+
dispatch(setOtpModalVisible(false));
|
|
149
|
+
}
|
|
150
|
+
}, [otp.response]);
|
|
151
|
+
|
|
152
|
+
if (
|
|
153
|
+
preOrder.payment_option?.payment_type !== 'masterpass' ||
|
|
154
|
+
!isDirectPurchase ||
|
|
155
|
+
!token
|
|
156
|
+
) {
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return (
|
|
161
|
+
<div className={twMerge('w-full', className)}>
|
|
162
|
+
<InfoModal open={isInfoModalOpen} setOpen={setIsInfoModalOpen} />
|
|
163
|
+
|
|
164
|
+
<div className="border border-[#ddd]">
|
|
165
|
+
<div className="p-4">
|
|
166
|
+
<div className="flex items-center">
|
|
167
|
+
<div className="mr-4">
|
|
168
|
+
<Checkbox
|
|
169
|
+
checked={isAgreementChecked}
|
|
170
|
+
onChange={() => {
|
|
171
|
+
setIsAgreementChecked(!isAgreementChecked);
|
|
172
|
+
}}
|
|
173
|
+
/>
|
|
174
|
+
</div>
|
|
175
|
+
<div className="mr-4">
|
|
176
|
+
<Image
|
|
177
|
+
className="w-40 h-6 object-contain"
|
|
178
|
+
width={140}
|
|
179
|
+
height={25}
|
|
180
|
+
src={masterpassLogo.src}
|
|
181
|
+
alt="masterpass"
|
|
182
|
+
/>
|
|
183
|
+
</div>
|
|
184
|
+
<div
|
|
185
|
+
className="cursor-pointer w-5 h-5 ml-auto"
|
|
186
|
+
onClick={() => setIsInfoModalOpen(true)}
|
|
187
|
+
>
|
|
188
|
+
<Icon name="bell" />
|
|
189
|
+
</div>
|
|
190
|
+
</div>
|
|
191
|
+
<p className="mt-5 text-sm">
|
|
192
|
+
{translations?.card_registration_consent ??
|
|
193
|
+
defaultTranslations.card_registration_consent}
|
|
194
|
+
</p>
|
|
195
|
+
<span
|
|
196
|
+
onClick={() => setIsInfoModalOpen(true)}
|
|
197
|
+
className="mt-5 text-sm underline cursor-pointer"
|
|
198
|
+
>
|
|
199
|
+
{translations?.terms_and_conditions ??
|
|
200
|
+
defaultTranslations.terms_and_conditions}
|
|
201
|
+
</span>
|
|
202
|
+
{isAgreementChecked && (
|
|
203
|
+
<div className="mt-5">
|
|
204
|
+
<Input
|
|
205
|
+
label={
|
|
206
|
+
translations?.enter_card_name ??
|
|
207
|
+
defaultTranslations.enter_card_name
|
|
208
|
+
}
|
|
209
|
+
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
|
|
210
|
+
setAccountAliasName(e.target.value);
|
|
211
|
+
}}
|
|
212
|
+
/>
|
|
213
|
+
<Button
|
|
214
|
+
type="button"
|
|
215
|
+
onClick={(event: React.MouseEvent<HTMLButtonElement>) => {
|
|
216
|
+
event.preventDefault();
|
|
217
|
+
registerCard();
|
|
218
|
+
}}
|
|
219
|
+
className="w-full uppercase mt-2"
|
|
220
|
+
>
|
|
221
|
+
{!isBusy ? (
|
|
222
|
+
translations?.continue ?? defaultTranslations.continue
|
|
223
|
+
) : (
|
|
224
|
+
<LoaderSpinner className="border-b-white w-4 h-4" />
|
|
225
|
+
)}
|
|
226
|
+
</Button>
|
|
227
|
+
|
|
228
|
+
{formError && (
|
|
229
|
+
<p className="mt-2 text-error text-xs">{formError}</p>
|
|
230
|
+
)}
|
|
231
|
+
</div>
|
|
232
|
+
)}
|
|
233
|
+
</div>
|
|
234
|
+
</div>
|
|
235
|
+
|
|
236
|
+
<a
|
|
237
|
+
href="#"
|
|
238
|
+
className="block text-xs underline mt-5"
|
|
239
|
+
onClick={(e) => {
|
|
240
|
+
e.preventDefault();
|
|
241
|
+
dispatch(setIsDirectPurchase(false));
|
|
242
|
+
dispatch(setInstallmentOptions([]));
|
|
243
|
+
}}
|
|
244
|
+
>
|
|
245
|
+
{translations?.pay_with_my_masterpass_card ??
|
|
246
|
+
defaultTranslations.pay_with_my_masterpass_card}
|
|
247
|
+
</a>
|
|
248
|
+
</div>
|
|
249
|
+
);
|
|
250
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Button, LoaderSpinner } from 'components';
|
|
3
|
+
import { useTranslation } from '@akinon/next/hooks';
|
|
4
|
+
import { useCountdown } from '../../hooks/use-countdown';
|
|
5
|
+
|
|
6
|
+
const SendSms = ({ resendSms, resendSmsFetching }) => {
|
|
7
|
+
const { t } = useTranslation('checkout');
|
|
8
|
+
|
|
9
|
+
return (
|
|
10
|
+
<Button
|
|
11
|
+
onClick={() => resendSms()}
|
|
12
|
+
className="mt-2 w-full text-primary bg-[#cccccc] border-0 hover:border"
|
|
13
|
+
>
|
|
14
|
+
{!resendSmsFetching ? (
|
|
15
|
+
t('payment.masterpass.add.send_sms_again')
|
|
16
|
+
) : (
|
|
17
|
+
<LoaderSpinner />
|
|
18
|
+
)}
|
|
19
|
+
</Button>
|
|
20
|
+
);
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const ShowCounter = ({ minutes, seconds }) => {
|
|
24
|
+
return (
|
|
25
|
+
<div className="flex">
|
|
26
|
+
{`0${minutes}`} : {`${seconds < 10 ? '0' : ''}${seconds}`}
|
|
27
|
+
</div>
|
|
28
|
+
);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const CountdownTimer = ({ targetDate, resendSms, resendSmsFetching }) => {
|
|
32
|
+
const [minutes, seconds] = useCountdown(targetDate);
|
|
33
|
+
|
|
34
|
+
if (minutes + seconds <= 0) {
|
|
35
|
+
return (
|
|
36
|
+
<SendSms resendSms={resendSms} resendSmsFetching={resendSmsFetching} />
|
|
37
|
+
);
|
|
38
|
+
} else {
|
|
39
|
+
return <ShowCounter minutes={minutes} seconds={seconds} />;
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
export default CountdownTimer;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { Button, LoaderSpinner, Modal, Image } from '@akinon/next/components';
|
|
4
|
+
|
|
5
|
+
import { useAppDispatch, useAppSelector } from '@akinon/next/redux/hooks';
|
|
6
|
+
import masterpassLogo from '../../../assets/img/mp_masterpass-logo.png';
|
|
7
|
+
import { useDeleteCard } from '../../hooks/use-delete-card';
|
|
8
|
+
import { setDeletionModalVisible } from '../../redux/reducer';
|
|
9
|
+
|
|
10
|
+
export interface MasterpassDeleteConfirmationModalProps {
|
|
11
|
+
translations?: {
|
|
12
|
+
remove_title?: string;
|
|
13
|
+
remove_button?: string;
|
|
14
|
+
cancel_button?: string;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const MasterpassDeleteConfirmationModal = ({
|
|
19
|
+
translations
|
|
20
|
+
}: MasterpassDeleteConfirmationModalProps) => {
|
|
21
|
+
const { deletion } = useAppSelector((state) => state.masterpass);
|
|
22
|
+
const { deleteCard, isLoading, error } = useDeleteCard();
|
|
23
|
+
const dispatch = useAppDispatch();
|
|
24
|
+
|
|
25
|
+
return (
|
|
26
|
+
<Modal
|
|
27
|
+
portalId="masterpass-remove-card-modal"
|
|
28
|
+
title={
|
|
29
|
+
<Image
|
|
30
|
+
width={120}
|
|
31
|
+
height={21}
|
|
32
|
+
src={masterpassLogo.src}
|
|
33
|
+
alt="Masterpass Logo"
|
|
34
|
+
/>
|
|
35
|
+
}
|
|
36
|
+
className="w-full sm:w-[28rem] max-h-[90vh] overflow-y-auto"
|
|
37
|
+
open={deletion.isModalVisible}
|
|
38
|
+
setOpen={() => dispatch(setDeletionModalVisible(false))}
|
|
39
|
+
>
|
|
40
|
+
<div className="px-6">
|
|
41
|
+
<h3 className="text-center mt-4 text-lg">
|
|
42
|
+
{translations?.remove_title ?? 'Remove Card'}
|
|
43
|
+
</h3>
|
|
44
|
+
<div className="flex flex-col gap-3 p-5 w-3/4 m-auto">
|
|
45
|
+
<Button className="py-3 h-auto" onClick={deleteCard}>
|
|
46
|
+
{isLoading ? (
|
|
47
|
+
<LoaderSpinner className="w-4 h-4" />
|
|
48
|
+
) : (
|
|
49
|
+
translations?.remove_button ?? 'Remove'
|
|
50
|
+
)}
|
|
51
|
+
</Button>
|
|
52
|
+
<Button
|
|
53
|
+
appearance="outlined"
|
|
54
|
+
className="underline px-5 py-3 h-auto"
|
|
55
|
+
onClick={() => dispatch(setDeletionModalVisible(false))}
|
|
56
|
+
>
|
|
57
|
+
{translations?.cancel_button ?? 'Cancel'}
|
|
58
|
+
</Button>
|
|
59
|
+
{error && <p className="text-error text-xs text-center">{error}</p>}
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
</Modal>
|
|
63
|
+
);
|
|
64
|
+
};
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { Modal } from 'components';
|
|
3
|
+
import masterpassLogo from '../../../assets/img/mp_masterpass-logo.png';
|
|
4
|
+
import { Image } from '@akinon/next/components/image';
|
|
5
|
+
|
|
6
|
+
interface InfoModalProps {
|
|
7
|
+
open: boolean;
|
|
8
|
+
setOpen: (arg: boolean) => void;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const InfoModal = ({ open, setOpen }: InfoModalProps) => {
|
|
12
|
+
return (
|
|
13
|
+
<Modal
|
|
14
|
+
portalId="agreement-masterpass"
|
|
15
|
+
title={
|
|
16
|
+
<Image
|
|
17
|
+
width={140}
|
|
18
|
+
height={25}
|
|
19
|
+
src={masterpassLogo.src}
|
|
20
|
+
alt="Masterpass Logo"
|
|
21
|
+
/>
|
|
22
|
+
}
|
|
23
|
+
open={open}
|
|
24
|
+
setOpen={setOpen}
|
|
25
|
+
className="w-full overflow-y-auto sm:w-[28rem] max-h-[90vh]"
|
|
26
|
+
>
|
|
27
|
+
<div className="px-6 py-4">
|
|
28
|
+
<p>
|
|
29
|
+
Giriş
|
|
30
|
+
<br />
|
|
31
|
+
<br />
|
|
32
|
+
MasterPass™ by Mastercard ("MasterPass"), Mastercard Europe SA
|
|
33
|
+
("Mastercard") tarafından sunulan ve seçtiğiniz ödeme ve teslimat
|
|
34
|
+
bilgilerinizi uygun ve güvenli bir yerde ("MasterPass by Mastercard,"
|
|
35
|
+
veya "MasterPass") toplayan, talebiniz üzerine sözkonusu bilgiyi web
|
|
36
|
+
sitelerinde, mobil sitelerde veya akıllı telefon uygulamalarında
|
|
37
|
+
MasterPass ödeme işareti ("MasterPass Checkout Butonu") taşıyan
|
|
38
|
+
çevrimiçi firmalara (herbiri "MasterPass Firması") aktaran bir dijital
|
|
39
|
+
ödeme hizmetidir.
|
|
40
|
+
<br />
|
|
41
|
+
<br />
|
|
42
|
+
İşbu Kullanım Şartları ("KŞ") ve MasterPass Gizlilik Bildirimi
|
|
43
|
+
("Gizlilik Bildirimi"), Mastercard ile aranızdaki bir yasal
|
|
44
|
+
sözleşmedir. İŞBU KŞ VE GİZLİLİK POLİTİKASINI KABUL ET SEÇENEĞİNE
|
|
45
|
+
TIKLANMASINI İÇEREN, MASTERPASS İNTERNET SAYFASI ÜZERİNDEN
|
|
46
|
+
MASTERPASS’E KAYIT OLMAKLA, İŞBU KŞ'DE VE GİZLİLİK BİLDİRİMİNDE YER
|
|
47
|
+
ALAN TÜM ŞART VE KOŞULLAR İLE MASTERPASS İLE İLGİLİ İŞBU KŞ'DE
|
|
48
|
+
TANIMLANDIĞI ÜZERE MASTERCARD TARAFINDAN SİZE ZAMAN ZAMAN
|
|
49
|
+
SUNULABİLECEK DİĞER KURAL, İLKE VE PROSEDÜRLERİ KABUL ETMEKTESİNİZ.
|
|
50
|
+
<br />
|
|
51
|
+
<br />
|
|
52
|
+
MasterPass™ by Mastercard ("MasterPass"), Mastercard Europe SA
|
|
53
|
+
("Mastercard") tarafından sunulan ve seçtiğiniz ödeme ve teslimat
|
|
54
|
+
MasterPass™ by Mastercard ("MasterPass"), Mastercard Europe SA
|
|
55
|
+
("Mastercard") tarafından sunulan ve seçtiğiniz ödeme ve teslimat
|
|
56
|
+
bilgilerinizi uygun ve güvenli bir yerde ("MasterPass by Mastercard,"
|
|
57
|
+
veya "MasterPass") toplayan, talebiniz üzerine sözkonusu bilgiyi web
|
|
58
|
+
sitelerinde, mobil sitelerde veya akıllı telefon uygulamalarında
|
|
59
|
+
MasterPass ödeme işareti ("MasterPass Checkout Butonu") taşıyan
|
|
60
|
+
çevrimiçi firmalara (herbiri "MasterPass Firması") aktaran bir dijital
|
|
61
|
+
ödeme hizmetidir.
|
|
62
|
+
<br />
|
|
63
|
+
<br />
|
|
64
|
+
İşbu Kullanım Şartları ("KŞ") ve MasterPass Gizlilik Bildirimi
|
|
65
|
+
("Gizlilik Bildirimi"), Mastercard ile aranızdaki bir yasal
|
|
66
|
+
sözleşmedir. İŞBU KŞ VE GİZLİLİK POLİTİKASINI KABUL ET SEÇENEĞİNE
|
|
67
|
+
TIKLANMASINI İÇEREN, MASTERPASS İNTERNET SAYFASI ÜZERİNDEN
|
|
68
|
+
MASTERPASS’E KAYIT OLMAKLA, İŞBU KŞ'DE VE GİZLİLİK BİLDİRİMİNDE YER
|
|
69
|
+
ALAN TÜM ŞART VE KOŞULLAR İLE MASTERPASS İLE İLGİLİ İŞBU KŞ'DE
|
|
70
|
+
TANIMLANDIĞI ÜZERE MASTERCARD TARAFINDAN SİZE ZAMAN ZAMAN
|
|
71
|
+
SUNULABİLECEK DİĞER KURAL, İLKE VE PROSEDÜRLERİ KABUL ETMEKTESİNİZ.
|
|
72
|
+
<br />
|
|
73
|
+
<br />
|
|
74
|
+
MasterPass™ by Mastercard ("MasterPass"), Mastercard Europe SA
|
|
75
|
+
("Mastercard") tarafından sunulan ve seçtiğiniz ödeme ve teslimat
|
|
76
|
+
MasterPass™ by Mastercard ("MasterPass"), Mastercard Europe SA
|
|
77
|
+
("Mastercard") tarafından sunulan ve seçtiğiniz ödeme ve teslimat
|
|
78
|
+
bilgilerinizi uygun ve güvenli bir yerde ("MasterPass by Mastercard,"
|
|
79
|
+
veya "MasterPass") toplayan, talebiniz üzerine sözkonusu bilgiyi web
|
|
80
|
+
sitelerinde, mobil sitelerde veya akıllı telefon uygulamalarında
|
|
81
|
+
MasterPass ödeme işareti ("MasterPass Checkout Butonu") taşıyan
|
|
82
|
+
çevrimiçi firmalara (herbiri "MasterPass Firması") aktaran bir dijital
|
|
83
|
+
ödeme hizmetidir.
|
|
84
|
+
<br />
|
|
85
|
+
<br />
|
|
86
|
+
İşbu Kullanım Şartları ("KŞ") ve MasterPass Gizlilik Bildirimi
|
|
87
|
+
("Gizlilik Bildirimi"), Mastercard ile aranızdaki bir yasal
|
|
88
|
+
sözleşmedir. İŞBU KŞ VE GİZLİLİK POLİTİKASINI KABUL ET SEÇENEĞİNE
|
|
89
|
+
TIKLANMASINI İÇEREN, MASTERPASS İNTERNET SAYFASI ÜZERİNDEN
|
|
90
|
+
MASTERPASS’E KAYIT OLMAKLA, İŞBU KŞ'DE VE GİZLİLİK BİLDİRİMİNDE YER
|
|
91
|
+
ALAN TÜM ŞART VE KOŞULLAR İLE MASTERPASS İLE İLGİLİ İŞBU KŞ'DE
|
|
92
|
+
TANIMLANDIĞI ÜZERE MASTERCARD TARAFINDAN SİZE ZAMAN ZAMAN
|
|
93
|
+
SUNULABİLECEK DİĞER KURAL, İLKE VE PROSEDÜRLERİ KABUL ETMEKTESİNİZ.
|
|
94
|
+
<br />
|
|
95
|
+
<br />
|
|
96
|
+
MasterPass™ by Mastercard ("MasterPass"), Mastercard Europe SA
|
|
97
|
+
("Mastercard") tarafından sunulan ve seçtiğiniz ödeme ve teslimat
|
|
98
|
+
</p>
|
|
99
|
+
</div>
|
|
100
|
+
</Modal>
|
|
101
|
+
);
|
|
102
|
+
};
|