@akinon/pz-pay-on-delivery 1.30.2
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/.gitattributes +15 -0
- package/.prettierrc +13 -0
- package/CHANGELOG.md +7 -0
- package/package.json +20 -0
- package/src/endpoints.ts +69 -0
- package/src/index.tsx +193 -0
package/.gitattributes
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
*.js text eol=lf
|
|
2
|
+
*.jsx text eol=lf
|
|
3
|
+
*.ts text eol=lf
|
|
4
|
+
*.tsx text eol=lf
|
|
5
|
+
*.json text eol=lf
|
|
6
|
+
*.md text eol=lf
|
|
7
|
+
|
|
8
|
+
.eslintignore text eol=lf
|
|
9
|
+
.eslintrc text eol=lf
|
|
10
|
+
.gitignore text eol=lf
|
|
11
|
+
.prettierrc text eol=lf
|
|
12
|
+
.yarnrc text eol=lf
|
|
13
|
+
|
|
14
|
+
* text=auto
|
|
15
|
+
|
package/.prettierrc
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"bracketSameLine": false,
|
|
3
|
+
"tabWidth": 2,
|
|
4
|
+
"singleQuote": true,
|
|
5
|
+
"jsxSingleQuote": false,
|
|
6
|
+
"bracketSpacing": true,
|
|
7
|
+
"semi": true,
|
|
8
|
+
"useTabs": false,
|
|
9
|
+
"arrowParens": "always",
|
|
10
|
+
"endOfLine": "lf",
|
|
11
|
+
"proseWrap": "never",
|
|
12
|
+
"trailingComma": "none"
|
|
13
|
+
}
|
package/CHANGELOG.md
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@akinon/pz-pay-on-delivery",
|
|
3
|
+
"version": "1.30.2",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"main": "src/index.tsx",
|
|
6
|
+
"peerDependencies": {
|
|
7
|
+
"react": "^18.0.0",
|
|
8
|
+
"react-dom": "^18.0.0"
|
|
9
|
+
},
|
|
10
|
+
"devDependencies": {
|
|
11
|
+
"@types/node": "^18.7.8",
|
|
12
|
+
"@types/react": "^18.0.17",
|
|
13
|
+
"@types/react-dom": "^18.0.6",
|
|
14
|
+
"react": "^18.2.0",
|
|
15
|
+
"react-dom": "^18.2.0",
|
|
16
|
+
"typescript": "^4.7.4",
|
|
17
|
+
"react-hook-form": "7.31.3",
|
|
18
|
+
"@hookform/resolvers": "2.9.0"
|
|
19
|
+
}
|
|
20
|
+
}
|
package/src/endpoints.ts
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { setPaymentStepBusy } from '@akinon/next/redux/reducers/checkout';
|
|
2
|
+
import { CheckoutContext, PreOrder } from '@akinon/next/types';
|
|
3
|
+
import { api } from '@akinon/next/data/client/api';
|
|
4
|
+
import { checkout } from '@akinon/next/data/urls';
|
|
5
|
+
import { buildClientRequestUrl } from '@akinon/next/utils';
|
|
6
|
+
|
|
7
|
+
interface CheckoutResponse {
|
|
8
|
+
pre_order?: PreOrder;
|
|
9
|
+
errors: {
|
|
10
|
+
non_field_errors: string;
|
|
11
|
+
};
|
|
12
|
+
context_list?: CheckoutContext[];
|
|
13
|
+
template_name?: string;
|
|
14
|
+
redirect_url?: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface PayOnDeliveryParams {
|
|
18
|
+
agreement: string;
|
|
19
|
+
paymentType: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const checkoutApi = api.injectEndpoints({
|
|
23
|
+
endpoints: (build) => ({
|
|
24
|
+
setCompletePayOnDelivery: build.mutation<
|
|
25
|
+
CheckoutResponse,
|
|
26
|
+
PayOnDeliveryParams
|
|
27
|
+
>({
|
|
28
|
+
query: ({ paymentType }) => ({
|
|
29
|
+
url: buildClientRequestUrl(
|
|
30
|
+
`${checkout.fetchCheckout}?page=PayOnDeliveryPage`,
|
|
31
|
+
{ useFormData: true }
|
|
32
|
+
),
|
|
33
|
+
method: 'POST',
|
|
34
|
+
body: {
|
|
35
|
+
agreement: '1',
|
|
36
|
+
paymentType
|
|
37
|
+
}
|
|
38
|
+
}),
|
|
39
|
+
async onQueryStarted(arg, { dispatch, queryFulfilled }) {
|
|
40
|
+
dispatch(setPaymentStepBusy(true));
|
|
41
|
+
await queryFulfilled;
|
|
42
|
+
dispatch(setPaymentStepBusy(false));
|
|
43
|
+
}
|
|
44
|
+
}),
|
|
45
|
+
setPayOnDeliveryChoice: build.mutation<CheckoutResponse, string>({
|
|
46
|
+
query: (body) => ({
|
|
47
|
+
url: buildClientRequestUrl(
|
|
48
|
+
`${checkout.fetchCheckout}?page=PayOnDeliveryPaymentChoicePage`,
|
|
49
|
+
{ useFormData: true }
|
|
50
|
+
),
|
|
51
|
+
method: 'POST',
|
|
52
|
+
body: {
|
|
53
|
+
payment_choice: body
|
|
54
|
+
}
|
|
55
|
+
}),
|
|
56
|
+
async onQueryStarted(arg, { dispatch, queryFulfilled }) {
|
|
57
|
+
dispatch(setPaymentStepBusy(true));
|
|
58
|
+
await queryFulfilled;
|
|
59
|
+
dispatch(setPaymentStepBusy(false));
|
|
60
|
+
}
|
|
61
|
+
})
|
|
62
|
+
}),
|
|
63
|
+
overrideExisting: false
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
export const {
|
|
67
|
+
useSetCompletePayOnDeliveryMutation,
|
|
68
|
+
useSetPayOnDeliveryChoiceMutation
|
|
69
|
+
} = checkoutApi;
|
package/src/index.tsx
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { useAppSelector } from '@akinon/next/redux/hooks';
|
|
2
|
+
import { yupResolver } from '@hookform/resolvers/yup';
|
|
3
|
+
import { Button, Icon, Price, Radio } from '@akinon/next/components';
|
|
4
|
+
import React, { useEffect, useState } from 'react';
|
|
5
|
+
import { SubmitHandler, useForm } from 'react-hook-form';
|
|
6
|
+
import { RootState } from 'redux/store';
|
|
7
|
+
import * as yup from 'yup';
|
|
8
|
+
import {
|
|
9
|
+
useSetCompletePayOnDeliveryMutation,
|
|
10
|
+
useSetPayOnDeliveryChoiceMutation
|
|
11
|
+
} from './endpoints';
|
|
12
|
+
|
|
13
|
+
interface PayOnDeliveryTranslationsProps {
|
|
14
|
+
paymentInformationTitle: string;
|
|
15
|
+
paymentInformationSubtitle: string;
|
|
16
|
+
totalAmountText: string;
|
|
17
|
+
serviceFeeText: string;
|
|
18
|
+
returnInfoText: string;
|
|
19
|
+
refundInfoText: string;
|
|
20
|
+
faqInfoText: string;
|
|
21
|
+
placeOrderText: string;
|
|
22
|
+
errorMessageText: string;
|
|
23
|
+
requiredFieldMessage: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const defaultTranslations = {
|
|
27
|
+
paymentInformationTitle: 'Payment information on delivery',
|
|
28
|
+
paymentInformationSubtitle: 'Payment Information at the Door',
|
|
29
|
+
totalAmountText:
|
|
30
|
+
'The amount you have to pay, including the Payment at the Door service fee, is',
|
|
31
|
+
serviceFeeText: 'The payment service fee at the door is',
|
|
32
|
+
returnInfoText:
|
|
33
|
+
'If you want to return the products you have received, you must fill in the fee return form link.',
|
|
34
|
+
refundInfoText:
|
|
35
|
+
'The amount to be refunded will be sent to your account via EFT.',
|
|
36
|
+
faqInfoText:
|
|
37
|
+
'For all your questions about Pay at the Door, you can visit our Frequently Asked Questions page.',
|
|
38
|
+
placeOrderText: 'Place Order',
|
|
39
|
+
errorMessageText: 'Error Message',
|
|
40
|
+
requiredFieldMessage: 'This field is required'
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
interface PayOnDeliveryProps {
|
|
44
|
+
translations: Record<string, PayOnDeliveryTranslationsProps>;
|
|
45
|
+
agreementCheckbox?: React.ReactElement;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export const PayOnDelivery = ({
|
|
49
|
+
translations,
|
|
50
|
+
agreementCheckbox
|
|
51
|
+
}: PayOnDeliveryProps) => {
|
|
52
|
+
const _translations = {
|
|
53
|
+
...defaultTranslations,
|
|
54
|
+
...translations
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const createPayOnDeliveryFormSchema = (
|
|
58
|
+
_translations: PayOnDeliveryTranslationsProps
|
|
59
|
+
) => {
|
|
60
|
+
return yup.object().shape({
|
|
61
|
+
agreement: yup
|
|
62
|
+
.boolean()
|
|
63
|
+
.oneOf([true], _translations.requiredFieldMessage),
|
|
64
|
+
paymentType: yup.string()
|
|
65
|
+
});
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const payOnDeliveryFormSchema = createPayOnDeliveryFormSchema(_translations);
|
|
69
|
+
|
|
70
|
+
const {
|
|
71
|
+
handleSubmit,
|
|
72
|
+
control,
|
|
73
|
+
register,
|
|
74
|
+
formState: { errors },
|
|
75
|
+
setValue
|
|
76
|
+
} = useForm({
|
|
77
|
+
resolver: yupResolver(payOnDeliveryFormSchema)
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
const [setCompletePayOnDelivery] = useSetCompletePayOnDeliveryMutation();
|
|
81
|
+
|
|
82
|
+
const [setPayOnDeliveryPaymentChoice, { isLoading }] =
|
|
83
|
+
useSetPayOnDeliveryChoiceMutation();
|
|
84
|
+
const [formError, setFormError] = useState(null);
|
|
85
|
+
|
|
86
|
+
const { preOrder, paymentChoices } = useAppSelector(
|
|
87
|
+
(state: RootState) => state.checkout
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
const onSubmit: SubmitHandler<null> = async (data) => {
|
|
91
|
+
const response = await setCompletePayOnDelivery(data).unwrap();
|
|
92
|
+
|
|
93
|
+
setFormError(response?.errors?.non_field_errors);
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const setPayOnDeliveryChoice = async (value: string) => {
|
|
97
|
+
if (isLoading) return;
|
|
98
|
+
|
|
99
|
+
const response = await setPayOnDeliveryPaymentChoice(value).unwrap();
|
|
100
|
+
setFormError(response?.errors?.non_field_errors);
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
useEffect(() => {
|
|
104
|
+
if (!preOrder?.payment_choice && paymentChoices.length) {
|
|
105
|
+
setPayOnDeliveryChoice(paymentChoices[0].value);
|
|
106
|
+
} else {
|
|
107
|
+
setValue('paymentType', preOrder?.payment_choice?.value);
|
|
108
|
+
}
|
|
109
|
+
}, [paymentChoices, preOrder?.payment_choice]);
|
|
110
|
+
|
|
111
|
+
return (
|
|
112
|
+
<form
|
|
113
|
+
id="pay_on_delivery"
|
|
114
|
+
className="flex flex-col w-full"
|
|
115
|
+
onSubmit={handleSubmit(onSubmit)}
|
|
116
|
+
>
|
|
117
|
+
<div className="flex justify-start items-center border-solid border-gray-400 px-4 py-2 sm:px-6 sm:py-3 sm:min-h-15">
|
|
118
|
+
<span className="text-black-800 text-lg font-medium sm:text-2xl">
|
|
119
|
+
{_translations.paymentInformationTitle}
|
|
120
|
+
</span>
|
|
121
|
+
</div>
|
|
122
|
+
|
|
123
|
+
<div className="w-full bg-white">
|
|
124
|
+
<div className="text-xs text-black-800 p-4 sm:p-6">
|
|
125
|
+
<div className="mb-2">{_translations.paymentInformationSubtitle}</div>
|
|
126
|
+
<div className="inline-flex gap-4 mb-4">
|
|
127
|
+
{paymentChoices?.map((paymentChoice, index) => (
|
|
128
|
+
<label
|
|
129
|
+
className="w-full flex items-center justify-start min-w-max"
|
|
130
|
+
key={index}
|
|
131
|
+
>
|
|
132
|
+
<Radio
|
|
133
|
+
name="payment_choices"
|
|
134
|
+
type="radio"
|
|
135
|
+
value={paymentChoice.value}
|
|
136
|
+
data-testid={`checkout-bank-account-${paymentChoice.label}`}
|
|
137
|
+
{...register('paymentType')}
|
|
138
|
+
onChange={() => setPayOnDeliveryChoice(paymentChoice.value)}
|
|
139
|
+
></Radio>
|
|
140
|
+
|
|
141
|
+
<span className="flex items-start justify-start flex-col pl-1 sm:flex-row sm:items-center">
|
|
142
|
+
{paymentChoice.label}
|
|
143
|
+
</span>
|
|
144
|
+
</label>
|
|
145
|
+
))}
|
|
146
|
+
</div>
|
|
147
|
+
<ul className="space-y-1">
|
|
148
|
+
<li>
|
|
149
|
+
{_translations.totalAmountText}{' '}
|
|
150
|
+
<Price value={preOrder?.total_amount} />.
|
|
151
|
+
</li>
|
|
152
|
+
<li>
|
|
153
|
+
{_translations.serviceFeeText}{' '}
|
|
154
|
+
<Price value={preOrder?.payment_choice?.price} />.
|
|
155
|
+
</li>
|
|
156
|
+
<li>{_translations.returnInfoText}</li>
|
|
157
|
+
<li>{_translations.refundInfoText}</li>
|
|
158
|
+
<li>{_translations.faqInfoText}</li>
|
|
159
|
+
</ul>
|
|
160
|
+
</div>
|
|
161
|
+
</div>
|
|
162
|
+
|
|
163
|
+
<div className="px-4 sm:px-6">
|
|
164
|
+
<div className="flex items-start flex-col border-solid border-gray-400 py-4 space-y-4">
|
|
165
|
+
{agreementCheckbox &&
|
|
166
|
+
React.cloneElement(agreementCheckbox, {
|
|
167
|
+
control,
|
|
168
|
+
error: errors.agreement,
|
|
169
|
+
fieldId: 'agreement'
|
|
170
|
+
})}
|
|
171
|
+
|
|
172
|
+
{formError && (
|
|
173
|
+
<div className="w-full text-xs text-start px-1 mt-3 text-error">
|
|
174
|
+
{_translations.errorMessageText}
|
|
175
|
+
</div>
|
|
176
|
+
)}
|
|
177
|
+
<Button
|
|
178
|
+
className="group uppercase mt-4 inline-flex items-center justify-center w-full"
|
|
179
|
+
type="submit"
|
|
180
|
+
data-testid="checkout-bank-account-place-order"
|
|
181
|
+
>
|
|
182
|
+
<span>{_translations.placeOrderText}</span>
|
|
183
|
+
<Icon
|
|
184
|
+
size={12}
|
|
185
|
+
name="chevron-end"
|
|
186
|
+
className="fill-primary-foreground ml-2 h-3 group-hover:fill-primary"
|
|
187
|
+
/>
|
|
188
|
+
</Button>
|
|
189
|
+
</div>
|
|
190
|
+
</div>
|
|
191
|
+
</form>
|
|
192
|
+
);
|
|
193
|
+
};
|