ordering-ui-react-native 0.17.94 → 0.17.96
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/package.json +1 -1
- package/themes/original/src/components/AddressForm/index.tsx +2 -2
- package/themes/original/src/components/Checkout/index.tsx +6 -0
- package/themes/original/src/components/ProductForm/index.tsx +29 -2
- package/themes/original/src/components/ProductOptionSubOption/index.tsx +2 -2
- package/themes/original/src/components/ProductOptionSubOption/styles.tsx +1 -1
- package/themes/original/src/components/ServiceForm/index.tsx +24 -1
- package/themes/original/src/components/SignupForm/index.tsx +16 -11
- package/themes/original/src/components/UserFormDetails/index.tsx +44 -6
- package/themes/original/src/types/index.tsx +4 -1
- package/themes/original/src/utils/index.tsx +5 -0
package/package.json
CHANGED
|
@@ -364,7 +364,7 @@ const AddressFormUI = (props: AddressFormParams) => {
|
|
|
364
364
|
if (
|
|
365
365
|
orderState.loading &&
|
|
366
366
|
!addressesList &&
|
|
367
|
-
orderState
|
|
367
|
+
orderState?.options?.address &&
|
|
368
368
|
auth &&
|
|
369
369
|
!afterSignup
|
|
370
370
|
) {
|
|
@@ -372,7 +372,7 @@ const AddressFormUI = (props: AddressFormParams) => {
|
|
|
372
372
|
? navigation.navigate('BottomTab')
|
|
373
373
|
: navigation.navigate('Business');
|
|
374
374
|
}
|
|
375
|
-
}, [orderState
|
|
375
|
+
}, [orderState?.options?.address]);
|
|
376
376
|
|
|
377
377
|
useEffect(() => {
|
|
378
378
|
if (alertState.open && alertState?.key !== 'ERROR_MAX_LIMIT_LOCATION') {
|
|
@@ -212,6 +212,11 @@ const CheckoutUI = (props: any) => {
|
|
|
212
212
|
setIsUserDetailsEdit(true)
|
|
213
213
|
}
|
|
214
214
|
|
|
215
|
+
const handlePlaceOrderAsGuest = () => {
|
|
216
|
+
setIsOpen(false)
|
|
217
|
+
handlerClickPlaceOrder && handlerClickPlaceOrder()
|
|
218
|
+
}
|
|
219
|
+
|
|
215
220
|
const handlePaymentMethodClick = (paymethod: any) => {
|
|
216
221
|
setShowGateway({ closedByUser: false, open: true })
|
|
217
222
|
setWebviewPaymethod(paymethod)
|
|
@@ -765,6 +770,7 @@ const CheckoutUI = (props: any) => {
|
|
|
765
770
|
togglePhoneUpdate={togglePhoneUpdate}
|
|
766
771
|
requiredFields={requiredFields}
|
|
767
772
|
hideUpdateButton
|
|
773
|
+
handlePlaceOrderAsGuest={handlePlaceOrderAsGuest}
|
|
768
774
|
onClose={() => {
|
|
769
775
|
setIsOpen(false)
|
|
770
776
|
handlePlaceOrder(null, true)
|
|
@@ -19,8 +19,10 @@ import {
|
|
|
19
19
|
useOrder,
|
|
20
20
|
useUtils,
|
|
21
21
|
ToastType,
|
|
22
|
-
useToast
|
|
22
|
+
useToast,
|
|
23
|
+
useConfig
|
|
23
24
|
} from 'ordering-components/native';
|
|
25
|
+
import uuid from 'react-native-uuid';
|
|
24
26
|
import { useTheme } from 'styled-components/native';
|
|
25
27
|
import { ProductIngredient } from '../ProductIngredient';
|
|
26
28
|
import { ProductOption } from '../ProductOption';
|
|
@@ -52,6 +54,7 @@ import { ProductOptionSubOption } from '../ProductOptionSubOption';
|
|
|
52
54
|
import { NotFoundSource } from '../NotFoundSource';
|
|
53
55
|
import { Placeholder, PlaceholderLine, Fade } from 'rn-placeholder';
|
|
54
56
|
import NavBar from '../NavBar';
|
|
57
|
+
import { orderTypeList } from '../../utils';
|
|
55
58
|
const windowWidth = Dimensions.get('window').width;
|
|
56
59
|
|
|
57
60
|
export const ProductOptionsUI = (props: any) => {
|
|
@@ -71,7 +74,9 @@ export const ProductOptionsUI = (props: any) => {
|
|
|
71
74
|
handleChangeSuboptionState,
|
|
72
75
|
handleChangeCommentState,
|
|
73
76
|
productObject,
|
|
74
|
-
productAddedToCartLength
|
|
77
|
+
productAddedToCartLength,
|
|
78
|
+
actionStatus,
|
|
79
|
+
handleCreateGuestUser
|
|
75
80
|
} = props;
|
|
76
81
|
|
|
77
82
|
const theme = useTheme();
|
|
@@ -178,6 +183,7 @@ export const ProductOptionsUI = (props: any) => {
|
|
|
178
183
|
const [, t] = useLanguage();
|
|
179
184
|
const [orderState] = useOrder();
|
|
180
185
|
const [{ auth }] = useSession();
|
|
186
|
+
const [{ configs }] = useConfig()
|
|
181
187
|
const { product, loading, error } = productObject;
|
|
182
188
|
const [gallery, setGallery] = useState([])
|
|
183
189
|
const [thumbsSwiper, setThumbsSwiper] = useState(0)
|
|
@@ -198,6 +204,9 @@ export const ProductOptionsUI = (props: any) => {
|
|
|
198
204
|
const [isScrollAvailable, setIsScrollAvailable] = useState(null)
|
|
199
205
|
const [editionsLayoutY, setEditionsLayoutY] = useState(null)
|
|
200
206
|
|
|
207
|
+
const guestCheckoutEnabled = configs?.guest_checkout_enabled?.value === '1'
|
|
208
|
+
const orderTypeEnabled = !orderTypeList[orderState?.options?.type - 1] || configs?.allowed_order_types_guest_checkout?.value?.includes(orderTypeList[orderState?.options?.type - 1])
|
|
209
|
+
|
|
201
210
|
const isError = (id: number) => {
|
|
202
211
|
let bgColor = theme.colors.white;
|
|
203
212
|
if (errors[`id:${id}`]) {
|
|
@@ -294,6 +303,11 @@ export const ProductOptionsUI = (props: any) => {
|
|
|
294
303
|
}
|
|
295
304
|
}
|
|
296
305
|
|
|
306
|
+
const handleUpdateGuest = () => {
|
|
307
|
+
const guestToken = uuid.v4()
|
|
308
|
+
if (guestToken) handleCreateGuestUser({ guest_token: guestToken })
|
|
309
|
+
}
|
|
310
|
+
|
|
297
311
|
const handleOnLayout = (event: any, optionId: any) => {
|
|
298
312
|
const _optionLayout = { ...optionLayout }
|
|
299
313
|
const optionKey = 'id:' + optionId
|
|
@@ -472,9 +486,22 @@ export const ProductOptionsUI = (props: any) => {
|
|
|
472
486
|
height: 44,
|
|
473
487
|
borderColor: theme.colors.primary,
|
|
474
488
|
backgroundColor: theme.colors.white,
|
|
489
|
+
paddingLeft: 0,
|
|
490
|
+
paddingRight: 0
|
|
475
491
|
}}
|
|
476
492
|
/>
|
|
477
493
|
)}
|
|
494
|
+
{!auth && guestCheckoutEnabled && orderTypeEnabled && (
|
|
495
|
+
<TouchableOpacity style={{ marginTop: 10 }} onPress={handleUpdateGuest}>
|
|
496
|
+
{actionStatus?.loading ? (
|
|
497
|
+
<Placeholder Animation={Fade}>
|
|
498
|
+
<PlaceholderLine width={60} height={20} />
|
|
499
|
+
</Placeholder>
|
|
500
|
+
) : (
|
|
501
|
+
<OText color={theme.colors.primary} size={13}>{t('WITH_GUEST_USER', 'With Guest user')}</OText>
|
|
502
|
+
)}
|
|
503
|
+
</TouchableOpacity>
|
|
504
|
+
)}
|
|
478
505
|
</View>
|
|
479
506
|
)
|
|
480
507
|
}
|
|
@@ -68,7 +68,7 @@ export const ProductOptionSubOptionUI = (props: any) => {
|
|
|
68
68
|
|
|
69
69
|
return (
|
|
70
70
|
<View>
|
|
71
|
-
<Container>
|
|
71
|
+
<Container onPress={() => handleSuboptionClick()}>
|
|
72
72
|
<IconControl disabled={disabled} onPress={() => handleSuboptionClick()}>
|
|
73
73
|
{((option?.min === 0 && option?.max === 1) || option?.max > 1) ? (
|
|
74
74
|
state?.selected ? (
|
|
@@ -139,7 +139,7 @@ export const ProductOptionSubOptionUI = (props: any) => {
|
|
|
139
139
|
)}
|
|
140
140
|
</PositionControl>
|
|
141
141
|
{price > 0 && (
|
|
142
|
-
<OText size={12} lineHeight={18} color={theme.colors.textSecondary}>
|
|
142
|
+
<OText size={12} lineHeight={18} color={theme.colors.textSecondary} style={{ paddingRight: 10 }}>
|
|
143
143
|
+ {parsePrice(price)}
|
|
144
144
|
</OText>
|
|
145
145
|
)}
|
|
@@ -4,12 +4,12 @@ export const Container = styled.TouchableOpacity`
|
|
|
4
4
|
flex-direction: row;
|
|
5
5
|
align-items: center;
|
|
6
6
|
justify-content: space-between;
|
|
7
|
-
padding: 10px;
|
|
8
7
|
width: 100%;
|
|
9
8
|
`
|
|
10
9
|
|
|
11
10
|
export const IconControl = styled.TouchableOpacity`
|
|
12
11
|
flex-direction: row;
|
|
12
|
+
padding: 10px;
|
|
13
13
|
width: 45%;
|
|
14
14
|
align-items: center;
|
|
15
15
|
`
|
|
@@ -11,6 +11,8 @@ import FeatherIcon from 'react-native-vector-icons/Feather';
|
|
|
11
11
|
import { useSafeAreaInsets } from 'react-native-safe-area-context'
|
|
12
12
|
import { ServiceFormParams } from '../../types'
|
|
13
13
|
import { Placeholder, PlaceholderLine, Fade } from 'rn-placeholder';
|
|
14
|
+
import uuid from 'react-native-uuid';
|
|
15
|
+
import { orderTypeList } from '../../utils'
|
|
14
16
|
|
|
15
17
|
import {
|
|
16
18
|
ProductForm as ProductFormController,
|
|
@@ -45,7 +47,9 @@ const ServiceFormUI = (props: ServiceFormParams) => {
|
|
|
45
47
|
maxProductQuantity,
|
|
46
48
|
onClose,
|
|
47
49
|
professionalListState,
|
|
48
|
-
isCartProduct
|
|
50
|
+
isCartProduct,
|
|
51
|
+
actionStatus,
|
|
52
|
+
handleCreateGuestUser
|
|
49
53
|
} = props
|
|
50
54
|
|
|
51
55
|
const theme = useTheme()
|
|
@@ -65,6 +69,9 @@ const ServiceFormUI = (props: ServiceFormParams) => {
|
|
|
65
69
|
const [isOpen, setIsOpen] = useState(false)
|
|
66
70
|
const [currentProfessional, setCurrentProfessional] = useState<any>(null)
|
|
67
71
|
|
|
72
|
+
const guestCheckoutEnabled = configs?.guest_checkout_enabled?.value === '1'
|
|
73
|
+
const orderTypeEnabled = !orderTypeList[orderState?.options?.type - 1] || configs?.allowed_order_types_guest_checkout?.value?.includes(orderTypeList[orderState?.options?.type - 1])
|
|
74
|
+
|
|
68
75
|
const dropdownRef = useRef<any>(null)
|
|
69
76
|
|
|
70
77
|
const styles = StyleSheet.create({
|
|
@@ -140,6 +147,11 @@ const ServiceFormUI = (props: ServiceFormParams) => {
|
|
|
140
147
|
)
|
|
141
148
|
}
|
|
142
149
|
|
|
150
|
+
const handleUpdateGuest = () => {
|
|
151
|
+
const guestToken = uuid.v4()
|
|
152
|
+
if (guestToken) handleCreateGuestUser({ guest_token: guestToken })
|
|
153
|
+
}
|
|
154
|
+
|
|
143
155
|
const customDayHeaderStylesCallback = () => {
|
|
144
156
|
return {
|
|
145
157
|
textStyle: {
|
|
@@ -563,6 +575,17 @@ const ServiceFormUI = (props: ServiceFormParams) => {
|
|
|
563
575
|
}}
|
|
564
576
|
/>
|
|
565
577
|
)}
|
|
578
|
+
{!auth && guestCheckoutEnabled && orderTypeEnabled && (
|
|
579
|
+
<TouchableOpacity style={{ marginTop: 10 }} onPress={handleUpdateGuest}>
|
|
580
|
+
{actionStatus?.loading ? (
|
|
581
|
+
<Placeholder Animation={Fade}>
|
|
582
|
+
<PlaceholderLine width={60} height={20} />
|
|
583
|
+
</Placeholder>
|
|
584
|
+
) : (
|
|
585
|
+
<OText color={theme.colors.primary} size={13}>{t('WITH_GUEST_USER', 'With Guest user')}</OText>
|
|
586
|
+
)}
|
|
587
|
+
</TouchableOpacity>
|
|
588
|
+
)}
|
|
566
589
|
</ButtonWrapper>
|
|
567
590
|
</Container>
|
|
568
591
|
)}
|
|
@@ -83,7 +83,8 @@ const SignupFormUI = (props: SignupParams) => {
|
|
|
83
83
|
signUpTab,
|
|
84
84
|
useSignUpFullDetails,
|
|
85
85
|
useSignUpOtpEmail,
|
|
86
|
-
useSignUpOtpCellphone
|
|
86
|
+
useSignUpOtpCellphone,
|
|
87
|
+
isGuest
|
|
87
88
|
} = props;
|
|
88
89
|
|
|
89
90
|
const theme = useTheme();
|
|
@@ -465,15 +466,19 @@ const SignupFormUI = (props: SignupParams) => {
|
|
|
465
466
|
|
|
466
467
|
return (
|
|
467
468
|
<View>
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
469
|
+
{isGuest ? (
|
|
470
|
+
<OText style={{ textAlign: 'center', marginBottom: 10 }} size={18}>{t('SIGNUP', 'Signup')}</OText>
|
|
471
|
+
) : (
|
|
472
|
+
<NavBar
|
|
473
|
+
title={t('SIGNUP', 'Signup')}
|
|
474
|
+
titleAlign={'center'}
|
|
475
|
+
onActionLeft={() => navigation?.canGoBack() && navigation.goBack()}
|
|
476
|
+
showCall={false}
|
|
477
|
+
btnStyle={{ paddingLeft: 0 }}
|
|
478
|
+
titleWrapStyle={{ paddingHorizontal: 0 }}
|
|
479
|
+
titleStyle={{ marginLeft: 0, marginRight: 0 }}
|
|
480
|
+
/>
|
|
481
|
+
)}
|
|
477
482
|
<FormSide>
|
|
478
483
|
{((Number(useSignUpFullDetails) + Number(useSignUpOtpEmail) + Number(useSignUpOtpCellphone)) > 1) && (
|
|
479
484
|
<SignupWith>
|
|
@@ -893,7 +898,7 @@ const SignupFormUI = (props: SignupParams) => {
|
|
|
893
898
|
</View>
|
|
894
899
|
)
|
|
895
900
|
}
|
|
896
|
-
{configs && Object.keys(configs).length > 0 && (
|
|
901
|
+
{configs && Object.keys(configs).length > 0 && !isGuest && (
|
|
897
902
|
(((configs?.facebook_login?.value === 'true' || configs?.facebook_login?.value === '1') && configs?.facebook_id?.value && facebookLoginEnabled) ||
|
|
898
903
|
((configs?.google_login_client_id?.value !== '' && configs?.google_login_client_id?.value !== null) && googleLoginEnabled) ||
|
|
899
904
|
((configs?.apple_login_client_id?.value !== '' && configs?.apple_login_client_id?.value !== null) && appleLoginEnabled)) &&
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import React, { useEffect, useState } from 'react';
|
|
2
|
-
import { Platform, StyleSheet, TouchableOpacity } from 'react-native';
|
|
2
|
+
import { Platform, StyleSheet, TouchableOpacity, ScrollView } from 'react-native';
|
|
3
3
|
import { useSession, useLanguage, ToastType, useToast, useConfig } from 'ordering-components/native';
|
|
4
4
|
import { useTheme } from 'styled-components/native';
|
|
5
5
|
import { useForm, Controller } from 'react-hook-form';
|
|
6
|
+
import { SignupForm } from '../SignupForm'
|
|
6
7
|
|
|
7
8
|
import { UDForm, UDLoader, UDWrapper, WrapperPhone } from './styles';
|
|
8
9
|
|
|
9
|
-
import { OText, OButton, OInput } from '../shared';
|
|
10
|
+
import { OText, OButton, OInput, OModal } from '../shared';
|
|
10
11
|
|
|
11
12
|
import { PhoneInputNumber } from '../PhoneInputNumber';
|
|
12
13
|
import { sortInputFields } from '../../utils';
|
|
@@ -27,6 +28,8 @@ export const UserFormDetailsUI = (props: any) => {
|
|
|
27
28
|
phoneUpdate,
|
|
28
29
|
hideUpdateButton,
|
|
29
30
|
setWillVerifyOtpState,
|
|
31
|
+
handlePlaceOrderAsGuest,
|
|
32
|
+
isCheckout
|
|
30
33
|
} = props;
|
|
31
34
|
|
|
32
35
|
const theme = useTheme();
|
|
@@ -68,10 +71,11 @@ export const UserFormDetailsUI = (props: any) => {
|
|
|
68
71
|
const [, { showToast }] = useToast();
|
|
69
72
|
const { handleSubmit, control, errors, setValue } = useForm();
|
|
70
73
|
|
|
71
|
-
const [{ user }] = useSession();
|
|
74
|
+
const [{ user }, { login }] = useSession();
|
|
72
75
|
const [userPhoneNumber, setUserPhoneNumber] = useState<any>(null);
|
|
73
76
|
const [isValid, setIsValid] = useState(false)
|
|
74
77
|
const [isChanged, setIsChanged] = useState(false)
|
|
78
|
+
const [isModalOpen, setIsModalOpen] = useState(false)
|
|
75
79
|
const [phoneInputData, setPhoneInputData] = useState({
|
|
76
80
|
error: '',
|
|
77
81
|
phone: {
|
|
@@ -82,6 +86,14 @@ export const UserFormDetailsUI = (props: any) => {
|
|
|
82
86
|
|
|
83
87
|
const showInputPhoneNumber = (validationFields?.fields?.checkout?.cellphone?.enabled ?? false) || configs?.verification_phone_required?.value === '1'
|
|
84
88
|
|
|
89
|
+
const handleSuccessSignup = (user: any) => {
|
|
90
|
+
login({
|
|
91
|
+
user,
|
|
92
|
+
token: user?.session?.access_token
|
|
93
|
+
})
|
|
94
|
+
handlePlaceOrderAsGuest && handlePlaceOrderAsGuest()
|
|
95
|
+
}
|
|
96
|
+
|
|
85
97
|
const getInputRules = (field: any) => {
|
|
86
98
|
const rules: any = {
|
|
87
99
|
required: isRequiredField(field.code)
|
|
@@ -187,6 +199,11 @@ export const UserFormDetailsUI = (props: any) => {
|
|
|
187
199
|
handleChangeInput(countryCode, true);
|
|
188
200
|
}
|
|
189
201
|
|
|
202
|
+
const handleClickBtn = () => {
|
|
203
|
+
if (!user?.guest_id) handleSubmit(onSubmit)
|
|
204
|
+
else setIsModalOpen(true)
|
|
205
|
+
}
|
|
206
|
+
|
|
190
207
|
useEffect(() => {
|
|
191
208
|
if (Object.keys(errors).length > 0) {
|
|
192
209
|
const list = Object.values(errors);
|
|
@@ -423,17 +440,38 @@ export const UserFormDetailsUI = (props: any) => {
|
|
|
423
440
|
text={
|
|
424
441
|
formState.loading
|
|
425
442
|
? t('UPDATING', 'Updating...')
|
|
426
|
-
:
|
|
443
|
+
: ((isCheckout && !!user?.guest_id)
|
|
444
|
+
? t('SIGN_UP_AND_PLACE_ORDER', 'Sign up and place order')
|
|
445
|
+
: t('CONTINUE', 'Continue'))
|
|
427
446
|
}
|
|
428
447
|
bgColor={theme.colors.white}
|
|
429
448
|
textStyle={{ color: theme.colors.primary, fontSize: 14 }}
|
|
430
449
|
borderColor={theme.colors.primary}
|
|
431
|
-
isDisabled={formState.loading || !isValid}
|
|
450
|
+
isDisabled={!user?.guest_id && (formState.loading || !isValid)}
|
|
432
451
|
imgRightSrc={null}
|
|
433
452
|
style={{ borderRadius: 7.6, shadowOpacity: 0, width: '100%', borderWidth: 1, marginTop: 20, marginBottom: 20 }}
|
|
434
|
-
onClick={
|
|
453
|
+
onClick={handleClickBtn}
|
|
435
454
|
/>
|
|
436
455
|
)}
|
|
456
|
+
{isCheckout && !!user?.guest_id && (
|
|
457
|
+
<TouchableOpacity style={{ marginTop: 10 }} onPress={() => handlePlaceOrderAsGuest()}>
|
|
458
|
+
<OText color={theme.colors.primary} style={{ textAlign: 'center' }}>{t('PLACE_ORDER_AS_GUEST', 'Place order as guest')}</OText>
|
|
459
|
+
</TouchableOpacity>
|
|
460
|
+
)}
|
|
461
|
+
<OModal
|
|
462
|
+
open={isModalOpen}
|
|
463
|
+
onClose={() => setIsModalOpen(false)}
|
|
464
|
+
>
|
|
465
|
+
<ScrollView style={{ paddingHorizontal: 20, width: '100%'}}>
|
|
466
|
+
<SignupForm
|
|
467
|
+
handleSuccessSignup={handleSuccessSignup}
|
|
468
|
+
isGuest
|
|
469
|
+
signupButtonText={t('SIGNUP', 'Signup')}
|
|
470
|
+
useSignupByEmail
|
|
471
|
+
useChekoutFileds
|
|
472
|
+
/>
|
|
473
|
+
</ScrollView>
|
|
474
|
+
</OModal>
|
|
437
475
|
</>
|
|
438
476
|
);
|
|
439
477
|
};
|
|
@@ -118,6 +118,7 @@ export interface SignupParams {
|
|
|
118
118
|
useSignUpOtpEmail?: boolean;
|
|
119
119
|
useSignUpOtpCellphone?: boolean;
|
|
120
120
|
willVerifyOtpState?: boolean;
|
|
121
|
+
isGuest?: any;
|
|
121
122
|
numOtpInputs?: number;
|
|
122
123
|
handleChangePromotions: () => void;
|
|
123
124
|
handleChangeInput?: (in1: any, in2: any) => void;
|
|
@@ -769,7 +770,9 @@ export interface ServiceFormParams {
|
|
|
769
770
|
professionalList: any,
|
|
770
771
|
productObject?: any,
|
|
771
772
|
professionalListState?: any,
|
|
772
|
-
isCartProduct?: any
|
|
773
|
+
isCartProduct?: any,
|
|
774
|
+
actionStatus?: any,
|
|
775
|
+
handleCreateGuestUser?: any
|
|
773
776
|
}
|
|
774
777
|
|
|
775
778
|
export interface ProfessionalFilterParams {
|
|
@@ -10,6 +10,11 @@ const theme = useTheme()
|
|
|
10
10
|
|
|
11
11
|
export const flatArray = (arr: any) => [].concat(...arr)
|
|
12
12
|
|
|
13
|
+
/**
|
|
14
|
+
* List of order type
|
|
15
|
+
*/
|
|
16
|
+
export const orderTypeList = ['delivery', 'pickup', 'eatin', 'curbside', 'drivethru', 'seatdelivery']
|
|
17
|
+
|
|
13
18
|
/**
|
|
14
19
|
* Function to return the traduction depending of a key 't'
|
|
15
20
|
* @param {string} key for traduction
|