@akinon/next 1.89.0 → 1.91.0-rc.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 +1099 -20
- package/__tests__/next-config.test.ts +83 -0
- package/__tests__/tsconfig.json +23 -0
- package/babel.config.js +6 -0
- package/bin/pz-prebuild.js +1 -0
- package/bin/pz-run-tests.js +99 -0
- package/components/input.tsx +2 -0
- package/components/link.tsx +16 -12
- package/data/server/basket.ts +72 -0
- package/hocs/server/with-segment-defaults.tsx +5 -2
- package/hooks/use-loyalty-availability.ts +21 -0
- package/instrumentation/node.ts +15 -13
- package/jest.config.js +19 -0
- package/lib/cache.ts +2 -0
- package/middlewares/complete-gpay.ts +2 -1
- package/middlewares/complete-masterpass.ts +2 -1
- package/middlewares/redirection-payment.ts +2 -1
- package/middlewares/saved-card-redirection.ts +2 -1
- package/middlewares/three-d-redirection.ts +2 -1
- package/middlewares/url-redirection.ts +8 -14
- package/package.json +11 -4
- package/redux/middlewares/checkout.ts +43 -2
- package/redux/middlewares/index.ts +2 -0
- package/redux/reducers/checkout.ts +7 -1
- package/types/commerce/order.ts +1 -0
- package/utils/app-fetch.ts +2 -2
- package/utils/index.ts +9 -8
- package/utils/redirect-ignore.ts +35 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { resolve } from 'path';
|
|
2
|
+
import type { NextConfig } from 'next';
|
|
3
|
+
|
|
4
|
+
function findBaseDir() {
|
|
5
|
+
const insideNodeModules = __dirname.includes('node_modules');
|
|
6
|
+
|
|
7
|
+
if (insideNodeModules) {
|
|
8
|
+
return resolve(__dirname, '../../../../');
|
|
9
|
+
} else {
|
|
10
|
+
return resolve(__dirname, '../../../apps/projectzeronext');
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const baseDir = findBaseDir();
|
|
15
|
+
|
|
16
|
+
jest.mock('next-pwa', () => {
|
|
17
|
+
return () => (config: NextConfig) => config;
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
jest.mock('@sentry/nextjs', () => ({
|
|
21
|
+
withSentryConfig: (config: NextConfig) => config
|
|
22
|
+
}));
|
|
23
|
+
|
|
24
|
+
jest.mock('../with-pz-config.js', () => {
|
|
25
|
+
return (config: NextConfig) => {
|
|
26
|
+
const originalHeaders = config.headers;
|
|
27
|
+
|
|
28
|
+
config.headers = async () => {
|
|
29
|
+
const originalHeadersResult = (await originalHeaders?.()) ?? [];
|
|
30
|
+
|
|
31
|
+
return [
|
|
32
|
+
{
|
|
33
|
+
source: '/(.*)',
|
|
34
|
+
headers: [
|
|
35
|
+
{
|
|
36
|
+
key: 'Content-Security-Policy',
|
|
37
|
+
value: 'https://*.akifast.com akifast.com'
|
|
38
|
+
}
|
|
39
|
+
]
|
|
40
|
+
},
|
|
41
|
+
...originalHeadersResult
|
|
42
|
+
];
|
|
43
|
+
};
|
|
44
|
+
return config;
|
|
45
|
+
};
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
interface Header {
|
|
49
|
+
key: string;
|
|
50
|
+
value: string;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
interface HeaderGroup {
|
|
54
|
+
source: string;
|
|
55
|
+
headers: Header[];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const nextConfigPath = resolve(baseDir, 'next.config.mjs');
|
|
59
|
+
let nextConfig: any;
|
|
60
|
+
|
|
61
|
+
beforeAll(async () => {
|
|
62
|
+
nextConfig = await import(nextConfigPath);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
describe('Next.js Configuration', () => {
|
|
66
|
+
it('should contain Content-Security-Policy header with akifast domain values', async () => {
|
|
67
|
+
const headers = nextConfig.default.headers;
|
|
68
|
+
expect(headers).toBeDefined();
|
|
69
|
+
|
|
70
|
+
const headersResult = await headers();
|
|
71
|
+
|
|
72
|
+
const cspHeaders = headersResult
|
|
73
|
+
.flatMap((headerGroup: HeaderGroup) => headerGroup.headers)
|
|
74
|
+
.filter((header: Header) => header.key === 'Content-Security-Policy');
|
|
75
|
+
|
|
76
|
+
expect(cspHeaders.length).toBeGreaterThan(0);
|
|
77
|
+
|
|
78
|
+
const lastCspHeader = cspHeaders[cspHeaders.length - 1];
|
|
79
|
+
|
|
80
|
+
expect(lastCspHeader.value).toContain('akifast.com');
|
|
81
|
+
expect(lastCspHeader.value).toContain('https://*.akifast.com');
|
|
82
|
+
});
|
|
83
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json.schemastore.org/tsconfig",
|
|
3
|
+
"display": "Default",
|
|
4
|
+
"compilerOptions": {
|
|
5
|
+
"composite": false,
|
|
6
|
+
"declaration": true,
|
|
7
|
+
"declarationMap": true,
|
|
8
|
+
"esModuleInterop": true,
|
|
9
|
+
"forceConsistentCasingInFileNames": true,
|
|
10
|
+
"inlineSources": false,
|
|
11
|
+
"isolatedModules": true,
|
|
12
|
+
"moduleResolution": "node",
|
|
13
|
+
"noUnusedLocals": false,
|
|
14
|
+
"noUnusedParameters": false,
|
|
15
|
+
"preserveWatchOutput": true,
|
|
16
|
+
"skipLibCheck": true,
|
|
17
|
+
"strict": true,
|
|
18
|
+
"jsx": "react-jsx",
|
|
19
|
+
"allowJs": true
|
|
20
|
+
},
|
|
21
|
+
"exclude": ["node_modules"],
|
|
22
|
+
"include": [".", "../."]
|
|
23
|
+
}
|
package/babel.config.js
ADDED
package/bin/pz-prebuild.js
CHANGED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { spawn } = require('child_process');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const glob = require('glob');
|
|
7
|
+
const findBaseDir = require('../utils/find-base-dir');
|
|
8
|
+
const checkMonorepo = require('../utils/check-monorepo');
|
|
9
|
+
|
|
10
|
+
const IS_MONOREPO = checkMonorepo() !== null;
|
|
11
|
+
const BASE_DIR = findBaseDir();
|
|
12
|
+
const PLUGINS = require(path.join(BASE_DIR, 'src', 'plugins.js'));
|
|
13
|
+
|
|
14
|
+
function findPluginTestFiles(akinonNextPackagePath) {
|
|
15
|
+
const pluginsRootPath = path.join(
|
|
16
|
+
akinonNextPackagePath,
|
|
17
|
+
'..',
|
|
18
|
+
'..',
|
|
19
|
+
IS_MONOREPO ? 'packages' : '@akinon'
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
if (!fs.existsSync(pluginsRootPath)) {
|
|
23
|
+
console.log('Plugins directory not found:', pluginsRootPath);
|
|
24
|
+
return [];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return PLUGINS.reduce((testFiles, pluginName) => {
|
|
28
|
+
const pluginDirectoryPath = path.join(pluginsRootPath, pluginName);
|
|
29
|
+
if (fs.existsSync(pluginDirectoryPath)) {
|
|
30
|
+
const pluginTestFiles = glob.sync('**/*.test.ts', {
|
|
31
|
+
cwd: pluginDirectoryPath,
|
|
32
|
+
absolute: true
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
return testFiles.concat(pluginTestFiles);
|
|
36
|
+
} else {
|
|
37
|
+
console.log(`Plugin directory not found: ${pluginName}`);
|
|
38
|
+
}
|
|
39
|
+
return testFiles;
|
|
40
|
+
}, []);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function isJestInstalled() {
|
|
44
|
+
try {
|
|
45
|
+
const jestExecutablePath = path.join(
|
|
46
|
+
BASE_DIR,
|
|
47
|
+
'node_modules',
|
|
48
|
+
'.bin',
|
|
49
|
+
'jest'
|
|
50
|
+
);
|
|
51
|
+
return fs.existsSync(jestExecutablePath);
|
|
52
|
+
} catch (error) {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function getAkinonNextPackagePath() {
|
|
58
|
+
if (!IS_MONOREPO) {
|
|
59
|
+
return path.resolve(__dirname, '..');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return path.resolve(__dirname, '../../../packages/akinon-next');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (!isJestInstalled()) {
|
|
66
|
+
console.error('\x1b[31mError: Jest is not installed in the project!\x1b[0m');
|
|
67
|
+
console.error(
|
|
68
|
+
'Please install all dependencies by running one of the following commands:'
|
|
69
|
+
);
|
|
70
|
+
console.error(' npm install');
|
|
71
|
+
console.error(' yarn install');
|
|
72
|
+
process.exit(1);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const jestExecutablePath = path.join(BASE_DIR, 'node_modules', '.bin', 'jest');
|
|
76
|
+
const akinonNextPackagePath = getAkinonNextPackagePath();
|
|
77
|
+
|
|
78
|
+
const testFiles = [
|
|
79
|
+
...glob.sync('__tests__/**/*.test.ts', {
|
|
80
|
+
cwd: akinonNextPackagePath,
|
|
81
|
+
absolute: true
|
|
82
|
+
}),
|
|
83
|
+
...findPluginTestFiles(akinonNextPackagePath)
|
|
84
|
+
];
|
|
85
|
+
|
|
86
|
+
console.log(`Found ${testFiles.length} test files to run`);
|
|
87
|
+
|
|
88
|
+
const jestArguments = [
|
|
89
|
+
...testFiles,
|
|
90
|
+
`--config ${path.join(akinonNextPackagePath, 'jest.config.js')}`,
|
|
91
|
+
'--runTestsByPath',
|
|
92
|
+
'--passWithNoTests'
|
|
93
|
+
];
|
|
94
|
+
|
|
95
|
+
spawn(jestExecutablePath, jestArguments, {
|
|
96
|
+
cwd: akinonNextPackagePath,
|
|
97
|
+
stdio: 'inherit',
|
|
98
|
+
shell: true
|
|
99
|
+
});
|
package/components/input.tsx
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import clsx from 'clsx';
|
|
2
2
|
import { forwardRef, FocusEvent, useState, Ref } from 'react';
|
|
3
3
|
import { Controller } from 'react-hook-form';
|
|
4
|
+
|
|
5
|
+
// @ts-ignore
|
|
4
6
|
import { PatternFormat, PatternFormatProps } from 'react-number-format';
|
|
5
7
|
import { InputProps } from '../types';
|
|
6
8
|
import { twMerge } from 'tailwind-merge';
|
package/components/link.tsx
CHANGED
|
@@ -10,7 +10,9 @@ type LinkProps = Omit<
|
|
|
10
10
|
React.AnchorHTMLAttributes<HTMLAnchorElement>,
|
|
11
11
|
keyof NextLinkProps
|
|
12
12
|
> &
|
|
13
|
-
NextLinkProps
|
|
13
|
+
NextLinkProps & {
|
|
14
|
+
href: string;
|
|
15
|
+
};
|
|
14
16
|
|
|
15
17
|
export const Link = ({ children, href, ...rest }: LinkProps) => {
|
|
16
18
|
const { locale, defaultLocaleValue, localeUrlStrategy } = useLocalization();
|
|
@@ -26,19 +28,21 @@ export const Link = ({ children, href, ...rest }: LinkProps) => {
|
|
|
26
28
|
return href;
|
|
27
29
|
}
|
|
28
30
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
31
|
+
if (typeof href === 'string' && !href.startsWith('http')) {
|
|
32
|
+
const pathnameWithoutLocale = href.replace(urlLocaleMatcherRegex, '');
|
|
33
|
+
const hrefWithLocale = `/${locale}${pathnameWithoutLocale}`;
|
|
34
|
+
|
|
35
|
+
if (localeUrlStrategy === LocaleUrlStrategy.ShowAllLocales) {
|
|
36
|
+
return hrefWithLocale;
|
|
37
|
+
} else if (
|
|
38
|
+
localeUrlStrategy === LocaleUrlStrategy.HideDefaultLocale &&
|
|
39
|
+
locale !== defaultLocaleValue
|
|
40
|
+
) {
|
|
41
|
+
return hrefWithLocale;
|
|
42
|
+
}
|
|
39
43
|
}
|
|
40
44
|
|
|
41
|
-
return href
|
|
45
|
+
return href;
|
|
42
46
|
}, [href, defaultLocaleValue, locale, localeUrlStrategy]);
|
|
43
47
|
|
|
44
48
|
return (
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { Cache, CacheKey } from '../../lib/cache';
|
|
2
|
+
import { basket } from '../../data/urls';
|
|
3
|
+
import { Basket } from '../../types';
|
|
4
|
+
import appFetch from '../../utils/app-fetch';
|
|
5
|
+
import { ServerVariables } from '../../utils/server-variables';
|
|
6
|
+
import logger from '../../utils/log';
|
|
7
|
+
|
|
8
|
+
type GetBasketParams = {
|
|
9
|
+
locale?: string;
|
|
10
|
+
currency?: string;
|
|
11
|
+
namespace?: string;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const getBasketDataHandler = ({
|
|
15
|
+
locale,
|
|
16
|
+
currency,
|
|
17
|
+
namespace
|
|
18
|
+
}: GetBasketParams) => {
|
|
19
|
+
return async function () {
|
|
20
|
+
try {
|
|
21
|
+
const url = namespace
|
|
22
|
+
? basket.getBasketDetail(namespace)
|
|
23
|
+
: basket.getBasket;
|
|
24
|
+
|
|
25
|
+
const basketData = await appFetch<{ basket: Basket }>({
|
|
26
|
+
url,
|
|
27
|
+
locale,
|
|
28
|
+
currency,
|
|
29
|
+
init: {
|
|
30
|
+
headers: {
|
|
31
|
+
Accept: 'application/json',
|
|
32
|
+
'Content-Type': 'application/json'
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
if (!basketData?.basket) {
|
|
38
|
+
logger.warn('Basket data is undefined', {
|
|
39
|
+
handler: 'getBasketDataHandler',
|
|
40
|
+
namespace
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return basketData;
|
|
45
|
+
} catch (error) {
|
|
46
|
+
logger.error('Error fetching basket data', {
|
|
47
|
+
handler: 'getBasketDataHandler',
|
|
48
|
+
error,
|
|
49
|
+
namespace
|
|
50
|
+
});
|
|
51
|
+
throw error;
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export const getBasketData = async ({
|
|
57
|
+
locale = ServerVariables.locale,
|
|
58
|
+
currency = ServerVariables.currency,
|
|
59
|
+
namespace
|
|
60
|
+
}: GetBasketParams = {}) => {
|
|
61
|
+
return Cache.wrap(
|
|
62
|
+
CacheKey.Basket(namespace),
|
|
63
|
+
locale,
|
|
64
|
+
getBasketDataHandler({ locale, currency, namespace }),
|
|
65
|
+
{
|
|
66
|
+
expire: 0,
|
|
67
|
+
cache: false
|
|
68
|
+
}
|
|
69
|
+
);
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
|
|
@@ -72,10 +72,13 @@ const addRootLayoutProps = async (componentProps: RootLayoutProps) => {
|
|
|
72
72
|
const checkRedisVariables = () => {
|
|
73
73
|
const requiredVariableValues = [
|
|
74
74
|
process.env.CACHE_HOST,
|
|
75
|
-
process.env.CACHE_PORT
|
|
76
|
-
process.env.CACHE_SECRET
|
|
75
|
+
process.env.CACHE_PORT
|
|
77
76
|
];
|
|
78
77
|
|
|
78
|
+
if (!settings.usePrettyUrlRoute) {
|
|
79
|
+
requiredVariableValues.push(process.env.CACHE_SECRET);
|
|
80
|
+
}
|
|
81
|
+
|
|
79
82
|
if (
|
|
80
83
|
!requiredVariableValues.every((v) => v) &&
|
|
81
84
|
process.env.NODE_ENV === 'production'
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { useAppSelector } from '../redux/hooks';
|
|
2
|
+
|
|
3
|
+
export const useLoyaltyAvailability = () => {
|
|
4
|
+
const { paymentOptions, unavailablePaymentOptions } = useAppSelector(
|
|
5
|
+
(state) => state.checkout
|
|
6
|
+
);
|
|
7
|
+
|
|
8
|
+
const hasLoyaltyInAvailable = paymentOptions.some(
|
|
9
|
+
(option) =>
|
|
10
|
+
option.payment_type === 'loyalty_money' ||
|
|
11
|
+
option.payment_type === 'loyalty'
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
const hasLoyaltyInUnavailable = unavailablePaymentOptions.some(
|
|
15
|
+
(option) =>
|
|
16
|
+
option.payment_type === 'loyalty_money' ||
|
|
17
|
+
option.payment_type === 'loyalty'
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
return hasLoyaltyInAvailable || hasLoyaltyInUnavailable;
|
|
21
|
+
};
|
package/instrumentation/node.ts
CHANGED
|
@@ -4,17 +4,19 @@ import { Resource } from '@opentelemetry/resources';
|
|
|
4
4
|
import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions';
|
|
5
5
|
import { SimpleSpanProcessor } from '@opentelemetry/sdk-trace-node';
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
new
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
7
|
+
if (process.env.NODE_ENV === 'development') {
|
|
8
|
+
const sdk = new NodeSDK({
|
|
9
|
+
resource: new Resource({
|
|
10
|
+
[SemanticResourceAttributes.SERVICE_NAME]: 'pz-next-app'
|
|
11
|
+
}),
|
|
12
|
+
spanProcessor: new SimpleSpanProcessor(
|
|
13
|
+
new OTLPTraceExporter({
|
|
14
|
+
url: `${
|
|
15
|
+
process.env.PZ_DASHBOARD_URL ?? 'http://localhost:3005'
|
|
16
|
+
}/api/traces`
|
|
17
|
+
})
|
|
18
|
+
)
|
|
19
|
+
});
|
|
19
20
|
|
|
20
|
-
sdk.start();
|
|
21
|
+
sdk.start();
|
|
22
|
+
}
|
package/jest.config.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
|
|
3
|
+
module.exports = {
|
|
4
|
+
preset: 'ts-jest',
|
|
5
|
+
testEnvironment: 'node',
|
|
6
|
+
rootDir: path.resolve(__dirname),
|
|
7
|
+
roots: [],
|
|
8
|
+
testMatch: ['**/*.test.ts'],
|
|
9
|
+
testPathIgnorePatterns: [],
|
|
10
|
+
transformIgnorePatterns: [],
|
|
11
|
+
transform: {
|
|
12
|
+
'^.+\\.(tsx?|jsx?|mjs?)$': [
|
|
13
|
+
'ts-jest',
|
|
14
|
+
{
|
|
15
|
+
tsconfig: path.resolve(__dirname, '__tests__/tsconfig.json')
|
|
16
|
+
}
|
|
17
|
+
]
|
|
18
|
+
}
|
|
19
|
+
};
|
package/lib/cache.ts
CHANGED
|
@@ -31,6 +31,8 @@ export const CacheKey = {
|
|
|
31
31
|
`category_${pk}_${encodeURIComponent(
|
|
32
32
|
JSON.stringify(searchParams)
|
|
33
33
|
)}${hashCacheKey(headers)}`,
|
|
34
|
+
Basket: (namespace?: string) => `basket${namespace ? `_${namespace}` : ''}`,
|
|
35
|
+
AllBaskets: () => 'all_baskets',
|
|
34
36
|
CategorySlug: (slug: string) => `category_${slug}`,
|
|
35
37
|
SpecialPage: (
|
|
36
38
|
pk: number,
|
|
@@ -145,7 +145,8 @@ const withCompleteGpay =
|
|
|
145
145
|
logger.info('Redirecting to order success page', {
|
|
146
146
|
middleware: 'complete-gpay',
|
|
147
147
|
redirectUrlWithLocale,
|
|
148
|
-
ip
|
|
148
|
+
ip,
|
|
149
|
+
setCookie: request.headers.get('set-cookie')
|
|
149
150
|
});
|
|
150
151
|
|
|
151
152
|
// Using POST method while redirecting causes an error,
|
|
@@ -145,7 +145,8 @@ const withCompleteMasterpass =
|
|
|
145
145
|
logger.info('Redirecting to order success page', {
|
|
146
146
|
middleware: 'complete-masterpass',
|
|
147
147
|
redirectUrlWithLocale,
|
|
148
|
-
ip
|
|
148
|
+
ip,
|
|
149
|
+
setCookie: request.headers.get('set-cookie')
|
|
149
150
|
});
|
|
150
151
|
|
|
151
152
|
// Using POST method while redirecting causes an error,
|
|
@@ -146,7 +146,8 @@ const withRedirectionPayment =
|
|
|
146
146
|
logger.info('Redirecting to order success page', {
|
|
147
147
|
middleware: 'redirection-payment',
|
|
148
148
|
redirectUrlWithLocale,
|
|
149
|
-
ip
|
|
149
|
+
ip,
|
|
150
|
+
setCookie: request.headers.get('set-cookie')
|
|
150
151
|
});
|
|
151
152
|
|
|
152
153
|
// Using POST method while redirecting causes an error,
|
|
@@ -145,7 +145,8 @@ const withSavedCardRedirection =
|
|
|
145
145
|
logger.info('Redirecting to order success page', {
|
|
146
146
|
middleware: 'saved-card-redirection',
|
|
147
147
|
redirectUrlWithLocale,
|
|
148
|
-
ip
|
|
148
|
+
ip,
|
|
149
|
+
setCookie: request.headers.get('set-cookie')
|
|
149
150
|
});
|
|
150
151
|
|
|
151
152
|
// Using POST method while redirecting causes an error,
|
|
@@ -145,7 +145,8 @@ const withThreeDRedirection =
|
|
|
145
145
|
logger.info('Redirecting to order success page', {
|
|
146
146
|
middleware: 'three-d-redirection',
|
|
147
147
|
redirectUrlWithLocale,
|
|
148
|
-
ip
|
|
148
|
+
ip,
|
|
149
|
+
setCookie: request.headers.get('set-cookie')
|
|
149
150
|
});
|
|
150
151
|
|
|
151
152
|
// Using POST method while redirecting causes an error,
|
|
@@ -4,6 +4,7 @@ import { PzNextRequest } from '.';
|
|
|
4
4
|
import logger from '../utils/log';
|
|
5
5
|
import { urlLocaleMatcherRegex } from '../utils';
|
|
6
6
|
import { getUrlPathWithLocale } from '../utils/localization';
|
|
7
|
+
import { shouldIgnoreRedirect } from '../utils/redirect-ignore';
|
|
7
8
|
import { ROUTES } from 'routes';
|
|
8
9
|
|
|
9
10
|
// This middleware is used to handle url redirections set in Omnitron
|
|
@@ -60,20 +61,13 @@ const withUrlRedirection =
|
|
|
60
61
|
|
|
61
62
|
const setCookies = request.headers.getSetCookie();
|
|
62
63
|
|
|
63
|
-
if (
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
)
|
|
71
|
-
)
|
|
72
|
-
);
|
|
73
|
-
|
|
74
|
-
if (shouldIgnoreRedirect) {
|
|
75
|
-
return middleware(req, event);
|
|
76
|
-
}
|
|
64
|
+
if (
|
|
65
|
+
shouldIgnoreRedirect(
|
|
66
|
+
url.pathname,
|
|
67
|
+
req.middlewareParams.rewrites.locale
|
|
68
|
+
)
|
|
69
|
+
) {
|
|
70
|
+
return middleware(req, event);
|
|
77
71
|
}
|
|
78
72
|
|
|
79
73
|
const response = NextResponse.redirect(redirectUrl.toString(), {
|
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.
|
|
4
|
+
"version": "1.91.0-rc.0",
|
|
5
5
|
"private": false,
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"bin": {
|
|
@@ -13,6 +13,9 @@
|
|
|
13
13
|
"pz-predev": "bin/pz-predev.js",
|
|
14
14
|
"pz-postdev": "bin/pz-postdev.js"
|
|
15
15
|
},
|
|
16
|
+
"scripts": {
|
|
17
|
+
"test": "jest"
|
|
18
|
+
},
|
|
16
19
|
"dependencies": {
|
|
17
20
|
"@opentelemetry/exporter-trace-otlp-http": "0.46.0",
|
|
18
21
|
"@opentelemetry/resources": "1.19.0",
|
|
@@ -20,7 +23,7 @@
|
|
|
20
23
|
"@opentelemetry/sdk-trace-node": "1.19.0",
|
|
21
24
|
"@opentelemetry/semantic-conventions": "1.19.0",
|
|
22
25
|
"@reduxjs/toolkit": "1.9.7",
|
|
23
|
-
"@neshca/cache-handler": "1.
|
|
26
|
+
"@neshca/cache-handler": "1.9.0",
|
|
24
27
|
"@sentry/nextjs": "9.5.0",
|
|
25
28
|
"cross-spawn": "7.0.3",
|
|
26
29
|
"generic-pool": "3.9.0",
|
|
@@ -31,13 +34,17 @@
|
|
|
31
34
|
"set-cookie-parser": "2.6.0"
|
|
32
35
|
},
|
|
33
36
|
"devDependencies": {
|
|
34
|
-
"@akinon/eslint-plugin-projectzero": "1.
|
|
37
|
+
"@akinon/eslint-plugin-projectzero": "1.91.0-rc.0",
|
|
35
38
|
"@types/react-redux": "7.1.30",
|
|
36
39
|
"@types/set-cookie-parser": "2.4.7",
|
|
37
40
|
"@typescript-eslint/eslint-plugin": "6.7.4",
|
|
38
41
|
"@typescript-eslint/parser": "6.7.4",
|
|
42
|
+
"babel-jest": "29.7.0",
|
|
39
43
|
"eslint": "8.56.0",
|
|
40
44
|
"eslint-config-next": "14.2.3",
|
|
41
|
-
"eslint-config-prettier": "8.5.0"
|
|
45
|
+
"eslint-config-prettier": "8.5.0",
|
|
46
|
+
"jest": "29.7.0",
|
|
47
|
+
"ts-jest": "29.3.2",
|
|
48
|
+
"typescript": "5.2.2"
|
|
42
49
|
}
|
|
43
50
|
}
|
|
@@ -20,7 +20,8 @@ import {
|
|
|
20
20
|
setShippingOptions,
|
|
21
21
|
setHepsipayAvailability,
|
|
22
22
|
setWalletPaymentData,
|
|
23
|
-
setPayOnDeliveryOtpModalActive
|
|
23
|
+
setPayOnDeliveryOtpModalActive,
|
|
24
|
+
setUnavailablePaymentOptions
|
|
24
25
|
} from '../../redux/reducers/checkout';
|
|
25
26
|
import { RootState, TypedDispatch } from 'redux/store';
|
|
26
27
|
import { checkoutApi } from '../../data/client/checkout';
|
|
@@ -36,6 +37,7 @@ interface CheckoutResult {
|
|
|
36
37
|
errors?: Record<string, string[]>;
|
|
37
38
|
pre_order?: PreOrder;
|
|
38
39
|
context_list?: CheckoutContext[];
|
|
40
|
+
redirect_url?: string;
|
|
39
41
|
};
|
|
40
42
|
}
|
|
41
43
|
|
|
@@ -49,7 +51,11 @@ export const errorMiddleware: Middleware = ({ dispatch }: MiddlewareParams) => {
|
|
|
49
51
|
const result: CheckoutResult = next(action);
|
|
50
52
|
const errors = result?.payload?.errors;
|
|
51
53
|
|
|
52
|
-
if (
|
|
54
|
+
if (
|
|
55
|
+
!!errors &&
|
|
56
|
+
((typeof errors === 'object' && Object.keys(errors).length > 0) ||
|
|
57
|
+
(Array.isArray(errors) && errors.length > 0))
|
|
58
|
+
) {
|
|
53
59
|
dispatch(setErrors(errors));
|
|
54
60
|
}
|
|
55
61
|
|
|
@@ -57,6 +63,33 @@ export const errorMiddleware: Middleware = ({ dispatch }: MiddlewareParams) => {
|
|
|
57
63
|
};
|
|
58
64
|
};
|
|
59
65
|
|
|
66
|
+
export const redirectUrlMiddleware: Middleware = () => {
|
|
67
|
+
return (next) => (action) => {
|
|
68
|
+
const result: CheckoutResult = next(action);
|
|
69
|
+
const redirectUrl = result?.payload?.redirect_url;
|
|
70
|
+
|
|
71
|
+
if (redirectUrl) {
|
|
72
|
+
const currentLocale = getCookie('pz-locale');
|
|
73
|
+
|
|
74
|
+
let url = redirectUrl;
|
|
75
|
+
|
|
76
|
+
if (currentLocale && !redirectUrl.includes('/orders/redirection')) {
|
|
77
|
+
const { defaultLocaleValue, localeUrlStrategy } = settings.localization;
|
|
78
|
+
|
|
79
|
+
url =
|
|
80
|
+
currentLocale === defaultLocaleValue &&
|
|
81
|
+
localeUrlStrategy !== LocaleUrlStrategy.ShowAllLocales
|
|
82
|
+
? redirectUrl
|
|
83
|
+
: `/${currentLocale}${redirectUrl}`;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
window.location.href = url;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return result;
|
|
90
|
+
};
|
|
91
|
+
};
|
|
92
|
+
|
|
60
93
|
export const contextListMiddleware: Middleware = ({
|
|
61
94
|
dispatch,
|
|
62
95
|
getState
|
|
@@ -148,6 +181,14 @@ export const contextListMiddleware: Middleware = ({
|
|
|
148
181
|
dispatch(setPaymentOptions(context.page_context.payment_options));
|
|
149
182
|
}
|
|
150
183
|
|
|
184
|
+
if (context.page_context.unavailable_options) {
|
|
185
|
+
dispatch(
|
|
186
|
+
setUnavailablePaymentOptions(
|
|
187
|
+
context.page_context.unavailable_options
|
|
188
|
+
)
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
|
|
151
192
|
if (context.page_context.credit_payment_options) {
|
|
152
193
|
dispatch(
|
|
153
194
|
setCreditPaymentOptions(context.page_context.credit_payment_options)
|