@akinon/projectzero 1.73.0-rc.0 → 1.73.0-rc.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,29 @@
1
1
  # @akinon/projectzero
2
2
 
3
+ ## 1.73.0-rc.10
4
+
5
+ ## 1.73.0-rc.9
6
+
7
+ ## 1.73.0-rc.8
8
+
9
+ ## 1.73.0-rc.7
10
+
11
+ ## 1.73.0-rc.6
12
+
13
+ ## 1.73.0-rc.5
14
+
15
+ ## 1.73.0-rc.4
16
+
17
+ ## 1.73.0-rc.3
18
+
19
+ ### Minor Changes
20
+
21
+ - 7727ae55: ZERO-3073: Refactor basket page to use server-side data fetching and simplify component structure
22
+
23
+ ## 1.73.0-rc.2
24
+
25
+ ## 1.73.0-rc.1
26
+
3
27
  ## 1.73.0-rc.0
4
28
 
5
29
  ### Minor Changes
@@ -1,5 +1,226 @@
1
1
  # projectzeronext
2
2
 
3
+ ## 1.73.0-rc.10
4
+
5
+ ### Minor Changes
6
+
7
+ - 20d6394b: ZERO-3090: Optimize Variant component for better performance
8
+
9
+ ### Patch Changes
10
+
11
+ - @akinon/next@1.73.0-rc.10
12
+ - @akinon/pz-akifast@1.73.0-rc.10
13
+ - @akinon/pz-b2b@1.73.0-rc.10
14
+ - @akinon/pz-basket-gift-pack@1.73.0-rc.10
15
+ - @akinon/pz-bkm@1.73.0-rc.10
16
+ - @akinon/pz-checkout-gift-pack@1.73.0-rc.10
17
+ - @akinon/pz-click-collect@1.73.0-rc.10
18
+ - @akinon/pz-credit-payment@1.73.0-rc.10
19
+ - @akinon/pz-gpay@1.73.0-rc.10
20
+ - @akinon/pz-masterpass@1.73.0-rc.10
21
+ - @akinon/pz-one-click-checkout@1.73.0-rc.10
22
+ - @akinon/pz-otp@1.73.0-rc.10
23
+ - @akinon/pz-pay-on-delivery@1.73.0-rc.10
24
+ - @akinon/pz-saved-card@1.73.0-rc.10
25
+ - @akinon/pz-tabby-extension@1.73.0-rc.10
26
+
27
+ ## 1.73.0-rc.9
28
+
29
+ ### Patch Changes
30
+
31
+ - Updated dependencies [6d6a39d5]
32
+ - @akinon/next@1.73.0-rc.9
33
+ - @akinon/pz-akifast@1.73.0-rc.9
34
+ - @akinon/pz-b2b@1.73.0-rc.9
35
+ - @akinon/pz-basket-gift-pack@1.73.0-rc.9
36
+ - @akinon/pz-bkm@1.73.0-rc.9
37
+ - @akinon/pz-checkout-gift-pack@1.73.0-rc.9
38
+ - @akinon/pz-click-collect@1.73.0-rc.9
39
+ - @akinon/pz-credit-payment@1.73.0-rc.9
40
+ - @akinon/pz-gpay@1.73.0-rc.9
41
+ - @akinon/pz-masterpass@1.73.0-rc.9
42
+ - @akinon/pz-one-click-checkout@1.73.0-rc.9
43
+ - @akinon/pz-otp@1.73.0-rc.9
44
+ - @akinon/pz-pay-on-delivery@1.73.0-rc.9
45
+ - @akinon/pz-saved-card@1.73.0-rc.9
46
+ - @akinon/pz-tabby-extension@1.73.0-rc.9
47
+
48
+ ## 1.73.0-rc.8
49
+
50
+ ### Patch Changes
51
+
52
+ - Updated dependencies [72fd4d67]
53
+ - @akinon/next@1.73.0-rc.8
54
+ - @akinon/pz-akifast@1.73.0-rc.8
55
+ - @akinon/pz-b2b@1.73.0-rc.8
56
+ - @akinon/pz-basket-gift-pack@1.73.0-rc.8
57
+ - @akinon/pz-bkm@1.73.0-rc.8
58
+ - @akinon/pz-checkout-gift-pack@1.73.0-rc.8
59
+ - @akinon/pz-click-collect@1.73.0-rc.8
60
+ - @akinon/pz-credit-payment@1.73.0-rc.8
61
+ - @akinon/pz-gpay@1.73.0-rc.8
62
+ - @akinon/pz-masterpass@1.73.0-rc.8
63
+ - @akinon/pz-one-click-checkout@1.73.0-rc.8
64
+ - @akinon/pz-otp@1.73.0-rc.8
65
+ - @akinon/pz-pay-on-delivery@1.73.0-rc.8
66
+ - @akinon/pz-saved-card@1.73.0-rc.8
67
+ - @akinon/pz-tabby-extension@1.73.0-rc.8
68
+
69
+ ## 1.73.0-rc.7
70
+
71
+ ### Patch Changes
72
+
73
+ - Updated dependencies [37239b31]
74
+ - @akinon/next@1.73.0-rc.7
75
+ - @akinon/pz-akifast@1.73.0-rc.7
76
+ - @akinon/pz-b2b@1.73.0-rc.7
77
+ - @akinon/pz-basket-gift-pack@1.73.0-rc.7
78
+ - @akinon/pz-bkm@1.73.0-rc.7
79
+ - @akinon/pz-checkout-gift-pack@1.73.0-rc.7
80
+ - @akinon/pz-click-collect@1.73.0-rc.7
81
+ - @akinon/pz-credit-payment@1.73.0-rc.7
82
+ - @akinon/pz-gpay@1.73.0-rc.7
83
+ - @akinon/pz-masterpass@1.73.0-rc.7
84
+ - @akinon/pz-one-click-checkout@1.73.0-rc.7
85
+ - @akinon/pz-otp@1.73.0-rc.7
86
+ - @akinon/pz-pay-on-delivery@1.73.0-rc.7
87
+ - @akinon/pz-saved-card@1.73.0-rc.7
88
+ - @akinon/pz-tabby-extension@1.73.0-rc.7
89
+
90
+ ## 1.73.0-rc.6
91
+
92
+ ### Patch Changes
93
+
94
+ - Updated dependencies [31d5a712]
95
+ - @akinon/next@1.73.0-rc.6
96
+ - @akinon/pz-akifast@1.73.0-rc.6
97
+ - @akinon/pz-b2b@1.73.0-rc.6
98
+ - @akinon/pz-basket-gift-pack@1.73.0-rc.6
99
+ - @akinon/pz-bkm@1.73.0-rc.6
100
+ - @akinon/pz-checkout-gift-pack@1.73.0-rc.6
101
+ - @akinon/pz-click-collect@1.73.0-rc.6
102
+ - @akinon/pz-credit-payment@1.73.0-rc.6
103
+ - @akinon/pz-gpay@1.73.0-rc.6
104
+ - @akinon/pz-masterpass@1.73.0-rc.6
105
+ - @akinon/pz-one-click-checkout@1.73.0-rc.6
106
+ - @akinon/pz-otp@1.73.0-rc.6
107
+ - @akinon/pz-pay-on-delivery@1.73.0-rc.6
108
+ - @akinon/pz-saved-card@1.73.0-rc.6
109
+ - @akinon/pz-tabby-extension@1.73.0-rc.6
110
+
111
+ ## 1.73.0-rc.5
112
+
113
+ ### Patch Changes
114
+
115
+ - Updated dependencies [24557b3c]
116
+ - Updated dependencies [178044ed]
117
+ - @akinon/next@1.73.0-rc.5
118
+ - @akinon/pz-akifast@1.73.0-rc.5
119
+ - @akinon/pz-b2b@1.73.0-rc.5
120
+ - @akinon/pz-basket-gift-pack@1.73.0-rc.5
121
+ - @akinon/pz-bkm@1.73.0-rc.5
122
+ - @akinon/pz-checkout-gift-pack@1.73.0-rc.5
123
+ - @akinon/pz-click-collect@1.73.0-rc.5
124
+ - @akinon/pz-credit-payment@1.73.0-rc.5
125
+ - @akinon/pz-gpay@1.73.0-rc.5
126
+ - @akinon/pz-masterpass@1.73.0-rc.5
127
+ - @akinon/pz-one-click-checkout@1.73.0-rc.5
128
+ - @akinon/pz-otp@1.73.0-rc.5
129
+ - @akinon/pz-pay-on-delivery@1.73.0-rc.5
130
+ - @akinon/pz-saved-card@1.73.0-rc.5
131
+ - @akinon/pz-tabby-extension@1.73.0-rc.5
132
+
133
+ ## 1.73.0-rc.4
134
+
135
+ ### Patch Changes
136
+
137
+ - Updated dependencies [c594b469]
138
+ - @akinon/pz-saved-card@1.73.0-rc.4
139
+ - @akinon/next@1.73.0-rc.4
140
+ - @akinon/pz-akifast@1.73.0-rc.4
141
+ - @akinon/pz-b2b@1.73.0-rc.4
142
+ - @akinon/pz-basket-gift-pack@1.73.0-rc.4
143
+ - @akinon/pz-bkm@1.73.0-rc.4
144
+ - @akinon/pz-checkout-gift-pack@1.73.0-rc.4
145
+ - @akinon/pz-click-collect@1.73.0-rc.4
146
+ - @akinon/pz-credit-payment@1.73.0-rc.4
147
+ - @akinon/pz-gpay@1.73.0-rc.4
148
+ - @akinon/pz-masterpass@1.73.0-rc.4
149
+ - @akinon/pz-one-click-checkout@1.73.0-rc.4
150
+ - @akinon/pz-otp@1.73.0-rc.4
151
+ - @akinon/pz-pay-on-delivery@1.73.0-rc.4
152
+ - @akinon/pz-tabby-extension@1.73.0-rc.4
153
+
154
+ ## 1.73.0-rc.3
155
+
156
+ ### Minor Changes
157
+
158
+ - 7727ae55: ZERO-3073: Refactor basket page to use server-side data fetching and simplify component structure
159
+ - 07b2298e: ZERO-3074: Exclude url schemes to init url without locales
160
+
161
+ ### Patch Changes
162
+
163
+ - Updated dependencies [5e3333dc]
164
+ - Updated dependencies [7727ae55]
165
+ - Updated dependencies [07b2298e]
166
+ - @akinon/pz-saved-card@1.73.0-rc.3
167
+ - @akinon/next@1.73.0-rc.3
168
+ - @akinon/pz-checkout-gift-pack@1.73.0-rc.3
169
+ - @akinon/pz-one-click-checkout@1.73.0-rc.3
170
+ - @akinon/pz-basket-gift-pack@1.73.0-rc.3
171
+ - @akinon/pz-pay-on-delivery@1.73.0-rc.3
172
+ - @akinon/pz-tabby-extension@1.73.0-rc.3
173
+ - @akinon/pz-credit-payment@1.73.0-rc.3
174
+ - @akinon/pz-click-collect@1.73.0-rc.3
175
+ - @akinon/pz-masterpass@1.73.0-rc.3
176
+ - @akinon/pz-akifast@1.73.0-rc.3
177
+ - @akinon/pz-gpay@1.73.0-rc.3
178
+ - @akinon/pz-b2b@1.73.0-rc.3
179
+ - @akinon/pz-bkm@1.73.0-rc.3
180
+ - @akinon/pz-otp@1.73.0-rc.3
181
+
182
+ ## 1.73.0-rc.2
183
+
184
+ ### Patch Changes
185
+
186
+ - Updated dependencies [eeb20bea]
187
+ - @akinon/next@1.73.0-rc.2
188
+ - @akinon/pz-akifast@1.73.0-rc.2
189
+ - @akinon/pz-b2b@1.73.0-rc.2
190
+ - @akinon/pz-basket-gift-pack@1.73.0-rc.2
191
+ - @akinon/pz-bkm@1.73.0-rc.2
192
+ - @akinon/pz-checkout-gift-pack@1.73.0-rc.2
193
+ - @akinon/pz-click-collect@1.73.0-rc.2
194
+ - @akinon/pz-credit-payment@1.73.0-rc.2
195
+ - @akinon/pz-gpay@1.73.0-rc.2
196
+ - @akinon/pz-masterpass@1.73.0-rc.2
197
+ - @akinon/pz-one-click-checkout@1.73.0-rc.2
198
+ - @akinon/pz-otp@1.73.0-rc.2
199
+ - @akinon/pz-pay-on-delivery@1.73.0-rc.2
200
+ - @akinon/pz-saved-card@1.73.0-rc.2
201
+ - @akinon/pz-tabby-extension@1.73.0-rc.2
202
+
203
+ ## 1.73.0-rc.1
204
+
205
+ ### Patch Changes
206
+
207
+ - Updated dependencies [fdd255ee]
208
+ - @akinon/next@1.73.0-rc.1
209
+ - @akinon/pz-akifast@1.73.0-rc.1
210
+ - @akinon/pz-b2b@1.73.0-rc.1
211
+ - @akinon/pz-basket-gift-pack@1.73.0-rc.1
212
+ - @akinon/pz-bkm@1.73.0-rc.1
213
+ - @akinon/pz-checkout-gift-pack@1.73.0-rc.1
214
+ - @akinon/pz-click-collect@1.73.0-rc.1
215
+ - @akinon/pz-credit-payment@1.73.0-rc.1
216
+ - @akinon/pz-gpay@1.73.0-rc.1
217
+ - @akinon/pz-masterpass@1.73.0-rc.1
218
+ - @akinon/pz-one-click-checkout@1.73.0-rc.1
219
+ - @akinon/pz-otp@1.73.0-rc.1
220
+ - @akinon/pz-pay-on-delivery@1.73.0-rc.1
221
+ - @akinon/pz-saved-card@1.73.0-rc.1
222
+ - @akinon/pz-tabby-extension@1.73.0-rc.1
223
+
3
224
  ## 1.73.0-rc.0
4
225
 
5
226
  ### Minor Changes
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "projectzeronext",
3
- "version": "1.73.0-rc.0",
3
+ "version": "1.73.0-rc.10",
4
4
  "private": true,
5
5
  "license": "MIT",
6
6
  "scripts": {
@@ -22,21 +22,21 @@
22
22
  "prestart": "pz-prestart"
23
23
  },
24
24
  "dependencies": {
25
- "@akinon/next": "1.73.0-rc.0",
26
- "@akinon/pz-akifast": "1.73.0-rc.0",
27
- "@akinon/pz-b2b": "1.73.0-rc.0",
28
- "@akinon/pz-basket-gift-pack": "1.73.0-rc.0",
29
- "@akinon/pz-bkm": "1.73.0-rc.0",
30
- "@akinon/pz-checkout-gift-pack": "1.73.0-rc.0",
31
- "@akinon/pz-click-collect": "1.73.0-rc.0",
32
- "@akinon/pz-credit-payment": "1.73.0-rc.0",
33
- "@akinon/pz-gpay": "1.73.0-rc.0",
34
- "@akinon/pz-masterpass": "1.73.0-rc.0",
35
- "@akinon/pz-one-click-checkout": "1.73.0-rc.0",
36
- "@akinon/pz-otp": "1.73.0-rc.0",
37
- "@akinon/pz-pay-on-delivery": "1.73.0-rc.0",
38
- "@akinon/pz-saved-card": "1.73.0-rc.0",
39
- "@akinon/pz-tabby-extension": "1.73.0-rc.0",
25
+ "@akinon/next": "1.73.0-rc.10",
26
+ "@akinon/pz-akifast": "1.73.0-rc.10",
27
+ "@akinon/pz-b2b": "1.73.0-rc.10",
28
+ "@akinon/pz-basket-gift-pack": "1.73.0-rc.10",
29
+ "@akinon/pz-bkm": "1.73.0-rc.10",
30
+ "@akinon/pz-checkout-gift-pack": "1.73.0-rc.10",
31
+ "@akinon/pz-click-collect": "1.73.0-rc.10",
32
+ "@akinon/pz-credit-payment": "1.73.0-rc.10",
33
+ "@akinon/pz-gpay": "1.73.0-rc.10",
34
+ "@akinon/pz-masterpass": "1.73.0-rc.10",
35
+ "@akinon/pz-one-click-checkout": "1.73.0-rc.10",
36
+ "@akinon/pz-otp": "1.73.0-rc.10",
37
+ "@akinon/pz-pay-on-delivery": "1.73.0-rc.10",
38
+ "@akinon/pz-saved-card": "1.73.0-rc.10",
39
+ "@akinon/pz-tabby-extension": "1.73.0-rc.10",
40
40
  "@hookform/resolvers": "2.9.0",
41
41
  "@next/third-parties": "14.1.0",
42
42
  "@react-google-maps/api": "2.17.1",
@@ -60,7 +60,7 @@
60
60
  "yup": "0.32.11"
61
61
  },
62
62
  "devDependencies": {
63
- "@akinon/eslint-plugin-projectzero": "1.73.0-rc.0",
63
+ "@akinon/eslint-plugin-projectzero": "1.73.0-rc.10",
64
64
  "@semantic-release/changelog": "6.0.2",
65
65
  "@semantic-release/exec": "6.0.3",
66
66
  "@semantic-release/git": "10.0.1",
@@ -1,87 +1,14 @@
1
- 'use client';
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 { data: basket, isLoading, isSuccess } = useGetBasketQuery();
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
- <div className="w-full text-sm text-black-800 text-center my-4 mb-2 sm:text-base">
74
- <p>{t('basket.empty.content_first')}</p>
75
- <p>{t('basket.empty.content_second')}.</p>
76
- </div>
8
+ const multiBasket: boolean =
9
+ typeof settings.plugins?.multiBasket === 'boolean'
10
+ ? settings.plugins.multiBasket
11
+ : false;
77
12
 
78
- <Link href={ROUTES.HOME} passHref>
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
  }
@@ -2,26 +2,27 @@
2
2
 
3
3
  import { useLocalization } from '@akinon/next/hooks';
4
4
  import { LocaleUrlStrategy } from '@akinon/next/localization';
5
- import { urlLocaleMatcherRegex } from '@akinon/next/utils';
5
+ import { urlLocaleMatcherRegex, urlSchemes } from '@akinon/next/utils';
6
6
  import NextLink, { LinkProps as NextLinkProps } from 'next/link';
7
7
  import { useMemo } from 'react';
8
8
 
9
- interface LinkProps extends NextLinkProps {
10
- children: React.ReactNode;
11
- className?: string;
12
- target?: '_blank' | '_self' | '_parent' | '_top';
13
- }
14
-
15
- export const Link = ({
16
- children,
17
- target,
18
- className,
19
- href,
20
- ...rest
21
- }: LinkProps) => {
9
+ type LinkProps = Omit<
10
+ React.AnchorHTMLAttributes<HTMLAnchorElement>,
11
+ keyof NextLinkProps
12
+ > &
13
+ NextLinkProps;
14
+
15
+ export const Link = ({ children, href, ...rest }: LinkProps) => {
22
16
  const { locale, defaultLocaleValue, localeUrlStrategy } = useLocalization();
23
17
  const formattedHref = useMemo(() => {
24
- if (typeof href !== 'string' || href.startsWith('http')) {
18
+ if (!href) {
19
+ return '#';
20
+ }
21
+
22
+ if (
23
+ typeof href !== 'string' ||
24
+ urlSchemes.some((scheme) => href.startsWith(scheme))
25
+ ) {
25
26
  return href;
26
27
  }
27
28
 
@@ -41,12 +42,7 @@ export const Link = ({
41
42
  }, [href, defaultLocaleValue, locale, localeUrlStrategy]);
42
43
 
43
44
  return (
44
- <NextLink
45
- href={formattedHref}
46
- target={target}
47
- className={className}
48
- {...rest}
49
- >
45
+ <NextLink href={formattedHref} {...rest}>
50
46
  {children}
51
47
  </NextLink>
52
48
  );
@@ -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
- await updateQuantityMutation(requestParams)
53
- .unwrap()
54
- .then((data) =>
55
- dispatch(
56
- basketApi.util.updateQueryData(
57
- 'getBasket',
58
- undefined,
59
- (draftBasket) => {
60
- Object.assign(draftBasket, data.basket);
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,6 +1,6 @@
1
1
  'use client';
2
2
 
3
- import React, { useMemo } from 'react';
3
+ import React, { useMemo, useCallback } from 'react';
4
4
  import { VariantOption, VariantType } from '@akinon/next/types';
5
5
  import { usePathname, useSearchParams } from 'next/navigation';
6
6
  import clsx from 'clsx';
@@ -12,20 +12,23 @@ type VariantProps = {
12
12
  preventDefaultClick?: boolean;
13
13
  } & VariantType;
14
14
 
15
- export const Variant = (props: VariantProps) => {
15
+ export const Variant = ({
16
+ attribute_key,
17
+ attribute_name,
18
+ options,
19
+ preventDefaultClick,
20
+ onChange,
21
+ className
22
+ }: VariantProps) => {
16
23
  const { t } = useLocalization();
17
- const {
18
- attribute_key,
19
- attribute_name,
20
- options,
21
- preventDefaultClick,
22
- onChange
23
- } = props;
24
24
  const router = useRouter();
25
25
  const pathname = usePathname();
26
26
  const searchParams = useSearchParams();
27
27
  // This is a workaround for the fact that we can't use the useSearchParams set method because of this is not implemented in next.js yet. So we have to use the URLSearchParams's set method.
28
- const params = new URLSearchParams(searchParams.toString());
28
+ const params = useMemo(
29
+ () => new URLSearchParams(searchParams.toString()),
30
+ [searchParams]
31
+ );
29
32
 
30
33
  const hasSelected = useMemo(
31
34
  () => options.some((option) => option.is_selected),
@@ -37,22 +40,23 @@ export const Variant = (props: VariantProps) => {
37
40
  [options]
38
41
  );
39
42
 
40
- const handleClick = (option: VariantOption) => {
41
- if (onChange) {
42
- onChange(option);
43
- }
43
+ const handleClick = useCallback(
44
+ (option: VariantOption) => {
45
+ if (onChange) {
46
+ onChange(option);
47
+ }
44
48
 
45
- if (preventDefaultClick) {
46
- return;
47
- }
49
+ if (preventDefaultClick) return;
48
50
 
49
- params.set(attribute_key, option.value);
50
- router.push(`${pathname}?${params.toString()}`);
51
- };
51
+ params.set(attribute_key, option.value);
52
+ router.push(`${pathname}?${params.toString()}`);
53
+ },
54
+ [onChange, preventDefaultClick, params, attribute_key, pathname, router]
55
+ );
52
56
 
53
57
  return (
54
58
  <div
55
- className={clsx('flex flex-col gap-2', props.className)}
59
+ className={clsx('flex flex-col gap-2', className)}
56
60
  data-testid={`product-variant-${attribute_name}`}
57
61
  >
58
62
  <p className="flex gap-2 text-xs leading-4">
@@ -66,30 +70,27 @@ export const Variant = (props: VariantProps) => {
66
70
  className="font-bold"
67
71
  data-testid={`product-variant-${attribute_name}-value`}
68
72
  >
69
- {selectedVariant.value}
73
+ {selectedVariant?.value}
70
74
  </span>
71
75
  )}
72
76
  </p>
73
77
  <div className="flex gap-3 flex-wrap justify-center">
74
78
  {options.map((option, i) => (
75
79
  <button
76
- key={i}
80
+ key={`${i}-${option.value}`}
77
81
  className={clsx(
78
82
  'h-10 px-4 transition-colors duration-200 text-xs',
79
- option.is_selected &&
80
- 'bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground pointer-events-none',
81
- option.is_selectable &&
82
- !option.is_selected &&
83
- 'bg-gray-200 hover:bg-gray-400',
84
- !option.is_selectable &&
85
- !option.is_selected &&
86
- 'border border-gray-300 text-gray-600',
87
- !option.is_selectable &&
88
- !option.is_selectable_without_stock &&
89
- 'border border-gray-300 text-gray-600 cursor-not-allowed',
90
- !option.is_selectable &&
91
- option.is_selected &&
92
- 'border border-dashed border-black bg-white text-gray-600 overflow-hidden relative'
83
+ {
84
+ 'bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground pointer-events-none':
85
+ option.is_selected,
86
+ 'bg-gray-200 hover:bg-gray-400':
87
+ option.is_selectable && !option.is_selected,
88
+ 'border border-gray-300 text-gray-600': !option.is_selectable,
89
+ 'cursor-not-allowed':
90
+ !option.is_selectable && !option.is_selectable_without_stock,
91
+ 'border border-dashed border-black bg-white text-gray-600 overflow-hidden relative':
92
+ !option.is_selectable && option.is_selected
93
+ }
93
94
  )}
94
95
  onClick={() => handleClick(option)}
95
96
  >
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akinon/projectzero",
3
- "version": "1.73.0-rc.0",
3
+ "version": "1.73.0-rc.10",
4
4
  "private": false,
5
5
  "description": "CLI tool to manage your Project Zero Next project",
6
6
  "bin": {