@akinon/next 1.24.1 → 1.25.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.
package/CHANGELOG.md CHANGED
@@ -1,10 +1,49 @@
1
1
  # @akinon/next
2
2
 
3
- ## 1.24.1
3
+ ## 1.25.0-rc.1
4
+
5
+ ### Minor Changes
6
+
7
+ - 6d4aadb: ZERO-2476: Auto install recommendenent extension
8
+ - e20b27f: ZERO-2454:add otp login
9
+ - e0a945e: ZERO-2494:Add LiveCommerce for managing basket and product actions
10
+ - a4674c6: ZERO-2461: add optional chaining for referrer header and add locale value for redirect url
11
+ - 1ec9775: ZERO-2462:add custom span to logs
12
+ - ab5a493: ZERO-2475:add types [key: string]: any; for unknown attributes
13
+ - 3690d3b: ZERO-2485: Check ESLint peerDependency version
14
+ - 92094d4: ZERO-2477: Update Sentry version and add hideSourceMaps option
15
+ - b4452e9: ZERO-2463: Refactor Sentry initialization and add Sentry DSN option to settings
16
+ - b2da5e4: Revert ZERO-2435
17
+ - d3edd3a: ZERO-2499:add style prop in link component
18
+ - f76f079: ZERO-2493: Add redirect util function
4
19
 
5
20
  ### Patch Changes
6
21
 
22
+ - da1e501: ZERO-2296: Fix ROUTES import
7
23
  - 95510c7: ZERO-2508: Enable rc branch pipeline and add check-publish-version step
24
+ - 2e44646: ZERO-2434: Fix category URL in getCategoryDataHandler function
25
+ - 8c7f5bc: ZERO-2440: Pipeline test
26
+ - Updated dependencies [95510c7]
27
+ - @akinon/eslint-plugin-projectzero@1.25.0-rc.1
28
+
29
+ ## 1.25.0-rc.0
30
+
31
+ ### Minor Changes
32
+
33
+ - 6d4aadb: ZERO-2476: Auto install recommendenent extension
34
+ - e20b27f: ZERO-2454:add otp login
35
+ - a4674c6: ZERO-2461: add optional chaining for referrer header and add locale value for redirect url
36
+ - 1ec9775: ZERO-2462:add custom span to logs
37
+ - 3690d3b: ZERO-2485: Check ESLint peerDependency version
38
+ - b4452e9: ZERO-2463: Refactor Sentry initialization and add Sentry DSN option to settings
39
+ - b2da5e4: Revert ZERO-2435
40
+
41
+ ### Patch Changes
42
+
43
+ - da1e501: ZERO-2296: Fix ROUTES import
44
+ - 2e44646: ZERO-2434: Fix category URL in getCategoryDataHandler function
45
+ - 8c7f5bc: ZERO-2440: Pipeline test
46
+ - @akinon/eslint-plugin-projectzero@1.25.0-rc.0
8
47
 
9
48
  ## 1.24.0
10
49
 
package/api/auth.ts CHANGED
@@ -199,8 +199,8 @@ const nextAuthOptions = (req: NextApiRequest, res: NextApiResponse) => {
199
199
  );
200
200
 
201
201
  const localeResults = req.headers.referer
202
- .replace(baseUrl, '')
203
- .match(urlLocaleMatcherRegex);
202
+ ?.replace(baseUrl, '')
203
+ ?.match(urlLocaleMatcherRegex);
204
204
 
205
205
  return `${baseUrl}${localeResults?.[0] || ''}${pathnameWithoutLocale}`;
206
206
  }
@@ -220,6 +220,17 @@ const nextAuthOptions = (req: NextApiRequest, res: NextApiResponse) => {
220
220
  pages: {
221
221
  signIn: ROUTES.AUTH,
222
222
  error: ROUTES.AUTH
223
+ },
224
+ cookies: {
225
+ sessionToken: {
226
+ name: `__Secure-next-auth.session-token`,
227
+ options: {
228
+ httpOnly: true,
229
+ sameSite: 'none',
230
+ path: '/',
231
+ secure: true
232
+ }
233
+ }
223
234
  }
224
235
  };
225
236
  };
@@ -23,6 +23,21 @@ function checkDir() {
23
23
 
24
24
  const BASE_DIR = checkDir();
25
25
 
26
+ function getProjectZeroNextPackagePath() {
27
+ const possiblePaths = [
28
+ path.join(BASE_DIR, 'apps/projectzeronext/package.json'),
29
+ path.join(BASE_DIR, 'package.json')
30
+ ];
31
+
32
+ for (const packagePath of possiblePaths) {
33
+ if (fs.existsSync(packagePath)) {
34
+ return packagePath;
35
+ }
36
+ }
37
+
38
+ throw new Error('Unable to find package.json in expected locations.');
39
+ }
40
+
26
41
  try {
27
42
  const akinonNextPackagePath = fs.existsSync(
28
43
  path.join(BASE_DIR, 'packages/akinon-next/package.json')
@@ -34,7 +49,7 @@ try {
34
49
  fs.readFileSync(akinonNextPackagePath, 'utf8')
35
50
  );
36
51
 
37
- const projectZeroNextPackagePath = path.join(BASE_DIR, 'package.json');
52
+ const projectZeroNextPackagePath = getProjectZeroNextPackagePath();
38
53
 
39
54
  const projectZeroNextPackage = JSON.parse(
40
55
  fs.readFileSync(projectZeroNextPackagePath, 'utf8')
@@ -0,0 +1,27 @@
1
+ const { execSync } = require('child_process');
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+
5
+ function findBaseDir() {
6
+ let currentDir = __dirname;
7
+ while (currentDir !== path.resolve(currentDir, '..')) {
8
+ if (fs.existsSync(path.join(currentDir, 'turbo.json'))) {
9
+ return currentDir;
10
+ }
11
+ currentDir = path.resolve(currentDir, '..');
12
+ }
13
+ return null;
14
+ }
15
+
16
+ const BASE_DIR = findBaseDir();
17
+
18
+ if (BASE_DIR) {
19
+ const extensions = ['bilal-akinon.pznext'];
20
+ extensions.forEach((extension) => {
21
+ try {
22
+ execSync(`code --install-extension ${extension}`, { stdio: 'inherit' });
23
+ } catch (error) {
24
+ console.error(`Error installing ${extension}:`);
25
+ }
26
+ });
27
+ }
package/bin/pz-predev.js CHANGED
@@ -1,4 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  const runScript = require('./run-script');
4
+
5
+ runScript('pz-install-extensions.js');
4
6
  runScript('pz-install-theme.js');
@@ -17,4 +17,5 @@ export * from './react-portal';
17
17
  export * from './selected-payment-option-view';
18
18
  export * from './trans';
19
19
  export * from './link';
20
- export * from './pagination';
20
+ export * from './pagination';
21
+ export * from './live-commerce';
@@ -6,19 +6,13 @@ import { urlLocaleMatcherRegex } from '@akinon/next/utils';
6
6
  import NextLink, { LinkProps as NextLinkProps } from 'next/link';
7
7
  import { useEffect, useState } from 'react';
8
8
 
9
- interface LinkProps extends NextLinkProps {
10
- children: React.ReactNode;
11
- className?: string;
12
- target?: '_blank' | '_self' | '_parent' | '_top';
13
- }
9
+ type LinkProps = Omit<
10
+ React.AnchorHTMLAttributes<HTMLAnchorElement>,
11
+ keyof NextLinkProps
12
+ > &
13
+ NextLinkProps;
14
14
 
15
- export const Link = ({
16
- children,
17
- target,
18
- className,
19
- href,
20
- ...rest
21
- }: LinkProps) => {
15
+ export const Link = ({ children, href, ...rest }: LinkProps) => {
22
16
  const { locale, defaultLocaleValue, localeUrlStrategy } = useLocalization();
23
17
  const [formattedHref, setFormattedHref] = useState(href ?? '#');
24
18
 
@@ -40,12 +34,7 @@ export const Link = ({
40
34
  }, [href, defaultLocaleValue, locale, localeUrlStrategy]);
41
35
 
42
36
  return (
43
- <NextLink
44
- href={formattedHref}
45
- target={target}
46
- className={className}
47
- {...rest}
48
- >
37
+ <NextLink href={formattedHref} {...rest}>
49
38
  {children}
50
39
  </NextLink>
51
40
  );
@@ -0,0 +1,42 @@
1
+ 'use client';
2
+
3
+ import { memo, useEffect } from 'react';
4
+ import { useAppDispatch } from '@akinon/next/redux/hooks';
5
+ import { useAddProductMutation } from '../data/client/product';
6
+ import { basketApi } from '@akinon/next/data/client/basket';
7
+ import { useGetBasketQuery } from '@akinon/next/data/client/basket';
8
+
9
+ export const LiveCommerce = memo(function Callback() {
10
+ const dispatch = useAppDispatch();
11
+ const { refetch: refetchBasketData } = useGetBasketQuery();
12
+ const [addProduct] = useAddProductMutation();
13
+
14
+ useEffect(() => {
15
+ document.addEventListener('refresh-basket', () => refetchBasketData());
16
+
17
+ document.addEventListener('add-to-cart', async (e: CustomEvent) => {
18
+ const productData = e.detail.data;
19
+
20
+ await addProduct({
21
+ product: productData.product,
22
+ quantity: productData.quantity,
23
+ attributes: productData.attributes
24
+ })
25
+ .unwrap()
26
+ .then((data) =>
27
+ dispatch(
28
+ basketApi.util.updateQueryData(
29
+ 'getBasket',
30
+ undefined,
31
+ (draftBasket) => {
32
+ Object.assign(draftBasket, data.basket);
33
+ }
34
+ )
35
+ )
36
+ );
37
+ });
38
+ // eslint-disable-next-line react-hooks/exhaustive-deps
39
+ }, []);
40
+
41
+ return null;
42
+ });
@@ -71,6 +71,17 @@ const userApi = api.injectEndpoints({
71
71
  body
72
72
  })
73
73
  }),
74
+ otpLogin: build.mutation<void, { phone: string }>({
75
+ query: ({ phone }) => ({
76
+ url: buildClientRequestUrl(user.otpLogin, {
77
+ contentType: 'application/json'
78
+ }),
79
+ method: 'POST',
80
+ body: {
81
+ phone
82
+ }
83
+ })
84
+ }),
74
85
  changeEmailVerification: build.query<void, string>({
75
86
  query: (token) => ({
76
87
  url: buildClientRequestUrl(user.changeEmailVerification(token), {
@@ -110,6 +121,7 @@ export const {
110
121
  useConfirmEmailVerificationQuery,
111
122
  useValidateCaptchaMutation,
112
123
  useLogoutMutation,
124
+ useOtpLoginMutation,
113
125
  useForgotPasswordMutation,
114
126
  useGetAnonymousTrackingMutation
115
127
  } = userApi;
package/data/urls.ts CHANGED
@@ -163,6 +163,7 @@ export const wishlist = {
163
163
  export const user = {
164
164
  currentUser: '/current_user/',
165
165
  login: '/users/login/',
166
+ otpLogin: '/users/otp-login/',
166
167
  register: '/users/registration/',
167
168
  logout: '/users/logout',
168
169
  captcha: '/users/pz-captcha/',
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.24.1",
4
+ "version": "1.25.0-rc.1",
5
5
  "private": false,
6
6
  "license": "MIT",
7
7
  "bin": {
@@ -34,5 +34,8 @@
34
34
  "eslint": "^8.14.0",
35
35
  "@akinon/eslint-plugin-projectzero": "1.24.0",
36
36
  "eslint-config-prettier": "8.5.0"
37
+ },
38
+ "peerDependencies": {
39
+ "@akinon/eslint-plugin-projectzero": "1.24.0"
37
40
  }
38
41
  }
package/sentry/index.ts CHANGED
@@ -1,27 +1,33 @@
1
1
  import * as Sentry from '@sentry/nextjs';
2
+ import settings from 'settings';
2
3
 
3
4
  const SENTRY_DSN: string =
4
- process.env.SENTRY_DSN || process.env.NEXT_PUBLIC_SENTRY_DSN;
5
+ settings.sentryDsn ||
6
+ process.env.SENTRY_DSN ||
7
+ process.env.NEXT_PUBLIC_SENTRY_DSN;
5
8
 
6
9
  export const initSentry = (
7
10
  type: 'Server' | 'Client' | 'Edge',
8
- options: Sentry.BrowserOptions | Sentry.NodeOptions | Sentry.EdgeOptions = {}
11
+ options: Sentry.BrowserOptions | Sentry.NodeOptions | Sentry.EdgeOptions = {
12
+ dsn: SENTRY_DSN,
13
+ integrations: [],
14
+ tracesSampleRate: 1.0
15
+ }
9
16
  ) => {
10
- // TODO: Handle options with ESLint rules
11
-
12
- // TODO: Remove Zero Project DSN
13
-
14
- Sentry.init({
15
- dsn:
16
- SENTRY_DSN ||
17
- 'https://d8558ef8997543deacf376c7d8d7cf4b@o64293.ingest.sentry.io/4504338423742464',
17
+ const initOptions = {
18
+ ...options,
18
19
  initialScope: {
19
20
  tags: {
21
+ ...((
22
+ options.initialScope as {
23
+ tags?: Record<string, string>;
24
+ }
25
+ )?.tags ?? {}),
20
26
  APP_TYPE: 'ProjectZeroNext',
21
27
  TYPE: type
22
28
  }
23
- },
24
- tracesSampleRate: 1.0,
25
- integrations: []
26
- });
29
+ }
30
+ };
31
+
32
+ Sentry.init(initOptions);
27
33
  };
@@ -91,4 +91,5 @@ export interface Address {
91
91
  address: string;
92
92
  fax_phone_number: string;
93
93
  image: string;
94
+ [key: string]: any;
94
95
  }
@@ -93,6 +93,7 @@ export interface GetCategoryResponse {
93
93
  search_text: string | null;
94
94
  sorters: SortOption[];
95
95
  special_page: SpecialPageType;
96
+ [key: string]: any;
96
97
  }
97
98
 
98
99
  export type BreadcrumbResultType = {
@@ -54,6 +54,10 @@ export interface Product {
54
54
  modified_date: string;
55
55
  description?: string;
56
56
  quantity: string;
57
+ extra_data: {
58
+ [key: string]: any;
59
+ };
60
+ [key: string]: any;
57
61
  }
58
62
 
59
63
  export interface FavoriteItem {
package/types/index.ts CHANGED
@@ -71,6 +71,12 @@ export interface Currency {
71
71
 
72
72
  export interface Settings {
73
73
  commerceUrl: string;
74
+ /**
75
+ * This option allows you to track Sentry events on the client side, in addition to server and edge environments.
76
+ *
77
+ * It overrides process.env.NEXT_PUBLIC_SENTRY_DSN and process.env.SENTRY_DSN.
78
+ */
79
+ sentryDsn?: string;
74
80
  redis: {
75
81
  defaultExpirationTime: number;
76
82
  };
package/utils/log.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { trace } from '@opentelemetry/api';
2
+
1
3
  enum LogLevel {
2
4
  TRACE = 'trace',
3
5
  DEBUG = 'debug',
@@ -78,61 +80,42 @@ const writeMsg = ({ level, message, payload }: Message) => {
78
80
  }
79
81
  };
80
82
 
81
- const info: LoggerFn = (message, payload) => {
82
- writeMsg({
83
- level: LogLevel.INFO,
84
- message,
85
- payload
86
- });
87
- };
83
+ function createCustomSpan({ level, message, payload }: Message) {
84
+ const tracer = trace.getTracer('pz-next-app');
85
+ const span = tracer.startSpan('custom-operation-log');
88
86
 
89
- const error: LoggerFn = (message, payload) => {
90
- writeMsg({
91
- level: LogLevel.ERROR,
92
- message,
93
- payload
94
- });
95
- };
96
-
97
- const warn: LoggerFn = (message, payload) => {
98
- writeMsg({
99
- level: LogLevel.WARN,
100
- message,
101
- payload
87
+ span.setAttributes({
88
+ level: level,
89
+ message: message,
90
+ payload: payload
102
91
  });
103
- };
104
92
 
105
- const debug: LoggerFn = (message, payload) => {
106
- writeMsg({
107
- level: LogLevel.DEBUG,
108
- message,
109
- payload
110
- });
111
- };
93
+ span.end();
94
+ }
112
95
 
113
- const trace: LoggerFn = (message, payload) => {
114
- writeMsg({
115
- level: LogLevel.TRACE,
96
+ const createLogAndSpan = (level: LogLevel, message: string, payload?: any) => {
97
+ const content = {
98
+ level,
116
99
  message,
117
- payload
118
- });
119
- };
100
+ payload: JSON.stringify(payload)
101
+ };
120
102
 
121
- const fatal: LoggerFn = (message, payload) => {
122
- writeMsg({
123
- level: LogLevel.FATAL,
124
- message,
125
- payload
126
- });
103
+ writeMsg(content);
104
+ createCustomSpan(content);
127
105
  };
128
106
 
129
- const logger = {
130
- trace,
131
- debug,
132
- info,
133
- warn,
134
- error,
135
- fatal
107
+ const logger: Record<LogLevel, LoggerFn> = {
108
+ trace: (message, payload) =>
109
+ createLogAndSpan(LogLevel.TRACE, message, payload),
110
+ debug: (message, payload) =>
111
+ createLogAndSpan(LogLevel.DEBUG, message, payload),
112
+ info: (message, payload) => createLogAndSpan(LogLevel.INFO, message, payload),
113
+ warn: (message, payload) => createLogAndSpan(LogLevel.WARN, message, payload),
114
+ error: (message, payload) =>
115
+ createLogAndSpan(LogLevel.ERROR, message, payload),
116
+ fatal: (message, payload) =>
117
+ createLogAndSpan(LogLevel.FATAL, message, payload),
118
+ silent: () => {}
136
119
  };
137
120
 
138
121
  export default logger;
@@ -0,0 +1,30 @@
1
+ import { redirect as nextRedirect, RedirectType } from 'next/navigation';
2
+ import Settings from 'settings';
3
+ import { headers } from 'next/headers';
4
+ import { ServerVariables } from '@akinon/next/utils/server-variables';
5
+ import { getUrlPathWithLocale } from '@akinon/next/utils/localization';
6
+
7
+ export const redirect = (path: string, type?: RedirectType) => {
8
+ const nextHeaders = headers();
9
+ const pageUrl = new URL(
10
+ nextHeaders.get('pz-url') ?? process.env.NEXT_PUBLIC_URL
11
+ );
12
+
13
+ const currentLocale = Settings.localization.locales.find(
14
+ (locale) => locale.value === ServerVariables.locale
15
+ );
16
+
17
+ const callbackUrl = pageUrl.pathname;
18
+ const redirectUrlWithLocale = getUrlPathWithLocale(
19
+ path,
20
+ currentLocale.localePath ?? currentLocale.value
21
+ );
22
+
23
+ const redirectUrl = `${redirectUrlWithLocale}?callbackUrl=${callbackUrl}`;
24
+
25
+ if (type) {
26
+ return nextRedirect(redirectUrl, type);
27
+ } else {
28
+ return nextRedirect(redirectUrl);
29
+ }
30
+ };
package/with-pz-config.js CHANGED
@@ -59,6 +59,9 @@ const defaultConfig = {
59
59
  }, {})
60
60
  };
61
61
  return config;
62
+ },
63
+ sentry: {
64
+ hideSourceMaps: true
62
65
  }
63
66
  };
64
67