@akinon/projectzero 1.84.0 → 1.85.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 +70 -3
- package/app-template/.gitignore +2 -0
- package/app-template/CHANGELOG.md +3612 -209
- package/app-template/next.config.mjs +4 -1
- package/app-template/package.json +18 -19
- package/app-template/public/locales/en/common.json +6 -0
- package/app-template/public/locales/tr/common.json +6 -0
- package/app-template/src/app/[commerce]/[locale]/[currency]/basket/page.tsx +9 -82
- package/app-template/src/app/[commerce]/[locale]/[currency]/error.tsx +12 -15
- package/app-template/src/settings.js +6 -1
- package/app-template/src/views/basket/basket-content.tsx +106 -0
- package/app-template/src/views/basket/basket-item.tsx +16 -13
- package/app-template/src/views/basket/summary.tsx +10 -7
- package/app-template/src/views/header/action-menu.tsx +6 -3
- package/app-template/src/views/header/mini-basket.tsx +6 -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
|
@@ -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,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "projectzeronext",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.85.0-rc.0",
|
|
4
4
|
"private": true,
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"scripts": {
|
|
@@ -22,26 +22,25 @@
|
|
|
22
22
|
"prestart": "pz-prestart"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@akinon/next": "1.
|
|
26
|
-
"@akinon/pz-akifast": "1.
|
|
27
|
-
"@akinon/pz-b2b": "1.
|
|
28
|
-
"@akinon/pz-basket-gift-pack": "1.
|
|
29
|
-
"@akinon/pz-bkm": "1.
|
|
30
|
-
"@akinon/pz-checkout-gift-pack": "1.
|
|
31
|
-
"@akinon/pz-click-collect": "1.
|
|
32
|
-
"@akinon/pz-credit-payment": "1.
|
|
33
|
-
"@akinon/pz-gpay": "1.
|
|
34
|
-
"@akinon/pz-masterpass": "1.
|
|
35
|
-
"@akinon/pz-one-click-checkout": "1.
|
|
36
|
-
"@akinon/pz-otp": "1.
|
|
37
|
-
"@akinon/pz-pay-on-delivery": "1.
|
|
38
|
-
"@akinon/pz-saved-card": "1.
|
|
39
|
-
"@akinon/pz-tabby-extension": "1.
|
|
40
|
-
"@akinon/pz-tamara-extension": "1.
|
|
25
|
+
"@akinon/next": "1.85.0-rc.0",
|
|
26
|
+
"@akinon/pz-akifast": "1.85.0-rc.0",
|
|
27
|
+
"@akinon/pz-b2b": "1.85.0-rc.0",
|
|
28
|
+
"@akinon/pz-basket-gift-pack": "1.85.0-rc.0",
|
|
29
|
+
"@akinon/pz-bkm": "1.85.0-rc.0",
|
|
30
|
+
"@akinon/pz-checkout-gift-pack": "1.85.0-rc.0",
|
|
31
|
+
"@akinon/pz-click-collect": "1.85.0-rc.0",
|
|
32
|
+
"@akinon/pz-credit-payment": "1.85.0-rc.0",
|
|
33
|
+
"@akinon/pz-gpay": "1.85.0-rc.0",
|
|
34
|
+
"@akinon/pz-masterpass": "1.85.0-rc.0",
|
|
35
|
+
"@akinon/pz-one-click-checkout": "1.85.0-rc.0",
|
|
36
|
+
"@akinon/pz-otp": "1.85.0-rc.0",
|
|
37
|
+
"@akinon/pz-pay-on-delivery": "1.85.0-rc.0",
|
|
38
|
+
"@akinon/pz-saved-card": "1.85.0-rc.0",
|
|
39
|
+
"@akinon/pz-tabby-extension": "1.85.0-rc.0",
|
|
40
|
+
"@akinon/pz-tamara-extension": "1.85.0-rc.0",
|
|
41
41
|
"@hookform/resolvers": "2.9.0",
|
|
42
42
|
"@next/third-parties": "14.1.0",
|
|
43
43
|
"@react-google-maps/api": "2.17.1",
|
|
44
|
-
"@sentry/nextjs": "7.116.0",
|
|
45
44
|
"dayjs": "1.11.5",
|
|
46
45
|
"lossless-json": "2.0.5",
|
|
47
46
|
"next": "14.2.25",
|
|
@@ -62,7 +61,7 @@
|
|
|
62
61
|
"yup": "0.32.11"
|
|
63
62
|
},
|
|
64
63
|
"devDependencies": {
|
|
65
|
-
"@akinon/eslint-plugin-projectzero": "1.
|
|
64
|
+
"@akinon/eslint-plugin-projectzero": "1.85.0-rc.0",
|
|
66
65
|
"@semantic-release/changelog": "6.0.2",
|
|
67
66
|
"@semantic-release/exec": "6.0.3",
|
|
68
67
|
"@semantic-release/git": "10.0.1",
|
|
@@ -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
|
},
|
|
@@ -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
|
},
|
|
@@ -1,87 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import { useEffect } from 'react';
|
|
4
|
-
import { ROUTES } from '@theme/routes';
|
|
5
|
-
import { useGetBasketQuery } from '@akinon/next/data/client/basket';
|
|
6
|
-
import { pushCartView } from '@theme/utils/gtm';
|
|
7
|
-
import { Button, LoaderSpinner, Link } from '@theme/components';
|
|
8
|
-
import { BasketItem, Summary } from '@theme/views/basket';
|
|
9
|
-
import { useLocalization } from '@akinon/next/hooks';
|
|
10
|
-
import PluginModule, { Component } from '@akinon/next/components/plugin-module';
|
|
1
|
+
import { BasketContent } from '@theme/views/basket/basket-content';
|
|
2
|
+
import { getBasketData } from '@akinon/next/data/server/basket';
|
|
11
3
|
import settings from '@theme/settings';
|
|
12
4
|
|
|
13
|
-
export default function Page() {
|
|
14
|
-
const {
|
|
15
|
-
const { t } = useLocalization();
|
|
16
|
-
const multiBasket = settings.plugins?.multiBasket ?? false;
|
|
17
|
-
|
|
18
|
-
useEffect(() => {
|
|
19
|
-
if (isSuccess) {
|
|
20
|
-
const products = basket.basketitem_set.map((basketItem) => ({
|
|
21
|
-
...basketItem.product
|
|
22
|
-
}));
|
|
23
|
-
pushCartView(products);
|
|
24
|
-
}
|
|
25
|
-
}, [basket, isSuccess]);
|
|
26
|
-
|
|
27
|
-
return (
|
|
28
|
-
<div className="max-w-screen-xl p-4 flex flex-col text-primary-800 lg:p-8 xl:flex-row xl:mx-auto">
|
|
29
|
-
{isLoading && (
|
|
30
|
-
<div className="flex justify-center w-full">
|
|
31
|
-
<LoaderSpinner />
|
|
32
|
-
</div>
|
|
33
|
-
)}
|
|
34
|
-
{isSuccess &&
|
|
35
|
-
(basket && basket.basketitem_set && basket.basketitem_set.length > 0 ? (
|
|
36
|
-
<>
|
|
37
|
-
<div className="flex-1 xl:mr-16">
|
|
38
|
-
<div className="flex items-center justify-between py-2 border-b border-gray-200 lg:py-3">
|
|
39
|
-
<h2 className="text-xl lg:text-2xl font-light">
|
|
40
|
-
{t('basket.my_cart')}
|
|
41
|
-
</h2>
|
|
42
|
-
<Link
|
|
43
|
-
href={ROUTES.HOME}
|
|
44
|
-
className="text-xs hover:text-secondary-500"
|
|
45
|
-
>
|
|
46
|
-
{t('basket.back_to_shopping')}
|
|
47
|
-
</Link>
|
|
48
|
-
</div>
|
|
49
|
-
<ul>
|
|
50
|
-
{multiBasket ? (
|
|
51
|
-
<PluginModule
|
|
52
|
-
component={Component.MultiBasket}
|
|
53
|
-
props={{ BasketItem }}
|
|
54
|
-
/>
|
|
55
|
-
) : (
|
|
56
|
-
basket.basketitem_set.map((basketItem, index) => (
|
|
57
|
-
<BasketItem basketItem={basketItem} key={index} />
|
|
58
|
-
))
|
|
59
|
-
)}
|
|
60
|
-
</ul>
|
|
61
|
-
</div>
|
|
62
|
-
<Summary basket={basket} />
|
|
63
|
-
</>
|
|
64
|
-
) : (
|
|
65
|
-
<div className="flex flex-col items-center container max-w-screen-sm py-4 px-4 xs:py-6 xs:px-6 sm:py-8 sm:px-8 lg:max-w-screen-xl">
|
|
66
|
-
<h1
|
|
67
|
-
className="w-full text-xl font-light text-secondary text-center sm:text-2xl"
|
|
68
|
-
data-testid="basket-empty"
|
|
69
|
-
>
|
|
70
|
-
{t('basket.empty.title')}
|
|
71
|
-
</h1>
|
|
5
|
+
export default async function Page() {
|
|
6
|
+
const { basket } = await getBasketData();
|
|
72
7
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
8
|
+
const multiBasket: boolean =
|
|
9
|
+
typeof settings.plugins?.multiBasket === 'boolean'
|
|
10
|
+
? settings.plugins.multiBasket
|
|
11
|
+
: false;
|
|
77
12
|
|
|
78
|
-
|
|
79
|
-
<Button className="px-10 mt-2" appearance="filled">
|
|
80
|
-
{t('basket.empty.button')}
|
|
81
|
-
</Button>
|
|
82
|
-
</Link>
|
|
83
|
-
</div>
|
|
84
|
-
))}
|
|
85
|
-
</div>
|
|
86
|
-
);
|
|
13
|
+
return <BasketContent initialBasket={basket} multiBasket={multiBasket} />;
|
|
87
14
|
}
|
|
@@ -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,6 +1,10 @@
|
|
|
1
1
|
const { LocaleUrlStrategy } = require('@akinon/next/localization');
|
|
2
2
|
const { ROUTES } = require('@theme/routes');
|
|
3
3
|
|
|
4
|
+
/* IMPORTANT *
|
|
5
|
+
* In order to use one locale in the locales array and hide the default locale in the URL, you need to set the localeUrlStrategy to LocaleUrlStrategy.ShowAllLocales.
|
|
6
|
+
*/
|
|
7
|
+
|
|
4
8
|
const commerceUrl = encodeURI(process.env.SERVICE_BACKEND_URL ?? 'default');
|
|
5
9
|
|
|
6
10
|
/** @type {import('@akinon/next/types').Settings} */
|
|
@@ -14,6 +18,7 @@ module.exports = {
|
|
|
14
18
|
{ translationKey: 'size', key: 'size' }
|
|
15
19
|
],
|
|
16
20
|
localization: {
|
|
21
|
+
// If there is one locale in the locales array, the default locale will be hidden in the URL.
|
|
17
22
|
locales: [
|
|
18
23
|
{
|
|
19
24
|
label: 'EN',
|
|
@@ -41,7 +46,7 @@ module.exports = {
|
|
|
41
46
|
}
|
|
42
47
|
],
|
|
43
48
|
defaultLocaleValue: 'en',
|
|
44
|
-
localeUrlStrategy: LocaleUrlStrategy.HideDefaultLocale,
|
|
49
|
+
localeUrlStrategy: LocaleUrlStrategy.HideDefaultLocale, // If there is one locale in the locales array, the default locale will be hidden in the URL and localeUrlStrategy should be set to LocaleUrlStrategy.ShowAllLocales.
|
|
45
50
|
redirectToDefaultLocale: true,
|
|
46
51
|
defaultCurrencyCode: 'usd'
|
|
47
52
|
},
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useLocalization } from '@akinon/next/hooks';
|
|
4
|
+
import { Basket } from '@akinon/next/types';
|
|
5
|
+
import { Button, LoaderSpinner, Link } from '@theme/components';
|
|
6
|
+
import { BasketItem, Summary } from '@theme/views/basket';
|
|
7
|
+
import { ROUTES } from '@theme/routes';
|
|
8
|
+
import PluginModule, { Component } from '@akinon/next/components/plugin-module';
|
|
9
|
+
import { useEffect, useState } from 'react';
|
|
10
|
+
import { pushCartView } from '@theme/utils/gtm';
|
|
11
|
+
|
|
12
|
+
interface BasketContentProps {
|
|
13
|
+
initialBasket: Basket;
|
|
14
|
+
multiBasket: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function BasketContent({
|
|
18
|
+
initialBasket,
|
|
19
|
+
multiBasket
|
|
20
|
+
}: BasketContentProps) {
|
|
21
|
+
const { t } = useLocalization();
|
|
22
|
+
const [basket, setBasket] = useState<Basket>(initialBasket);
|
|
23
|
+
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
if (basket) {
|
|
26
|
+
const products = basket.basketitem_set.map((basketItem) => ({
|
|
27
|
+
...basketItem.product
|
|
28
|
+
}));
|
|
29
|
+
pushCartView(products);
|
|
30
|
+
}
|
|
31
|
+
}, [basket]);
|
|
32
|
+
|
|
33
|
+
const handleBasketUpdate = (updatedBasket: Basket) => {
|
|
34
|
+
setBasket(updatedBasket);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
if (!basket) {
|
|
38
|
+
return (
|
|
39
|
+
<div className="flex justify-center w-full">
|
|
40
|
+
<LoaderSpinner />
|
|
41
|
+
</div>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<div className="max-w-screen-xl p-4 flex flex-col text-primary-800 lg:p-8 xl:flex-row xl:mx-auto">
|
|
47
|
+
{basket.basketitem_set && basket.basketitem_set.length > 0 ? (
|
|
48
|
+
<>
|
|
49
|
+
<div className="flex-1 xl:mr-16">
|
|
50
|
+
<div className="flex items-center justify-between py-2 border-b border-gray-200 lg:py-3">
|
|
51
|
+
<h2 className="text-xl lg:text-2xl font-light">
|
|
52
|
+
{t('basket.my_cart')}
|
|
53
|
+
</h2>
|
|
54
|
+
<Link
|
|
55
|
+
href={ROUTES.HOME}
|
|
56
|
+
className="text-xs hover:text-secondary-500"
|
|
57
|
+
>
|
|
58
|
+
{t('basket.back_to_shopping')}
|
|
59
|
+
</Link>
|
|
60
|
+
</div>
|
|
61
|
+
<ul>
|
|
62
|
+
{multiBasket ? (
|
|
63
|
+
<PluginModule
|
|
64
|
+
component={Component.MultiBasket}
|
|
65
|
+
props={{
|
|
66
|
+
BasketItem,
|
|
67
|
+
onBasketUpdate: handleBasketUpdate
|
|
68
|
+
}}
|
|
69
|
+
/>
|
|
70
|
+
) : (
|
|
71
|
+
basket.basketitem_set.map((basketItem, index) => (
|
|
72
|
+
<BasketItem
|
|
73
|
+
key={index}
|
|
74
|
+
basketItem={basketItem}
|
|
75
|
+
onBasketUpdate={handleBasketUpdate}
|
|
76
|
+
/>
|
|
77
|
+
))
|
|
78
|
+
)}
|
|
79
|
+
</ul>
|
|
80
|
+
</div>
|
|
81
|
+
<Summary basket={basket} onBasketUpdate={handleBasketUpdate} />
|
|
82
|
+
</>
|
|
83
|
+
) : (
|
|
84
|
+
<div className="flex flex-col items-center container max-w-screen-sm py-4 px-4 xs:py-6 xs:px-6 sm:py-8 sm:px-8 lg:max-w-screen-xl">
|
|
85
|
+
<h1
|
|
86
|
+
className="w-full text-xl font-light text-secondary text-center sm:text-2xl"
|
|
87
|
+
data-testid="basket-empty"
|
|
88
|
+
>
|
|
89
|
+
{t('basket.empty.title')}
|
|
90
|
+
</h1>
|
|
91
|
+
|
|
92
|
+
<div className="w-full text-sm text-black-800 text-center my-4 mb-2 sm:text-base">
|
|
93
|
+
<p>{t('basket.empty.content_first')}</p>
|
|
94
|
+
<p>{t('basket.empty.content_second')}.</p>
|
|
95
|
+
</div>
|
|
96
|
+
|
|
97
|
+
<Link href={ROUTES.HOME} passHref>
|
|
98
|
+
<Button className="px-10 mt-2" appearance="filled">
|
|
99
|
+
{t('basket.empty.button')}
|
|
100
|
+
</Button>
|
|
101
|
+
</Link>
|
|
102
|
+
</div>
|
|
103
|
+
)}
|
|
104
|
+
</div>
|
|
105
|
+
);
|
|
106
|
+
}
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
useUpdateQuantityMutation
|
|
4
4
|
} from '@akinon/next/data/client/basket';
|
|
5
5
|
import { useAppDispatch } from '@akinon/next/redux/hooks';
|
|
6
|
-
import { BasketItem as BasketItemType } from '@akinon/next/types';
|
|
6
|
+
import { Basket, BasketItem as BasketItemType } from '@akinon/next/types';
|
|
7
7
|
import { Price, Button, Icon, Modal, Select, Link } from '@theme/components';
|
|
8
8
|
import { useState } from 'react';
|
|
9
9
|
import { useAddFavoriteMutation } from '@akinon/next/data/client/wishlist';
|
|
@@ -19,11 +19,12 @@ import { pushRemoveFromCart } from '@theme/utils/gtm';
|
|
|
19
19
|
interface Props {
|
|
20
20
|
basketItem?: BasketItemType;
|
|
21
21
|
namespace?: string;
|
|
22
|
+
onBasketUpdate?: (basket: Basket) => void;
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
export const BasketItem = (props: Props) => {
|
|
25
26
|
const { t } = useLocalization();
|
|
26
|
-
const { basketItem, namespace } = props;
|
|
27
|
+
const { basketItem, namespace, onBasketUpdate } = props;
|
|
27
28
|
const [updateQuantityMutation] = useUpdateQuantityMutation();
|
|
28
29
|
const dispatch = useAppDispatch();
|
|
29
30
|
const [isRemoveBasketModalOpen, setRemoveBasketModalOpen] = useState(false);
|
|
@@ -49,19 +50,21 @@ export const BasketItem = (props: Props) => {
|
|
|
49
50
|
requestParams.namespace = namespace;
|
|
50
51
|
}
|
|
51
52
|
|
|
52
|
-
|
|
53
|
-
.unwrap()
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
(draftBasket)
|
|
60
|
-
|
|
61
|
-
}
|
|
62
|
-
)
|
|
53
|
+
try {
|
|
54
|
+
const response = await updateQuantityMutation(requestParams).unwrap();
|
|
55
|
+
dispatch(
|
|
56
|
+
basketApi.util.updateQueryData(
|
|
57
|
+
'getBasket',
|
|
58
|
+
undefined,
|
|
59
|
+
(draftBasket) => {
|
|
60
|
+
Object.assign(draftBasket, response.basket);
|
|
61
|
+
}
|
|
63
62
|
)
|
|
64
63
|
);
|
|
64
|
+
onBasketUpdate?.(response.basket);
|
|
65
|
+
} catch (error) {
|
|
66
|
+
console.error('Error updating quantity:', error);
|
|
67
|
+
}
|
|
65
68
|
};
|
|
66
69
|
|
|
67
70
|
const deleteProduct = async (productPk?: number) => {
|
|
@@ -18,6 +18,7 @@ import clsx from 'clsx';
|
|
|
18
18
|
|
|
19
19
|
interface Props {
|
|
20
20
|
basket: Basket;
|
|
21
|
+
onBasketUpdate?: (basket: Basket) => void;
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
const voucherCodeFormSchema = (t) =>
|
|
@@ -27,7 +28,7 @@ const voucherCodeFormSchema = (t) =>
|
|
|
27
28
|
|
|
28
29
|
export const Summary = (props: Props) => {
|
|
29
30
|
const { t } = useLocalization();
|
|
30
|
-
const { basket } = props;
|
|
31
|
+
const { basket, onBasketUpdate } = props;
|
|
31
32
|
const router = useRouter();
|
|
32
33
|
const {
|
|
33
34
|
register,
|
|
@@ -53,7 +54,7 @@ export const Summary = (props: Props) => {
|
|
|
53
54
|
const removeVoucherCode = () => {
|
|
54
55
|
removeVoucherCodeMutation()
|
|
55
56
|
.unwrap()
|
|
56
|
-
.then((basket) =>
|
|
57
|
+
.then((basket) => {
|
|
57
58
|
dispatch(
|
|
58
59
|
basketApi.util.updateQueryData(
|
|
59
60
|
'getBasket',
|
|
@@ -62,8 +63,9 @@ export const Summary = (props: Props) => {
|
|
|
62
63
|
Object.assign(draftBasket, basket);
|
|
63
64
|
}
|
|
64
65
|
)
|
|
65
|
-
)
|
|
66
|
-
|
|
66
|
+
);
|
|
67
|
+
onBasketUpdate?.(basket);
|
|
68
|
+
})
|
|
67
69
|
.catch((error: Error) => {
|
|
68
70
|
setError('voucherCode', { message: error.data.non_field_errors });
|
|
69
71
|
});
|
|
@@ -74,7 +76,7 @@ export const Summary = (props: Props) => {
|
|
|
74
76
|
voucher_code: data.voucherCode
|
|
75
77
|
})
|
|
76
78
|
.unwrap()
|
|
77
|
-
.then((basket) =>
|
|
79
|
+
.then((basket) => {
|
|
78
80
|
dispatch(
|
|
79
81
|
basketApi.util.updateQueryData(
|
|
80
82
|
'getBasket',
|
|
@@ -83,8 +85,9 @@ export const Summary = (props: Props) => {
|
|
|
83
85
|
Object.assign(draftBasket, basket);
|
|
84
86
|
}
|
|
85
87
|
)
|
|
86
|
-
)
|
|
87
|
-
|
|
88
|
+
);
|
|
89
|
+
onBasketUpdate?.(basket);
|
|
90
|
+
})
|
|
88
91
|
.catch((error: Error) => {
|
|
89
92
|
setError('voucherCode', { message: error.data.non_field_errors });
|
|
90
93
|
});
|
|
@@ -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';
|
|
@@ -151,12 +152,16 @@ export default function MiniBasket() {
|
|
|
151
152
|
);
|
|
152
153
|
const dispatch = useAppDispatch();
|
|
153
154
|
const { data: basket, isLoading, isSuccess } = useGetBasketQuery();
|
|
155
|
+
const { data: miniBasket } = useGetMiniBasketQuery();
|
|
154
156
|
const { t } = useLocalization();
|
|
155
157
|
const { highlightedItem } = useAppSelector((state) => state.root.miniBasket);
|
|
156
158
|
const [highlightedItemPk, setHighlightedItemPk] = useState(0);
|
|
157
159
|
const [sortedBasket, setSortedBasket] = useState([]);
|
|
158
160
|
|
|
159
|
-
const totalQuantity = useMemo(
|
|
161
|
+
const totalQuantity = useMemo(
|
|
162
|
+
() => miniBasket?.total_quantity ?? 0,
|
|
163
|
+
[miniBasket]
|
|
164
|
+
);
|
|
160
165
|
const miniBasketList = useRef();
|
|
161
166
|
|
|
162
167
|
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;
|