@akinon/next 1.65.0 → 1.66.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/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # @akinon/next
2
2
 
3
+ ## 1.66.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 572d2e8: ZERO-2667: Add iframe support for redirection payment methods
8
+ - 2e6104d: ZERO-2888:Edit the numbering in the pagination and the visibility of the prev and next buttons
9
+ - 7b05522: ZERO-2905: Fix resend and close button in otp package
10
+ - 29ead87: ZERO-2905: Fix resend and close button in otp package
11
+ - 12a873e: ZERO-2846:Edit the siteKey regex in the response from GetCaptcha
12
+ - f2c325c: ZERO-2838: Move file input component into @akinon/next
13
+
3
14
  ## 1.65.0
4
15
 
5
16
  ### Minor Changes
@@ -0,0 +1,49 @@
1
+ .checkout-payment-iframe-wrapper {
2
+ position: fixed;
3
+ top: 0;
4
+ left: 0;
5
+ width: 100%;
6
+ height: 100%;
7
+ border: none;
8
+ z-index: 1000;
9
+ background-color: white;
10
+ }
11
+ .checkout-payment-iframe-wrapper iframe {
12
+ width: 100%;
13
+ height: 100%;
14
+ border: none;
15
+ background-color: white;
16
+ }
17
+ .checkout-payment-iframe-wrapper .close-button {
18
+ position: fixed;
19
+ top: 16px;
20
+ right: 16px;
21
+ width: 32px;
22
+ height: 32px;
23
+ display: flex;
24
+ align-items: center;
25
+ justify-content: center;
26
+ z-index: 1001;
27
+ }
28
+
29
+ .checkout-payment-redirection-iframe-wrapper {
30
+ width: 100%;
31
+ position: relative;
32
+ }
33
+ .checkout-payment-redirection-iframe-wrapper iframe {
34
+ width: 100%;
35
+ height: 100%;
36
+ border: none;
37
+ background-color: white;
38
+ }
39
+ .checkout-payment-redirection-iframe-wrapper .close-button {
40
+ position: absolute;
41
+ top: 16px;
42
+ right: 16px;
43
+ width: 32px;
44
+ height: 32px;
45
+ display: flex;
46
+ align-items: center;
47
+ justify-content: center;
48
+ z-index: 1001;
49
+ }/*# sourceMappingURL=index.css.map */
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["index.scss","index.css"],"names":[],"mappings":"AAAA;EACE,eAAA;EACA,MAAA;EACA,OAAA;EACA,WAAA;EACA,YAAA;EACA,YAAA;EACA,aAAA;EACA,uBAAA;ACCF;ADCE;EACE,WAAA;EACA,YAAA;EACA,YAAA;EACA,uBAAA;ACCJ;ADEE;EACE,eAAA;EACA,SAAA;EACA,WAAA;EACA,WAAA;EACA,YAAA;EACA,aAAA;EACA,mBAAA;EACA,uBAAA;EACA,aAAA;ACAJ;;ADIA;EACE,WAAA;EACA,kBAAA;ACDF;ADGE;EACE,WAAA;EACA,YAAA;EACA,YAAA;EACA,uBAAA;ACDJ;ADIE;EACE,kBAAA;EACA,SAAA;EACA,WAAA;EACA,WAAA;EACA,YAAA;EACA,aAAA;EACA,mBAAA;EACA,uBAAA;EACA,aAAA;ACFJ","file":"index.css"}
@@ -1,29 +1,53 @@
1
1
  .checkout-payment-iframe-wrapper {
2
- position: fixed;
3
- top: 0;
4
- left: 0;
5
- width: 100%;
6
- height: 100%;
7
- border: none;
8
- z-index: 1000;
9
- background-color: white;
2
+ position: fixed;
3
+ top: 0;
4
+ left: 0;
5
+ width: 100%;
6
+ height: 100%;
7
+ border: none;
8
+ z-index: 1000;
9
+ background-color: white;
10
10
 
11
- iframe {
12
- width: 100%;
13
- height: 100%;
14
- border: none;
15
- background-color: white;
16
- }
11
+ iframe {
12
+ width: 100%;
13
+ height: 100%;
14
+ border: none;
15
+ background-color: white;
16
+ }
17
17
 
18
- .close-button {
19
- position: fixed;
20
- top: 16px;
21
- right: 16px;
22
- width: 32px;
23
- height: 32px;
24
- display: flex;
25
- align-items: center;
26
- justify-content: center;
27
- z-index: 1001;
28
- }
29
- }
18
+ .close-button {
19
+ position: fixed;
20
+ top: 16px;
21
+ right: 16px;
22
+ width: 32px;
23
+ height: 32px;
24
+ display: flex;
25
+ align-items: center;
26
+ justify-content: center;
27
+ z-index: 1001;
28
+ }
29
+ }
30
+
31
+ .checkout-payment-redirection-iframe-wrapper {
32
+ width: 100%;
33
+ position: relative;
34
+
35
+ iframe {
36
+ width: 100%;
37
+ height: 100%;
38
+ border: none;
39
+ background-color: white;
40
+ }
41
+
42
+ .close-button {
43
+ position: absolute;
44
+ top: 16px;
45
+ right: 16px;
46
+ width: 32px;
47
+ height: 32px;
48
+ display: flex;
49
+ align-items: center;
50
+ justify-content: center;
51
+ z-index: 1001;
52
+ }
53
+ }
@@ -0,0 +1,8 @@
1
+ import { forwardRef } from 'react';
2
+ import { FileInputProps } from '../types/index';
3
+
4
+ export const FileInput = forwardRef<HTMLInputElement, FileInputProps>(
5
+ function fileInput(props, ref) {
6
+ return <input type="file" {...props} ref={ref} />;
7
+ }
8
+ );
@@ -20,3 +20,4 @@ export * from './trans';
20
20
  export * from './link';
21
21
  export * from './pagination';
22
22
  export * from './live-commerce';
23
+ export * from './file-input';
@@ -22,11 +22,12 @@ const userApi = api.injectEndpoints({
22
22
  getCaptcha: build.query<GetCaptchaResponse, void>({
23
23
  query: () => buildClientRequestUrl(user.captcha),
24
24
  transformResponse: (response: { html: string }) => {
25
- const siteKeyMatch = response.html.match(/sitekey=["|'][^"']+/gi);
25
+ const siteKey = response.html.match(/data-sitekey="([^"]+)"/i)[1];
26
+
26
27
  const csrfTokenMatch = response.html.match(
27
28
  /name=['|"]csrfmiddlewaretoken['|"] value=['|"][^'"]+/gi
28
29
  );
29
- const siteKey = siteKeyMatch?.[0].replace(/sitekey=["|']/, '') || '';
30
+
30
31
  const csrfToken =
31
32
  csrfTokenMatch?.[0].replace(
32
33
  /name=['|"]csrfmiddlewaretoken['|"] value=['|"]/gi,
package/hooks/index.ts CHANGED
@@ -8,4 +8,5 @@ export * from './use-media-query';
8
8
  export * from './use-on-click-outside';
9
9
  export * from './use-mobile-iframe-handler';
10
10
  export * from './use-payment-options';
11
- export * from './use-pagination';
11
+ export * from './use-pagination';
12
+ export * from './use-message-listener';
@@ -0,0 +1,24 @@
1
+ import { useEffect } from 'react';
2
+
3
+ export const useMessageListener = () => {
4
+ useEffect(() => {
5
+ const handleMessage = (event: MessageEvent) => {
6
+ if (event.origin !== window.location.origin) {
7
+ return;
8
+ }
9
+
10
+ if (event.data && typeof event.data === 'string') {
11
+ const messageData = JSON.parse(event.data);
12
+ if (messageData?.url) {
13
+ window.location.href = messageData.url;
14
+ }
15
+ }
16
+ };
17
+
18
+ window.addEventListener('message', handleMessage);
19
+
20
+ return () => {
21
+ window.removeEventListener('message', handleMessage);
22
+ };
23
+ }, []);
24
+ };
@@ -116,7 +116,7 @@ export default function usePagination(
116
116
  urlSearchParams.set('page', (Number(state.page) - 1).toString());
117
117
  return `${pathname}?${urlSearchParams.toString()}`;
118
118
  }
119
- return '#';
119
+ return null;
120
120
  }, [state.page, pathname, urlSearchParams]);
121
121
 
122
122
  const next = useMemo(() => {
@@ -124,7 +124,7 @@ export default function usePagination(
124
124
  urlSearchParams.set('page', (Number(state.page) + 1).toString());
125
125
  return `${pathname}?${urlSearchParams.toString()}`;
126
126
  }
127
- return '#';
127
+ return null;
128
128
  }, [state.page, state.last, pathname, urlSearchParams]);
129
129
 
130
130
  return {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@akinon/next",
3
3
  "description": "Core package for Project Zero Next",
4
- "version": "1.65.0",
4
+ "version": "1.66.0",
5
5
  "private": false,
6
6
  "license": "MIT",
7
7
  "bin": {
@@ -30,7 +30,7 @@
30
30
  "set-cookie-parser": "2.6.0"
31
31
  },
32
32
  "devDependencies": {
33
- "@akinon/eslint-plugin-projectzero": "1.65.0",
33
+ "@akinon/eslint-plugin-projectzero": "1.66.0",
34
34
  "@types/react-redux": "7.1.30",
35
35
  "@types/set-cookie-parser": "2.4.7",
36
36
  "@typescript-eslint/eslint-plugin": "6.7.4",
package/plugins.d.ts CHANGED
@@ -21,6 +21,7 @@ declare module '@akinon/pz-otp' {
21
21
 
22
22
  declare module '@akinon/pz-otp/src/redux/reducer' {
23
23
  export const showPopup: any;
24
+ export const hidePopup: any;
24
25
  }
25
26
 
26
27
  declare module 'pz-saved-card' {
@@ -27,6 +27,7 @@ import { getCookie, setCookie } from '../../utils';
27
27
  import settings from 'settings';
28
28
  import { LocaleUrlStrategy } from '../../localization';
29
29
  import { showMobile3dIframe } from '../../utils/mobile-3d-iframe';
30
+ import { showRedirectionIframe } from '../../utils/redirection-iframe';
30
31
 
31
32
  interface CheckoutResult {
32
33
  payload: {
@@ -182,6 +183,8 @@ export const contextListMiddleware: Middleware = ({
182
183
  if (result?.payload?.context_list) {
183
184
  result.payload.context_list.forEach((context) => {
184
185
  const redirectUrl = context.page_context.redirect_url;
186
+ const isIframe = context.page_context.is_frame ?? false;
187
+
185
188
  if (redirectUrl) {
186
189
  const currentLocale = getCookie('pz-locale');
187
190
 
@@ -199,16 +202,19 @@ export const contextListMiddleware: Middleware = ({
199
202
  }
200
203
 
201
204
  const urlObj = new URL(url, window.location.origin);
205
+ const isMobileDevice =
206
+ isMobileApp ||
207
+ /iPad|iPhone|iPod|Android/i.test(navigator.userAgent);
208
+ const isIframePaymentOptionExcluded =
209
+ settings.checkout?.iframeExcludedPaymentOptions?.includes(
210
+ result.payload?.pre_order?.payment_option?.slug
211
+ );
202
212
  urlObj.searchParams.set('t', new Date().getTime().toString());
203
213
 
204
- if (
205
- (isMobileApp ||
206
- /iPad|iPhone|iPod|Android/i.test(navigator.userAgent)) &&
207
- !settings.checkout?.iframeExcludedPaymentOptions?.includes(
208
- result.payload?.pre_order?.payment_option?.slug
209
- )
210
- ) {
214
+ if (isMobileDevice && !isIframePaymentOptionExcluded) {
211
215
  showMobile3dIframe(urlObj.toString());
216
+ } else if (isIframe && !isIframePaymentOptionExcluded) {
217
+ showRedirectionIframe(urlObj.toString());
212
218
  } else {
213
219
  window.location.href = urlObj.toString();
214
220
  }
package/types/index.ts CHANGED
@@ -269,6 +269,8 @@ export interface ButtonProps
269
269
  appearance?: 'filled' | 'outlined' | 'ghost';
270
270
  }
271
271
 
272
+ export type FileInputProps = React.HTMLProps<HTMLInputElement>;
273
+
272
274
  export interface PriceProps {
273
275
  currencyCode?: string;
274
276
  useCurrencySymbol?: boolean;
@@ -0,0 +1,85 @@
1
+ const iframeURLChange = (iframe, callback) => {
2
+ iframe.addEventListener('load', () => {
3
+ setTimeout(() => {
4
+ if (iframe?.contentWindow?.location) {
5
+ callback(iframe.contentWindow.location);
6
+ }
7
+ }, 0);
8
+ });
9
+ };
10
+
11
+ const removeIframe = async () => {
12
+ const iframeSelector = document.querySelector(
13
+ '.checkout-payment-redirection-iframe-wrapper'
14
+ );
15
+
16
+ const redirectionPaymentWrapper = document.querySelector(
17
+ '.checkout-redirection-payment-wrapper'
18
+ );
19
+
20
+ const form = redirectionPaymentWrapper.querySelector('form') as HTMLElement;
21
+
22
+ if (!iframeSelector) {
23
+ return;
24
+ }
25
+
26
+ iframeSelector.remove();
27
+
28
+ if (form) {
29
+ form.style.display = 'block';
30
+ }
31
+
32
+ location.reload();
33
+ };
34
+
35
+ export const showRedirectionIframe = (redirectUrl: string) => {
36
+ const iframeWrapper = document.createElement('div');
37
+ const iframe = document.createElement('iframe');
38
+ const closeButton = document.createElement('div');
39
+ const redirectionPaymentWrapper = document.querySelector(
40
+ '.checkout-redirection-payment-wrapper'
41
+ );
42
+ const form = redirectionPaymentWrapper.querySelector('form') as HTMLElement;
43
+
44
+ iframeWrapper.className = 'checkout-payment-redirection-iframe-wrapper';
45
+ closeButton.className = 'close-button';
46
+
47
+ iframe.setAttribute('src', redirectUrl);
48
+ closeButton.innerHTML = '&#x2715';
49
+ closeButton.addEventListener('click', removeIframe);
50
+
51
+ iframeWrapper.append(iframe, closeButton);
52
+
53
+ if (form) {
54
+ form.style.display = 'none';
55
+ }
56
+
57
+ redirectionPaymentWrapper.appendChild(iframeWrapper);
58
+
59
+ iframeURLChange(iframe, (location) => {
60
+ if (location.origin !== window.location.origin) {
61
+ return false;
62
+ }
63
+
64
+ const searchParams = new URLSearchParams(location.search);
65
+ const isOrderCompleted = location.href.includes('/orders/completed');
66
+
67
+ if (isOrderCompleted) {
68
+ (window.parent as any)?.postMessage?.(
69
+ JSON.stringify({
70
+ url: location.pathname
71
+ })
72
+ );
73
+ }
74
+
75
+ if (
76
+ searchParams.has('success') ||
77
+ isOrderCompleted ||
78
+ location.href.includes('/orders/checkout')
79
+ ) {
80
+ setTimeout(() => {
81
+ removeIframe();
82
+ }, 0);
83
+ }
84
+ });
85
+ };