@akinon/projectzero 2.0.0-beta.6 → 2.0.0-beta.8
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 +4 -0
- package/app-template/.env.example +2 -0
- package/app-template/.gitignore +2 -0
- package/app-template/CHANGELOG.md +51 -0
- package/app-template/README.md +6 -0
- package/app-template/config/prebuild-tests.json +5 -0
- package/app-template/next.config.ts +4 -1
- package/app-template/package.json +24 -22
- package/app-template/public/locales/en/account.json +4 -0
- package/app-template/public/locales/en/common.json +6 -0
- package/app-template/public/locales/tr/account.json +4 -0
- package/app-template/public/locales/tr/common.json +6 -0
- package/app-template/src/__tests__/middleware-matcher.test.ts +135 -0
- package/app-template/src/app/[commerce]/[locale]/[currency]/account/orders/[id]/page.tsx +65 -3
- package/app-template/src/app/[commerce]/[locale]/[currency]/error.tsx +12 -15
- package/app-template/src/app/[commerce]/[locale]/[currency]/xml-sitemap/[node]/route.ts +47 -1
- package/app-template/src/components/select.tsx +1 -1
- package/app-template/src/components/tabs.tsx +2 -2
- package/app-template/src/redux/middlewares/category.ts +1 -1
- package/app-template/src/redux/reducers/category.ts +1 -1
- package/app-template/src/utils/convert-facet-search-params.ts +1 -1
- package/app-template/src/views/account/orders/order-cancellation-item.tsx +0 -19
- package/app-template/src/views/basket/basket-item.tsx +1 -0
- package/app-template/src/views/category/category-active-filters.tsx +1 -1
- package/app-template/src/views/category/category-header.tsx +1 -1
- package/app-template/src/views/category/category-info.tsx +3 -3
- package/app-template/src/views/category/filters/index.tsx +2 -2
- package/app-template/src/views/checkout/steps/payment/index.tsx +1 -1
- package/app-template/src/views/header/action-menu.tsx +6 -3
- package/app-template/src/views/header/mini-basket.tsx +13 -2
- package/app-template/src/views/installment-options/index.tsx +1 -1
- package/app-template/src/views/otp-login/index.tsx +12 -14
- package/codemods/sentry-9/index.js +30 -0
- package/codemods/sentry-9/remove-sentry-configs.js +14 -0
- package/codemods/sentry-9/remove-sentry-dependency.js +25 -0
- package/codemods/sentry-9/replace-error-page.js +32 -0
- package/commands/codemod.ts +18 -0
- package/commands/index.ts +3 -1
- package/dist/codemods/sentry-9/templates/error.js +14 -0
- package/dist/commands/codemod.js +16 -0
- package/dist/commands/index.js +3 -1
- package/package.json +1 -1
- package/app-template/sentry.client.config.ts +0 -16
- package/app-template/sentry.edge.config.ts +0 -3
- package/app-template/sentry.properties +0 -4
- package/app-template/sentry.server.config.ts +0 -3
- package/app-template/src/app/[commerce]/[locale]/[currency]/product/[pk]/loading.tsx +0 -67
package/CHANGELOG.md
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
NEXTAUTH_SECRET=PDpBb/aSJESgBbPLHw1+jveHXqyvkC7GC1Z82jvE04s=
|
|
2
|
+
# When using dev-ssl command, NEXTAUTH_URL should be updated to https://localhost:3000
|
|
2
3
|
NEXTAUTH_URL=http://localhost:3000
|
|
3
4
|
NEXT_PUBLIC_MAP_API_KEY=YOUR_MAP_API_KEY
|
|
4
5
|
NEXT_PUBLIC_GTM_KEY=GTM_KEY
|
|
5
6
|
NEXT_PUBLIC_URL=http://localhost:3000
|
|
6
7
|
SERVICE_BACKEND_URL=https://02fde10fee4440269e695aa10707dfaf.lb.akinoncloud.com
|
|
8
|
+
SITEMAP_S3_BUCKET_NAME=0fb534
|
|
7
9
|
|
|
8
10
|
# LOG_LEVEL_DEV=debug # For more details, please refer to the Logging documentation.
|
|
9
11
|
|
package/app-template/.gitignore
CHANGED
|
@@ -1,5 +1,56 @@
|
|
|
1
1
|
# projectzeronext
|
|
2
2
|
|
|
3
|
+
## 2.0.0-beta.8
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 071d0f5: ZERO-3352: Resolve Single item size exceeds maxSize error and upgrade dependencies
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- Updated dependencies [071d0f5]
|
|
12
|
+
- @akinon/next@2.0.0-beta.8
|
|
13
|
+
- @akinon/pz-akifast@2.0.0-beta.8
|
|
14
|
+
- @akinon/pz-b2b@2.0.0-beta.8
|
|
15
|
+
- @akinon/pz-basket-gift-pack@2.0.0-beta.8
|
|
16
|
+
- @akinon/pz-bkm@2.0.0-beta.8
|
|
17
|
+
- @akinon/pz-checkout-gift-pack@2.0.0-beta.8
|
|
18
|
+
- @akinon/pz-click-collect@2.0.0-beta.8
|
|
19
|
+
- @akinon/pz-credit-payment@2.0.0-beta.8
|
|
20
|
+
- @akinon/pz-gpay@2.0.0-beta.8
|
|
21
|
+
- @akinon/pz-masterpass@2.0.0-beta.8
|
|
22
|
+
- @akinon/pz-one-click-checkout@2.0.0-beta.8
|
|
23
|
+
- @akinon/pz-otp@2.0.0-beta.8
|
|
24
|
+
- @akinon/pz-pay-on-delivery@2.0.0-beta.8
|
|
25
|
+
- @akinon/pz-saved-card@2.0.0-beta.8
|
|
26
|
+
- @akinon/pz-tabby-extension@2.0.0-beta.8
|
|
27
|
+
- @akinon/pz-tamara-extension@2.0.0-beta.8
|
|
28
|
+
|
|
29
|
+
## 2.0.0-beta.7
|
|
30
|
+
|
|
31
|
+
### Minor Changes
|
|
32
|
+
|
|
33
|
+
- 1bbba84: ZERO-3291: Update next.js and related dependencies to version 15.2.3
|
|
34
|
+
|
|
35
|
+
### Patch Changes
|
|
36
|
+
|
|
37
|
+
- @akinon/next@2.0.0-beta.7
|
|
38
|
+
- @akinon/pz-akifast@2.0.0-beta.7
|
|
39
|
+
- @akinon/pz-b2b@2.0.0-beta.7
|
|
40
|
+
- @akinon/pz-basket-gift-pack@2.0.0-beta.7
|
|
41
|
+
- @akinon/pz-bkm@2.0.0-beta.7
|
|
42
|
+
- @akinon/pz-checkout-gift-pack@2.0.0-beta.7
|
|
43
|
+
- @akinon/pz-click-collect@2.0.0-beta.7
|
|
44
|
+
- @akinon/pz-credit-payment@2.0.0-beta.7
|
|
45
|
+
- @akinon/pz-gpay@2.0.0-beta.7
|
|
46
|
+
- @akinon/pz-masterpass@2.0.0-beta.7
|
|
47
|
+
- @akinon/pz-one-click-checkout@2.0.0-beta.7
|
|
48
|
+
- @akinon/pz-otp@2.0.0-beta.7
|
|
49
|
+
- @akinon/pz-pay-on-delivery@2.0.0-beta.7
|
|
50
|
+
- @akinon/pz-saved-card@2.0.0-beta.7
|
|
51
|
+
- @akinon/pz-tabby-extension@2.0.0-beta.7
|
|
52
|
+
- @akinon/pz-tamara-extension@2.0.0-beta.7
|
|
53
|
+
|
|
3
54
|
## 2.0.0-beta.6
|
|
4
55
|
|
|
5
56
|
### Minor Changes
|
package/app-template/README.md
CHANGED
|
@@ -6,6 +6,12 @@ Headless Akinon Commerce Cloud Storefront.
|
|
|
6
6
|
|
|
7
7
|
Run `cp .env.example .env && yarn` command at project root and all dependencies should be installed.
|
|
8
8
|
|
|
9
|
+
## Environment Variables
|
|
10
|
+
|
|
11
|
+
### Required Environment Variables
|
|
12
|
+
|
|
13
|
+
- `SITEMAP_S3_BUCKET_NAME`: S3 bucket name for XML sitemaps (e.g., "0fb534"). This is required for the sitemap route to function correctly.
|
|
14
|
+
|
|
9
15
|
### Build
|
|
10
16
|
|
|
11
17
|
To build the app, run the following command:
|
|
@@ -26,7 +26,10 @@ const withPwaConfig = withPWA({
|
|
|
26
26
|
|
|
27
27
|
const sentryConfig = {
|
|
28
28
|
silent: true,
|
|
29
|
-
dryRun: !process.env.SENTRY_DSN
|
|
29
|
+
dryRun: !process.env.SENTRY_DSN,
|
|
30
|
+
org: 'akinon'
|
|
31
|
+
// project: 'enter_your_project_name_here',
|
|
32
|
+
// authToken: 'enter_your_auth_token_here'
|
|
30
33
|
};
|
|
31
34
|
|
|
32
35
|
const enhancedConfig = withPzConfig(nextConfig);
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "projectzeronext",
|
|
3
|
-
"version": "2.0.0-beta.
|
|
3
|
+
"version": "2.0.0-beta.8",
|
|
4
4
|
"private": true,
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"clean": "rm -rf node_modules && rm -rf .next",
|
|
8
8
|
"dev": "next dev",
|
|
9
|
+
"dev-ssl": "next dev --experimental-https",
|
|
9
10
|
"build": "next build",
|
|
10
11
|
"start": "next start -p 8008",
|
|
11
12
|
"type-check": "tsc",
|
|
@@ -19,32 +20,33 @@
|
|
|
19
20
|
"prebuild": "pz-prebuild",
|
|
20
21
|
"postbuild": "pz-postbuild",
|
|
21
22
|
"poststart": "pz-poststart",
|
|
22
|
-
"prestart": "pz-prestart"
|
|
23
|
+
"prestart": "pz-prestart",
|
|
24
|
+
"test:middleware": "jest middleware-matcher.test.ts --bail"
|
|
23
25
|
},
|
|
24
26
|
"dependencies": {
|
|
25
|
-
"@akinon/next": "2.0.0-beta.
|
|
26
|
-
"@akinon/pz-akifast": "2.0.0-beta.
|
|
27
|
-
"@akinon/pz-b2b": "2.0.0-beta.
|
|
28
|
-
"@akinon/pz-basket-gift-pack": "2.0.0-beta.
|
|
29
|
-
"@akinon/pz-bkm": "2.0.0-beta.
|
|
30
|
-
"@akinon/pz-checkout-gift-pack": "2.0.0-beta.
|
|
31
|
-
"@akinon/pz-click-collect": "2.0.0-beta.
|
|
32
|
-
"@akinon/pz-credit-payment": "2.0.0-beta.
|
|
33
|
-
"@akinon/pz-gpay": "2.0.0-beta.
|
|
34
|
-
"@akinon/pz-masterpass": "2.0.0-beta.
|
|
35
|
-
"@akinon/pz-one-click-checkout": "2.0.0-beta.
|
|
36
|
-
"@akinon/pz-otp": "2.0.0-beta.
|
|
37
|
-
"@akinon/pz-pay-on-delivery": "2.0.0-beta.
|
|
38
|
-
"@akinon/pz-saved-card": "2.0.0-beta.
|
|
39
|
-
"@akinon/pz-tabby-extension": "2.0.0-beta.
|
|
27
|
+
"@akinon/next": "2.0.0-beta.8",
|
|
28
|
+
"@akinon/pz-akifast": "2.0.0-beta.8",
|
|
29
|
+
"@akinon/pz-b2b": "2.0.0-beta.8",
|
|
30
|
+
"@akinon/pz-basket-gift-pack": "2.0.0-beta.8",
|
|
31
|
+
"@akinon/pz-bkm": "2.0.0-beta.8",
|
|
32
|
+
"@akinon/pz-checkout-gift-pack": "2.0.0-beta.8",
|
|
33
|
+
"@akinon/pz-click-collect": "2.0.0-beta.8",
|
|
34
|
+
"@akinon/pz-credit-payment": "2.0.0-beta.8",
|
|
35
|
+
"@akinon/pz-gpay": "2.0.0-beta.8",
|
|
36
|
+
"@akinon/pz-masterpass": "2.0.0-beta.8",
|
|
37
|
+
"@akinon/pz-one-click-checkout": "2.0.0-beta.8",
|
|
38
|
+
"@akinon/pz-otp": "2.0.0-beta.8",
|
|
39
|
+
"@akinon/pz-pay-on-delivery": "2.0.0-beta.8",
|
|
40
|
+
"@akinon/pz-saved-card": "2.0.0-beta.8",
|
|
41
|
+
"@akinon/pz-tabby-extension": "2.0.0-beta.8",
|
|
42
|
+
"@akinon/pz-tamara-extension": "2.0.0-beta.8",
|
|
40
43
|
"@hookform/resolvers": "2.9.0",
|
|
41
44
|
"@next/third-parties": "14.1.0",
|
|
42
45
|
"@react-google-maps/api": "2.17.1",
|
|
43
|
-
"@sentry/nextjs": "7.116.0",
|
|
44
46
|
"@tailwindcss/postcss": "4.0.0",
|
|
45
47
|
"dayjs": "1.11.5",
|
|
46
48
|
"lossless-json": "2.0.5",
|
|
47
|
-
"next": "15.1
|
|
49
|
+
"next": "15.3.1",
|
|
48
50
|
"next-auth": "4.24.11",
|
|
49
51
|
"next-pwa": "5.6.0",
|
|
50
52
|
"pino": "8.11.0",
|
|
@@ -58,11 +60,10 @@
|
|
|
58
60
|
"react-string-replace": "1.1.0",
|
|
59
61
|
"start-server-and-test": "2.0.3",
|
|
60
62
|
"tailwind-merge": "1.8.0",
|
|
61
|
-
"tailwindcss": "4.0.0",
|
|
62
63
|
"yup": "0.32.11"
|
|
63
64
|
},
|
|
64
65
|
"devDependencies": {
|
|
65
|
-
"@akinon/eslint-plugin-projectzero": "2.0.0-beta.
|
|
66
|
+
"@akinon/eslint-plugin-projectzero": "2.0.0-beta.8",
|
|
66
67
|
"@semantic-release/changelog": "6.0.2",
|
|
67
68
|
"@semantic-release/exec": "6.0.3",
|
|
68
69
|
"@semantic-release/git": "10.0.1",
|
|
@@ -87,6 +88,7 @@
|
|
|
87
88
|
"husky": "8.0.0",
|
|
88
89
|
"jest": "29.7.0",
|
|
89
90
|
"jest-css-modules-transform": "4.3.0",
|
|
91
|
+
"jest-environment-jsdom": "29.7.0",
|
|
90
92
|
"lint-staged": "13.1.0",
|
|
91
93
|
"prettier": "3.4.2",
|
|
92
94
|
"react-number-format": "5.4.2",
|
|
@@ -98,7 +100,7 @@
|
|
|
98
100
|
"stylelint-config-standard": "25.0.0",
|
|
99
101
|
"stylelint-scss": "4.2.0",
|
|
100
102
|
"stylelint-selector-bem-pattern": "2.1.1",
|
|
101
|
-
"tailwindcss": "4.
|
|
103
|
+
"tailwindcss": "4.1.3",
|
|
102
104
|
"ts-jest": "29.1.1",
|
|
103
105
|
"ts-node": "10.7.0",
|
|
104
106
|
"typescript": "5.7.2"
|
|
@@ -254,6 +254,10 @@
|
|
|
254
254
|
"cancelled": "Cancelled",
|
|
255
255
|
"cancellation_request_recieved": "Cancellation Request Recieved",
|
|
256
256
|
"close_button": "Close",
|
|
257
|
+
"return_status": "Return Status",
|
|
258
|
+
"rejected": "Return Rejected",
|
|
259
|
+
"completed": "Return Completed",
|
|
260
|
+
"explanation": "Explanation",
|
|
257
261
|
"success": {
|
|
258
262
|
"title": "Success",
|
|
259
263
|
"description": "Your request have been recieved and will be evaluated as soon as possible."
|
|
@@ -38,6 +38,12 @@
|
|
|
38
38
|
"description": "Please try again later.",
|
|
39
39
|
"link_text": "Return to home page"
|
|
40
40
|
},
|
|
41
|
+
"client_error": {
|
|
42
|
+
"title": "We encountered a problem with the page.",
|
|
43
|
+
"description": "This appears to be a client-side issue. Please try refreshing the page or clearing your browser cache.",
|
|
44
|
+
"link_text": "Return to home page"
|
|
45
|
+
},
|
|
46
|
+
"try_again": "Try Again",
|
|
41
47
|
"breadcrumb": {
|
|
42
48
|
"homepage": "Homepage"
|
|
43
49
|
},
|
|
@@ -254,6 +254,10 @@
|
|
|
254
254
|
"cancelled": "İptal edildi",
|
|
255
255
|
"cancellation_request_recieved": "İptal talebi alındı",
|
|
256
256
|
"close_button": "Kapat",
|
|
257
|
+
"return_status": "İade Durumu",
|
|
258
|
+
"rejected": "İade reddedildi",
|
|
259
|
+
"completed": "İade tamamlandı",
|
|
260
|
+
"explanation": "Açıklama",
|
|
257
261
|
"success": {
|
|
258
262
|
"title": "Başarılı",
|
|
259
263
|
"description": "Talebiniz alınmış olup en kısa sürede değerlendirilecektir."
|
|
@@ -38,6 +38,12 @@
|
|
|
38
38
|
"description": "Lütfen daha sonra tekrar deneyiniz.",
|
|
39
39
|
"link_text": "Ana sayfaya dön"
|
|
40
40
|
},
|
|
41
|
+
"client_error": {
|
|
42
|
+
"title": "Sayfada bir sorunla karşılaştık.",
|
|
43
|
+
"description": "Bu bir tarayıcı hatası gibi görünüyor. Lütfen sayfayı yenileyin veya tarayıcı önbelleğinizi temizleyin.",
|
|
44
|
+
"link_text": "Ana sayfaya dön"
|
|
45
|
+
},
|
|
46
|
+
"try_again": "Tekrar Dene",
|
|
41
47
|
"breadcrumb": {
|
|
42
48
|
"homepage": "Anasayfa"
|
|
43
49
|
},
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
|
|
4
|
+
describe('Middleware matcher regex tests', () => {
|
|
5
|
+
const middlewareFilePath = path.resolve(__dirname, '../middleware.ts');
|
|
6
|
+
const middlewareContent = fs.readFileSync(middlewareFilePath, 'utf8');
|
|
7
|
+
|
|
8
|
+
let actualMatcherStrings: string[] = [];
|
|
9
|
+
let matcherPatterns: RegExp[] = [];
|
|
10
|
+
|
|
11
|
+
const matcherBlockRegex = middlewareContent.match(/matcher:\s*\[([\s\S]*?)\](?=\s*[,}])/);
|
|
12
|
+
|
|
13
|
+
if (matcherBlockRegex && matcherBlockRegex[1]) {
|
|
14
|
+
const matcherContentInsideBrackets = matcherBlockRegex[1];
|
|
15
|
+
|
|
16
|
+
actualMatcherStrings = matcherContentInsideBrackets
|
|
17
|
+
.split(',')
|
|
18
|
+
.map(line => {
|
|
19
|
+
const uncommentedLine = line.replace(/\/\/.*$/, '').trim();
|
|
20
|
+
const quoteMatch = uncommentedLine.match(/^(['"])(.*)\1$/);
|
|
21
|
+
return quoteMatch ? quoteMatch[2] : null;
|
|
22
|
+
})
|
|
23
|
+
.filter((pattern): pattern is string => pattern !== null && pattern !== '');
|
|
24
|
+
|
|
25
|
+
matcherPatterns = matcherContentInsideBrackets
|
|
26
|
+
.split(',')
|
|
27
|
+
.map((pattern) => pattern.trim())
|
|
28
|
+
.filter(Boolean)
|
|
29
|
+
.map((pattern) => {
|
|
30
|
+
let cleanPattern = pattern
|
|
31
|
+
.replace(/^['"]|['"]$/g, '');
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
if (cleanPattern.includes('(?!') && cleanPattern.includes('api') && cleanPattern.includes('_next')) {
|
|
35
|
+
cleanPattern = '^(?!/(?:api|_next)/)(?!.*\\.\\w+$).*$';
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return new RegExp(cleanPattern);
|
|
39
|
+
} catch (error) {
|
|
40
|
+
console.error(`Invalid simplified regex: ${cleanPattern}`, error);
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
})
|
|
44
|
+
.filter(Boolean) as RegExp[];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const testPath = (path: string): boolean => {
|
|
48
|
+
return matcherPatterns.some((pattern) => {
|
|
49
|
+
try {
|
|
50
|
+
const result = pattern.test(path);
|
|
51
|
+
return result;
|
|
52
|
+
} catch (error) {
|
|
53
|
+
console.error(
|
|
54
|
+
`Error testing path: ${path} | Pattern: ${pattern.toString()}`,
|
|
55
|
+
error
|
|
56
|
+
);
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
it('should NOT match api routes', () => {
|
|
63
|
+
const apiPaths = ['/api/products', '/api/auth/login', '/api/v1/users'];
|
|
64
|
+
apiPaths.forEach((path) => {
|
|
65
|
+
expect(testPath(path)).toBe(false);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('should NOT match _next routes', () => {
|
|
70
|
+
const nextPaths = [
|
|
71
|
+
'/_next/static/chunks/main.js',
|
|
72
|
+
'/_next/image',
|
|
73
|
+
'/_next/data/build-id/products.json'
|
|
74
|
+
];
|
|
75
|
+
nextPaths.forEach((path) => {
|
|
76
|
+
expect(testPath(path)).toBe(false);
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('should NOT match static files with extensions', () => {
|
|
81
|
+
const staticFiles = [
|
|
82
|
+
'/images/logo.png',
|
|
83
|
+
'/styles/main.css',
|
|
84
|
+
'/fonts/roboto.woff2',
|
|
85
|
+
'/favicon.ico',
|
|
86
|
+
'/manifest.json'
|
|
87
|
+
];
|
|
88
|
+
staticFiles.forEach((path) => {
|
|
89
|
+
expect(testPath(path)).toBe(false);
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('should match dynamic routes and specific patterns', () => {
|
|
94
|
+
const validPaths = [
|
|
95
|
+
'/profile/settings',
|
|
96
|
+
'/dashboard/stats',
|
|
97
|
+
'/products/123'
|
|
98
|
+
];
|
|
99
|
+
validPaths.forEach((path) => {
|
|
100
|
+
expect(testPath(path)).toBe(true);
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('should match checkout-with-token routes', () => {
|
|
105
|
+
const expectedRegexString = '\'/(.*orders\\\\/checkout-with-token.*)\'';
|
|
106
|
+
expect(middlewareContent.includes(expectedRegexString)).toBe(true);
|
|
107
|
+
|
|
108
|
+
const checkoutPaths = [
|
|
109
|
+
'/orders/checkout-with-token/123',
|
|
110
|
+
'/orders/checkout-with-token/abc-xyz',
|
|
111
|
+
'/orders/checkout-with-token'
|
|
112
|
+
];
|
|
113
|
+
checkoutPaths.forEach((path) => {
|
|
114
|
+
expect(testPath(path)).toBe(true);
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('should contain the exact specific extensions regex string in the file content', () => {
|
|
119
|
+
const expectedRegexString = '\'/(.+\\\\.)(html|htm|aspx|asp|php)\'';
|
|
120
|
+
expect(middlewareContent.includes(expectedRegexString)).toBe(true);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('should include the sitemap pattern specifically within the matcher array', () => {
|
|
124
|
+
const sitemapPattern = '/(.*sitemap\\\\.xml)';
|
|
125
|
+
expect(actualMatcherStrings).toContain(sitemapPattern);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it('should verify that api pattern is excluded in the matcher configuration', () => {
|
|
129
|
+
expect(/api/.test(middlewareContent)).toBe(true);
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('should verify that _next pattern is excluded in the matcher configuration', () => {
|
|
133
|
+
expect(/_next/.test(middlewareContent)).toBe(true);
|
|
134
|
+
});
|
|
135
|
+
});
|
|
@@ -208,8 +208,7 @@ const AccountOrderDetail = ({ params }) => {
|
|
|
208
208
|
</div>
|
|
209
209
|
|
|
210
210
|
{(item.is_cancellable || item.is_refundable) &&
|
|
211
|
-
order.is_cancellable &&
|
|
212
|
-
item.status.value == '400' && (
|
|
211
|
+
order.is_cancellable && (
|
|
213
212
|
<div className="lg:ml-24">
|
|
214
213
|
<Link
|
|
215
214
|
href={`${ROUTES.ACCOUNT_ORDERS}/${order.id}/cancellation`}
|
|
@@ -227,7 +226,6 @@ const AccountOrderDetail = ({ params }) => {
|
|
|
227
226
|
</div>
|
|
228
227
|
)}
|
|
229
228
|
</div>
|
|
230
|
-
|
|
231
229
|
<div className="flex flex-col justify-center items-end lg:ml-6 lg:min-w-[7rem]">
|
|
232
230
|
{parseFloat(item.retail_price) >
|
|
233
231
|
parseFloat(item.price) && (
|
|
@@ -251,6 +249,70 @@ const AccountOrderDetail = ({ params }) => {
|
|
|
251
249
|
);
|
|
252
250
|
})}
|
|
253
251
|
</div>
|
|
252
|
+
|
|
253
|
+
{group.map((item) =>
|
|
254
|
+
item.cancellationrequest_set?.map((cancellationItem) => {
|
|
255
|
+
const status = cancellationItem.status?.value;
|
|
256
|
+
const isRejected = status === 'rejected';
|
|
257
|
+
const isCompleted = status === 'completed';
|
|
258
|
+
|
|
259
|
+
const filteredImages =
|
|
260
|
+
cancellationItem.cancellation_request_image_set?.filter(
|
|
261
|
+
(img) =>
|
|
262
|
+
isRejected
|
|
263
|
+
? !img.is_uploaded_by_user
|
|
264
|
+
: isCompleted
|
|
265
|
+
? img.is_uploaded_by_user
|
|
266
|
+
: false
|
|
267
|
+
) || [];
|
|
268
|
+
|
|
269
|
+
const statusText = isRejected
|
|
270
|
+
? t('account.my_orders.return.rejected')
|
|
271
|
+
: isCompleted
|
|
272
|
+
? t('account.my_orders.return.completed')
|
|
273
|
+
: null;
|
|
274
|
+
|
|
275
|
+
if (!statusText || filteredImages.length === 0) return null;
|
|
276
|
+
|
|
277
|
+
return (
|
|
278
|
+
<div
|
|
279
|
+
className="w-full px-4 lg:px-7"
|
|
280
|
+
key={cancellationItem.id}
|
|
281
|
+
>
|
|
282
|
+
<div className="flex flex-col py-2 gap-4 border-t border-gray">
|
|
283
|
+
<div className="flex flex-col">
|
|
284
|
+
<div className="text-sm font-semibold">
|
|
285
|
+
{t('account.my_orders.return.return_status')}
|
|
286
|
+
<span className="font-normal"> {statusText}</span>
|
|
287
|
+
</div>
|
|
288
|
+
|
|
289
|
+
<div className="flex gap-2 mt-2 flex-wrap">
|
|
290
|
+
{filteredImages.map((img) => (
|
|
291
|
+
<div className="flex flex-col gap-2" key={img.id}>
|
|
292
|
+
<Link href={img.image} target="_blank">
|
|
293
|
+
<Image
|
|
294
|
+
src={img.image}
|
|
295
|
+
width={112}
|
|
296
|
+
height={150}
|
|
297
|
+
alt={img.description}
|
|
298
|
+
/>
|
|
299
|
+
</Link>
|
|
300
|
+
|
|
301
|
+
{img.description && (
|
|
302
|
+
<p className="text-xs">
|
|
303
|
+
{t('account.my_orders.return.explanation')}:
|
|
304
|
+
{img.description}
|
|
305
|
+
</p>
|
|
306
|
+
)}
|
|
307
|
+
</div>
|
|
308
|
+
))}
|
|
309
|
+
</div>
|
|
310
|
+
</div>
|
|
311
|
+
</div>
|
|
312
|
+
</div>
|
|
313
|
+
);
|
|
314
|
+
})
|
|
315
|
+
)}
|
|
254
316
|
</div>
|
|
255
317
|
);
|
|
256
318
|
})}
|
|
@@ -1,20 +1,17 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
5
|
-
import { ROUTES } from '@theme/routes';
|
|
3
|
+
import { useSentryUncaughtErrors } from '@akinon/next/hooks';
|
|
4
|
+
import PzErrorPage from '@akinon/next/views/error-page';
|
|
6
5
|
|
|
7
|
-
export default function
|
|
8
|
-
|
|
6
|
+
export default function ErrorPage({
|
|
7
|
+
error,
|
|
8
|
+
reset
|
|
9
|
+
}: {
|
|
10
|
+
error: Error & { digest?: string; isServerError?: boolean };
|
|
11
|
+
reset: () => void;
|
|
12
|
+
}) {
|
|
13
|
+
// DO NOT REMOVE THIS LINE TO REPORT UNCAUGHT ERRORS TO SENTRY
|
|
14
|
+
useSentryUncaughtErrors(error);
|
|
9
15
|
|
|
10
|
-
return
|
|
11
|
-
<section className="text-center px-6 my-14 md:px-0 md:m-14">
|
|
12
|
-
<div className="text-7xl font-bold md:text-8xl">500</div>
|
|
13
|
-
<h1 className="text-lg md:text-xl"> {t('common.page_500.title')} </h1>
|
|
14
|
-
<p className="text-lg md:text-xl"> {t('common.page_500.description')} </p>
|
|
15
|
-
<Link href={ROUTES.HOME} className="text-lg underline">
|
|
16
|
-
{t('common.page_500.link_text')}
|
|
17
|
-
</Link>
|
|
18
|
-
</section>
|
|
19
|
-
);
|
|
16
|
+
return <PzErrorPage error={error} reset={reset} />;
|
|
20
17
|
}
|
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
import { urlLocaleMatcherRegex } from '@akinon/next/utils';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* XML Sitemap Route
|
|
5
|
+
*
|
|
6
|
+
* This route serves XML sitemaps from an S3 bucket.
|
|
7
|
+
*
|
|
8
|
+
* Required environment variables:
|
|
9
|
+
* - SITEMAP_S3_BUCKET_NAME: The name of the S3 bucket containing the sitemaps
|
|
10
|
+
* Example: "0fb534"
|
|
11
|
+
*
|
|
12
|
+
* If the environment variable is not set, the route will return a 503 Service Unavailable
|
|
13
|
+
* response with a JSON error message.
|
|
14
|
+
*/
|
|
15
|
+
|
|
3
16
|
export const dynamic = 'force-dynamic';
|
|
4
17
|
|
|
5
18
|
export async function GET(request: Request, context: { params }) {
|
|
@@ -7,9 +20,42 @@ export async function GET(request: Request, context: { params }) {
|
|
|
7
20
|
const url = new URL(request.url);
|
|
8
21
|
const matchedLocale = url.pathname.match(urlLocaleMatcherRegex);
|
|
9
22
|
|
|
23
|
+
const s3BucketName = process.env.SITEMAP_S3_BUCKET_NAME;
|
|
24
|
+
|
|
25
|
+
if (!s3BucketName) {
|
|
26
|
+
return new Response(
|
|
27
|
+
JSON.stringify({
|
|
28
|
+
error: 'Configuration error',
|
|
29
|
+
message: 'Please set the SITEMAP_S3_BUCKET_NAME environment variable'
|
|
30
|
+
}),
|
|
31
|
+
{
|
|
32
|
+
status: 503,
|
|
33
|
+
headers: {
|
|
34
|
+
'Content-Type': 'application/json'
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
10
40
|
const sitemap = await fetch(
|
|
11
|
-
`https://s3.eu-central-1.amazonaws.com/
|
|
41
|
+
`https://s3.eu-central-1.amazonaws.com/${s3BucketName}/sitemaps/sitemaps/sitemap-${node}.xml.gz`
|
|
12
42
|
);
|
|
43
|
+
|
|
44
|
+
if (!sitemap.ok) {
|
|
45
|
+
return new Response(
|
|
46
|
+
JSON.stringify({
|
|
47
|
+
error: 'Sitemap not found',
|
|
48
|
+
message: `Failed to fetch sitemap for node: ${node}`
|
|
49
|
+
}),
|
|
50
|
+
{
|
|
51
|
+
status: 503,
|
|
52
|
+
headers: {
|
|
53
|
+
'Content-Type': 'application/json'
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
13
59
|
let sitemapContent = await sitemap.text();
|
|
14
60
|
|
|
15
61
|
sitemapContent = sitemapContent.replace(
|
|
@@ -39,7 +39,7 @@ export const Tabs = (props: Props) => {
|
|
|
39
39
|
tabBarPosition === 'left' || tabBarPosition === 'right'
|
|
40
40
|
})}
|
|
41
41
|
>
|
|
42
|
-
{children
|
|
42
|
+
{children?.map((item, index) => (
|
|
43
43
|
<Tab
|
|
44
44
|
key={item.props.title}
|
|
45
45
|
title={item.props.title}
|
|
@@ -52,7 +52,7 @@ export const Tabs = (props: Props) => {
|
|
|
52
52
|
))}
|
|
53
53
|
</ul>
|
|
54
54
|
|
|
55
|
-
<div className="w-full">{children[selectedTabIndex]}</div>
|
|
55
|
+
<div className="w-full">{children?.[selectedTabIndex]}</div>
|
|
56
56
|
</div>
|
|
57
57
|
);
|
|
58
58
|
};
|
|
@@ -32,7 +32,7 @@ const categorySlice = createSlice({
|
|
|
32
32
|
toggleFacet(state, action) {
|
|
33
33
|
const facets = JSON.parse(JSON.stringify(state.facets));
|
|
34
34
|
|
|
35
|
-
state.selectedFacets = facets
|
|
35
|
+
state.selectedFacets = facets?.map((facet) => {
|
|
36
36
|
if (facet.key === action.payload.facet.key) {
|
|
37
37
|
facet.data.choices = facet.data.choices
|
|
38
38
|
.map((choice) => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Facet } from '@akinon/next/types';
|
|
2
2
|
|
|
3
3
|
const convertFacetSearchParams = (facets: Facet[]) => {
|
|
4
|
-
const _facets: string[][] = facets
|
|
4
|
+
const _facets: string[][] = facets?.flatMap((facet) => {
|
|
5
5
|
return [
|
|
6
6
|
...facet.data.choices.map((choice) => {
|
|
7
7
|
return [facet.search_key, choice.value as string];
|
|
@@ -94,25 +94,6 @@ export const OrderCancellationItem = ({
|
|
|
94
94
|
{selectOption}
|
|
95
95
|
</div>
|
|
96
96
|
<div>{fileInput}</div>
|
|
97
|
-
{item.active_cancellation_request?.cancellation_request_image_set && (
|
|
98
|
-
<div className="flex flex-wrap gap-2">
|
|
99
|
-
{item.active_cancellation_request?.cancellation_request_image_set?.map(
|
|
100
|
-
(item) => {
|
|
101
|
-
return (
|
|
102
|
-
<div className="relative w-16 h-16" key={item.id}>
|
|
103
|
-
<Image
|
|
104
|
-
src={item.image}
|
|
105
|
-
alt={item.description}
|
|
106
|
-
aspectRatio={1}
|
|
107
|
-
sizes="64px"
|
|
108
|
-
fill
|
|
109
|
-
/>
|
|
110
|
-
</div>
|
|
111
|
-
);
|
|
112
|
-
}
|
|
113
|
-
)}
|
|
114
|
-
</div>
|
|
115
|
-
)}
|
|
116
97
|
</div>
|
|
117
98
|
</div>
|
|
118
99
|
);
|
|
@@ -87,6 +87,7 @@ export const BasketItem = (props: Props) => {
|
|
|
87
87
|
<li
|
|
88
88
|
key={basketItem.id}
|
|
89
89
|
className="flex border-b border-gray-200 py-3 relative"
|
|
90
|
+
data-testid="basket-item"
|
|
90
91
|
>
|
|
91
92
|
<div className="w-20 lg:w-[105px] mr-4 shrink-0">
|
|
92
93
|
<Link href={basketItem.product.absolute_url} passHref>
|
|
@@ -69,7 +69,7 @@ const CategoryActiveFilters = () => {
|
|
|
69
69
|
|
|
70
70
|
return (
|
|
71
71
|
<div className="grid grid-cols-2 md:grid-cols-4 lg:grid-cols-6 gap-2 mb-4">
|
|
72
|
-
{facets
|
|
72
|
+
{facets?.map((facet) =>
|
|
73
73
|
facet?.data?.choices
|
|
74
74
|
?.filter((choice) => choice.is_selected)
|
|
75
75
|
?.map(
|
|
@@ -121,7 +121,7 @@ export const CategoryHeader = (props: Props) => {
|
|
|
121
121
|
</Button>
|
|
122
122
|
<Select
|
|
123
123
|
options={sortOptions}
|
|
124
|
-
value={sortOptions
|
|
124
|
+
value={sortOptions?.find(({ is_selected }) => is_selected)?.value}
|
|
125
125
|
data-testid="list-sorter"
|
|
126
126
|
onChange={(e) => {
|
|
127
127
|
handleSelectFilter({
|
|
@@ -102,7 +102,7 @@ export default function ListPage(props: ListPageProps) {
|
|
|
102
102
|
</div>
|
|
103
103
|
)}
|
|
104
104
|
|
|
105
|
-
{data.products
|
|
105
|
+
{data.products?.length === 0 && page > 1 && <LoaderSpinner />}
|
|
106
106
|
|
|
107
107
|
<div
|
|
108
108
|
className={clsx('grid gap-x-4 gap-y-12 grid-cols-2', {
|
|
@@ -111,7 +111,7 @@ export default function ListPage(props: ListPageProps) {
|
|
|
111
111
|
'lg:grid-cols-3': layoutSize === 3
|
|
112
112
|
})}
|
|
113
113
|
>
|
|
114
|
-
{data
|
|
114
|
+
{data?.products?.map((product, index) => (
|
|
115
115
|
<ProductItem
|
|
116
116
|
key={product.pk}
|
|
117
117
|
product={product}
|
|
@@ -121,7 +121,7 @@ export default function ListPage(props: ListPageProps) {
|
|
|
121
121
|
/>
|
|
122
122
|
))}
|
|
123
123
|
</div>
|
|
124
|
-
{data
|
|
124
|
+
{data?.products?.length > 0 && (
|
|
125
125
|
<Pagination
|
|
126
126
|
total={data.pagination.total_count}
|
|
127
127
|
limit={data.pagination.page_size}
|
|
@@ -23,7 +23,7 @@ export const Filters = (props: Props) => {
|
|
|
23
23
|
const [isPending, startTransition] = useTransition();
|
|
24
24
|
|
|
25
25
|
const haveFilter = useMemo(() => {
|
|
26
|
-
return facets
|
|
26
|
+
return facets?.some((facet) =>
|
|
27
27
|
facet?.data?.choices?.some((choice) => choice.is_selected)
|
|
28
28
|
);
|
|
29
29
|
}, [facets]);
|
|
@@ -50,7 +50,7 @@ export const Filters = (props: Props) => {
|
|
|
50
50
|
<span>{t('category.filters.ready_to_wear')}</span>
|
|
51
51
|
</div>
|
|
52
52
|
|
|
53
|
-
{facets
|
|
53
|
+
{facets?.map((facet) => {
|
|
54
54
|
return (
|
|
55
55
|
<FilterItem
|
|
56
56
|
key={facet.key}
|
|
@@ -18,7 +18,7 @@ const PaymentStep = () => {
|
|
|
18
18
|
'pointer-events-none opacity-30': isPaymentStepBusy
|
|
19
19
|
})}
|
|
20
20
|
>
|
|
21
|
-
<div className="w-full mt-4 flex justify-start border border-gray-400 -mb-px z-10 md:mt-0 md:border-r-0 md:border-b-0 md:w-auto order-2 md:order-none">
|
|
21
|
+
<div className="w-full mt-4 flex justify-start border border-gray-400 -mb-px z-10 md:mt-0 md:border-r-0 md:border-b-0 md:w-auto order-2 md:order-none overflow-x-auto">
|
|
22
22
|
<PaymentOptionButtons />
|
|
23
23
|
</div>
|
|
24
24
|
<div className="w-full border border-solid border-gray-400 bg-white">
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { ReactNode, useMemo, useRef, useCallback } from 'react';
|
|
4
|
-
import {
|
|
4
|
+
import { useGetMiniBasketQuery } from '@akinon/next/data/client/basket';
|
|
5
5
|
import { useAppDispatch, useAppSelector } from '@akinon/next/redux/hooks';
|
|
6
6
|
import {
|
|
7
7
|
closeMiniBasket,
|
|
@@ -29,8 +29,11 @@ interface MenuItem {
|
|
|
29
29
|
export default function ActionMenu() {
|
|
30
30
|
const dispatch = useAppDispatch();
|
|
31
31
|
|
|
32
|
-
const { data } =
|
|
33
|
-
const totalQuantity = useMemo(
|
|
32
|
+
const { data: miniBasket } = useGetMiniBasketQuery();
|
|
33
|
+
const totalQuantity = useMemo(
|
|
34
|
+
() => miniBasket?.total_quantity ?? 0,
|
|
35
|
+
[miniBasket]
|
|
36
|
+
);
|
|
34
37
|
|
|
35
38
|
const { open: miniBasketOpen } = useAppSelector(
|
|
36
39
|
(state) => state.root.miniBasket
|
|
@@ -5,6 +5,7 @@ import clsx from 'clsx';
|
|
|
5
5
|
import {
|
|
6
6
|
basketApi,
|
|
7
7
|
useGetBasketQuery,
|
|
8
|
+
useGetMiniBasketQuery,
|
|
8
9
|
useUpdateQuantityMutation
|
|
9
10
|
} from '@akinon/next/data/client/basket';
|
|
10
11
|
import { useAppDispatch, useAppSelector } from '@akinon/next/redux/hooks';
|
|
@@ -150,13 +151,23 @@ export default function MiniBasket() {
|
|
|
150
151
|
(state) => state.root.miniBasket
|
|
151
152
|
);
|
|
152
153
|
const dispatch = useAppDispatch();
|
|
153
|
-
const {
|
|
154
|
+
const {
|
|
155
|
+
data: basket,
|
|
156
|
+
isLoading,
|
|
157
|
+
isSuccess
|
|
158
|
+
} = useGetBasketQuery(undefined, {
|
|
159
|
+
skip: !miniBasketOpen
|
|
160
|
+
});
|
|
161
|
+
const { data: miniBasket } = useGetMiniBasketQuery();
|
|
154
162
|
const { t } = useLocalization();
|
|
155
163
|
const { highlightedItem } = useAppSelector((state) => state.root.miniBasket);
|
|
156
164
|
const [highlightedItemPk, setHighlightedItemPk] = useState(0);
|
|
157
165
|
const [sortedBasket, setSortedBasket] = useState([]);
|
|
158
166
|
|
|
159
|
-
const totalQuantity = useMemo(
|
|
167
|
+
const totalQuantity = useMemo(
|
|
168
|
+
() => miniBasket?.total_quantity ?? 0,
|
|
169
|
+
[miniBasket]
|
|
170
|
+
);
|
|
160
171
|
const miniBasketList = useRef(null);
|
|
161
172
|
|
|
162
173
|
useEffect(() => {
|
|
@@ -10,9 +10,10 @@ import { yupResolver } from '@hookform/resolvers/yup';
|
|
|
10
10
|
import clsx from 'clsx';
|
|
11
11
|
import { useLocalization } from '@akinon/next/hooks';
|
|
12
12
|
import { useOtpLoginMutation } from '@akinon/next/data/client/user';
|
|
13
|
-
import { useAppSelector } from '@akinon/next/redux/hooks';
|
|
13
|
+
import { useAppDispatch, useAppSelector } from '@akinon/next/redux/hooks';
|
|
14
14
|
import PluginModule, { Component } from '@akinon/next/components/plugin-module';
|
|
15
15
|
import { AuthError } from '@akinon/next/types';
|
|
16
|
+
import { showPopup } from '@akinon/pz-otp/src/redux/reducer';
|
|
16
17
|
|
|
17
18
|
const loginFormSchema = (t) =>
|
|
18
19
|
yup.object().shape({
|
|
@@ -25,9 +26,9 @@ const loginFormSchema = (t) =>
|
|
|
25
26
|
});
|
|
26
27
|
|
|
27
28
|
export const OtpLogin = () => {
|
|
29
|
+
const dispatch = useAppDispatch();
|
|
28
30
|
const { user_phone_format } = useAppSelector((state) => state.config);
|
|
29
31
|
const { t, locale } = useLocalization();
|
|
30
|
-
const [showOtpModal, setShowOtpModal] = useState(false);
|
|
31
32
|
const [otpLoginMutation] = useOtpLoginMutation();
|
|
32
33
|
|
|
33
34
|
const {
|
|
@@ -79,7 +80,7 @@ export const OtpLogin = () => {
|
|
|
79
80
|
})
|
|
80
81
|
.unwrap()
|
|
81
82
|
.then(() => {
|
|
82
|
-
|
|
83
|
+
dispatch(showPopup());
|
|
83
84
|
})
|
|
84
85
|
.catch((error) => {
|
|
85
86
|
if (error.status === 429) {
|
|
@@ -136,17 +137,14 @@ export const OtpLogin = () => {
|
|
|
136
137
|
</Button>
|
|
137
138
|
</form>
|
|
138
139
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
}}
|
|
148
|
-
/>
|
|
149
|
-
)}
|
|
140
|
+
<PluginModule
|
|
141
|
+
component={Component.Otp}
|
|
142
|
+
props={{
|
|
143
|
+
data: getValues(),
|
|
144
|
+
submitAction: loginHandler,
|
|
145
|
+
error: formError
|
|
146
|
+
}}
|
|
147
|
+
/>
|
|
150
148
|
</section>
|
|
151
149
|
);
|
|
152
150
|
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const { execSync } = require('child_process');
|
|
3
|
+
|
|
4
|
+
const codemodScripts = [
|
|
5
|
+
path.resolve(__dirname, 'remove-sentry-dependency.js'),
|
|
6
|
+
path.resolve(__dirname, 'remove-sentry-configs.js'),
|
|
7
|
+
path.resolve(__dirname, 'replace-error-page.js')
|
|
8
|
+
];
|
|
9
|
+
|
|
10
|
+
const transform = () => {
|
|
11
|
+
const workingDir = path.resolve(process.cwd());
|
|
12
|
+
|
|
13
|
+
codemodScripts.forEach((script) => {
|
|
14
|
+
try {
|
|
15
|
+
execSync(
|
|
16
|
+
`jscodeshift --ignore-pattern="**/node_modules/**" -t ${script} ${workingDir} --extensions=json,ts,tsx,js,jsx,properties,md`,
|
|
17
|
+
{
|
|
18
|
+
cwd: workingDir,
|
|
19
|
+
stdio: 'inherit'
|
|
20
|
+
}
|
|
21
|
+
);
|
|
22
|
+
} catch (e) {
|
|
23
|
+
console.error(e);
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
module.exports = {
|
|
29
|
+
transform
|
|
30
|
+
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
|
|
3
|
+
const transform = (fileInfo, api, options) => {
|
|
4
|
+
const filePath = fileInfo.path;
|
|
5
|
+
const regex = /sentry\.\w+\.config\.(ts|js)$|sentry\.properties$/i;
|
|
6
|
+
|
|
7
|
+
if (regex.test(filePath) && fs.existsSync(filePath)) {
|
|
8
|
+
fs.unlinkSync(filePath);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
return fileInfo.source;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
module.exports = transform;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
const transform = (fileInfo, api, options) => {
|
|
2
|
+
if (fileInfo.path.endsWith('package.json')) {
|
|
3
|
+
const packageJson = JSON.parse(fileInfo.source);
|
|
4
|
+
|
|
5
|
+
if (
|
|
6
|
+
packageJson.dependencies &&
|
|
7
|
+
packageJson.dependencies['@sentry/nextjs']
|
|
8
|
+
) {
|
|
9
|
+
delete packageJson.dependencies['@sentry/nextjs'];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
if (
|
|
13
|
+
packageJson.devDependencies &&
|
|
14
|
+
packageJson.devDependencies['@sentry/nextjs']
|
|
15
|
+
) {
|
|
16
|
+
delete packageJson.devDependencies['@sentry/nextjs'];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return JSON.stringify(packageJson, null, 2);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return fileInfo.source;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
module.exports = transform;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
|
|
3
|
+
const template = `
|
|
4
|
+
'use client';
|
|
5
|
+
|
|
6
|
+
import { useSentryUncaughtErrors } from '@akinon/next/hooks';
|
|
7
|
+
import PzErrorPage from '@akinon/next/views/error-page';
|
|
8
|
+
|
|
9
|
+
export default function ErrorPage({
|
|
10
|
+
error,
|
|
11
|
+
reset
|
|
12
|
+
}: {
|
|
13
|
+
error: Error & { digest?: string; isServerError?: boolean };
|
|
14
|
+
reset: () => void;
|
|
15
|
+
}) {
|
|
16
|
+
// DO NOT REMOVE THIS LINE TO REPORT UNCAUGHT ERRORS TO SENTRY
|
|
17
|
+
useSentryUncaughtErrors(error);
|
|
18
|
+
|
|
19
|
+
return <PzErrorPage error={error} reset={reset} />;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
`;
|
|
23
|
+
|
|
24
|
+
const transform = (fileInfo, api, options) => {
|
|
25
|
+
const filePath = fileInfo.path;
|
|
26
|
+
|
|
27
|
+
if (filePath.endsWith('error.tsx')) {
|
|
28
|
+
fs.writeFileSync(filePath, template, { encoding: 'utf8' });
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
module.exports = transform;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
|
|
3
|
+
const yargs = require('yargs/yargs');
|
|
4
|
+
const { hideBin } = require('yargs/helpers');
|
|
5
|
+
const args = yargs(hideBin(process.argv)).argv;
|
|
6
|
+
|
|
7
|
+
export default () => {
|
|
8
|
+
const workingDir = path.resolve(process.cwd());
|
|
9
|
+
const codemodName = args.codemod;
|
|
10
|
+
const codemodPath = path.resolve(
|
|
11
|
+
__dirname,
|
|
12
|
+
`../../codemods/${codemodName}/index.js`
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
const codemod = require(codemodPath);
|
|
16
|
+
|
|
17
|
+
codemod.transform();
|
|
18
|
+
};
|
package/commands/index.ts
CHANGED
|
@@ -4,6 +4,7 @@ import addLanguage from './add-language';
|
|
|
4
4
|
import removeLanguage from './remove-language';
|
|
5
5
|
import defaultLanguage from './default-language';
|
|
6
6
|
import plugins from './plugins';
|
|
7
|
+
import codemod from './codemod';
|
|
7
8
|
|
|
8
9
|
export default {
|
|
9
10
|
commerceUrl,
|
|
@@ -11,5 +12,6 @@ export default {
|
|
|
11
12
|
addLanguage,
|
|
12
13
|
removeLanguage,
|
|
13
14
|
defaultLanguage,
|
|
14
|
-
plugins
|
|
15
|
+
plugins,
|
|
16
|
+
codemod
|
|
15
17
|
};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
'use client';
|
|
3
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
4
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
|
+
};
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
const hooks_1 = require("@akinon/next/hooks");
|
|
8
|
+
const error_page_1 = __importDefault(require("@akinon/next/views/error-page"));
|
|
9
|
+
function ErrorPage({ error, reset }) {
|
|
10
|
+
// DO NOT REMOVE THIS LINE TO REPORT UNCAUGHT ERRORS TO SENTRY
|
|
11
|
+
(0, hooks_1.useSentryUncaughtErrors)(error);
|
|
12
|
+
return React.createElement(error_page_1.default, { error: error, reset: reset });
|
|
13
|
+
}
|
|
14
|
+
exports.default = ErrorPage;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const path_1 = __importDefault(require("path"));
|
|
7
|
+
const yargs = require('yargs/yargs');
|
|
8
|
+
const { hideBin } = require('yargs/helpers');
|
|
9
|
+
const args = yargs(hideBin(process.argv)).argv;
|
|
10
|
+
exports.default = () => {
|
|
11
|
+
const workingDir = path_1.default.resolve(process.cwd());
|
|
12
|
+
const codemodName = args.codemod;
|
|
13
|
+
const codemodPath = path_1.default.resolve(__dirname, `../../codemods/${codemodName}/index.js`);
|
|
14
|
+
const codemod = require(codemodPath);
|
|
15
|
+
codemod.transform();
|
|
16
|
+
};
|
package/dist/commands/index.js
CHANGED
|
@@ -9,11 +9,13 @@ const add_language_1 = __importDefault(require("./add-language"));
|
|
|
9
9
|
const remove_language_1 = __importDefault(require("./remove-language"));
|
|
10
10
|
const default_language_1 = __importDefault(require("./default-language"));
|
|
11
11
|
const plugins_1 = __importDefault(require("./plugins"));
|
|
12
|
+
const codemod_1 = __importDefault(require("./codemod"));
|
|
12
13
|
exports.default = {
|
|
13
14
|
commerceUrl: commerce_url_1.default,
|
|
14
15
|
create: create_1.default,
|
|
15
16
|
addLanguage: add_language_1.default,
|
|
16
17
|
removeLanguage: remove_language_1.default,
|
|
17
18
|
defaultLanguage: default_language_1.default,
|
|
18
|
-
plugins: plugins_1.default
|
|
19
|
+
plugins: plugins_1.default,
|
|
20
|
+
codemod: codemod_1.default
|
|
19
21
|
};
|
package/package.json
CHANGED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { initSentry } from '@akinon/next/sentry';
|
|
2
|
-
|
|
3
|
-
async function initializeSentry() {
|
|
4
|
-
const response = await fetch('/api/sentry', { next: { revalidate: 0 } });
|
|
5
|
-
const data = await response.json();
|
|
6
|
-
|
|
7
|
-
const options = {
|
|
8
|
-
dsn: data.dsn,
|
|
9
|
-
integrations: [],
|
|
10
|
-
tracesSampleRate: 1.0
|
|
11
|
-
};
|
|
12
|
-
|
|
13
|
-
initSentry('Client', options);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
initializeSentry();
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import { Skeleton, SkeletonWrapper } from 'components';
|
|
2
|
-
|
|
3
|
-
export default function Loading() {
|
|
4
|
-
return (
|
|
5
|
-
<div className="container mx-auto">
|
|
6
|
-
<div className="max-w-5xl mx-auto my-5 px-7">
|
|
7
|
-
<SkeletonWrapper className="md:mb-7">
|
|
8
|
-
<Skeleton className="w-[17.25rem] h-4 lg:w-64" />
|
|
9
|
-
</SkeletonWrapper>
|
|
10
|
-
</div>
|
|
11
|
-
<div className="grid max-w-5xl grid-cols-2 lg:gap-8 mx-auto px-7">
|
|
12
|
-
<div className="col-span-2 mb-7 md:mb-0 lg:col-span-1">
|
|
13
|
-
<div className="flex gap-1">
|
|
14
|
-
<SkeletonWrapper className="hidden md:block md:mb-7">
|
|
15
|
-
{Array(5)
|
|
16
|
-
.fill(null)
|
|
17
|
-
.map((_, index) => (
|
|
18
|
-
<Skeleton key={index} className="w-20 h-24 mb-2" />
|
|
19
|
-
))}
|
|
20
|
-
</SkeletonWrapper>
|
|
21
|
-
|
|
22
|
-
<div className="flex-1">
|
|
23
|
-
<SkeletonWrapper className="md:mb-7">
|
|
24
|
-
<Skeleton className="w-full h-[30.375rem] md:h-[36.375rem]" />
|
|
25
|
-
</SkeletonWrapper>
|
|
26
|
-
</div>
|
|
27
|
-
</div>
|
|
28
|
-
</div>
|
|
29
|
-
<div className="flex flex-col items-center col-span-2 lg:col-span-1">
|
|
30
|
-
<div className="w-full">
|
|
31
|
-
<SkeletonWrapper className="w-full md:mb-7 flex justify-center items-center">
|
|
32
|
-
<Skeleton className="w-96 h-16 mb-9" />
|
|
33
|
-
<Skeleton className="hidden w-36 h-14 mb-9 md:block" />
|
|
34
|
-
|
|
35
|
-
<div className="flex flex-col justify-center items-center mb-9">
|
|
36
|
-
<Skeleton className="w-36 h-4 mb-2" />
|
|
37
|
-
<div className="flex items-center gap-2">
|
|
38
|
-
{Array(3)
|
|
39
|
-
.fill(null)
|
|
40
|
-
.map((_, index) => (
|
|
41
|
-
<Skeleton key={index} className="w-24 h-10" />
|
|
42
|
-
))}
|
|
43
|
-
</div>
|
|
44
|
-
</div>
|
|
45
|
-
|
|
46
|
-
<div className="flex flex-col justify-center items-center mb-4">
|
|
47
|
-
<Skeleton className="w-36 h-4 mb-2" />
|
|
48
|
-
<div className="flex items-center gap-2">
|
|
49
|
-
{Array(5)
|
|
50
|
-
.fill(null)
|
|
51
|
-
.map((_, index) => (
|
|
52
|
-
<Skeleton key={index} className="w-11 h-11" />
|
|
53
|
-
))}
|
|
54
|
-
</div>
|
|
55
|
-
</div>
|
|
56
|
-
|
|
57
|
-
<Skeleton className="hidden w-full h-14 mb-6 md:block" />
|
|
58
|
-
<Skeleton className="w-40 h-10 mb-9 md:w-72" />
|
|
59
|
-
<Skeleton className="w-24 h-10 mb-7" />
|
|
60
|
-
<Skeleton className="w-full h-36" />
|
|
61
|
-
</SkeletonWrapper>
|
|
62
|
-
</div>
|
|
63
|
-
</div>
|
|
64
|
-
</div>
|
|
65
|
-
</div>
|
|
66
|
-
);
|
|
67
|
-
}
|