@akinon/next 1.44.0 → 1.45.0-rc.1

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.
Files changed (48) hide show
  1. package/CHANGELOG.md +248 -0
  2. package/api/client.ts +33 -9
  3. package/assets/styles/index.css +49 -0
  4. package/assets/styles/index.css.map +1 -0
  5. package/assets/styles/index.scss +50 -26
  6. package/components/input.tsx +21 -7
  7. package/components/link.tsx +17 -13
  8. package/components/pagination.tsx +1 -2
  9. package/components/price.tsx +11 -4
  10. package/components/selected-payment-option-view.tsx +26 -38
  11. package/data/client/account.ts +10 -9
  12. package/data/client/address.ts +32 -8
  13. package/data/client/api.ts +2 -2
  14. package/data/client/basket.ts +11 -13
  15. package/data/client/checkout.ts +47 -4
  16. package/data/server/category.ts +2 -2
  17. package/data/server/list.ts +2 -2
  18. package/data/server/product.ts +15 -13
  19. package/data/server/special-page.ts +2 -2
  20. package/data/urls.ts +5 -1
  21. package/hooks/index.ts +2 -1
  22. package/hooks/use-message-listener.ts +24 -0
  23. package/hooks/use-payment-options.ts +2 -1
  24. package/lib/cache-handler.mjs +33 -0
  25. package/lib/cache.ts +18 -6
  26. package/middlewares/default.ts +50 -2
  27. package/middlewares/locale.ts +32 -30
  28. package/middlewares/pretty-url.ts +4 -0
  29. package/middlewares/url-redirection.ts +4 -0
  30. package/package.json +5 -4
  31. package/plugins.d.ts +1 -0
  32. package/redux/middlewares/checkout.ts +70 -11
  33. package/redux/reducers/checkout.ts +24 -5
  34. package/redux/reducers/config.ts +2 -0
  35. package/routes/pretty-url.tsx +194 -0
  36. package/types/commerce/account.ts +1 -0
  37. package/types/commerce/address.ts +1 -1
  38. package/types/commerce/checkout.ts +30 -0
  39. package/types/commerce/misc.ts +2 -0
  40. package/types/commerce/order.ts +12 -0
  41. package/types/index.ts +28 -2
  42. package/utils/app-fetch.ts +1 -1
  43. package/utils/generate-commerce-search-params.ts +6 -2
  44. package/utils/index.ts +27 -6
  45. package/utils/menu-generator.ts +2 -2
  46. package/utils/redirection-iframe.ts +85 -0
  47. package/utils/server-translation.ts +5 -1
  48. package/with-pz-config.js +11 -1
@@ -33,6 +33,23 @@ export interface ShippingOption {
33
33
  slug: string;
34
34
  }
35
35
 
36
+ export interface DataSource {
37
+ pk: number;
38
+ name: string;
39
+ data_source_shipping_options: DataSourceShippingOption[];
40
+ }
41
+
42
+ export interface DataSourceShippingOption {
43
+ pk: number;
44
+ shipping_amount: string;
45
+ shipping_option_name: string;
46
+ shipping_option_logo: string | null;
47
+ data_source: {
48
+ pk: number;
49
+ title: string | null;
50
+ };
51
+ }
52
+
36
53
  export interface CheckoutAddressType {
37
54
  label: string;
38
55
  text: string;
@@ -78,6 +95,8 @@ export interface PreOrder {
78
95
  retail_store?: RetailStore;
79
96
  gift_box?: GiftBox;
80
97
  credit_payment_option?: CheckoutCreditPaymentOption;
98
+ data_source_shipping_options: any;
99
+ attribute_based_shipping_options?: AttributeBasedShippingOption[];
81
100
  }
82
101
 
83
102
  export interface GuestLoginFormParams {
@@ -117,6 +136,7 @@ export interface CheckoutContext {
117
136
  redirect_url?: string;
118
137
  context_data?: any;
119
138
  balance?: string;
139
+ attribute_based_shipping_options?: AttributeBasedShippingOption[];
120
140
  [key: string]: any;
121
141
  };
122
142
  }
@@ -142,3 +162,13 @@ export interface BankAccount {
142
162
  pk: number;
143
163
  sort_order: number;
144
164
  }
165
+
166
+ export interface AttributeBasedShippingOption {
167
+ pk: number;
168
+ shipping_amount: string | null;
169
+ shipping_option_name: string | null;
170
+ shipping_option_logo: string | null;
171
+ attribute_value: string | null;
172
+ attribute_key: string;
173
+ [key: string]: any;
174
+ }
@@ -2,6 +2,8 @@ import { District } from './address';
2
2
  export interface Config {
3
3
  user_phone_regex: string;
4
4
  user_phone_format: string;
5
+ user_post_code_regex: string;
6
+ user_post_code_format: string;
5
7
  country: {
6
8
  pk: number;
7
9
  name: string;
@@ -52,6 +52,10 @@ export interface OrderItem {
52
52
  quantity: string;
53
53
  unit_price: string;
54
54
  total_amount: string;
55
+ attributes: {
56
+ gift_note: string;
57
+ [key: string]: any;
58
+ };
55
59
  }
56
60
 
57
61
  export interface Order {
@@ -105,6 +109,14 @@ export interface Order {
105
109
  shipping_option: number;
106
110
  tracking_number: string;
107
111
  tracking_url: string;
112
+ [key: string]: any;
113
+ bank: {
114
+ pk: number;
115
+ name: string;
116
+ slug: string;
117
+ logo: string;
118
+ [key: string]: any;
119
+ };
108
120
  }
109
121
 
110
122
  export interface Quotations {
package/types/index.ts CHANGED
@@ -2,7 +2,7 @@ import { LocaleUrlStrategy } from '../localization';
2
2
  import { PzNextRequest } from '../middlewares';
3
3
  import { Control, FieldError } from 'react-hook-form';
4
4
  import { ReactNode } from 'react';
5
-
5
+ import { UsePaginationType } from '../hooks/use-pagination';
6
6
  declare global {
7
7
  interface Window {
8
8
  // we did it like this because declare types needs to be identical, if not it will fail
@@ -68,9 +68,17 @@ export interface Currency {
68
68
  * @see https://en.wikipedia.org/wiki/ISO_4217
69
69
  */
70
70
  code: string;
71
+ /**
72
+ * Number of decimal places to display.
73
+ *
74
+ * @example
75
+ * decimalScale: 3
76
+ */
77
+ decimalScale?: number;
71
78
  }
72
79
 
73
80
  export interface Settings {
81
+ usePrettyUrlRoute?: boolean;
74
82
  commerceUrl: string;
75
83
  redis: {
76
84
  defaultExpirationTime: number;
@@ -216,7 +224,7 @@ export type Translations = { [key: string]: object };
216
224
 
217
225
  export interface PageProps<T = any> {
218
226
  params: T;
219
- searchParams: URLSearchParams;
227
+ searchParams: { [key: string]: string | string[] | undefined };
220
228
  }
221
229
 
222
230
  export interface LayoutProps<T = any> extends PageProps<T> {
@@ -283,3 +291,21 @@ export interface AccordionProps {
283
291
  export interface PluginModuleComponentProps {
284
292
  settings?: Record<string, any>;
285
293
  }
294
+
295
+ export interface PaginationProps {
296
+ total: number | undefined;
297
+ limit?: number | undefined;
298
+ currentPage: number | undefined;
299
+ numberOfPages?: number | undefined;
300
+ containerClassName?: string;
301
+ moreButtonClassName?: string;
302
+ prevClassName?: string;
303
+ nextClassName?: string;
304
+ pageClassName?: string;
305
+ threshold?: number | undefined;
306
+ delta?: number | undefined;
307
+ render?: (pagination: UsePaginationType) => ReactNode;
308
+ type?: 'infinite' | 'list' | 'more';
309
+ onPageChange?: (page: number) => void;
310
+ direction?: 'next' | 'prev';
311
+ }
@@ -41,7 +41,7 @@ const appFetch = async <T>(
41
41
  };
42
42
 
43
43
  init.next = {
44
- revalidate: 60
44
+ revalidate: Settings.usePrettyUrlRoute ? 0 : 60
45
45
  };
46
46
 
47
47
  logger.debug(`FETCH START ${url}`, { requestURL, init, ip });
@@ -1,11 +1,15 @@
1
1
  import logger from './log';
2
2
 
3
- export const generateCommerceSearchParams = (searchParams?: URLSearchParams) => {
3
+ export const generateCommerceSearchParams = (searchParams?: {
4
+ [key: string]: string | string[] | undefined;
5
+ }) => {
4
6
  if (!searchParams) {
5
7
  return null;
6
8
  }
7
9
 
8
- const urlSerchParams = new URLSearchParams(searchParams);
10
+ const urlSerchParams = new URLSearchParams(
11
+ searchParams as Record<string, string>
12
+ );
9
13
 
10
14
  Object.entries(searchParams).forEach(([key, value]) => {
11
15
  if (typeof value === 'object') {
package/utils/index.ts CHANGED
@@ -63,18 +63,33 @@ export function getTranslateFn(path: string, translations: any) {
63
63
 
64
64
  export function buildClientRequestUrl(
65
65
  path: string,
66
- options?: ClientRequestOptions
66
+ options?: ClientRequestOptions & { cache?: boolean }
67
67
  ) {
68
68
  let url = `/api/client${path}`;
69
69
 
70
+ let hasQuery = url.includes('?');
71
+
70
72
  if (options) {
71
- if (url.includes('?')) {
72
- url += '&';
73
- } else {
74
- url += '?';
73
+ const { cache, ...otherOptions } = options;
74
+
75
+ if (Object.keys(otherOptions).length > 0) {
76
+ if (hasQuery) {
77
+ url += '&';
78
+ } else {
79
+ url += '?';
80
+ hasQuery = true;
81
+ }
82
+ url += `options=${encodeURIComponent(JSON.stringify(otherOptions))}`;
75
83
  }
76
84
 
77
- url += `options=${encodeURIComponent(JSON.stringify(options))}`;
85
+ if (cache === false) {
86
+ if (hasQuery) {
87
+ url += '&';
88
+ } else {
89
+ url += '?';
90
+ }
91
+ url += `t=${Date.now()}`;
92
+ }
78
93
  }
79
94
 
80
95
  return url;
@@ -102,6 +117,12 @@ export function buildCDNUrl(url: string, config?: CDNOptions) {
102
117
  ''
103
118
  );
104
119
 
120
+ const noOptionFileExtension = url?.split('.').pop()?.toLowerCase() ?? '';
121
+
122
+ if (noOptionFileExtension === 'svg' || noOptionFileExtension === 'gif') {
123
+ return url;
124
+ }
125
+
105
126
  let options = '';
106
127
 
107
128
  if (config) {
@@ -13,8 +13,8 @@ export const menuGenerator = (arr: MenuItemType[]) => {
13
13
  });
14
14
 
15
15
  Object.values(data).forEach((item) => {
16
- if (item.parent) {
17
- data[item.parent.pk].children.push(item);
16
+ if (item.parent_pk) {
17
+ data[item.parent_pk].children.push(item);
18
18
  } else {
19
19
  tree.push(item);
20
20
  }
@@ -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
+ };
@@ -5,10 +5,10 @@ import { getTranslateFn } from '../utils';
5
5
  import { Translations } from '../types';
6
6
  import settings from 'settings';
7
7
  import logger from './log';
8
+ import { unstable_cache } from 'next/cache';
8
9
 
9
10
  export const translations: Translations = {};
10
11
 
11
- // TODO: Read translations from cache
12
12
  export async function getTranslations(locale_: string) {
13
13
  try {
14
14
  const locale = settings.localization.locales.find(
@@ -52,6 +52,10 @@ export async function getTranslations(locale_: string) {
52
52
  return translations;
53
53
  }
54
54
 
55
+ export const getCachedTranslations = unstable_cache(async (locale: string) =>
56
+ getTranslations(locale)
57
+ );
58
+
55
59
  export function t(path: string) {
56
60
  return getTranslateFn(path, translations);
57
61
  }
package/with-pz-config.js CHANGED
@@ -8,6 +8,7 @@ const defaultConfig = {
8
8
  transpilePackages: ['@akinon/next', ...pzPlugins.map((p) => `@akinon/${p}`)],
9
9
  skipTrailingSlashRedirect: true,
10
10
  poweredByHeader: false,
11
+ cacheMaxMemorySize: 0,
11
12
  env: {
12
13
  NEXT_PUBLIC_SENTRY_DSN: process.env.SENTRY_DSN
13
14
  },
@@ -65,7 +66,12 @@ const defaultConfig = {
65
66
  }
66
67
  };
67
68
 
68
- const withPzConfig = (myNextConfig = {}) => {
69
+ const withPzConfig = (
70
+ myNextConfig = {},
71
+ options = {
72
+ useCacheHandler: false
73
+ }
74
+ ) => {
69
75
  let extendedConfig = deepMerge({}, defaultConfig, myNextConfig);
70
76
 
71
77
  const originalPzHeadersFunction = defaultConfig.headers;
@@ -91,6 +97,10 @@ const withPzConfig = (myNextConfig = {}) => {
91
97
  return [...pzRewrites, ...myRewrites];
92
98
  };
93
99
 
100
+ if (options.useCacheHandler && process.env.CACHE_HOST) {
101
+ extendedConfig.cacheHandler = require.resolve('./lib/cache-handler.mjs');
102
+ }
103
+
94
104
  return extendedConfig;
95
105
  };
96
106