@infosoftas/s4-orders-js-sdk 0.0.1-alpha1e → 0.0.1-alpha2a
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/src/App.tsx +19 -1
- package/src/api/OrdersApi.ts +3 -0
- package/src/api/SubscribeApi.ts +1 -10
- package/src/assets/styles/forms.scss +59 -0
- package/src/assets/styles/general.scss +4 -0
- package/src/components/FormFields/AddressField.tsx +5 -6
- package/src/components/FormFields/EmailField.tsx +5 -4
- package/src/components/FormFields/FormFields.helper.ts +13 -0
- package/src/components/FormFields/FormFieldsMapper.ts +13 -0
- package/src/components/FormFields/NameField.tsx +5 -4
- package/src/components/FormFields/PhoneField.tsx +5 -6
- package/src/components/FormFields/ToggleFiled.tsx +58 -0
- package/src/components/OrderForm/OrderForm.tsx +63 -30
- package/src/enums/api.ts +1 -0
- package/src/enums/general.ts +4 -0
- package/src/types/api.ts +12 -0
- package/src/types/general.ts +5 -0
- package/src/types/order.ts +12 -0
- package/src/utils/fetcher.ts +4 -0
- package/src/utils/order.helper.ts +160 -5
package/package.json
CHANGED
package/src/App.tsx
CHANGED
|
@@ -27,6 +27,7 @@ const App: FC<ConfigType> = ({
|
|
|
27
27
|
redirectUrl,
|
|
28
28
|
showIframe,
|
|
29
29
|
paymentMethodsOptions,
|
|
30
|
+
invoiceAddressSelection,
|
|
30
31
|
availablePaymentMethods = [],
|
|
31
32
|
language = 'en-US',
|
|
32
33
|
merchantAgreementUrl = '',
|
|
@@ -186,7 +187,23 @@ const App: FC<ConfigType> = ({
|
|
|
186
187
|
top?.location?.origin || '*'
|
|
187
188
|
);
|
|
188
189
|
}
|
|
189
|
-
if (
|
|
190
|
+
if (
|
|
191
|
+
data?.paymentMethod === PaymentMethodEnum.Email ||
|
|
192
|
+
data?.paymentMethod === PaymentMethodEnum.Invoice
|
|
193
|
+
) {
|
|
194
|
+
if (window === top) {
|
|
195
|
+
setIsConfirmed(true);
|
|
196
|
+
setLoading(false);
|
|
197
|
+
top.postMessage(
|
|
198
|
+
{
|
|
199
|
+
type: MessageEventTypeEnum.ORDER_FLOW_COMPLETE,
|
|
200
|
+
isCompleted: true,
|
|
201
|
+
orderInfo: data || null,
|
|
202
|
+
},
|
|
203
|
+
top?.location?.origin || '*'
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
} else if (url) {
|
|
190
207
|
if (
|
|
191
208
|
showIframe &&
|
|
192
209
|
data?.paymentMethod === PaymentMethodEnum.SwedbankPay
|
|
@@ -240,6 +257,7 @@ const App: FC<ConfigType> = ({
|
|
|
240
257
|
paymentMethodsOptions={paymentMethodsOptions}
|
|
241
258
|
language={language}
|
|
242
259
|
merchantAgreementUrl={merchantAgreementUrl}
|
|
260
|
+
invoiceAddressSelection={invoiceAddressSelection}
|
|
243
261
|
/>
|
|
244
262
|
)}
|
|
245
263
|
{showIframe && <MainIframe iframeSrc={iframeSrc} />}
|
package/src/api/OrdersApi.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { CreateSubscriberRequestType } from '../types/api';
|
|
1
2
|
import { AgreementsType } from '../types/order';
|
|
2
3
|
import fetcher from '../utils/fetcher';
|
|
3
4
|
|
|
@@ -8,6 +9,8 @@ type RequestOrderStartType = {
|
|
|
8
9
|
templateSubscriptionPlanId?: string;
|
|
9
10
|
};
|
|
10
11
|
paymentAgreement: AgreementsType;
|
|
12
|
+
invoiceContact?: CreateSubscriberRequestType;
|
|
13
|
+
orderReference?: string;
|
|
11
14
|
};
|
|
12
15
|
|
|
13
16
|
type OrderResponseType = {
|
package/src/api/SubscribeApi.ts
CHANGED
|
@@ -1,14 +1,5 @@
|
|
|
1
1
|
import fetcher from '../utils/fetcher';
|
|
2
|
-
|
|
3
|
-
type CreateSubscriberRequestType = {
|
|
4
|
-
name?: string;
|
|
5
|
-
email?: string;
|
|
6
|
-
phone?: string;
|
|
7
|
-
country?: string;
|
|
8
|
-
city?: string;
|
|
9
|
-
addressLines?: string[];
|
|
10
|
-
zip?: string;
|
|
11
|
-
};
|
|
2
|
+
import { CreateSubscriberRequestType } from '../types/api';
|
|
12
3
|
|
|
13
4
|
type CreateSubscriberResponseType = {
|
|
14
5
|
id: string;
|
|
@@ -102,4 +102,63 @@
|
|
|
102
102
|
}
|
|
103
103
|
}
|
|
104
104
|
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.sdk-on-off-label {
|
|
108
|
+
margin-right: 0.5rem;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.sdk-on-off {
|
|
112
|
+
position: relative;
|
|
113
|
+
display : inline-block;
|
|
114
|
+
width : 60px;
|
|
115
|
+
height : 28px;
|
|
116
|
+
|
|
117
|
+
input {
|
|
118
|
+
opacity: 0;
|
|
119
|
+
width : 0;
|
|
120
|
+
height : 0;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
input:checked+.slider:before {
|
|
124
|
+
-webkit-transform: translateX(32px);
|
|
125
|
+
-ms-transform : translateX(32px);
|
|
126
|
+
transform : translateX(32px);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
input:checked+.slider {
|
|
130
|
+
background-color: $primary-color;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.slider {
|
|
134
|
+
position : absolute;
|
|
135
|
+
cursor : pointer;
|
|
136
|
+
top : 0;
|
|
137
|
+
left : 0;
|
|
138
|
+
right : 0;
|
|
139
|
+
bottom : 0;
|
|
140
|
+
background-color : $input-border-disabled-color;
|
|
141
|
+
-webkit-transition: .4s;
|
|
142
|
+
transition : .4s;
|
|
143
|
+
|
|
144
|
+
&:before {
|
|
145
|
+
position : absolute;
|
|
146
|
+
content : "";
|
|
147
|
+
height : 24px;
|
|
148
|
+
width : 24px;
|
|
149
|
+
left : 2px;
|
|
150
|
+
bottom : 2px;
|
|
151
|
+
background-color : white;
|
|
152
|
+
-webkit-transition: .4s;
|
|
153
|
+
transition : .4s;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
&.round {
|
|
157
|
+
border-radius: 28px;
|
|
158
|
+
|
|
159
|
+
&:before {
|
|
160
|
+
border-radius: 50%;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
105
164
|
}
|
|
@@ -10,13 +10,14 @@ type Props = {
|
|
|
10
10
|
label?: string;
|
|
11
11
|
errorReqMsg?: string;
|
|
12
12
|
errors?: {
|
|
13
|
-
|
|
13
|
+
[key: string]: {
|
|
14
14
|
message?: string;
|
|
15
15
|
};
|
|
16
16
|
};
|
|
17
17
|
};
|
|
18
18
|
|
|
19
19
|
const AddressField: FC<Props> = ({
|
|
20
|
+
name = 'address',
|
|
20
21
|
required = false,
|
|
21
22
|
readOnly = false,
|
|
22
23
|
label = 'Address',
|
|
@@ -33,17 +34,15 @@ const AddressField: FC<Props> = ({
|
|
|
33
34
|
placeholder=" "
|
|
34
35
|
readOnly={readOnly}
|
|
35
36
|
className={`input-control ${readOnly ? 'read-only' : ''}`}
|
|
36
|
-
{...register(
|
|
37
|
+
{...register(name, {
|
|
37
38
|
required: required ? errorReqMsg : false,
|
|
38
39
|
})}
|
|
39
40
|
/>
|
|
40
41
|
<label className="label-control" htmlFor={id}>
|
|
41
42
|
{label} {required && <span className="text-error">*</span>}
|
|
42
43
|
</label>
|
|
43
|
-
{errors && errors
|
|
44
|
-
<div className="text-error caption">
|
|
45
|
-
{errors.address.message}
|
|
46
|
-
</div>
|
|
44
|
+
{errors && errors[name] && (
|
|
45
|
+
<div className="text-error caption">{errors[name].message}</div>
|
|
47
46
|
)}
|
|
48
47
|
</div>
|
|
49
48
|
);
|
|
@@ -11,13 +11,14 @@ type Props = {
|
|
|
11
11
|
errorReqMsg?: string;
|
|
12
12
|
errorInvalidEmailMsg?: string;
|
|
13
13
|
errors?: {
|
|
14
|
-
|
|
14
|
+
[key: string]: {
|
|
15
15
|
message?: string;
|
|
16
16
|
};
|
|
17
17
|
};
|
|
18
18
|
};
|
|
19
19
|
|
|
20
20
|
const EmailField: FC<Props> = ({
|
|
21
|
+
name = 'email',
|
|
21
22
|
required = false,
|
|
22
23
|
readOnly = false,
|
|
23
24
|
label = 'Email',
|
|
@@ -36,7 +37,7 @@ const EmailField: FC<Props> = ({
|
|
|
36
37
|
className={`input-control ${readOnly ? 'read-only' : ''}`}
|
|
37
38
|
readOnly={readOnly}
|
|
38
39
|
type="email"
|
|
39
|
-
{...register(
|
|
40
|
+
{...register(name, {
|
|
40
41
|
required: required ? errorReqMsg : false,
|
|
41
42
|
pattern: {
|
|
42
43
|
value: /^[-!#-'*+/-9=?^-~]+(?:\.[-!#-'*+/-9=?^-~]+)*@[-!#-'*+/-9=?^-~]+(?:\.[-!#-'*+/-9=?^-~]{2,20})+$/i,
|
|
@@ -47,8 +48,8 @@ const EmailField: FC<Props> = ({
|
|
|
47
48
|
<label className="label-control" htmlFor={id}>
|
|
48
49
|
{label} {required && <span className="text-error">*</span>}
|
|
49
50
|
</label>
|
|
50
|
-
{errors && errors
|
|
51
|
-
<div className="text-error caption">{errors.
|
|
51
|
+
{errors && errors[name] && (
|
|
52
|
+
<div className="text-error caption">{errors[name].message}</div>
|
|
52
53
|
)}
|
|
53
54
|
</div>
|
|
54
55
|
);
|
|
@@ -8,6 +8,19 @@ export const FIELD_TYPES = {
|
|
|
8
8
|
CITY: 'city',
|
|
9
9
|
ADDRESS: 'address',
|
|
10
10
|
ZIP: 'zip',
|
|
11
|
+
CARE_OF: 'careOf',
|
|
12
|
+
ORGANIZATION_NUMBER: 'organizationNumber',
|
|
13
|
+
BUYER_REFERENCE: 'buyerReference',
|
|
14
|
+
ORDER_REFERENCE: 'orderReference',
|
|
15
|
+
INVOICE_NAME: 'invoiceName',
|
|
16
|
+
INVOICE_ADDRESS: 'invoiceAddress',
|
|
17
|
+
INVOICE_CITY: 'invoiceCity',
|
|
18
|
+
INVOICE_ZIP: 'invoiceZip',
|
|
19
|
+
INVOICE_COUNTRY: 'invoiceCountry',
|
|
20
|
+
INVOICE_PHONE: 'invoicePhoneNumber',
|
|
21
|
+
INVOICE_EMAIL: 'invoiceEmail',
|
|
22
|
+
INVOICE_CARE_OF: 'invoiceCareOf',
|
|
23
|
+
INVOICE_BUYER_REFERENCE: 'invoiceBuyerReference',
|
|
11
24
|
};
|
|
12
25
|
|
|
13
26
|
export const DEFAULT_ORDER_FORM_FIELDS: OrderFormFiledType[] = [
|
|
@@ -14,6 +14,19 @@ const formFieldsMapper = {
|
|
|
14
14
|
[FIELD_TYPES.COUNTRY]: InputField,
|
|
15
15
|
[FIELD_TYPES.CITY]: InputField,
|
|
16
16
|
[FIELD_TYPES.ZIP]: InputField,
|
|
17
|
+
[FIELD_TYPES.CARE_OF]: InputField,
|
|
18
|
+
[FIELD_TYPES.ORGANIZATION_NUMBER]: InputField,
|
|
19
|
+
[FIELD_TYPES.BUYER_REFERENCE]: InputField,
|
|
20
|
+
[FIELD_TYPES.ORDER_REFERENCE]: InputField,
|
|
21
|
+
[FIELD_TYPES.INVOICE_PHONE]: PhoneField,
|
|
22
|
+
[FIELD_TYPES.INVOICE_NAME]: NameField,
|
|
23
|
+
[FIELD_TYPES.INVOICE_EMAIL]: EmailField,
|
|
24
|
+
[FIELD_TYPES.INVOICE_ADDRESS]: AddressField,
|
|
25
|
+
[FIELD_TYPES.INVOICE_COUNTRY]: InputField,
|
|
26
|
+
[FIELD_TYPES.INVOICE_CITY]: InputField,
|
|
27
|
+
[FIELD_TYPES.INVOICE_ZIP]: InputField,
|
|
28
|
+
[FIELD_TYPES.INVOICE_CARE_OF]: InputField,
|
|
29
|
+
[FIELD_TYPES.INVOICE_BUYER_REFERENCE]: InputField,
|
|
17
30
|
};
|
|
18
31
|
|
|
19
32
|
export default formFieldsMapper;
|
|
@@ -10,13 +10,14 @@ type Props = {
|
|
|
10
10
|
label?: string;
|
|
11
11
|
errorReqMsg?: string;
|
|
12
12
|
errors?: {
|
|
13
|
-
|
|
13
|
+
[key: string]: {
|
|
14
14
|
message?: string;
|
|
15
15
|
};
|
|
16
16
|
};
|
|
17
17
|
};
|
|
18
18
|
|
|
19
19
|
const NameField: FC<Props> = ({
|
|
20
|
+
name = 'name',
|
|
20
21
|
required = false,
|
|
21
22
|
readOnly = false,
|
|
22
23
|
label = 'Name',
|
|
@@ -33,15 +34,15 @@ const NameField: FC<Props> = ({
|
|
|
33
34
|
placeholder=" "
|
|
34
35
|
readOnly={readOnly}
|
|
35
36
|
className={`input-control ${readOnly ? 'read-only' : ''}`}
|
|
36
|
-
{...register(
|
|
37
|
+
{...register(name, {
|
|
37
38
|
required: required ? errorReqMsg : false,
|
|
38
39
|
})}
|
|
39
40
|
/>
|
|
40
41
|
<label className="label-control" htmlFor={id}>
|
|
41
42
|
{label} {required && <span className="text-error">*</span>}
|
|
42
43
|
</label>
|
|
43
|
-
{errors && errors
|
|
44
|
-
<div className="text-error caption">{errors
|
|
44
|
+
{errors && errors[name] && (
|
|
45
|
+
<div className="text-error caption">{errors[name].message}</div>
|
|
45
46
|
)}
|
|
46
47
|
</div>
|
|
47
48
|
);
|
|
@@ -11,13 +11,14 @@ type Props = {
|
|
|
11
11
|
errorReqMsg?: string;
|
|
12
12
|
errorInvalidPhoneMsg?: string;
|
|
13
13
|
errors?: {
|
|
14
|
-
|
|
14
|
+
[key: string]: {
|
|
15
15
|
message?: string;
|
|
16
16
|
};
|
|
17
17
|
};
|
|
18
18
|
};
|
|
19
19
|
|
|
20
20
|
const PhoneFiled: FC<Props> = ({
|
|
21
|
+
name = 'phoneNumber',
|
|
21
22
|
required = false,
|
|
22
23
|
readOnly = false,
|
|
23
24
|
label = 'Phone number',
|
|
@@ -36,7 +37,7 @@ const PhoneFiled: FC<Props> = ({
|
|
|
36
37
|
placeholder=" "
|
|
37
38
|
readOnly={readOnly}
|
|
38
39
|
type="tel"
|
|
39
|
-
{...register(
|
|
40
|
+
{...register(name, {
|
|
40
41
|
required: required ? errorReqMsg : false,
|
|
41
42
|
pattern: {
|
|
42
43
|
value: /^[+]*[(]{0,1}[0-9]{1,3}[)]{0,1}[-\s\./0-9]{6,14}$/g,
|
|
@@ -47,10 +48,8 @@ const PhoneFiled: FC<Props> = ({
|
|
|
47
48
|
<label className="label-control" htmlFor={id}>
|
|
48
49
|
{label} {required && <span className="text-error">*</span>}
|
|
49
50
|
</label>
|
|
50
|
-
{errors && errors
|
|
51
|
-
<div className="text-error caption">
|
|
52
|
-
{errors.phoneNumber.message}
|
|
53
|
-
</div>
|
|
51
|
+
{errors && errors[name] && (
|
|
52
|
+
<div className="text-error caption">{errors[name].message}</div>
|
|
54
53
|
)}
|
|
55
54
|
</div>
|
|
56
55
|
);
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { FC } from 'react';
|
|
2
|
+
import { useFormContext } from 'react-hook-form';
|
|
3
|
+
|
|
4
|
+
import { uuidv4 } from '../../utils/helper';
|
|
5
|
+
|
|
6
|
+
type Props = {
|
|
7
|
+
name: string;
|
|
8
|
+
required?: boolean;
|
|
9
|
+
readOnly?: boolean;
|
|
10
|
+
label?: string;
|
|
11
|
+
errorReqMsg?: string;
|
|
12
|
+
errors?: {
|
|
13
|
+
[key: string]: {
|
|
14
|
+
message?: string;
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const ToggleField: FC<Props> = ({
|
|
20
|
+
name,
|
|
21
|
+
required = false,
|
|
22
|
+
readOnly = false,
|
|
23
|
+
label = '',
|
|
24
|
+
errorReqMsg = 'This field is required!',
|
|
25
|
+
errors,
|
|
26
|
+
}) => {
|
|
27
|
+
const { register } = useFormContext();
|
|
28
|
+
const id = uuidv4();
|
|
29
|
+
return (
|
|
30
|
+
<div className="field-wrapper">
|
|
31
|
+
<div className="d-flex align-center flex-wrap">
|
|
32
|
+
<label className="sdk-on-off-label" htmlFor={id}>
|
|
33
|
+
{label} {required && <span className="text-error">*</span>}
|
|
34
|
+
</label>
|
|
35
|
+
<label
|
|
36
|
+
className="sdk-on-off"
|
|
37
|
+
data-testid={`sdk-${name}-field-id`}
|
|
38
|
+
>
|
|
39
|
+
<input
|
|
40
|
+
id={id}
|
|
41
|
+
type="checkbox"
|
|
42
|
+
readOnly={readOnly}
|
|
43
|
+
className={`${readOnly ? 'read-only' : ''}`}
|
|
44
|
+
{...register(name, {
|
|
45
|
+
required: required ? errorReqMsg : false,
|
|
46
|
+
})}
|
|
47
|
+
/>
|
|
48
|
+
<span className="slider round"></span>
|
|
49
|
+
</label>
|
|
50
|
+
</div>
|
|
51
|
+
{errors && errors[name] && (
|
|
52
|
+
<div className="text-error caption">{errors[name].message}</div>
|
|
53
|
+
)}
|
|
54
|
+
</div>
|
|
55
|
+
);
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export default ToggleField;
|
|
@@ -7,6 +7,7 @@ import { PaymentMethodEnum } from '../../enums/general';
|
|
|
7
7
|
import { WRONG_MSG, PAYMENT_METHOD_DEFAULT } from '../../constants/index';
|
|
8
8
|
import Alert from '../Alert/Alert';
|
|
9
9
|
import Button from '../Button/Button';
|
|
10
|
+
import ToggleField from '../FormFields/ToggleFiled';
|
|
10
11
|
import formFieldsMapper from '../FormFields/FormFieldsMapper';
|
|
11
12
|
import { DEFAULT_ORDER_FORM_FIELDS } from '../FormFields/FormFields.helper';
|
|
12
13
|
import { OrderFormInputsType, OrderInfoType } from '../../types/order';
|
|
@@ -15,7 +16,12 @@ import {
|
|
|
15
16
|
OrderFormFiledType,
|
|
16
17
|
ErrorsMsg,
|
|
17
18
|
} from '../../types/general';
|
|
18
|
-
import {
|
|
19
|
+
import {
|
|
20
|
+
orderInvoiceContactFields,
|
|
21
|
+
prepareAgreementModel,
|
|
22
|
+
prepareContactModel,
|
|
23
|
+
prepareInvoiceModel,
|
|
24
|
+
} from '../../utils/order.helper';
|
|
19
25
|
import {
|
|
20
26
|
prepareErrorMessage,
|
|
21
27
|
prepareErrorsArrayMessage,
|
|
@@ -42,9 +48,15 @@ type Props = {
|
|
|
42
48
|
errorReqMsg?: string;
|
|
43
49
|
errorInvalidEmailMsg?: string;
|
|
44
50
|
errorInvalidPhoneMsg?: string;
|
|
51
|
+
invoiceAddressSelection?: {
|
|
52
|
+
enabled?: boolean;
|
|
53
|
+
label?: string;
|
|
54
|
+
fields?: OrderFormFiledType[];
|
|
55
|
+
};
|
|
45
56
|
};
|
|
46
57
|
|
|
47
58
|
const initialData = {
|
|
59
|
+
invoiceAddressSelection: false,
|
|
48
60
|
name: '',
|
|
49
61
|
email: '',
|
|
50
62
|
phoneNumber: '',
|
|
@@ -53,6 +65,7 @@ const initialData = {
|
|
|
53
65
|
address: '',
|
|
54
66
|
zip: '',
|
|
55
67
|
paymentMethod: PAYMENT_METHOD_DEFAULT,
|
|
68
|
+
orderReference: '',
|
|
56
69
|
};
|
|
57
70
|
|
|
58
71
|
const OrderForm: FC<Props> = ({
|
|
@@ -69,6 +82,7 @@ const OrderForm: FC<Props> = ({
|
|
|
69
82
|
language,
|
|
70
83
|
merchantAgreementUrl,
|
|
71
84
|
paymentMethods = [],
|
|
85
|
+
invoiceAddressSelection,
|
|
72
86
|
buttonText = 'Start',
|
|
73
87
|
paymentMethodLabel = 'Select Payment Method',
|
|
74
88
|
errorReqMsg = '',
|
|
@@ -79,24 +93,32 @@ const OrderForm: FC<Props> = ({
|
|
|
79
93
|
const [apiErrorMsg, setApiErrorMsg] = useState<string>('');
|
|
80
94
|
const [errorsMsg, setErrorsMsg] = useState<string[]>([]);
|
|
81
95
|
const [orderFields, setOrderFields] = useState<OrderFormFiledType[]>(
|
|
82
|
-
paymentMethodsOptions?.[
|
|
83
|
-
|
|
96
|
+
paymentMethodsOptions?.[
|
|
97
|
+
defaultValues?.paymentMethod || PAYMENT_METHOD_DEFAULT
|
|
98
|
+
]?.orderFormFields ?? DEFAULT_ORDER_FORM_FIELDS
|
|
84
99
|
);
|
|
85
100
|
|
|
86
101
|
const methods = useForm<OrderFormInputsType>({
|
|
87
102
|
defaultValues: {
|
|
88
103
|
...initialData,
|
|
89
104
|
...(defaultValues ?? {}),
|
|
105
|
+
paymentMethod:
|
|
106
|
+
defaultValues?.paymentMethod || PAYMENT_METHOD_DEFAULT,
|
|
90
107
|
},
|
|
91
108
|
});
|
|
109
|
+
const invoiceOrderFields =
|
|
110
|
+
invoiceAddressSelection?.fields || orderInvoiceContactFields;
|
|
92
111
|
|
|
93
112
|
const {
|
|
94
113
|
register,
|
|
95
114
|
handleSubmit,
|
|
96
115
|
setValue,
|
|
116
|
+
watch,
|
|
97
117
|
formState: { errors },
|
|
98
118
|
} = methods;
|
|
99
119
|
|
|
120
|
+
const invoiceAddressToggle = watch('invoiceAddressSelection');
|
|
121
|
+
|
|
100
122
|
const onSubmit: SubmitHandler<OrderFormInputsType> = async (
|
|
101
123
|
data
|
|
102
124
|
): Promise<void> => {
|
|
@@ -106,35 +128,10 @@ const OrderForm: FC<Props> = ({
|
|
|
106
128
|
setLoading(true);
|
|
107
129
|
try {
|
|
108
130
|
let id: string | undefined = subscriberId;
|
|
131
|
+
const contactModel = prepareContactModel({ data, orderFields });
|
|
109
132
|
if (!subscriberId) {
|
|
110
133
|
const response = await createSubscriber({
|
|
111
|
-
|
|
112
|
-
? data.name?.trim() || undefined
|
|
113
|
-
: undefined,
|
|
114
|
-
email: orderFields.find((i) => i.name === 'email')
|
|
115
|
-
? data.email?.trim() || undefined
|
|
116
|
-
: undefined,
|
|
117
|
-
phone: orderFields.find((i) => i.name === 'phoneNumber')
|
|
118
|
-
? data.phoneNumber?.trim() || undefined
|
|
119
|
-
: undefined,
|
|
120
|
-
country: orderFields.find((i) => i.name === 'country')
|
|
121
|
-
? data.country?.trim() || undefined
|
|
122
|
-
: undefined,
|
|
123
|
-
city: orderFields.find((i) => i.name === 'city')
|
|
124
|
-
? data.city?.trim() || undefined
|
|
125
|
-
: undefined,
|
|
126
|
-
addressLines: orderFields.find((i) => i.name === 'address')
|
|
127
|
-
? data.address?.trim()
|
|
128
|
-
? data.address
|
|
129
|
-
?.trim()
|
|
130
|
-
.replace(/\r\n/g, '\n')
|
|
131
|
-
.split('\n')
|
|
132
|
-
.filter((line) => line)
|
|
133
|
-
: undefined
|
|
134
|
-
: undefined,
|
|
135
|
-
zip: orderFields.find((i) => i.name === 'zip')
|
|
136
|
-
? data.zip?.trim() || undefined
|
|
137
|
-
: undefined,
|
|
134
|
+
...contactModel,
|
|
138
135
|
});
|
|
139
136
|
id = response.id;
|
|
140
137
|
|
|
@@ -177,6 +174,13 @@ const OrderForm: FC<Props> = ({
|
|
|
177
174
|
templateSubscriptionPlanId: templatePackageId,
|
|
178
175
|
},
|
|
179
176
|
paymentAgreement: { ...agreements },
|
|
177
|
+
invoiceContact: invoiceAddressToggle
|
|
178
|
+
? prepareInvoiceModel({
|
|
179
|
+
data,
|
|
180
|
+
orderFields: invoiceOrderFields,
|
|
181
|
+
})
|
|
182
|
+
: undefined,
|
|
183
|
+
orderReference: data.orderReference || undefined,
|
|
180
184
|
});
|
|
181
185
|
callback(responseOrder.terminalRedirectUrl, {
|
|
182
186
|
orderId: responseOrder.id,
|
|
@@ -275,6 +279,35 @@ const OrderForm: FC<Props> = ({
|
|
|
275
279
|
})}
|
|
276
280
|
</Suspense>
|
|
277
281
|
)}
|
|
282
|
+
{invoiceAddressSelection?.enabled && (
|
|
283
|
+
<ToggleField
|
|
284
|
+
name="invoiceAddressSelection"
|
|
285
|
+
label={
|
|
286
|
+
invoiceAddressSelection?.label ?? 'Invoice Address'
|
|
287
|
+
}
|
|
288
|
+
/>
|
|
289
|
+
)}
|
|
290
|
+
{invoiceAddressToggle && invoiceOrderFields?.length > 0 && (
|
|
291
|
+
<Suspense fallback={null}>
|
|
292
|
+
{invoiceOrderFields.map((field) => {
|
|
293
|
+
const Component = formFieldsMapper[field.name];
|
|
294
|
+
|
|
295
|
+
return Component ? (
|
|
296
|
+
<Component
|
|
297
|
+
key={field.name}
|
|
298
|
+
name={field.name}
|
|
299
|
+
label={field.label}
|
|
300
|
+
required={field.required || false}
|
|
301
|
+
readOnly={field.readOnly || false}
|
|
302
|
+
errors={errors}
|
|
303
|
+
errorReqMsg={errorReqMsg}
|
|
304
|
+
errorInvalidEmailMsg={errorInvalidEmailMsg}
|
|
305
|
+
errorInvalidPhoneMsg={errorInvalidPhoneMsg}
|
|
306
|
+
/>
|
|
307
|
+
) : null;
|
|
308
|
+
})}
|
|
309
|
+
</Suspense>
|
|
310
|
+
)}
|
|
278
311
|
<Button
|
|
279
312
|
type="submit"
|
|
280
313
|
loading={loading}
|
package/src/enums/api.ts
CHANGED
package/src/enums/general.ts
CHANGED
package/src/types/api.ts
CHANGED
|
@@ -10,3 +10,15 @@ export interface ApiResponseType<T> {
|
|
|
10
10
|
query: string;
|
|
11
11
|
url: string;
|
|
12
12
|
}
|
|
13
|
+
|
|
14
|
+
export type CreateSubscriberRequestType = {
|
|
15
|
+
name?: string;
|
|
16
|
+
email?: string;
|
|
17
|
+
phone?: string;
|
|
18
|
+
country?: string;
|
|
19
|
+
city?: string;
|
|
20
|
+
addressLines?: string[];
|
|
21
|
+
zip?: string;
|
|
22
|
+
careOf?: string;
|
|
23
|
+
buyerReference?: string;
|
|
24
|
+
};
|
package/src/types/general.ts
CHANGED
|
@@ -38,6 +38,11 @@ export type ConfigType = {
|
|
|
38
38
|
paymentMethodsOptions?: PaymentMethodOptionsType;
|
|
39
39
|
language?: string;
|
|
40
40
|
merchantAgreementUrl?: string;
|
|
41
|
+
invoiceAddressSelection?: {
|
|
42
|
+
enabled?: boolean;
|
|
43
|
+
label?: string;
|
|
44
|
+
fields?: OrderFormFiledType[];
|
|
45
|
+
};
|
|
41
46
|
settings?: {
|
|
42
47
|
successText?: string;
|
|
43
48
|
failureText?: string;
|
package/src/types/order.ts
CHANGED
|
@@ -9,6 +9,18 @@ export type OrderFormInputsType = {
|
|
|
9
9
|
city?: string;
|
|
10
10
|
address?: string;
|
|
11
11
|
zip?: string;
|
|
12
|
+
careOf?: string;
|
|
13
|
+
invoiceAddressSelection?: boolean;
|
|
14
|
+
orderReference?: string;
|
|
15
|
+
invoiceName?: string;
|
|
16
|
+
invoiceEmail?: string;
|
|
17
|
+
invoicePhoneNumber?: string;
|
|
18
|
+
invoiceCountry?: string;
|
|
19
|
+
invoiceCity?: string;
|
|
20
|
+
invoiceAddress?: string;
|
|
21
|
+
invoiceZip?: string;
|
|
22
|
+
invoiceCareOf?: string;
|
|
23
|
+
invoiceBuyerReference?: string;
|
|
12
24
|
};
|
|
13
25
|
|
|
14
26
|
export type VippsMobilePay = {
|
package/src/utils/fetcher.ts
CHANGED
|
@@ -68,6 +68,10 @@ async function fetcher<ResponseType, BodyType = void>(
|
|
|
68
68
|
reject({ message: 'Not Authorized user!' });
|
|
69
69
|
} else if (response.status === HttpStatusCode.FORBIDDEN) {
|
|
70
70
|
reject({ message: 'Something Wrong with permission!' });
|
|
71
|
+
} else if (response.status === HttpStatusCode.LIMIT) {
|
|
72
|
+
reject({
|
|
73
|
+
message: 'System busy please try again shortly!',
|
|
74
|
+
});
|
|
71
75
|
} else {
|
|
72
76
|
if (response.ok) {
|
|
73
77
|
response
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { MessageEventTypeEnum, PaymentMethodEnum } from '../enums/general';
|
|
2
|
-
import { AgreementsType } from '../types/order';
|
|
2
|
+
import { AgreementsType, OrderFormInputsType } from '../types/order';
|
|
3
|
+
import { OrderFormFiledType } from '../types/general';
|
|
4
|
+
import { FIELD_TYPES } from '../components/FormFields/FormFields.helper';
|
|
3
5
|
|
|
4
6
|
type AgreementModelType = {
|
|
5
7
|
paymentMethod: PaymentMethodEnum;
|
|
@@ -55,14 +57,167 @@ export const prepareAgreementModel = ({
|
|
|
55
57
|
return model;
|
|
56
58
|
}
|
|
57
59
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
60
|
+
if (
|
|
61
|
+
paymentMethod === PaymentMethodEnum.Vipps ||
|
|
62
|
+
paymentMethod === PaymentMethodEnum.MobilePay
|
|
63
|
+
) {
|
|
64
|
+
model.vippsMobilePay = {
|
|
61
65
|
generateSubscriberContact,
|
|
62
66
|
merchantAgreementUrl,
|
|
63
67
|
accountId,
|
|
64
68
|
phoneNumber,
|
|
65
69
|
merchantRedirectUrl: redirectUrl || window.location.href,
|
|
66
|
-
}
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
return model;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return model;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
type ContactModelType = {
|
|
79
|
+
data: OrderFormInputsType;
|
|
80
|
+
orderFields: OrderFormFiledType[];
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
export const prepareContactModel = ({
|
|
84
|
+
data,
|
|
85
|
+
orderFields,
|
|
86
|
+
}: ContactModelType) => {
|
|
87
|
+
const model = {
|
|
88
|
+
name: orderFields.find((i) => i.name === 'name')
|
|
89
|
+
? data.name?.trim() || undefined
|
|
90
|
+
: undefined,
|
|
91
|
+
email: orderFields.find((i) => i.name === 'email')
|
|
92
|
+
? data.email?.trim() || undefined
|
|
93
|
+
: undefined,
|
|
94
|
+
phone: orderFields.find((i) => i.name === 'phoneNumber')
|
|
95
|
+
? data.phoneNumber?.trim() || undefined
|
|
96
|
+
: undefined,
|
|
97
|
+
country: orderFields.find((i) => i.name === 'country')
|
|
98
|
+
? data.country?.trim() || undefined
|
|
99
|
+
: undefined,
|
|
100
|
+
city: orderFields.find((i) => i.name === 'city')
|
|
101
|
+
? data.city?.trim() || undefined
|
|
102
|
+
: undefined,
|
|
103
|
+
addressLines: orderFields.find((i) => i.name === 'address')
|
|
104
|
+
? data.address?.trim()
|
|
105
|
+
? data.address
|
|
106
|
+
?.trim()
|
|
107
|
+
.replace(/\r\n/g, '\n')
|
|
108
|
+
.split('\n')
|
|
109
|
+
.filter((line) => line)
|
|
110
|
+
: undefined
|
|
111
|
+
: undefined,
|
|
112
|
+
zip: orderFields.find((i) => i.name === 'zip')
|
|
113
|
+
? data.zip?.trim() || undefined
|
|
114
|
+
: undefined,
|
|
115
|
+
careOf: orderFields.find((i) => i.name === 'careOf')
|
|
116
|
+
? data.careOf?.trim() || undefined
|
|
117
|
+
: undefined,
|
|
67
118
|
};
|
|
119
|
+
|
|
120
|
+
return model;
|
|
68
121
|
};
|
|
122
|
+
|
|
123
|
+
export const prepareInvoiceModel = ({
|
|
124
|
+
data,
|
|
125
|
+
orderFields,
|
|
126
|
+
}: ContactModelType) => {
|
|
127
|
+
const model = {
|
|
128
|
+
name: orderFields.find((i) => i.name === 'invoiceName')
|
|
129
|
+
? data.invoiceName?.trim() || undefined
|
|
130
|
+
: undefined,
|
|
131
|
+
email: orderFields.find((i) => i.name === 'invoiceEmail')
|
|
132
|
+
? data.invoiceEmail?.trim() || undefined
|
|
133
|
+
: undefined,
|
|
134
|
+
phone: orderFields.find((i) => i.name === 'invoicePhoneNumber')
|
|
135
|
+
? data.invoicePhoneNumber?.trim() || undefined
|
|
136
|
+
: undefined,
|
|
137
|
+
country: orderFields.find((i) => i.name === 'invoiceCountry')
|
|
138
|
+
? data.invoiceCountry?.trim() || undefined
|
|
139
|
+
: undefined,
|
|
140
|
+
city: orderFields.find((i) => i.name === 'invoiceCity')
|
|
141
|
+
? data.invoiceCity?.trim() || undefined
|
|
142
|
+
: undefined,
|
|
143
|
+
addressLines: orderFields.find((i) => i.name === 'invoiceAddress')
|
|
144
|
+
? data.invoiceAddress?.trim()
|
|
145
|
+
? data.invoiceAddress
|
|
146
|
+
?.trim()
|
|
147
|
+
.replace(/\r\n/g, '\n')
|
|
148
|
+
.split('\n')
|
|
149
|
+
.filter((line) => line)
|
|
150
|
+
: undefined
|
|
151
|
+
: undefined,
|
|
152
|
+
zip: orderFields.find((i) => i.name === 'invoiceZip')
|
|
153
|
+
? data.invoiceZip?.trim() || undefined
|
|
154
|
+
: undefined,
|
|
155
|
+
careOf: orderFields.find((i) => i.name === 'invoiceCareOf')
|
|
156
|
+
? data.invoiceCareOf?.trim() || undefined
|
|
157
|
+
: undefined,
|
|
158
|
+
buyerReference: orderFields.find(
|
|
159
|
+
(i) => i.name === 'invoiceBuyerReference'
|
|
160
|
+
)
|
|
161
|
+
? data.invoiceBuyerReference?.trim() || undefined
|
|
162
|
+
: undefined,
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
return model;
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
export const orderInvoiceContactFields = [
|
|
169
|
+
{
|
|
170
|
+
name: FIELD_TYPES.INVOICE_NAME,
|
|
171
|
+
label: 'Name',
|
|
172
|
+
required: false,
|
|
173
|
+
readOnly: false,
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
name: FIELD_TYPES.INVOICE_EMAIL,
|
|
177
|
+
label: 'Email',
|
|
178
|
+
required: false,
|
|
179
|
+
readOnly: false,
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
name: FIELD_TYPES.INVOICE_PHONE,
|
|
183
|
+
label: 'Phone',
|
|
184
|
+
required: false,
|
|
185
|
+
readOnly: false,
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
name: FIELD_TYPES.INVOICE_COUNTRY,
|
|
189
|
+
label: 'Country',
|
|
190
|
+
required: false,
|
|
191
|
+
readOnly: false,
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
name: FIELD_TYPES.INVOICE_CITY,
|
|
195
|
+
label: 'City',
|
|
196
|
+
required: false,
|
|
197
|
+
readOnly: false,
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
name: FIELD_TYPES.INVOICE_ADDRESS,
|
|
201
|
+
label: 'Address',
|
|
202
|
+
required: false,
|
|
203
|
+
readOnly: false,
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
name: FIELD_TYPES.INVOICE_ZIP,
|
|
207
|
+
label: 'Zip',
|
|
208
|
+
required: false,
|
|
209
|
+
readOnly: false,
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
name: FIELD_TYPES.INVOICE_CARE_OF,
|
|
213
|
+
label: 'Contact Person',
|
|
214
|
+
required: false,
|
|
215
|
+
readOnly: false,
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
name: FIELD_TYPES.INVOICE_BUYER_REFERENCE,
|
|
219
|
+
label: 'Buyer Reference',
|
|
220
|
+
required: false,
|
|
221
|
+
readOnly: false,
|
|
222
|
+
},
|
|
223
|
+
];
|