@akinon/next 2.0.0-beta.7 → 2.0.0-beta.9
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/.eslintrc.js +12 -0
- package/CHANGELOG.md +12 -0
- package/api/auth.ts +15 -0
- package/bin/run-prebuild-tests.js +46 -0
- package/components/client-root.tsx +20 -0
- package/data/client/api.ts +1 -0
- package/data/client/basket.ts +27 -5
- package/data/client/checkout.ts +23 -1
- package/data/client/misc.ts +25 -1
- package/data/client/product.ts +19 -2
- package/data/urls.ts +11 -3
- package/hooks/index.ts +1 -0
- package/hooks/use-router.ts +5 -2
- package/hooks/use-sentry-uncaught-errors.ts +24 -0
- package/instrumentation/index.ts +10 -0
- package/lib/cache.ts +4 -4
- package/localization/index.ts +2 -1
- package/middlewares/default.ts +6 -2
- package/middlewares/locale.ts +31 -10
- package/package.json +4 -3
- package/redux/middlewares/checkout.ts +16 -144
- package/redux/middlewares/pre-order/address.ts +2 -0
- package/redux/middlewares/pre-order/attribute-based-shipping-option.ts +2 -0
- package/redux/middlewares/pre-order/data-source-shipping-option.ts +2 -0
- package/redux/middlewares/pre-order/delivery-option.ts +2 -0
- package/redux/middlewares/pre-order/index.ts +14 -10
- package/redux/middlewares/pre-order/installment-option.ts +2 -0
- package/redux/middlewares/pre-order/payment-option.ts +2 -0
- package/redux/middlewares/pre-order/redirection.ts +2 -0
- package/redux/middlewares/pre-order/shipping-option.ts +2 -0
- package/redux/reducers/checkout.ts +8 -2
- package/redux/reducers/root.ts +7 -2
- package/sentry/index.ts +36 -17
- package/types/commerce/checkout.ts +9 -0
- package/utils/index.ts +11 -8
- package/utils/localization.ts +4 -0
- package/views/error-page.tsx +93 -0
- package/with-pz-config.js +3 -2
package/.eslintrc.js
CHANGED
|
@@ -24,6 +24,18 @@ module.exports = {
|
|
|
24
24
|
parserOptions: {
|
|
25
25
|
sourceType: 'script'
|
|
26
26
|
}
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
env: {
|
|
30
|
+
node: true
|
|
31
|
+
},
|
|
32
|
+
files: ['redux/middlewares/pre-order/index.ts'],
|
|
33
|
+
rules: {
|
|
34
|
+
'@akinon/projectzero/check-pre-order-middleware-order': 'error'
|
|
35
|
+
},
|
|
36
|
+
parserOptions: {
|
|
37
|
+
sourceType: 'script'
|
|
38
|
+
}
|
|
27
39
|
}
|
|
28
40
|
],
|
|
29
41
|
parser: '@typescript-eslint/parser',
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @akinon/next
|
|
2
2
|
|
|
3
|
+
## 2.0.0-beta.9
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 0fe7711: ZERO-3387: Upgrade nextjs, eslint-config-next
|
|
8
|
+
|
|
9
|
+
## 2.0.0-beta.8
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- 071d0f5: ZERO-3352: Resolve Single item size exceeds maxSize error and upgrade dependencies
|
|
14
|
+
|
|
3
15
|
## 2.0.0-beta.7
|
|
4
16
|
|
|
5
17
|
## 2.0.0-beta.6
|
package/api/auth.ts
CHANGED
|
@@ -99,6 +99,21 @@ const nextAuthOptions = (req: NextApiRequest, res: NextApiResponse) => {
|
|
|
99
99
|
userIp
|
|
100
100
|
});
|
|
101
101
|
|
|
102
|
+
const checkCurrentUser = await getCurrentUser(
|
|
103
|
+
req.cookies['osessionid'] ?? '',
|
|
104
|
+
req.cookies['pz-currency'] ?? ''
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
if (checkCurrentUser?.pk) {
|
|
108
|
+
const sessionCookie = headers
|
|
109
|
+
.get('cookie')
|
|
110
|
+
?.match(/osessionid=\w+/)?.[0]
|
|
111
|
+
.replace(/osessionid=/, '');
|
|
112
|
+
if (sessionCookie) {
|
|
113
|
+
headers.set('cookie', sessionCookie);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
102
117
|
const apiRequest = await fetch(
|
|
103
118
|
`${Settings.commerceUrl}${user[credentials.formType]}`,
|
|
104
119
|
{
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
const path = require('path')
|
|
2
|
+
const fs = require('fs')
|
|
3
|
+
|
|
4
|
+
function runPrebuildTests() {
|
|
5
|
+
const workspaceRoot = process.cwd()
|
|
6
|
+
const configPath = path.join(workspaceRoot, 'config/prebuild-tests.json')
|
|
7
|
+
|
|
8
|
+
try {
|
|
9
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'))
|
|
10
|
+
|
|
11
|
+
if (config.tests && Array.isArray(config.tests)) {
|
|
12
|
+
for (const testScript of config.tests) {
|
|
13
|
+
console.log(`🧪 Running test script: ${testScript}`)
|
|
14
|
+
const result = require('child_process').spawnSync('yarn', [testScript], {
|
|
15
|
+
stdio: 'inherit',
|
|
16
|
+
shell: true
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
if (result.status !== 0) {
|
|
20
|
+
console.error(`❌ Test script '${testScript}' failed`)
|
|
21
|
+
process.exit(1)
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
console.log('✅ All prebuild tests passed successfully!')
|
|
25
|
+
}
|
|
26
|
+
} catch (error) {
|
|
27
|
+
if (error.code === 'ENOENT') {
|
|
28
|
+
console.log('🧪 Running default test: test:middleware')
|
|
29
|
+
const result = require('child_process').spawnSync('yarn', ['test:middleware'], {
|
|
30
|
+
stdio: 'inherit',
|
|
31
|
+
shell: true
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
if (result.status !== 0) {
|
|
35
|
+
console.error('❌ Middleware test failed')
|
|
36
|
+
process.exit(1)
|
|
37
|
+
}
|
|
38
|
+
console.log('✅ Default test passed successfully!')
|
|
39
|
+
} else {
|
|
40
|
+
console.error('❌ Error reading test configuration:', error)
|
|
41
|
+
process.exit(1)
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
module.exports = runPrebuildTests
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { useMobileIframeHandler } from '../hooks';
|
|
4
|
+
import * as Sentry from '@sentry/nextjs';
|
|
5
|
+
import { initSentry } from '../sentry';
|
|
6
|
+
import { useEffect } from 'react';
|
|
4
7
|
|
|
5
8
|
export default function ClientRoot({
|
|
6
9
|
children,
|
|
@@ -11,6 +14,23 @@ export default function ClientRoot({
|
|
|
11
14
|
}) {
|
|
12
15
|
const { preventPageRender } = useMobileIframeHandler({ sessionId });
|
|
13
16
|
|
|
17
|
+
const initializeSentry = async () => {
|
|
18
|
+
const response = await fetch('/api/sentry', { next: { revalidate: 0 } });
|
|
19
|
+
const data = await response.json();
|
|
20
|
+
|
|
21
|
+
const options = {
|
|
22
|
+
dsn: data.dsn
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
initSentry('Client', options);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
if (!Sentry.isInitialized()) {
|
|
30
|
+
initializeSentry();
|
|
31
|
+
}
|
|
32
|
+
}, []);
|
|
33
|
+
|
|
14
34
|
if (preventPageRender) {
|
|
15
35
|
return null;
|
|
16
36
|
}
|
package/data/client/api.ts
CHANGED
package/data/client/basket.ts
CHANGED
|
@@ -24,6 +24,26 @@ export const basketApi = api.injectEndpoints({
|
|
|
24
24
|
transformResponse: (response: { basket: Basket }) => response.basket,
|
|
25
25
|
providesTags: ['Basket']
|
|
26
26
|
}),
|
|
27
|
+
getMiniBasket: build.query<
|
|
28
|
+
{ pk: number | null; total_quantity: number },
|
|
29
|
+
void
|
|
30
|
+
>({
|
|
31
|
+
query: () =>
|
|
32
|
+
buildClientRequestUrl(basket.getMiniBasket, {
|
|
33
|
+
contentType: 'application/json'
|
|
34
|
+
}),
|
|
35
|
+
providesTags: ['MiniBasket']
|
|
36
|
+
}),
|
|
37
|
+
getMiniBasketDetail: build.query<
|
|
38
|
+
{ pk: number | null; total_quantity: number },
|
|
39
|
+
{ namespace: string }
|
|
40
|
+
>({
|
|
41
|
+
query: ({ namespace }) =>
|
|
42
|
+
buildClientRequestUrl(basket.getMiniBasketDetail(namespace), {
|
|
43
|
+
contentType: 'application/json'
|
|
44
|
+
}),
|
|
45
|
+
providesTags: ['MiniBasket']
|
|
46
|
+
}),
|
|
27
47
|
getBasketDetail: build.query<Basket, { namespace: string }>({
|
|
28
48
|
query: ({ namespace }) =>
|
|
29
49
|
buildClientRequestUrl(basket.getBasketDetail(namespace)),
|
|
@@ -46,7 +66,7 @@ export const basketApi = api.injectEndpoints({
|
|
|
46
66
|
method: 'DELETE',
|
|
47
67
|
body: { pk }
|
|
48
68
|
}),
|
|
49
|
-
invalidatesTags: ['MultiBasket', 'Basket']
|
|
69
|
+
invalidatesTags: ['MultiBasket', 'Basket', 'MiniBasket']
|
|
50
70
|
}),
|
|
51
71
|
selectMainBasket: build.mutation<Basket, { pk: number }>({
|
|
52
72
|
query: ({ pk }) => ({
|
|
@@ -57,7 +77,7 @@ export const basketApi = api.injectEndpoints({
|
|
|
57
77
|
body: { pk }
|
|
58
78
|
}),
|
|
59
79
|
transformResponse: (response: { baskets: Basket }) => response.baskets,
|
|
60
|
-
invalidatesTags: ['MultiBasket', 'Basket']
|
|
80
|
+
invalidatesTags: ['MultiBasket', 'Basket', 'MiniBasket']
|
|
61
81
|
}),
|
|
62
82
|
selectNameSpaceMainBasket: build.mutation<Basket, { namespace: string }>({
|
|
63
83
|
query: ({ namespace }) => ({
|
|
@@ -71,7 +91,7 @@ export const basketApi = api.injectEndpoints({
|
|
|
71
91
|
body: { namespace }
|
|
72
92
|
}),
|
|
73
93
|
transformResponse: (response: { baskets: Basket }) => response.baskets,
|
|
74
|
-
invalidatesTags: ['MultiBasket', 'Basket']
|
|
94
|
+
invalidatesTags: ['MultiBasket', 'Basket', 'MiniBasket']
|
|
75
95
|
}),
|
|
76
96
|
updateQuantity: build.mutation<
|
|
77
97
|
UpdateQuantityResponse,
|
|
@@ -84,7 +104,7 @@ export const basketApi = api.injectEndpoints({
|
|
|
84
104
|
method: 'PUT',
|
|
85
105
|
body
|
|
86
106
|
}),
|
|
87
|
-
invalidatesTags: ['MultiBasket', 'Basket']
|
|
107
|
+
invalidatesTags: ['MultiBasket', 'Basket', 'MiniBasket']
|
|
88
108
|
}),
|
|
89
109
|
clearBasket: build.mutation<Basket, void>({
|
|
90
110
|
query: (body) => ({
|
|
@@ -95,7 +115,7 @@ export const basketApi = api.injectEndpoints({
|
|
|
95
115
|
body
|
|
96
116
|
}),
|
|
97
117
|
transformResponse: (response: { basket: Basket }) => response.basket,
|
|
98
|
-
invalidatesTags: ['Basket']
|
|
118
|
+
invalidatesTags: ['Basket', 'MiniBasket', 'MiniBasket']
|
|
99
119
|
}),
|
|
100
120
|
applyVoucherCode: build.mutation<Basket, { voucher_code: string }>({
|
|
101
121
|
query: (body) => ({
|
|
@@ -125,6 +145,8 @@ export const basketApi = api.injectEndpoints({
|
|
|
125
145
|
|
|
126
146
|
export const {
|
|
127
147
|
useGetBasketQuery,
|
|
148
|
+
useGetMiniBasketQuery,
|
|
149
|
+
useGetMiniBasketDetailQuery,
|
|
128
150
|
useLazyGetBasketDetailQuery,
|
|
129
151
|
useGetAllBasketsQuery,
|
|
130
152
|
useRemoveBasketMutation,
|
package/data/client/checkout.ts
CHANGED
|
@@ -13,7 +13,9 @@ import {
|
|
|
13
13
|
ExtraField,
|
|
14
14
|
GuestLoginFormParams,
|
|
15
15
|
Order,
|
|
16
|
-
PreOrder
|
|
16
|
+
PreOrder,
|
|
17
|
+
SendSmsType,
|
|
18
|
+
VerifySmsType
|
|
17
19
|
} from '../../types';
|
|
18
20
|
import { buildClientRequestUrl } from '../../utils';
|
|
19
21
|
import { api } from './api';
|
|
@@ -847,6 +849,24 @@ export const checkoutApi = api.injectEndpoints({
|
|
|
847
849
|
selected_loyalty_amount: amount
|
|
848
850
|
}
|
|
849
851
|
})
|
|
852
|
+
}),
|
|
853
|
+
sendSms: build.mutation<CheckoutResponse, SendSmsType>({
|
|
854
|
+
query: (body) => ({
|
|
855
|
+
url: buildClientRequestUrl(checkout.sendSmsPage, {
|
|
856
|
+
useFormData: true
|
|
857
|
+
}),
|
|
858
|
+
method: 'POST',
|
|
859
|
+
body
|
|
860
|
+
})
|
|
861
|
+
}),
|
|
862
|
+
verifySms: build.mutation<CheckoutResponse, VerifySmsType>({
|
|
863
|
+
query: (body) => ({
|
|
864
|
+
url: buildClientRequestUrl(checkout.verifySmsPage, {
|
|
865
|
+
useFormData: true
|
|
866
|
+
}),
|
|
867
|
+
method: 'POST',
|
|
868
|
+
body
|
|
869
|
+
})
|
|
850
870
|
})
|
|
851
871
|
}),
|
|
852
872
|
overrideExisting: false
|
|
@@ -892,5 +912,7 @@ export const {
|
|
|
892
912
|
useSetWalletSelectionPageMutation,
|
|
893
913
|
useSetWalletPaymentPageMutation,
|
|
894
914
|
useSetWalletCompletePageMutation,
|
|
915
|
+
useSendSmsMutation,
|
|
916
|
+
useVerifySmsMutation,
|
|
895
917
|
useResetCheckoutStateQuery
|
|
896
918
|
} = checkoutApi;
|
package/data/client/misc.ts
CHANGED
|
@@ -92,6 +92,29 @@ export const miscApi = api.injectEndpoints({
|
|
|
92
92
|
transformResponse: (response: { menu: MenuItemType[] }) => {
|
|
93
93
|
return response.menu;
|
|
94
94
|
}
|
|
95
|
+
}),
|
|
96
|
+
getBukalemunImageUrl: builder.query<
|
|
97
|
+
any,
|
|
98
|
+
{ current_chapter: string; sku: string; selected_attributes: any }
|
|
99
|
+
>({
|
|
100
|
+
query: ({ current_chapter, sku, selected_attributes }) => {
|
|
101
|
+
const data = {
|
|
102
|
+
...selected_attributes,
|
|
103
|
+
current_chapter,
|
|
104
|
+
sku
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const params = new URLSearchParams(data);
|
|
108
|
+
|
|
109
|
+
return {
|
|
110
|
+
url: buildClientRequestUrl(
|
|
111
|
+
misc.bukalemunImageUrl(params.toString()),
|
|
112
|
+
{
|
|
113
|
+
responseType: 'json'
|
|
114
|
+
}
|
|
115
|
+
)
|
|
116
|
+
};
|
|
117
|
+
}
|
|
95
118
|
})
|
|
96
119
|
}),
|
|
97
120
|
overrideExisting: true
|
|
@@ -102,5 +125,6 @@ export const {
|
|
|
102
125
|
useEmailSubscriptionMutation,
|
|
103
126
|
useSetLanguageMutation,
|
|
104
127
|
useGetWidgetQuery,
|
|
105
|
-
useGetMenuQuery
|
|
128
|
+
useGetMenuQuery,
|
|
129
|
+
useGetBukalemunImageUrlQuery
|
|
106
130
|
} = miscApi;
|
package/data/client/product.ts
CHANGED
|
@@ -69,12 +69,28 @@ export const productApi = api.injectEndpoints({
|
|
|
69
69
|
}),
|
|
70
70
|
method: 'POST',
|
|
71
71
|
body
|
|
72
|
-
})
|
|
72
|
+
}),
|
|
73
|
+
invalidatesTags: ['MiniBasket']
|
|
73
74
|
}),
|
|
74
75
|
getInstallments: build.query<any, any>({
|
|
75
76
|
query: (productPk) => ({
|
|
76
77
|
url: buildClientRequestUrl(product.installments(productPk))
|
|
77
78
|
})
|
|
79
|
+
}),
|
|
80
|
+
getBundleProductData: build.query<
|
|
81
|
+
any,
|
|
82
|
+
{ productPk: string; queryString: string }
|
|
83
|
+
>({
|
|
84
|
+
query: ({ productPk, queryString }) => {
|
|
85
|
+
return {
|
|
86
|
+
url: buildClientRequestUrl(
|
|
87
|
+
product.bundleProduct(productPk, queryString),
|
|
88
|
+
{
|
|
89
|
+
responseType: 'json'
|
|
90
|
+
}
|
|
91
|
+
)
|
|
92
|
+
};
|
|
93
|
+
}
|
|
78
94
|
})
|
|
79
95
|
}),
|
|
80
96
|
overrideExisting: true
|
|
@@ -85,5 +101,6 @@ export const {
|
|
|
85
101
|
useGetProductByPkQuery,
|
|
86
102
|
useGetRetailStoreStockMutation,
|
|
87
103
|
useGetInstallmentsQuery,
|
|
88
|
-
useGetProductByParamsQuery
|
|
104
|
+
useGetProductByParamsQuery,
|
|
105
|
+
useGetBundleProductDataQuery
|
|
89
106
|
} = productApi;
|
package/data/urls.ts
CHANGED
|
@@ -74,7 +74,10 @@ export const address = {
|
|
|
74
74
|
|
|
75
75
|
export const basket = {
|
|
76
76
|
getBasket: '/baskets/basket/',
|
|
77
|
+
getMiniBasket: '/baskets/basket/mini/',
|
|
77
78
|
getBasketDetail: (namespace: string) => `/baskets/basket/${namespace}/`,
|
|
79
|
+
getMiniBasketDetail: (namespace: string) =>
|
|
80
|
+
`/baskets/basket/${namespace}/mini/`,
|
|
78
81
|
getAllBaskets: '/baskets/basket-list/',
|
|
79
82
|
removeBasket: (pk: number) => `/baskets/basket-list/${pk}/`,
|
|
80
83
|
selectMainBasket: (pk: number) => `/baskets/basket-list/id/${pk}/main/`,
|
|
@@ -137,7 +140,9 @@ export const checkout = {
|
|
|
137
140
|
'/orders/checkout/?page=AttributeBasedShippingOptionSelectionPage',
|
|
138
141
|
deliveryBagsPage: '/orders/checkout/?page=DeliveryBagsPage',
|
|
139
142
|
setOrderSelectionPage: '/orders/checkout/?page=OrderSelectionPage',
|
|
140
|
-
loyaltyCardPage: '/orders/checkout/?page=LoyaltyCardPage'
|
|
143
|
+
loyaltyCardPage: '/orders/checkout/?page=LoyaltyCardPage',
|
|
144
|
+
sendSmsPage: '/orders/checkout/?page=SendSmsPage',
|
|
145
|
+
verifySmsPage: '/orders/checkout/?page=VerifySmsPage'
|
|
141
146
|
};
|
|
142
147
|
|
|
143
148
|
export const flatpage = {
|
|
@@ -160,7 +165,8 @@ export const misc = {
|
|
|
160
165
|
parent ? `&parent=${parent}` : ''
|
|
161
166
|
}`,
|
|
162
167
|
cmsSeo: (slug: string | string[]) => `/cms/seo/?url=${slug ? slug : '/'}`,
|
|
163
|
-
setCurrency: '/users/activate-currency/'
|
|
168
|
+
setCurrency: '/users/activate-currency/',
|
|
169
|
+
bukalemunImageUrl: (params: string) => `/bukalemun/?${params}`
|
|
164
170
|
};
|
|
165
171
|
|
|
166
172
|
export const product = {
|
|
@@ -174,7 +180,9 @@ export const product = {
|
|
|
174
180
|
slug: (slug: string) => `/${slug}/`,
|
|
175
181
|
categoryUrl: (pk: number) => `/products/${pk}/category_nodes/?limit=1`,
|
|
176
182
|
breadcrumbUrl: (menuitemmodel: string) =>
|
|
177
|
-
`/menus/generate_breadcrumb/?item=${menuitemmodel}&generator_name=menu_item
|
|
183
|
+
`/menus/generate_breadcrumb/?item=${menuitemmodel}&generator_name=menu_item`,
|
|
184
|
+
bundleProduct: (productPk: string, queryString: string) =>
|
|
185
|
+
`/bundle-product/${productPk}/?${queryString}`
|
|
178
186
|
};
|
|
179
187
|
|
|
180
188
|
export const wishlist = {
|
package/hooks/index.ts
CHANGED
package/hooks/use-router.ts
CHANGED
|
@@ -27,8 +27,11 @@ export const useRouter = () => {
|
|
|
27
27
|
);
|
|
28
28
|
|
|
29
29
|
url.pathname = `${
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
localeUrlStrategy === LocaleUrlStrategy.Subdomain ||
|
|
31
|
+
(locale === defaultLocale?.value &&
|
|
32
|
+
[LocaleUrlStrategy.HideDefaultLocale].includes(
|
|
33
|
+
localeUrlStrategy as LocaleUrlStrategy
|
|
34
|
+
))
|
|
32
35
|
? ''
|
|
33
36
|
: `/${locale}`
|
|
34
37
|
}${pathnameWithoutLocale}`;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
import * as Sentry from '@sentry/nextjs';
|
|
3
|
+
import { ClientLogType } from '@akinon/next/sentry';
|
|
4
|
+
|
|
5
|
+
export const useSentryUncaughtErrors = (error: Error & { digest?: string }) => {
|
|
6
|
+
useEffect(() => {
|
|
7
|
+
Sentry.withScope(function (scope) {
|
|
8
|
+
scope.setLevel('fatal');
|
|
9
|
+
scope.setTags({
|
|
10
|
+
APP_TYPE: 'ProjectZeroNext',
|
|
11
|
+
TYPE: 'Client',
|
|
12
|
+
LOG_TYPE: ClientLogType.UNCAUGHT_ERROR_PAGE
|
|
13
|
+
});
|
|
14
|
+
scope.setExtra('error', error);
|
|
15
|
+
|
|
16
|
+
const error_ = new Error('FATAL: Uncaught client error');
|
|
17
|
+
error_.name = 'UNCAUGHT_ERROR_PAGE';
|
|
18
|
+
|
|
19
|
+
Sentry.captureException(error_, {
|
|
20
|
+
fingerprint: ['UNCAUGHT_ERROR_PAGE', error.digest]
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
}, [error]);
|
|
24
|
+
};
|
package/instrumentation/index.ts
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
|
+
import { initSentry } from '../sentry';
|
|
2
|
+
import * as Sentry from '@sentry/nextjs';
|
|
3
|
+
|
|
1
4
|
export async function register() {
|
|
2
5
|
if (process.env.NEXT_RUNTIME === 'nodejs') {
|
|
3
6
|
await import('./node');
|
|
7
|
+
initSentry('Server');
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
if (process.env.NEXT_RUNTIME === 'edge') {
|
|
11
|
+
initSentry('Edge');
|
|
4
12
|
}
|
|
5
13
|
}
|
|
14
|
+
|
|
15
|
+
export const onRequestError = Sentry.captureRequestError;
|
package/lib/cache.ts
CHANGED
|
@@ -156,10 +156,6 @@ export class Cache {
|
|
|
156
156
|
handler: () => Promise<T>,
|
|
157
157
|
options?: CacheOptions
|
|
158
158
|
): Promise<T> {
|
|
159
|
-
if (Settings.usePrettyUrlRoute) {
|
|
160
|
-
return await handler();
|
|
161
|
-
}
|
|
162
|
-
|
|
163
159
|
const requiredVariables = [
|
|
164
160
|
process.env.CACHE_HOST,
|
|
165
161
|
process.env.CACHE_PORT,
|
|
@@ -178,6 +174,10 @@ export class Cache {
|
|
|
178
174
|
const _options = Object.assign(defaultOptions, options);
|
|
179
175
|
const formattedKey = Cache.formatKey(key, locale);
|
|
180
176
|
|
|
177
|
+
if (Settings.usePrettyUrlRoute) {
|
|
178
|
+
_options.expire = 120;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
181
|
logger.debug('Cache wrap', { key, formattedKey, _options });
|
|
182
182
|
|
|
183
183
|
if (_options.cache) {
|
package/localization/index.ts
CHANGED
package/middlewares/default.ts
CHANGED
|
@@ -95,7 +95,9 @@ const withPzDefault =
|
|
|
95
95
|
|
|
96
96
|
if (
|
|
97
97
|
req.nextUrl.pathname.includes('/orders/hooks/') ||
|
|
98
|
-
req.nextUrl.pathname.includes('/orders/checkout-with-token/')
|
|
98
|
+
req.nextUrl.pathname.includes('/orders/checkout-with-token/') ||
|
|
99
|
+
req.nextUrl.pathname.includes('/hooks/cash_register/complete/') ||
|
|
100
|
+
req.nextUrl.pathname.includes('/hooks/cash_register/pre_order/')
|
|
99
101
|
) {
|
|
100
102
|
const queryString = searchParams.toString();
|
|
101
103
|
const currency = searchParams.get('currency')?.toLowerCase();
|
|
@@ -285,7 +287,9 @@ const withPzDefault =
|
|
|
285
287
|
url.pathname =
|
|
286
288
|
url.pathname +
|
|
287
289
|
(/\/$/.test(url.pathname) ? '' : '/') +
|
|
288
|
-
`searchparams|${
|
|
290
|
+
`searchparams|${encodeURIComponent(
|
|
291
|
+
url.searchParams.toString()
|
|
292
|
+
)}`;
|
|
289
293
|
}
|
|
290
294
|
|
|
291
295
|
Settings.rewrites.forEach((rewrite) => {
|
package/middlewares/locale.ts
CHANGED
|
@@ -4,17 +4,32 @@ import { PzNextRequest } from '.';
|
|
|
4
4
|
import { LocaleUrlStrategy } from '../localization';
|
|
5
5
|
import { urlLocaleMatcherRegex } from '../utils';
|
|
6
6
|
import logger from '../utils/log';
|
|
7
|
+
import { getUrlPathWithLocale } from '../utils/localization';
|
|
7
8
|
|
|
8
|
-
const getMatchedLocale = (pathname: string) => {
|
|
9
|
+
const getMatchedLocale = (pathname: string, req: PzNextRequest) => {
|
|
9
10
|
let matchedLocale = pathname.match(urlLocaleMatcherRegex)?.[0] ?? '';
|
|
10
11
|
matchedLocale = matchedLocale.replace('/', '');
|
|
11
12
|
|
|
13
|
+
const { localeUrlStrategy, defaultLocaleValue } = settings.localization;
|
|
14
|
+
|
|
15
|
+
if (localeUrlStrategy === LocaleUrlStrategy.Subdomain) {
|
|
16
|
+
const host = req.headers.get('x-forwarded-host');
|
|
17
|
+
|
|
18
|
+
if (host) {
|
|
19
|
+
const subDomain = host.split('.')[0] || '';
|
|
20
|
+
const subDomainLocaleMatched = `/${subDomain}`.match(
|
|
21
|
+
urlLocaleMatcherRegex
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
if (subDomainLocaleMatched && subDomainLocaleMatched[0]) {
|
|
25
|
+
matchedLocale = subDomainLocaleMatched[0].slice(1);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
12
30
|
if (!matchedLocale.length) {
|
|
13
|
-
if (
|
|
14
|
-
|
|
15
|
-
LocaleUrlStrategy.ShowAllLocales
|
|
16
|
-
) {
|
|
17
|
-
matchedLocale = settings.localization.defaultLocaleValue;
|
|
31
|
+
if (localeUrlStrategy !== LocaleUrlStrategy.ShowAllLocales) {
|
|
32
|
+
matchedLocale = defaultLocaleValue;
|
|
18
33
|
}
|
|
19
34
|
}
|
|
20
35
|
|
|
@@ -28,7 +43,7 @@ const withLocale =
|
|
|
28
43
|
|
|
29
44
|
try {
|
|
30
45
|
const url = req.nextUrl.clone();
|
|
31
|
-
const matchedLocale = getMatchedLocale(url.pathname);
|
|
46
|
+
const matchedLocale = getMatchedLocale(url.pathname, req);
|
|
32
47
|
let { localeUrlStrategy, defaultLocaleValue, redirectToDefaultLocale } =
|
|
33
48
|
settings.localization;
|
|
34
49
|
|
|
@@ -50,8 +65,15 @@ const withLocale =
|
|
|
50
65
|
redirectToDefaultLocale &&
|
|
51
66
|
req.method === 'GET'
|
|
52
67
|
) {
|
|
53
|
-
|
|
54
|
-
|
|
68
|
+
// Redirect to existing or default locale
|
|
69
|
+
|
|
70
|
+
url.pathname = getUrlPathWithLocale(
|
|
71
|
+
url.pathname,
|
|
72
|
+
req.cookies.get('pz-locale')?.value
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
// Use 303 for POST requests
|
|
76
|
+
return NextResponse.redirect(url, 303);
|
|
55
77
|
}
|
|
56
78
|
|
|
57
79
|
req.middlewareParams.rewrites.locale = matchedLocale;
|
|
@@ -61,7 +83,6 @@ const withLocale =
|
|
|
61
83
|
ip
|
|
62
84
|
});
|
|
63
85
|
}
|
|
64
|
-
|
|
65
86
|
return middleware(req, event);
|
|
66
87
|
};
|
|
67
88
|
|
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": "2.0.0-beta.
|
|
4
|
+
"version": "2.0.0-beta.9",
|
|
5
5
|
"private": false,
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"bin": {
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"@opentelemetry/semantic-conventions": "1.19.0",
|
|
22
22
|
"@reduxjs/toolkit": "1.9.7",
|
|
23
23
|
"@neshca/cache-handler": "1.5.1",
|
|
24
|
+
"@sentry/nextjs": "9.5.0",
|
|
24
25
|
"cross-spawn": "7.0.3",
|
|
25
26
|
"generic-pool": "3.9.0",
|
|
26
27
|
"react-redux": "8.1.3",
|
|
@@ -30,13 +31,13 @@
|
|
|
30
31
|
"set-cookie-parser": "2.6.0"
|
|
31
32
|
},
|
|
32
33
|
"devDependencies": {
|
|
33
|
-
"@akinon/eslint-plugin-projectzero": "2.0.0-beta.
|
|
34
|
+
"@akinon/eslint-plugin-projectzero": "2.0.0-beta.9",
|
|
34
35
|
"@types/react-redux": "7.1.30",
|
|
35
36
|
"@types/set-cookie-parser": "2.4.7",
|
|
36
37
|
"@typescript-eslint/eslint-plugin": "8.18.2",
|
|
37
38
|
"@typescript-eslint/parser": "8.18.2",
|
|
38
39
|
"eslint": "9.17.0",
|
|
39
|
-
"eslint-config-next": "15.
|
|
40
|
+
"eslint-config-next": "15.3.2",
|
|
40
41
|
"eslint-config-prettier": "9.1.0"
|
|
41
42
|
}
|
|
42
43
|
}
|
|
@@ -16,12 +16,11 @@ import {
|
|
|
16
16
|
setLoyaltyBalance,
|
|
17
17
|
setPaymentChoices,
|
|
18
18
|
setPaymentOptions,
|
|
19
|
-
setPreOrder,
|
|
20
19
|
setRetailStores,
|
|
21
20
|
setShippingOptions,
|
|
22
|
-
setShippingStepCompleted,
|
|
23
21
|
setHepsipayAvailability,
|
|
24
|
-
setWalletPaymentData
|
|
22
|
+
setWalletPaymentData,
|
|
23
|
+
setPayOnDeliveryOtpModalActive
|
|
25
24
|
} from '../../redux/reducers/checkout';
|
|
26
25
|
import { RootState, TypedDispatch } from 'redux/store';
|
|
27
26
|
import { checkoutApi } from '../../data/client/checkout';
|
|
@@ -58,153 +57,14 @@ export const errorMiddleware: Middleware = ({ dispatch }: MiddlewareParams) => {
|
|
|
58
57
|
};
|
|
59
58
|
};
|
|
60
59
|
|
|
61
|
-
export const preOrderMiddleware: Middleware = ({
|
|
62
|
-
getState,
|
|
63
|
-
dispatch
|
|
64
|
-
}: MiddlewareParams) => {
|
|
65
|
-
return (next) => (action) => {
|
|
66
|
-
const result: CheckoutResult = next(action);
|
|
67
|
-
const preOrder = result?.payload?.pre_order;
|
|
68
|
-
|
|
69
|
-
if (
|
|
70
|
-
!preOrder ||
|
|
71
|
-
action?.meta?.arg?.endpointName === 'guestLogin' ||
|
|
72
|
-
action?.meta?.arg?.endpointName === 'getCheckoutLoyaltyBalance'
|
|
73
|
-
) {
|
|
74
|
-
return result;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const {
|
|
78
|
-
deliveryOptions,
|
|
79
|
-
addressList: addresses,
|
|
80
|
-
shippingOptions,
|
|
81
|
-
dataSourceShippingOptions,
|
|
82
|
-
paymentOptions,
|
|
83
|
-
installmentOptions,
|
|
84
|
-
attributeBasedShippingOptions
|
|
85
|
-
} = getState().checkout;
|
|
86
|
-
const { endpoints: apiEndpoints } = checkoutApi;
|
|
87
|
-
|
|
88
|
-
if (preOrder.is_redirected) {
|
|
89
|
-
const contextList = result?.payload?.context_list;
|
|
90
|
-
|
|
91
|
-
if (
|
|
92
|
-
contextList.find(
|
|
93
|
-
(ctx) => ctx.page_name === 'RedirectionPaymentSelectedPage'
|
|
94
|
-
)
|
|
95
|
-
) {
|
|
96
|
-
dispatch(
|
|
97
|
-
apiEndpoints.setPaymentOption.initiate(preOrder.payment_option?.pk)
|
|
98
|
-
);
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
dispatch(setPreOrder(preOrder));
|
|
104
|
-
|
|
105
|
-
if (!preOrder.delivery_option && deliveryOptions.length > 0) {
|
|
106
|
-
dispatch(
|
|
107
|
-
apiEndpoints.setDeliveryOption.initiate(
|
|
108
|
-
deliveryOptions.find((opt) => opt.delivery_option_type === 'customer')
|
|
109
|
-
?.pk
|
|
110
|
-
)
|
|
111
|
-
);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
if (
|
|
115
|
-
(!preOrder.shipping_address || !preOrder.billing_address) &&
|
|
116
|
-
addresses.length > 0 &&
|
|
117
|
-
(!preOrder.delivery_option ||
|
|
118
|
-
preOrder.delivery_option.delivery_option_type === 'customer')
|
|
119
|
-
) {
|
|
120
|
-
dispatch(
|
|
121
|
-
apiEndpoints.setAddresses.initiate({
|
|
122
|
-
shippingAddressPk: addresses[0].pk,
|
|
123
|
-
billingAddressPk: addresses[0].pk
|
|
124
|
-
})
|
|
125
|
-
);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
if (
|
|
129
|
-
shippingOptions.length > 0 &&
|
|
130
|
-
(!preOrder.shipping_option ||
|
|
131
|
-
!shippingOptions.find((opt) => opt.pk === preOrder.shipping_option?.pk))
|
|
132
|
-
) {
|
|
133
|
-
dispatch(apiEndpoints.setShippingOption.initiate(shippingOptions[0].pk));
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
if (
|
|
137
|
-
dataSourceShippingOptions.length > 0 &&
|
|
138
|
-
!preOrder.data_source_shipping_options
|
|
139
|
-
) {
|
|
140
|
-
const selectedDataSourceShippingOptionsPks =
|
|
141
|
-
dataSourceShippingOptions.map(
|
|
142
|
-
(opt) => opt.data_source_shipping_options[0].pk
|
|
143
|
-
);
|
|
144
|
-
|
|
145
|
-
dispatch(
|
|
146
|
-
apiEndpoints.setDataSourceShippingOptions.initiate(
|
|
147
|
-
selectedDataSourceShippingOptionsPks
|
|
148
|
-
)
|
|
149
|
-
);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
if (
|
|
153
|
-
Object.keys(attributeBasedShippingOptions).length > 0 &&
|
|
154
|
-
!preOrder.attribute_based_shipping_options
|
|
155
|
-
) {
|
|
156
|
-
const initialSelectedOptions: Record<string, number> = Object.fromEntries(
|
|
157
|
-
Object.entries(attributeBasedShippingOptions).map(([key, options]) => [
|
|
158
|
-
key,
|
|
159
|
-
options.attribute_based_shipping_options[0].pk
|
|
160
|
-
])
|
|
161
|
-
);
|
|
162
|
-
|
|
163
|
-
dispatch(
|
|
164
|
-
apiEndpoints.setAttributeBasedShippingOptions.initiate(
|
|
165
|
-
initialSelectedOptions
|
|
166
|
-
)
|
|
167
|
-
);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
if (!preOrder.payment_option && paymentOptions.length > 0) {
|
|
171
|
-
dispatch(apiEndpoints.setPaymentOption.initiate(paymentOptions[0].pk));
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
if (
|
|
175
|
-
!preOrder.installment &&
|
|
176
|
-
preOrder.payment_option?.payment_type !== 'saved_card' &&
|
|
177
|
-
installmentOptions.length > 0
|
|
178
|
-
) {
|
|
179
|
-
dispatch(
|
|
180
|
-
apiEndpoints.setInstallmentOption.initiate(installmentOptions[0].pk)
|
|
181
|
-
);
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
dispatch(
|
|
185
|
-
setShippingStepCompleted(
|
|
186
|
-
[
|
|
187
|
-
preOrder.delivery_option?.delivery_option_type === 'retail_store'
|
|
188
|
-
? true
|
|
189
|
-
: preOrder.shipping_address?.pk,
|
|
190
|
-
preOrder.billing_address?.pk,
|
|
191
|
-
preOrder.shipping_option?.pk,
|
|
192
|
-
addresses.length > 0
|
|
193
|
-
].every(Boolean)
|
|
194
|
-
)
|
|
195
|
-
);
|
|
196
|
-
|
|
197
|
-
return result;
|
|
198
|
-
};
|
|
199
|
-
};
|
|
200
|
-
|
|
201
60
|
export const contextListMiddleware: Middleware = ({
|
|
202
61
|
dispatch,
|
|
203
62
|
getState
|
|
204
63
|
}: MiddlewareParams) => {
|
|
205
64
|
return (next) => (action) => {
|
|
206
|
-
const { isMobileApp } = getState().root;
|
|
65
|
+
const { isMobileApp, userPhoneNumber } = getState().root;
|
|
207
66
|
const result: CheckoutResult = next(action);
|
|
67
|
+
const preOrder = result?.payload?.pre_order;
|
|
208
68
|
|
|
209
69
|
if (result?.payload?.context_list) {
|
|
210
70
|
result.payload.context_list.forEach((context) => {
|
|
@@ -323,6 +183,18 @@ export const contextListMiddleware: Middleware = ({
|
|
|
323
183
|
if (context.page_context.retail_stores) {
|
|
324
184
|
dispatch(setRetailStores(context.page_context.retail_stores));
|
|
325
185
|
}
|
|
186
|
+
|
|
187
|
+
if (context.page_name === 'SendSmsPage' && !preOrder?.phone_number) {
|
|
188
|
+
dispatch(
|
|
189
|
+
checkoutApi.endpoints.sendSms.initiate({
|
|
190
|
+
phone_number: userPhoneNumber ?? preOrder?.user_phone_number
|
|
191
|
+
})
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (context.page_name === 'VerifySmsPage') {
|
|
196
|
+
dispatch(setPayOnDeliveryOtpModalActive(true));
|
|
197
|
+
}
|
|
326
198
|
});
|
|
327
199
|
}
|
|
328
200
|
|
|
@@ -10,16 +10,20 @@ import { paymentOptionMiddleware } from './payment-option';
|
|
|
10
10
|
import { installmentOptionMiddleware } from './installment-option';
|
|
11
11
|
import { shippingStepMiddleware } from './shipping-step';
|
|
12
12
|
|
|
13
|
+
// ⚠️ WARNING: Redux Toolkit applies middlewares in reverse order (from last to first).
|
|
14
|
+
// This list is written **in reverse execution order** to ensure they run in the correct sequence.
|
|
15
|
+
// If you add a new middleware, make sure to insert it **in reverse order** based on execution priority.
|
|
16
|
+
|
|
13
17
|
export const preOrderMiddlewares = [
|
|
14
|
-
|
|
15
|
-
redirectionMiddleware,
|
|
16
|
-
setPreOrderMiddleware,
|
|
17
|
-
deliveryOptionMiddleware,
|
|
18
|
-
setAddressMiddleware,
|
|
19
|
-
shippingOptionMiddleware,
|
|
20
|
-
dataSourceShippingOptionMiddleware,
|
|
21
|
-
attributeBasedShippingOptionMiddleware,
|
|
22
|
-
paymentOptionMiddleware,
|
|
18
|
+
shippingStepMiddleware,
|
|
23
19
|
installmentOptionMiddleware,
|
|
24
|
-
|
|
20
|
+
paymentOptionMiddleware,
|
|
21
|
+
attributeBasedShippingOptionMiddleware,
|
|
22
|
+
dataSourceShippingOptionMiddleware,
|
|
23
|
+
shippingOptionMiddleware,
|
|
24
|
+
setAddressMiddleware,
|
|
25
|
+
deliveryOptionMiddleware,
|
|
26
|
+
setPreOrderMiddleware,
|
|
27
|
+
redirectionMiddleware,
|
|
28
|
+
preOrderValidationMiddleware
|
|
25
29
|
];
|
|
@@ -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;
|
package/redux/reducers/root.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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
|
};
|
|
@@ -108,6 +108,7 @@ export interface PreOrder {
|
|
|
108
108
|
context_extras?: ExtraField;
|
|
109
109
|
token?: string;
|
|
110
110
|
agreement_confirmed?: boolean;
|
|
111
|
+
phone_number?: string;
|
|
111
112
|
}
|
|
112
113
|
|
|
113
114
|
export type ExtraField = Record<string, any>;
|
|
@@ -200,3 +201,11 @@ export interface MiddlewareParams {
|
|
|
200
201
|
getState: () => RootState;
|
|
201
202
|
dispatch: TypedDispatch;
|
|
202
203
|
}
|
|
204
|
+
|
|
205
|
+
export type SendSmsType = {
|
|
206
|
+
phone_number: string;
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
export type VerifySmsType = {
|
|
210
|
+
verify_code: string;
|
|
211
|
+
};
|
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
|
-
`^/(${
|
|
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
|
);
|
package/utils/localization.ts
CHANGED
|
@@ -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,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
|
@@ -45,7 +45,8 @@ const defaultConfig = {
|
|
|
45
45
|
},
|
|
46
46
|
{
|
|
47
47
|
key: 'Content-Security-Policy',
|
|
48
|
-
value:
|
|
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 = (
|