@akinon/next 2.0.0-beta.1 → 2.0.0-beta.10

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 (63) hide show
  1. package/.eslintrc.js +12 -0
  2. package/CHANGELOG.md +50 -0
  3. package/api/auth.ts +17 -0
  4. package/bin/run-prebuild-tests.js +46 -0
  5. package/components/accordion.tsx +1 -1
  6. package/components/button.tsx +51 -36
  7. package/components/client-root.tsx +20 -0
  8. package/components/input.tsx +1 -1
  9. package/components/modal.tsx +1 -1
  10. package/components/select.tsx +1 -1
  11. package/components/selected-payment-option-view.tsx +1 -1
  12. package/data/client/api.ts +2 -0
  13. package/data/client/basket.ts +27 -5
  14. package/data/client/checkout.ts +62 -7
  15. package/data/client/misc.ts +25 -1
  16. package/data/client/product.ts +19 -2
  17. package/data/client/user.ts +16 -8
  18. package/data/server/flatpage.ts +8 -4
  19. package/data/server/form.ts +12 -4
  20. package/data/server/landingpage.ts +8 -4
  21. package/data/server/menu.ts +7 -2
  22. package/data/server/product.ts +16 -5
  23. package/data/server/seo.ts +11 -4
  24. package/data/server/widget.ts +19 -4
  25. package/data/urls.ts +11 -3
  26. package/hooks/index.ts +1 -0
  27. package/hooks/use-router.ts +5 -2
  28. package/hooks/use-sentry-uncaught-errors.ts +24 -0
  29. package/instrumentation/index.ts +10 -0
  30. package/lib/cache-handler.mjs +2 -2
  31. package/lib/cache.ts +4 -4
  32. package/localization/index.ts +2 -1
  33. package/middlewares/default.ts +6 -15
  34. package/middlewares/locale.ts +31 -10
  35. package/middlewares/url-redirection.ts +16 -0
  36. package/package.json +4 -3
  37. package/plugins.js +2 -1
  38. package/redux/middlewares/checkout.ts +16 -144
  39. package/redux/middlewares/index.ts +4 -2
  40. package/redux/middlewares/pre-order/address.ts +50 -0
  41. package/redux/middlewares/pre-order/attribute-based-shipping-option.ts +46 -0
  42. package/redux/middlewares/pre-order/data-source-shipping-option.ts +43 -0
  43. package/redux/middlewares/pre-order/delivery-option.ts +40 -0
  44. package/redux/middlewares/pre-order/index.ts +29 -0
  45. package/redux/middlewares/pre-order/installment-option.ts +42 -0
  46. package/redux/middlewares/pre-order/payment-option.ts +34 -0
  47. package/redux/middlewares/pre-order/pre-order-validation.ts +24 -0
  48. package/redux/middlewares/pre-order/redirection.ts +40 -0
  49. package/redux/middlewares/pre-order/set-pre-order.ts +22 -0
  50. package/redux/middlewares/pre-order/shipping-option.ts +44 -0
  51. package/redux/middlewares/pre-order/shipping-step.ts +38 -0
  52. package/redux/reducers/checkout.ts +8 -2
  53. package/redux/reducers/index.ts +5 -3
  54. package/redux/reducers/root.ts +7 -2
  55. package/sentry/index.ts +36 -17
  56. package/types/commerce/account.ts +5 -1
  57. package/types/commerce/checkout.ts +23 -0
  58. package/types/index.ts +6 -2
  59. package/utils/index.ts +11 -8
  60. package/utils/localization.ts +4 -0
  61. package/utils/override-middleware.ts +25 -0
  62. package/views/error-page.tsx +93 -0
  63. package/with-pz-config.js +4 -3
@@ -70,6 +70,7 @@ export interface CheckoutState {
70
70
  };
71
71
  supportedMethods: string;
72
72
  };
73
+ payOnDeliveryOtpModalActive: boolean;
73
74
  }
74
75
 
75
76
  const initialState: CheckoutState = {
@@ -103,7 +104,8 @@ const initialState: CheckoutState = {
103
104
  retailStores: [],
104
105
  attributeBasedShippingOptions: [],
105
106
  selectedShippingOptions: {},
106
- hepsipayAvailability: false
107
+ hepsipayAvailability: false,
108
+ payOnDeliveryOtpModalActive: false
107
109
  };
108
110
 
109
111
  const checkoutSlice = createSlice({
@@ -193,6 +195,9 @@ const checkoutSlice = createSlice({
193
195
  },
194
196
  setWalletPaymentData(state, { payload }) {
195
197
  state.walletPaymentData = payload;
198
+ },
199
+ setPayOnDeliveryOtpModalActive(state, { payload }) {
200
+ state.payOnDeliveryOtpModalActive = payload;
196
201
  }
197
202
  }
198
203
  });
@@ -225,7 +230,8 @@ export const {
225
230
  setAttributeBasedShippingOptions,
226
231
  setSelectedShippingOptions,
227
232
  setHepsipayAvailability,
228
- setWalletPaymentData
233
+ setWalletPaymentData,
234
+ setPayOnDeliveryOtpModalActive
229
235
  } = checkoutSlice.actions;
230
236
 
231
237
  export default checkoutSlice.reducer;
@@ -9,15 +9,17 @@ import { masterpassReducer } from '@akinon/pz-masterpass';
9
9
  import { otpReducer } from '@akinon/pz-otp';
10
10
  import { savedCardReducer } from '@akinon/pz-saved-card';
11
11
 
12
+ const fallbackReducer = (state = {}) => state;
13
+
12
14
  const reducers = {
13
15
  [api.reducerPath]: api.reducer,
14
16
  root: rootReducer,
15
17
  checkout: checkoutReducer,
16
18
  config: configReducer,
17
19
  header: headerReducer,
18
- masterpass: masterpassReducer,
19
- otp: otpReducer,
20
- savedCard: savedCardReducer
20
+ masterpass: masterpassReducer || fallbackReducer,
21
+ otp: otpReducer || fallbackReducer,
22
+ savedCard: savedCardReducer || fallbackReducer
21
23
  };
22
24
 
23
25
  export default reducers;
@@ -14,7 +14,8 @@ const initialState = {
14
14
  open: false,
15
15
  title: null,
16
16
  content: null
17
- }
17
+ },
18
+ userPhoneNumber: null
18
19
  };
19
20
 
20
21
  const rootSlice = createSlice({
@@ -45,6 +46,9 @@ const rootSlice = createSlice({
45
46
  state.rootModal.open = false;
46
47
  state.rootModal.title = null;
47
48
  state.rootModal.content = null;
49
+ },
50
+ setUserPhoneNumber(state, { payload }) {
51
+ state.userPhoneNumber = payload;
48
52
  }
49
53
  }
50
54
  });
@@ -55,7 +59,8 @@ export const {
55
59
  toggleMiniBasket,
56
60
  setHighlightedItem,
57
61
  openRootModal,
58
- closeRootModal
62
+ closeRootModal,
63
+ setUserPhoneNumber
59
64
  } = rootSlice.actions;
60
65
 
61
66
  export default rootSlice.reducer;
package/sentry/index.ts CHANGED
@@ -1,29 +1,48 @@
1
1
  import * as Sentry from '@sentry/nextjs';
2
2
 
3
- const SENTRY_DSN: string =
3
+ const SENTRY_DSN: string | undefined =
4
4
  process.env.SENTRY_DSN || process.env.NEXT_PUBLIC_SENTRY_DSN;
5
5
 
6
+ export enum ClientLogType {
7
+ UNCAUGHT_ERROR_PAGE = 'UNCAUGHT_ERROR_PAGE',
8
+ CHECKOUT = 'CHECKOUT'
9
+ }
10
+
11
+ const ALLOWED_CLIENT_LOG_TYPES: ClientLogType[] = [
12
+ ClientLogType.UNCAUGHT_ERROR_PAGE,
13
+ ClientLogType.CHECKOUT
14
+ ];
15
+
6
16
  export const initSentry = (
7
17
  type: 'Server' | 'Client' | 'Edge',
8
18
  options: Sentry.BrowserOptions | Sentry.NodeOptions | Sentry.EdgeOptions = {}
9
19
  ) => {
10
20
  // TODO: Handle options with ESLint rules
11
21
 
12
- // TODO: Remove Zero Project DSN
22
+ Sentry.init({
23
+ dsn:
24
+ options.dsn ||
25
+ SENTRY_DSN ||
26
+ 'https://d8558ef8997543deacf376c7d8d7cf4b@o64293.ingest.sentry.io/4504338423742464',
27
+ initialScope: {
28
+ tags: {
29
+ APP_TYPE: 'ProjectZeroNext',
30
+ TYPE: type
31
+ }
32
+ },
33
+ 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
+ }
13
44
 
14
- if (type === 'Server' || type === 'Edge') {
15
- Sentry.init({
16
- dsn:
17
- SENTRY_DSN ||
18
- 'https://d8558ef8997543deacf376c7d8d7cf4b@o64293.ingest.sentry.io/4504338423742464',
19
- initialScope: {
20
- tags: {
21
- APP_TYPE: 'ProjectZeroNext',
22
- TYPE: type
23
- }
24
- },
25
- tracesSampleRate: 1.0,
26
- integrations: []
27
- });
28
- }
45
+ return event;
46
+ }
47
+ });
29
48
  };
@@ -19,6 +19,10 @@ export type AccountOrderCancellation = {
19
19
  description: string;
20
20
  order_item: string;
21
21
  reason: string;
22
+ cancellation_request_image_set?: Array<{
23
+ image: string;
24
+ description: string;
25
+ }>;
22
26
  }>;
23
27
  };
24
28
 
@@ -61,5 +65,5 @@ export type ContactFormType = {
61
65
  order?: string;
62
66
  country_code?: string;
63
67
  order_needed?: boolean;
64
- file?: FileList
68
+ file?: FileList;
65
69
  };
@@ -4,6 +4,7 @@ import { RetailStore } from './misc';
4
4
  import { PaymentOption } from './order';
5
5
  import { Product } from './product';
6
6
  import { JSX } from 'react';
7
+ import { RootState, TypedDispatch } from 'redux/store';
7
8
 
8
9
  export enum CheckoutStep {
9
10
  Shipping = 'shipping',
@@ -107,6 +108,7 @@ export interface PreOrder {
107
108
  context_extras?: ExtraField;
108
109
  token?: string;
109
110
  agreement_confirmed?: boolean;
111
+ phone_number?: string;
110
112
  }
111
113
 
112
114
  export type ExtraField = Record<string, any>;
@@ -186,3 +188,24 @@ export interface AttributeBasedShippingOption {
186
188
  attribute_key: string;
187
189
  [key: string]: any;
188
190
  }
191
+
192
+ export interface CheckoutResult {
193
+ payload: {
194
+ errors?: Record<string, string[]>;
195
+ pre_order?: PreOrder;
196
+ context_list?: CheckoutContext[];
197
+ };
198
+ }
199
+
200
+ export interface MiddlewareParams {
201
+ getState: () => RootState;
202
+ dispatch: TypedDispatch;
203
+ }
204
+
205
+ export type SendSmsType = {
206
+ phone_number: string;
207
+ };
208
+
209
+ export type VerifySmsType = {
210
+ verify_code: string;
211
+ };
package/types/index.ts CHANGED
@@ -199,15 +199,16 @@ export interface Settings {
199
199
  extraPaymentTypes?: string[];
200
200
  masterpassJsUrl?: string;
201
201
  };
202
- customNotFoundEnabled: boolean;
203
202
  useOptimizedTranslations?: boolean;
204
203
  plugins?: Record<string, Record<string, any>>;
205
204
  includedProxyHeaders?: string[];
205
+ commerceRedirectionIgnoreList?: string[];
206
206
  /**
207
207
  * By default, the currency will be reset when the currency is changed.
208
208
  * If you want to keep the basket when the currency is changed, you can set this option to `false`.
209
209
  */
210
210
  resetBasketOnCurrencyChange?: boolean;
211
+ frontendIds?: Record<string, number>;
211
212
  }
212
213
 
213
214
  export interface CacheOptions {
@@ -275,7 +276,10 @@ export interface IconProps extends React.ComponentPropsWithRef<'i'> {
275
276
 
276
277
  export interface ButtonProps
277
278
  extends React.ButtonHTMLAttributes<HTMLButtonElement> {
278
- appearance?: 'filled' | 'outlined' | 'ghost';
279
+ appearance?: 'filled' | 'outlined' | 'ghost' | 'link' | string;
280
+ size?: 'sm' | 'md' | 'lg' | 'xl';
281
+ href?: string;
282
+ target?: '_blank' | '_self' | '_parent' | '_top';
279
283
  }
280
284
 
281
285
  export type FileInputProps = React.HTMLProps<HTMLInputElement>;
package/utils/index.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import settings from 'settings';
2
2
  import { LocaleUrlStrategy } from '../localization';
3
- import { CDNOptions, ClientRequestOptions } from '../types';
3
+ import { CDNOptions, ClientRequestOptions, Locale } from '../types';
4
4
 
5
5
  export * from './get-currency';
6
6
  export * from './menu-generator';
@@ -152,14 +152,17 @@ export function buildCDNUrl(url: string, config?: CDNOptions) {
152
152
  return `${rootWithoutOptions}${options}${fileExtension}`;
153
153
  }
154
154
 
155
+ const { locales, localeUrlStrategy, defaultLocaleValue } =
156
+ settings.localization;
157
+
158
+ const isLocaleExcluded = (locale: Locale) =>
159
+ ![LocaleUrlStrategy.ShowAllLocales, LocaleUrlStrategy.Subdomain].includes(
160
+ localeUrlStrategy
161
+ ) && locale.value !== defaultLocaleValue;
162
+
155
163
  export const urlLocaleMatcherRegex = new RegExp(
156
- `^/(${settings.localization.locales
157
- .filter((l) =>
158
- settings.localization.localeUrlStrategy !==
159
- LocaleUrlStrategy.ShowAllLocales
160
- ? l.value !== settings.localization.defaultLocaleValue
161
- : l
162
- )
164
+ `^/(${locales
165
+ .filter((l) => !isLocaleExcluded(l))
163
166
  .map((l) => l.value)
164
167
  .join('|')})(?=/|$)`
165
168
  );
@@ -11,6 +11,10 @@ export const getUrlPathWithLocale = (
11
11
  currentLocale = defaultLocaleValue;
12
12
  }
13
13
 
14
+ if (localeUrlStrategy === LocaleUrlStrategy.Subdomain) {
15
+ return pathname;
16
+ }
17
+
14
18
  if (localeUrlStrategy === LocaleUrlStrategy.HideAllLocales) {
15
19
  return pathname;
16
20
  }
@@ -0,0 +1,25 @@
1
+ import { Middleware } from '@reduxjs/toolkit';
2
+
3
+ export enum MiddlewareNames {
4
+ PreOrderValidationMiddleware = 'preOrderValidationMiddleware',
5
+ RedirectionMiddleware = 'redirectionMiddleware',
6
+ SetPreOrderMiddleware = 'setPreOrderMiddleware',
7
+ DeliveryOptionMiddleware = 'deliveryOptionMiddleware',
8
+ SetAddressMiddleware = 'setAddressMiddleware',
9
+ ShippingOptionMiddleware = 'shippingOptionMiddleware',
10
+ DataSourceShippingOptionMiddleware = 'dataSourceShippingOptionMiddleware',
11
+ AttributeBasedShippingOptionMiddleware = 'attributeBasedShippingOptionMiddleware',
12
+ PaymentOptionMiddleware = 'paymentOptionMiddleware',
13
+ InstallmentOptionMiddleware = 'installmentOptionMiddleware',
14
+ ShippingStepMiddleware = 'shippingStepMiddleware'
15
+ }
16
+
17
+ export const overrideMiddleware = (
18
+ originalMiddlewares: Middleware[],
19
+ overrides: Record<string, Middleware>
20
+ ): Middleware[] => {
21
+ return originalMiddlewares.map((middleware) => {
22
+ const middlewareKey = middleware.name || middleware.toString();
23
+ return overrides[middlewareKey] || middleware;
24
+ });
25
+ };
@@ -0,0 +1,93 @@
1
+ import { useEffect, useState } from 'react';
2
+ import { useLocalization } from '../hooks';
3
+ import { Button, Link } from '../components';
4
+ import { ROUTES } from 'routes';
5
+
6
+ export default function PzErrorPage({
7
+ error,
8
+ reset
9
+ }: {
10
+ error: Error & { digest?: string; isServerError?: boolean };
11
+ reset: () => void;
12
+ }) {
13
+ const [isServerError, setIsServerError] = useState(false);
14
+
15
+ useEffect(() => {
16
+ if ('isServerError' in error) {
17
+ setIsServerError(true);
18
+ return;
19
+ }
20
+
21
+ setIsServerError(!!error.digest);
22
+ }, [error]);
23
+
24
+ return isServerError ? (
25
+ <ServerErrorUI />
26
+ ) : (
27
+ <ClientErrorUI error={error} reset={reset} />
28
+ );
29
+ }
30
+
31
+ function ClientErrorUI({
32
+ error,
33
+ reset
34
+ }: {
35
+ error: Error & { digest?: string };
36
+ reset: () => void;
37
+ }) {
38
+ const { t } = useLocalization();
39
+
40
+ const errorMessage = error?.message || 'Unknown error';
41
+
42
+ return (
43
+ <section className="text-center px-6 py-6 my-14 md:px-0 md:m-14">
44
+ <div className="text-4xl font-bold md:text-6xl text-red-500">500</div>
45
+ <h1 className="text-lg md:text-xl mt-4">
46
+ {t('common.client_error.title')}
47
+ </h1>
48
+ <p className="text-lg md:text-xl mt-2">
49
+ {t('common.client_error.description')}
50
+ </p>
51
+
52
+ <div className="mt-4 mx-auto max-w-lg">
53
+ <p className="text-xs text-gray-600 font-mono bg-gray-100 p-3 rounded overflow-auto text-left">
54
+ <span className="font-semibold">Error:</span> {errorMessage}
55
+ </p>
56
+ </div>
57
+
58
+ <div className="mt-6 flex flex-col gap-4 items-center justify-center">
59
+ <Link href={ROUTES.HOME} className="text-lg underline">
60
+ {t('common.client_error.link_text')}
61
+ </Link>
62
+ <Button onClick={reset} className="text-lg">
63
+ {t('common.try_again')}
64
+ </Button>
65
+ </div>
66
+ </section>
67
+ );
68
+ }
69
+
70
+ function ServerErrorUI() {
71
+ const { t } = useLocalization();
72
+
73
+ const reloadPage = () => {
74
+ window.location.reload();
75
+ };
76
+
77
+ return (
78
+ <section className="text-center px-6 my-14 md:px-0 md:m-14">
79
+ <div className="text-7xl font-bold md:text-8xl">500</div>
80
+ <h1 className="text-lg md:text-xl"> {t('common.page_500.title')} </h1>
81
+ <p className="text-lg md:text-xl"> {t('common.page_500.description')} </p>
82
+
83
+ <div className="mt-6 flex flex-col gap-4 items-center justify-center">
84
+ <Link href={ROUTES.HOME} className="text-lg underline">
85
+ {t('common.page_500.link_text')}
86
+ </Link>
87
+ <Button onClick={reloadPage} className="text-lg">
88
+ {t('common.try_again')}
89
+ </Button>
90
+ </div>
91
+ </section>
92
+ );
93
+ }
package/with-pz-config.js CHANGED
@@ -44,8 +44,9 @@ const defaultConfig = {
44
44
  value: 'max-age=63072000; includeSubDomains; preload'
45
45
  },
46
46
  {
47
- key: 'X-Frame-Options',
48
- value: 'SAMEORIGIN'
47
+ key: 'Content-Security-Policy',
48
+ value:
49
+ "frame-ancestors 'self' https://*.akifast.com akifast.com https://*.akinoncloud.com akinoncloud.com"
49
50
  }
50
51
  ]
51
52
  }
@@ -64,7 +65,7 @@ const defaultConfig = {
64
65
  },
65
66
  sentry: {
66
67
  hideSourceMaps: true
67
- }
68
+ } // TODO: This section will be reviewed again in the Sentry 8 update.
68
69
  };
69
70
 
70
71
  const withPzConfig = (