@akinon/next 2.0.0-beta.9 → 2.0.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.
Files changed (121) hide show
  1. package/CHANGELOG.md +434 -23
  2. package/__tests__/next-config.test.ts +83 -0
  3. package/__tests__/tsconfig.json +23 -0
  4. package/api/auth.ts +367 -63
  5. package/api/barcode-search.ts +59 -0
  6. package/api/cache.ts +41 -5
  7. package/api/client.ts +21 -4
  8. package/api/form.ts +85 -0
  9. package/api/image-proxy.ts +75 -0
  10. package/api/product-categories.ts +53 -0
  11. package/api/similar-product-list.ts +63 -0
  12. package/api/similar-products.ts +111 -0
  13. package/api/virtual-try-on.ts +382 -0
  14. package/assets/styles/index.scss +84 -0
  15. package/babel.config.js +6 -0
  16. package/bin/pz-generate-routes.js +115 -0
  17. package/bin/pz-install-plugins.js +1 -1
  18. package/bin/pz-prebuild.js +1 -0
  19. package/bin/pz-predev.js +1 -0
  20. package/bin/pz-run-tests.js +99 -0
  21. package/components/accordion.tsx +21 -6
  22. package/components/client-root.tsx +119 -3
  23. package/components/file-input.tsx +65 -3
  24. package/components/index.ts +1 -0
  25. package/components/input.tsx +2 -2
  26. package/components/link.tsx +46 -16
  27. package/components/logger-popup.tsx +213 -0
  28. package/components/modal.tsx +32 -16
  29. package/components/plugin-module.tsx +62 -3
  30. package/components/price.tsx +2 -2
  31. package/components/select.tsx +3 -3
  32. package/components/selected-payment-option-view.tsx +21 -0
  33. package/data/client/account.ts +17 -2
  34. package/data/client/basket.ts +39 -0
  35. package/data/client/checkout.ts +336 -99
  36. package/data/client/misc.ts +13 -1
  37. package/data/server/category.ts +11 -9
  38. package/data/server/flatpage.ts +4 -1
  39. package/data/server/form.ts +4 -1
  40. package/data/server/landingpage.ts +4 -1
  41. package/data/server/list.ts +5 -4
  42. package/data/server/menu.ts +4 -1
  43. package/data/server/product.ts +97 -52
  44. package/data/server/seo.ts +4 -1
  45. package/data/server/special-page.ts +5 -4
  46. package/data/server/widget.ts +71 -1
  47. package/data/urls.ts +6 -3
  48. package/hocs/client/with-segment-defaults.tsx +2 -2
  49. package/hocs/server/with-segment-defaults.tsx +81 -20
  50. package/hooks/index.ts +3 -0
  51. package/hooks/use-localization.ts +24 -10
  52. package/hooks/use-logger-context.tsx +114 -0
  53. package/hooks/use-logger.ts +92 -0
  54. package/hooks/use-loyalty-availability.ts +21 -0
  55. package/hooks/use-payment-options.ts +2 -1
  56. package/hooks/use-pz-params.ts +37 -0
  57. package/hooks/use-router.ts +53 -19
  58. package/instrumentation/index.ts +0 -1
  59. package/instrumentation/node.ts +2 -20
  60. package/jest.config.js +25 -0
  61. package/lib/cache-handler.mjs +534 -16
  62. package/lib/cache.ts +269 -34
  63. package/localization/provider.tsx +2 -5
  64. package/middlewares/bfcache-headers.ts +18 -0
  65. package/middlewares/checkout-provider.ts +1 -1
  66. package/middlewares/complete-gpay.ts +32 -26
  67. package/middlewares/complete-masterpass.ts +33 -26
  68. package/middlewares/complete-wallet.ts +182 -0
  69. package/middlewares/default.ts +357 -203
  70. package/middlewares/index.ts +10 -2
  71. package/middlewares/locale.ts +5 -3
  72. package/middlewares/masterpass-rest-callback.ts +230 -0
  73. package/middlewares/oauth-login.ts +200 -57
  74. package/middlewares/pretty-url.ts +21 -8
  75. package/middlewares/redirection-payment.ts +32 -26
  76. package/middlewares/saved-card-redirection.ts +33 -26
  77. package/middlewares/three-d-redirection.ts +32 -26
  78. package/middlewares/url-redirection.ts +9 -15
  79. package/middlewares/wallet-complete-redirection.ts +206 -0
  80. package/package.json +24 -10
  81. package/plugins.d.ts +19 -4
  82. package/plugins.js +9 -1
  83. package/redux/actions.ts +47 -0
  84. package/redux/middlewares/checkout.ts +61 -8
  85. package/redux/middlewares/index.ts +14 -10
  86. package/redux/middlewares/pre-order/address.ts +1 -1
  87. package/redux/middlewares/pre-order/attribute-based-shipping-option.ts +1 -1
  88. package/redux/middlewares/pre-order/data-source-shipping-option.ts +1 -1
  89. package/redux/middlewares/pre-order/delivery-option.ts +1 -1
  90. package/redux/middlewares/pre-order/index.ts +3 -1
  91. package/redux/middlewares/pre-order/installment-option.ts +2 -1
  92. package/redux/middlewares/pre-order/payment-option-reset.ts +37 -0
  93. package/redux/middlewares/pre-order/payment-option.ts +1 -1
  94. package/redux/middlewares/pre-order/pre-order-validation.ts +4 -3
  95. package/redux/middlewares/pre-order/redirection.ts +2 -2
  96. package/redux/middlewares/pre-order/set-pre-order.ts +2 -2
  97. package/redux/middlewares/pre-order/shipping-option.ts +1 -1
  98. package/redux/middlewares/pre-order/shipping-step.ts +1 -1
  99. package/redux/reducers/checkout.ts +15 -1
  100. package/redux/reducers/index.ts +7 -1
  101. package/redux/reducers/widget.ts +80 -0
  102. package/sentry/index.ts +54 -17
  103. package/tailwind/content.js +16 -0
  104. package/types/commerce/checkout.ts +26 -1
  105. package/types/commerce/widget.ts +33 -0
  106. package/types/index.ts +114 -5
  107. package/types/next-auth.d.ts +2 -2
  108. package/types/widget.ts +80 -0
  109. package/utils/app-fetch.ts +7 -2
  110. package/utils/generate-commerce-search-params.ts +3 -2
  111. package/utils/get-checkout-path.ts +3 -0
  112. package/utils/get-root-hostname.ts +28 -0
  113. package/utils/index.ts +69 -18
  114. package/utils/mobile-3d-iframe.ts +8 -2
  115. package/utils/override-middleware.ts +1 -0
  116. package/utils/pz-segments.ts +92 -0
  117. package/utils/redirect-ignore.ts +35 -0
  118. package/utils/redirect.ts +9 -3
  119. package/utils/redirection-iframe.ts +8 -2
  120. package/utils/widget-styles.ts +107 -0
  121. package/with-pz-config.js +20 -7
@@ -2,12 +2,15 @@ import rootReducer from './root';
2
2
  import checkoutReducer from './checkout';
3
3
  import configReducer from './config';
4
4
  import headerReducer from './header';
5
+ import widgetReducer from './widget';
5
6
  import { api } from '../../data/client/api';
6
7
 
7
8
  // Plugin reducers
8
9
  import { masterpassReducer } from '@akinon/pz-masterpass';
9
10
  import { otpReducer } from '@akinon/pz-otp';
10
11
  import { savedCardReducer } from '@akinon/pz-saved-card';
12
+ import cyberSourceUcReducer from '@akinon/pz-cybersource-uc/src/redux/reducer';
13
+ import { masterpassRestReducer } from '@akinon/pz-masterpass-rest';
11
14
 
12
15
  const fallbackReducer = (state = {}) => state;
13
16
 
@@ -17,9 +20,12 @@ const reducers = {
17
20
  checkout: checkoutReducer,
18
21
  config: configReducer,
19
22
  header: headerReducer,
23
+ widget: widgetReducer,
20
24
  masterpass: masterpassReducer || fallbackReducer,
21
25
  otp: otpReducer || fallbackReducer,
22
- savedCard: savedCardReducer || fallbackReducer
26
+ savedCard: savedCardReducer || fallbackReducer,
27
+ cybersource_uc: cyberSourceUcReducer || fallbackReducer,
28
+ masterpassRest: masterpassRestReducer || fallbackReducer
23
29
  };
24
30
 
25
31
  export default reducers;
@@ -0,0 +1,80 @@
1
+ import { createSlice, PayloadAction } from '@reduxjs/toolkit';
2
+ import { WidgetComponentData, WidgetDataSource } from '../../types/widget';
3
+
4
+ export interface WidgetState {
5
+ designMode: boolean;
6
+ responsive: string;
7
+ components: WidgetComponentData[];
8
+ placeholders: {
9
+ slug: string;
10
+ widgetSlugs: string[];
11
+ }[];
12
+ selectedPlaceholder?: string;
13
+ selectedWidget?: string;
14
+ selectedComponentId?: string;
15
+ draggingActive?: boolean;
16
+ dataSources: WidgetDataSource[];
17
+ }
18
+
19
+ const initialState: WidgetState = {
20
+ designMode: true,
21
+ responsive: 'mobile',
22
+ components: [],
23
+ placeholders: [],
24
+ selectedPlaceholder: undefined,
25
+ selectedWidget: undefined,
26
+ selectedComponentId: undefined,
27
+ draggingActive: false,
28
+ dataSources: []
29
+ };
30
+
31
+ const widgetSlice = createSlice({
32
+ name: 'widget',
33
+ initialState,
34
+ reducers: {
35
+ setDesignMode(state, { payload }: PayloadAction<boolean>) {
36
+ state.designMode = payload;
37
+ },
38
+ setResponsive(state, { payload }: PayloadAction<string>) {
39
+ state.responsive = payload;
40
+ },
41
+ setComponents(state, { payload }: PayloadAction<WidgetComponentData[]>) {
42
+ state.components = payload;
43
+ },
44
+ setPlaceholders(
45
+ state,
46
+ { payload }: PayloadAction<WidgetState['placeholders']>
47
+ ) {
48
+ state.placeholders = payload;
49
+ },
50
+ setSelectedPlaceholder(state, { payload }: PayloadAction<string>) {
51
+ state.selectedPlaceholder = payload;
52
+ },
53
+ setSelectedWidget(state, { payload }: PayloadAction<string>) {
54
+ state.selectedWidget = payload;
55
+ },
56
+ setSelectedComponentId(state, { payload }: PayloadAction<string>) {
57
+ state.selectedComponentId = payload;
58
+ },
59
+ setDraggingActive(state, { payload }: PayloadAction<boolean>) {
60
+ state.draggingActive = payload;
61
+ },
62
+ setDataSources(state, { payload }: PayloadAction<WidgetDataSource[]>) {
63
+ state.dataSources = payload;
64
+ }
65
+ }
66
+ });
67
+
68
+ export const {
69
+ setDesignMode,
70
+ setResponsive,
71
+ setComponents,
72
+ setPlaceholders,
73
+ setSelectedPlaceholder,
74
+ setSelectedWidget,
75
+ setSelectedComponentId,
76
+ setDraggingActive,
77
+ setDataSources
78
+ } = widgetSlice.actions;
79
+
80
+ export default widgetSlice.reducer;
package/sentry/index.ts CHANGED
@@ -13,36 +13,73 @@ const ALLOWED_CLIENT_LOG_TYPES: ClientLogType[] = [
13
13
  ClientLogType.CHECKOUT
14
14
  ];
15
15
 
16
+ const isNetworkError = (exception: unknown): boolean => {
17
+ if (!(exception instanceof Error)) return false;
18
+
19
+ const networkErrorPatterns = [
20
+ 'networkerror',
21
+ 'failed to fetch',
22
+ 'network request failed',
23
+ 'network error',
24
+ 'loading chunk',
25
+ 'chunk load failed'
26
+ ];
27
+
28
+ if (exception.name === 'NetworkError') return true;
29
+
30
+ if (exception.name === 'TypeError') {
31
+ return networkErrorPatterns.some((pattern) =>
32
+ exception.message.toLowerCase().includes(pattern)
33
+ );
34
+ }
35
+
36
+ return networkErrorPatterns.some((pattern) =>
37
+ exception.message.toLowerCase().includes(pattern)
38
+ );
39
+ };
40
+
16
41
  export const initSentry = (
17
42
  type: 'Server' | 'Client' | 'Edge',
18
43
  options: Sentry.BrowserOptions | Sentry.NodeOptions | Sentry.EdgeOptions = {}
19
44
  ) => {
20
- // TODO: Handle options with ESLint rules
45
+ // TODO: Remove Zero Project DSN
21
46
 
22
- Sentry.init({
47
+ const baseConfig = {
23
48
  dsn:
24
- options.dsn ||
25
49
  SENTRY_DSN ||
50
+ options.dsn ||
26
51
  'https://d8558ef8997543deacf376c7d8d7cf4b@o64293.ingest.sentry.io/4504338423742464',
27
52
  initialScope: {
28
53
  tags: {
29
54
  APP_TYPE: 'ProjectZeroNext',
30
- TYPE: type
55
+ TYPE: type,
56
+ ...((options.initialScope as any)?.tags || {})
31
57
  }
32
58
  },
33
59
  tracesSampleRate: 0,
34
- integrations: [],
35
- beforeSend: (event, hint) => {
36
- if (
37
- type === 'Client' &&
38
- !ALLOWED_CLIENT_LOG_TYPES.includes(
39
- event.tags?.LOG_TYPE as ClientLogType
40
- )
41
- ) {
42
- return null;
43
- }
60
+ integrations: []
61
+ };
44
62
 
45
- return event;
46
- }
47
- });
63
+ if (type === 'Server' || type === 'Edge') {
64
+ Sentry.init(baseConfig);
65
+ } else if (type === 'Client') {
66
+ Sentry.init({
67
+ ...baseConfig,
68
+ beforeSend: (event, hint) => {
69
+ if (
70
+ !ALLOWED_CLIENT_LOG_TYPES.includes(
71
+ event.tags?.LOG_TYPE as ClientLogType
72
+ )
73
+ ) {
74
+ return null;
75
+ }
76
+
77
+ if (isNetworkError(hint?.originalException)) {
78
+ return null;
79
+ }
80
+
81
+ return event;
82
+ }
83
+ });
84
+ }
48
85
  };
@@ -0,0 +1,16 @@
1
+ const defaultPlugins = require('../plugins');
2
+
3
+ const getAkinonNextContent = (plugins = defaultPlugins) => {
4
+ return [
5
+ `./node_modules/@akinon/next/components/**/*.{js,ts,jsx,tsx}`,
6
+ `../../node_modules/@akinon/next/components/**/*.{js,ts,jsx,tsx}`,
7
+ ...plugins
8
+ .map((plugin) => [
9
+ `./node_modules/@akinon/${plugin}/**/*.{js,ts,jsx,tsx}`,
10
+ `../../node_modules/@akinon/${plugin}/**/*.{js,ts,jsx,tsx}`
11
+ ])
12
+ .flat()
13
+ ];
14
+ };
15
+
16
+ module.exports = getAkinonNextContent;
@@ -1,9 +1,9 @@
1
+ import type { JSX } from 'react';
1
2
  import { Address } from './address';
2
3
  import { Basket } from './basket';
3
4
  import { RetailStore } from './misc';
4
5
  import { PaymentOption } from './order';
5
6
  import { Product } from './product';
6
- import { JSX } from 'react';
7
7
  import { RootState, TypedDispatch } from 'redux/store';
8
8
 
9
9
  export enum CheckoutStep {
@@ -68,6 +68,18 @@ export interface CheckoutPaymentOption {
68
68
  viewProps?: any;
69
69
  }
70
70
 
71
+ export interface LoyaltyBalanceItem {
72
+ label_id: number | null;
73
+ label: string | null;
74
+ balance: string;
75
+ currency?: string;
76
+ }
77
+
78
+ export interface AccountUsage {
79
+ label_id: number | null;
80
+ amount: number;
81
+ }
82
+
71
83
  export interface GiftBox {
72
84
  note: string;
73
85
  gift_video: boolean;
@@ -91,6 +103,7 @@ export interface PreOrder {
91
103
  notes?: string;
92
104
  user_phone_number?: string;
93
105
  loyalty_money?: string;
106
+ loyalty_account_usages?: AccountUsage[];
94
107
  currency_type_label?: string;
95
108
  is_guest?: boolean;
96
109
  is_post_order?: boolean;
@@ -109,6 +122,7 @@ export interface PreOrder {
109
122
  token?: string;
110
123
  agreement_confirmed?: boolean;
111
124
  phone_number?: string;
125
+ number?: string;
112
126
  }
113
127
 
114
128
  export type ExtraField = Record<string, any>;
@@ -150,6 +164,8 @@ export interface CheckoutContext {
150
164
  redirect_url?: string;
151
165
  context_data?: any;
152
166
  balance?: string;
167
+ balances?: LoyaltyBalanceItem[];
168
+ accounts?: LoyaltyBalanceItem[];
153
169
  attribute_based_shipping_options?: AttributeBasedShippingOption[];
154
170
  paymentData?: any;
155
171
  paymentMethod?: string;
@@ -202,6 +218,15 @@ export interface MiddlewareParams {
202
218
  dispatch: TypedDispatch;
203
219
  }
204
220
 
221
+ export interface MiddlewareAction {
222
+ type: string;
223
+ payload?: { status?: number; [key: string]: unknown };
224
+ meta?: {
225
+ arg?: { endpointName?: string };
226
+ baseQueryMeta?: { request?: { url?: string } };
227
+ };
228
+ }
229
+
205
230
  export type SendSmsType = {
206
231
  phone_number: string;
207
232
  };
@@ -26,3 +26,36 @@ export type WidgetResultType<T> = {
26
26
  slug?: string;
27
27
  template?: string;
28
28
  };
29
+
30
+ export type WidgetSchemaType<T> = {
31
+ pk?: number;
32
+ name?: string;
33
+ schema?: T;
34
+ };
35
+
36
+ export type DynamicWidgetResultType = WidgetResultType<{
37
+ [key: string]: {
38
+ value: string;
39
+ kwargs: {
40
+ data_type: string;
41
+ value?: string;
42
+ url?: string;
43
+ };
44
+ };
45
+ }>;
46
+
47
+ export type DynamicWidgetSchemaType = WidgetSchemaType<{
48
+ [key: string]: {
49
+ key: string;
50
+ data_type: string;
51
+ label: string;
52
+ properties: {
53
+ parent_id?: string;
54
+ style?: {
55
+ [key: string]: any;
56
+ };
57
+ tag?: string;
58
+ type?: string;
59
+ };
60
+ };
61
+ }>;
package/types/index.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  import { LocaleUrlStrategy } from '../localization';
2
2
  import { PzNextRequest } from '../middlewares';
3
+ import { NextFetchEvent } from 'next/server';
4
+ import { NextURL } from 'next/dist/server/web/next-url';
3
5
  import { Control, FieldError } from 'react-hook-form';
4
6
  import { ReactNode } from 'react';
5
7
  import { UsePaginationType } from '../hooks/use-pagination';
@@ -199,6 +201,7 @@ export interface Settings {
199
201
  extraPaymentTypes?: string[];
200
202
  masterpassJsUrl?: string;
201
203
  };
204
+ customNotFoundEnabled: boolean;
202
205
  useOptimizedTranslations?: boolean;
203
206
  plugins?: Record<string, Record<string, any>>;
204
207
  includedProxyHeaders?: string[];
@@ -209,12 +212,26 @@ export interface Settings {
209
212
  */
210
213
  resetBasketOnCurrencyChange?: boolean;
211
214
  frontendIds?: Record<string, number>;
215
+ usePzSegment?: boolean;
216
+ pzSegments?: {
217
+ separator?: string;
218
+ segments: PzSegmentDefinition[];
219
+ };
212
220
  }
213
221
 
214
222
  export interface CacheOptions {
215
223
  cache?: boolean;
216
224
  expire?: number;
217
225
  useProxy?: boolean;
226
+ compressed?: boolean;
227
+ }
228
+
229
+ export interface SetCookieOptions {
230
+ expires?: number; // days
231
+ path?: string;
232
+ domain?: string;
233
+ secure?: boolean;
234
+ sameSite?: 'strict' | 'lax' | 'none';
218
235
  }
219
236
 
220
237
  export interface ClientRequestOptions {
@@ -239,22 +256,87 @@ export type ImageOptions = CDNOptions & {
239
256
 
240
257
  export type Translations = { [key: string]: object };
241
258
 
259
+ export interface PzSegmentResolveContext {
260
+ req: PzNextRequest;
261
+ event: NextFetchEvent;
262
+ url: NextURL;
263
+ locale: string;
264
+ currency: string;
265
+ pathname: string;
266
+ }
267
+
268
+ export interface PzSegmentDefinition {
269
+ name: string;
270
+ resolve?: (context: PzSegmentResolveContext) => string;
271
+ }
272
+
273
+ export interface PzSegmentsConfig {
274
+ separator: string;
275
+ segments: PzSegmentDefinition[];
276
+ }
277
+
278
+ // Search params type compatible with both Next.js resolved searchParams and URLSearchParams
279
+ export type SearchParams = Record<string, string | string[] | undefined> | URLSearchParams;
280
+
281
+ // Raw Next 16 server prop shape, used at the middleware/HOC boundary before normalization
282
+ export type RawSearchParams = Record<string, string | string[] | undefined>;
283
+
284
+ // Page/Layout props — sync params for backward compatibility with v1 brands.
285
+ // `searchParams` is always exposed as `URLSearchParams` (matching v1 behavior).
286
+ // The server HOC normalizes Next 16's raw Record into a URLSearchParams instance.
242
287
  export interface PageProps<T = any> {
243
- params: Promise<T & { locale: string; currency: string }>;
244
- searchParams: Promise<URLSearchParams>;
288
+ params: T & {
289
+ pz?: string;
290
+ commerce?: string;
291
+ locale?: string;
292
+ currency?: string;
293
+ url?: string;
294
+ [key: string]: any;
295
+ };
296
+ searchParams: URLSearchParams;
245
297
  }
246
298
 
247
299
  export interface LayoutProps<T = any> extends PageProps<T> {
248
300
  children: React.ReactNode;
249
301
  }
250
302
 
251
- export interface RootLayoutProps<
303
+ export interface RootLayoutProps<T = any> extends LayoutProps<T> {
304
+ translations: Translations;
305
+ locale: Locale & {
306
+ isoCode: string;
307
+ };
308
+ }
309
+
310
+ // Async versions for Next.js 16 generateMetadata and internal use
311
+ export interface AsyncPageProps<T = any> {
312
+ params: Promise<T & {
313
+ pz?: string;
314
+ commerce?: string;
315
+ locale?: string;
316
+ currency?: string;
317
+ url?: string;
318
+ [key: string]: any;
319
+ }>;
320
+ searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
321
+ }
322
+
323
+ // Resolved (sync) versions for inner components after withSegmentDefaults resolves props
324
+ export interface ResolvedPageProps<T = any> {
325
+ params: T & { locale: string; currency: string };
326
+ searchParams: URLSearchParams;
327
+ }
328
+
329
+ export interface ResolvedLayoutProps<T = any> extends ResolvedPageProps<T> {
330
+ children: React.ReactNode;
331
+ }
332
+
333
+ export interface ResolvedRootLayoutProps<
252
334
  T = {
253
335
  commerce: string;
254
336
  locale: string;
255
337
  currency: string;
256
338
  }
257
- > extends LayoutProps<T> {
339
+ > extends ResolvedLayoutProps<T> {
258
340
  translations: Translations;
259
341
  locale: Locale & {
260
342
  isoCode: string;
@@ -282,7 +364,13 @@ export interface ButtonProps
282
364
  target?: '_blank' | '_self' | '_parent' | '_top';
283
365
  }
284
366
 
285
- export type FileInputProps = React.HTMLProps<HTMLInputElement>;
367
+ export interface FileInputProps extends React.HTMLProps<HTMLInputElement> {
368
+ fileClassName?: string;
369
+ fileNameWrapperClassName?: string;
370
+ fileInputClassName?: string;
371
+ onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void;
372
+ buttonClassName?: string;
373
+ }
286
374
 
287
375
  export interface PriceProps {
288
376
  currencyCode?: string;
@@ -303,15 +391,19 @@ export interface InputProps extends React.HTMLProps<HTMLInputElement> {
303
391
 
304
392
  export interface AccordionProps {
305
393
  isCollapse?: boolean;
394
+ collapseClassName?: string;
306
395
  title?: string;
307
396
  subTitle?: string;
308
397
  icons?: string[];
309
398
  iconSize?: number;
310
399
  iconColor?: string;
311
400
  children?: ReactNode;
401
+ headerClassName?: string;
312
402
  className?: string;
313
403
  titleClassName?: string;
404
+ subTitleClassName?: string;
314
405
  dataTestId?: string;
406
+ contentClassName?: string;
315
407
  }
316
408
 
317
409
  export interface PluginModuleComponentProps {
@@ -336,3 +428,20 @@ export interface PaginationProps {
336
428
  direction?: 'next' | 'prev';
337
429
  isLoading?: boolean;
338
430
  }
431
+
432
+ export interface ModalProps {
433
+ portalId: string;
434
+ children?: React.ReactNode;
435
+ open?: boolean;
436
+ setOpen?: (open: boolean) => void;
437
+ title?: React.ReactNode;
438
+ showCloseButton?: React.ReactNode;
439
+ className?: string;
440
+ overlayClassName?: string;
441
+ headerWrapperClassName?: string;
442
+ titleClassName?: string;
443
+ closeButtonClassName?: string;
444
+ iconName?: string;
445
+ iconSize?: number;
446
+ iconClassName?: string;
447
+ }
@@ -1,8 +1,8 @@
1
- import NextAuth from 'next-auth';
1
+ import 'next-auth';
2
2
 
3
3
  declare module 'next-auth' {
4
4
  interface User {
5
- id?: number;
5
+ id?: number | string;
6
6
  pk: number;
7
7
  firstName: string;
8
8
  lastName: string;
@@ -0,0 +1,80 @@
1
+ export type WidgetStyle = {
2
+ [breakpoint: string]: Record<string, string>;
3
+ };
4
+
5
+ export enum WidgetDataSourceType {
6
+ COLLECTION = 'collection',
7
+ API = 'api',
8
+ STATIC = 'static'
9
+ }
10
+
11
+ export interface WidgetDataSource {
12
+ type: WidgetDataSourceType;
13
+ id: string;
14
+ widgetSlug?: string;
15
+ details: {
16
+ collection?: {
17
+ pk: number;
18
+ productLimit: number;
19
+ };
20
+ api?: {
21
+ url: string;
22
+ method: string;
23
+ headers: Record<string, string>;
24
+ body: Record<string, string>;
25
+ };
26
+ static?: {
27
+ name: string;
28
+ data: Record<string, any>;
29
+ };
30
+ };
31
+ }
32
+
33
+ export type ImageSrc = {
34
+ [key: string]: {
35
+ src: string;
36
+ dimensions: { width: number; height: number };
37
+ };
38
+ };
39
+
40
+ export type WidgetComponentData = {
41
+ id: string;
42
+ widgetSlug: string;
43
+ type: string;
44
+ tag: string;
45
+ style: WidgetStyle;
46
+ value: {
47
+ text?: string;
48
+ src?: string;
49
+ alt?: string;
50
+ title?: string;
51
+ url?: string;
52
+ type?: string;
53
+ width?: string;
54
+ height?: string;
55
+ autoplay?: boolean;
56
+ controls?: boolean;
57
+ muted?: boolean;
58
+ loop?: boolean;
59
+ [key: string]: any;
60
+ };
61
+ parentId: string | null;
62
+ iteratingNode: string;
63
+ iteratorValue: string;
64
+ iteratingValue: string;
65
+ selectedDataSourceId: string;
66
+ dataSources: WidgetDataSource[];
67
+ imageSrc?: ImageSrc;
68
+ href?: string;
69
+ target?: string;
70
+ slider?: {
71
+ items?: number;
72
+ slidesToSlide?: number;
73
+ autoPlay?: boolean;
74
+ autoPlaySpeed?: number;
75
+ infinite?: boolean;
76
+ showDots?: boolean;
77
+ arrows?: boolean;
78
+ itemsToShow?: number;
79
+ };
80
+ };
@@ -2,6 +2,7 @@ import Settings from 'settings';
2
2
  import logger from '../utils/log';
3
3
  import { headers, cookies } from 'next/headers';
4
4
  import { ServerVariables } from './server-variables';
5
+ import { notFound } from 'next/navigation';
5
6
 
6
7
  export enum FetchResponseType {
7
8
  JSON = 'json',
@@ -43,12 +44,12 @@ const appFetch = async <T>({
43
44
  const requestURL = `${decodeURIComponent(commerceUrl)}${url}`;
44
45
 
45
46
  init.headers = {
47
+ cookie: nextCookies.toString(),
46
48
  ...(init.headers ?? {}),
47
49
  ...(ServerVariables.globalHeaders ?? {}),
48
50
  'Accept-Language': currentLocale.apiValue,
49
51
  'x-currency': currency,
50
- 'x-forwarded-for': ip,
51
- cookie: nextCookies.toString()
52
+ 'x-forwarded-for': ip
52
53
  };
53
54
 
54
55
  init.next = {
@@ -75,6 +76,10 @@ const appFetch = async <T>({
75
76
  }
76
77
  }
77
78
 
79
+ if (status === 422) {
80
+ notFound();
81
+ }
82
+
78
83
  return response;
79
84
  };
80
85
 
@@ -1,13 +1,14 @@
1
+ import { SearchParams } from '../types';
1
2
  import logger from './log';
2
3
 
3
4
  export const generateCommerceSearchParams = (
4
- searchParams?: URLSearchParams
5
+ searchParams?: SearchParams
5
6
  ) => {
6
7
  if (!searchParams) {
7
8
  return null;
8
9
  }
9
10
 
10
- const urlSerchParams = new URLSearchParams(searchParams);
11
+ const urlSerchParams = new URLSearchParams(searchParams as any);
11
12
 
12
13
  Object.entries(searchParams).forEach(([key, value]) => {
13
14
  if (typeof value === 'object') {
@@ -0,0 +1,3 @@
1
+ export const getCheckoutPath = (isPostCheckout: boolean): string => {
2
+ return isPostCheckout ? '/orders/post-checkout/' : '/orders/checkout/';
3
+ };
@@ -0,0 +1,28 @@
1
+ import Settings from 'settings';
2
+
3
+ export default function getRootHostname(
4
+ url: string | undefined
5
+ ): string | null {
6
+ if (!url) return null;
7
+
8
+ try {
9
+ const urlObj = new URL(url);
10
+ const hostname = urlObj.hostname;
11
+ const parts = hostname.split('.');
12
+
13
+ const firstPart = parts[0];
14
+
15
+ const isLocale = Settings.localization.locales.some(
16
+ (locale) => locale.value === firstPart
17
+ );
18
+
19
+ if (isLocale) {
20
+ const hostnameAfterLocale = parts.slice(1).join('.');
21
+ return `.${hostnameAfterLocale}`;
22
+ }
23
+
24
+ return `.${hostname}`;
25
+ } catch {
26
+ return null;
27
+ }
28
+ }