@akinon/projectzero 1.96.0-rc.57 → 1.96.0-snapshot-ZERO-35861-20250908151109

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.
Files changed (54) hide show
  1. package/CHANGELOG.md +5 -236
  2. package/app-template/.env.example +0 -1
  3. package/app-template/CHANGELOG.md +312 -4844
  4. package/app-template/README.md +1 -25
  5. package/app-template/package.json +19 -21
  6. package/app-template/public/locales/en/common.json +1 -42
  7. package/app-template/public/locales/tr/common.json +1 -42
  8. package/app-template/src/app/[commerce]/[locale]/[currency]/basket/page.tsx +82 -9
  9. package/app-template/src/app/[commerce]/[locale]/[currency]/category/[pk]/page.tsx +4 -17
  10. package/app-template/src/app/[commerce]/[locale]/[currency]/flat-page/[pk]/page.tsx +1 -12
  11. package/app-template/src/app/[commerce]/[locale]/[currency]/group-product/[pk]/page.tsx +11 -29
  12. package/app-template/src/app/[commerce]/[locale]/[currency]/landing-page/[pk]/page.tsx +1 -12
  13. package/app-template/src/app/[commerce]/[locale]/[currency]/product/[pk]/page.tsx +10 -28
  14. package/app-template/src/app/[commerce]/[locale]/[currency]/special-page/[pk]/page.tsx +1 -12
  15. package/app-template/src/assets/fonts/pz-icon.css +0 -3
  16. package/app-template/src/components/accordion.tsx +19 -22
  17. package/app-template/src/components/currency-select.tsx +0 -1
  18. package/app-template/src/components/file-input.tsx +7 -27
  19. package/app-template/src/components/input.tsx +1 -2
  20. package/app-template/src/components/modal.tsx +16 -32
  21. package/app-template/src/components/select.tsx +26 -38
  22. package/app-template/src/components/types/index.ts +1 -25
  23. package/app-template/src/hooks/index.ts +0 -2
  24. package/app-template/src/plugins.js +1 -3
  25. package/app-template/src/settings.js +2 -8
  26. package/app-template/src/types/index.ts +3 -74
  27. package/app-template/src/views/account/address-form.tsx +4 -8
  28. package/app-template/src/views/account/content-header.tsx +2 -2
  29. package/app-template/src/views/basket/basket-item.tsx +13 -16
  30. package/app-template/src/views/basket/summary.tsx +7 -10
  31. package/app-template/src/views/guest-login/index.tsx +1 -6
  32. package/app-template/src/views/header/action-menu.tsx +1 -1
  33. package/app-template/src/views/header/search/index.tsx +5 -17
  34. package/app-template/src/views/login/index.tsx +10 -11
  35. package/app-template/src/views/otp-login/index.tsx +6 -11
  36. package/app-template/src/views/product/product-info.tsx +263 -61
  37. package/app-template/src/views/product/slider.tsx +73 -86
  38. package/app-template/src/views/register/index.tsx +11 -15
  39. package/commands/plugins.ts +16 -63
  40. package/dist/commands/plugins.js +16 -57
  41. package/package.json +1 -1
  42. package/app-template/.github/instructions/checkout.instructions.md +0 -678
  43. package/app-template/AGENTS.md +0 -7
  44. package/app-template/src/app/[commerce]/[locale]/[currency]/product/[pk]/loading.tsx +0 -67
  45. package/app-template/src/app/api/image-proxy/route.ts +0 -1
  46. package/app-template/src/app/api/similar-product-list/route.ts +0 -1
  47. package/app-template/src/app/api/similar-products/route.ts +0 -1
  48. package/app-template/src/hooks/use-product-cart.ts +0 -77
  49. package/app-template/src/hooks/use-stock-alert.ts +0 -74
  50. package/app-template/src/utils/variant-validation.ts +0 -41
  51. package/app-template/src/views/basket/basket-content.tsx +0 -106
  52. package/app-template/src/views/product/product-actions.tsx +0 -165
  53. package/app-template/src/views/product/product-share.tsx +0 -56
  54. package/app-template/src/views/product/product-variants.tsx +0 -26
@@ -1,21 +1,11 @@
1
1
  import { useState } from 'react';
2
2
  import { forwardRef } from 'react';
3
3
  import { FileInputProps } from '@theme/components/types';
4
+ import clsx from 'clsx';
4
5
  import { useLocalization } from '@akinon/next/hooks';
5
- import { twMerge } from 'tailwind-merge';
6
6
 
7
7
  export const FileInput = forwardRef<HTMLInputElement, FileInputProps>(
8
- function FileInput(
9
- {
10
- buttonClassName,
11
- onChange,
12
- fileClassName,
13
- fileNameWrapperClassName,
14
- fileInputClassName,
15
- ...props
16
- },
17
- ref
18
- ) {
8
+ function FileInput({ className, onChange, ...props }, ref) {
19
9
  const { t } = useLocalization();
20
10
  const [fileNames, setFileNames] = useState<string[]>([]);
21
11
 
@@ -34,34 +24,24 @@ export const FileInput = forwardRef<HTMLInputElement, FileInputProps>(
34
24
  type="file"
35
25
  {...props}
36
26
  ref={ref}
37
- className={twMerge(
38
- 'absolute inset-0 w-full h-full opacity-0 cursor-pointer',
39
- fileInputClassName
40
- )}
27
+ className="absolute inset-0 w-full h-full opacity-0 cursor-pointer"
41
28
  onChange={handleFileChange}
42
29
  />
43
30
  <button
44
31
  type="button"
45
- className={twMerge(
46
- 'bg-primary text-white py-2 px-4 text-sm',
47
- buttonClassName
48
- )}
32
+ className={clsx('bg-primary text-white py-2 px-4 text-sm', className)}
49
33
  >
50
34
  {t('common.file_input.select_file')}
51
35
  </button>
52
- <div
53
- className={twMerge('mt-1 text-gray-500', fileNameWrapperClassName)}
54
- >
36
+ <div className="mt-1 text-gray-500">
55
37
  {fileNames.length > 0 ? (
56
- <ul className={twMerge('list-disc pl-4 text-xs', fileClassName)}>
38
+ <ul className="list-disc pl-4 text-xs">
57
39
  {fileNames.map((name, index) => (
58
40
  <li key={index}>{name}</li>
59
41
  ))}
60
42
  </ul>
61
43
  ) : (
62
- <span className={twMerge('text-xs', fileClassName)}>
63
- {t('common.file_input.no_file')}
64
- </span>
44
+ <span className="text-xs">{t('common.file_input.no_file')}</span>
65
45
  )}
66
46
  </div>
67
47
  </div>
@@ -72,8 +72,7 @@ export const Input = forwardRef<
72
72
  hasFloatingLabel,
73
73
  'mb-2': !hasFloatingLabel,
74
74
  '-translate-y-2 bg-white inline-flex h-auto':
75
- hasFloatingLabel &&
76
- (focused || hasValue || props.value || props.format)
75
+ hasFloatingLabel && (focused || hasValue)
77
76
  })
78
77
  )}
79
78
  >
@@ -4,7 +4,16 @@ import ReactPortal from './react-portal';
4
4
  import { Icon } from './icon';
5
5
  import { twMerge } from 'tailwind-merge';
6
6
  import { useEffect } from 'react';
7
- import { ModalProps } from '@theme/types';
7
+
8
+ export interface ModalProps {
9
+ portalId: string;
10
+ children?: React.ReactNode;
11
+ open?: boolean;
12
+ setOpen?: (open: boolean) => void;
13
+ title?: React.ReactNode;
14
+ showCloseButton?: React.ReactNode;
15
+ className?: string;
16
+ }
8
17
 
9
18
  export const Modal = (props: ModalProps) => {
10
19
  const {
@@ -14,14 +23,7 @@ export const Modal = (props: ModalProps) => {
14
23
  setOpen,
15
24
  title = '',
16
25
  showCloseButton = true,
17
- className,
18
- overlayClassName,
19
- headerWrapperClassName,
20
- titleClassName,
21
- closeButtonClassName,
22
- iconName = 'close',
23
- iconSize = 16,
24
- iconClassName
26
+ className
25
27
  } = props;
26
28
 
27
29
  useEffect(() => {
@@ -36,12 +38,7 @@ export const Modal = (props: ModalProps) => {
36
38
 
37
39
  return (
38
40
  <ReactPortal wrapperId={portalId}>
39
- <div
40
- className={twMerge(
41
- 'fixed top-0 left-0 w-screen h-screen bg-primary bg-opacity-60 z-50',
42
- overlayClassName
43
- )}
44
- />
41
+ <div className="fixed top-0 left-0 w-screen h-screen bg-primary bg-opacity-60 z-50" />
45
42
  <section
46
43
  className={twMerge(
47
44
  'fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 z-50 bg-white',
@@ -49,28 +46,15 @@ export const Modal = (props: ModalProps) => {
49
46
  )}
50
47
  >
51
48
  {(showCloseButton || title) && (
52
- <div
53
- className={twMerge(
54
- 'flex px-6 py-4 border-b border-gray-400',
55
- headerWrapperClassName
56
- )}
57
- >
58
- {title && (
59
- <h3 className={twMerge('text-lg font-light', titleClassName)}>
60
- {title}
61
- </h3>
62
- )}
49
+ <div className="flex px-6 py-4 border-b border-gray-400">
50
+ {title && <h3 className="text-lg font-light">{title}</h3>}
63
51
  {showCloseButton && (
64
52
  <button
65
53
  type="button"
66
54
  onClick={() => setOpen(false)}
67
- className={twMerge('ml-auto', closeButtonClassName)}
55
+ className="ml-auto"
68
56
  >
69
- <Icon
70
- name={iconName}
71
- size={iconSize}
72
- className={iconClassName}
73
- />
57
+ <Icon name="close" size={16} />
74
58
  </button>
75
59
  )}
76
60
  </div>
@@ -14,18 +14,14 @@ const Select = forwardRef<HTMLSelectElement, SelectProps>((props, ref) => {
14
14
  error,
15
15
  label,
16
16
  required = false,
17
- labelClassName,
18
17
  ...rest
19
18
  } = props;
20
19
 
21
20
  return (
22
21
  <label
23
- className={twMerge(
24
- clsx('flex flex-col relative text-xs text-gray-800', {
25
- 'pl-7': icon
26
- }),
27
- labelClassName
28
- )}
22
+ className={clsx('flex flex-col relative text-xs text-gray-800', {
23
+ 'pl-7': icon
24
+ })}
29
25
  >
30
26
  {icon && (
31
27
  <Icon
@@ -36,40 +32,32 @@ const Select = forwardRef<HTMLSelectElement, SelectProps>((props, ref) => {
36
32
  )}
37
33
 
38
34
  {label && (
39
- <span className={twMerge('mb-1', labelClassName)}>
35
+ <span className="mb-1">
40
36
  {label} {required && <span className="text-secondary">*</span>}
41
37
  </span>
42
38
  )}
43
- <div className="relative">
44
- <select
45
- {...rest}
46
- ref={ref}
47
- className={twMerge(
48
- clsx(
49
- 'cursor-pointer truncate h-10 w-40 px-2.5 shrink-0 outline-none',
50
- !borderless &&
51
- 'border border-gray-200 transition-all duration-150 hover:border-primary',
52
- 'appearance-none bg-transparent'
53
- ),
54
- className
55
- )}
56
- >
57
- {options?.map((option) => (
58
- <option
59
- key={option.value}
60
- value={option.value}
61
- className={option.class}
62
- >
63
- {option.label}
64
- </option>
65
- ))}
66
- </select>
67
- <div className="pointer-events-none absolute inset-y-0 right-0 flex items-center px-2 text-gray-700">
68
- <svg className="h-4 w-4 fill-current" viewBox="0 0 20 20">
69
- <path d="M9.293 12.95l.707.707L15.657 8l-1.414-1.414L10 10.828 5.757 6.586 4.343 8z" />
70
- </svg>
71
- </div>
72
- </div>
39
+ <select
40
+ {...rest}
41
+ ref={ref}
42
+ className={twMerge(
43
+ clsx(
44
+ 'cursor-pointer truncate h-10 w-40 px-2.5 shrink-0 outline-none',
45
+ !borderless &&
46
+ 'border border-gray-200 transition-all duration-150 hover:border-primary'
47
+ ),
48
+ className
49
+ )}
50
+ >
51
+ {options?.map((option) => (
52
+ <option
53
+ key={option.value}
54
+ value={option.value}
55
+ className={option.class}
56
+ >
57
+ {option.label}
58
+ </option>
59
+ ))}
60
+ </select>
73
61
  {error && (
74
62
  <span className="mt-1 text-sm text-error">{error.message}</span>
75
63
  )}
@@ -29,13 +29,7 @@ export interface PaginationProps {
29
29
  isLoading?: boolean;
30
30
  }
31
31
 
32
- export interface FileInputProps extends React.HTMLProps<HTMLInputElement> {
33
- fileClassName?: string;
34
- fileNameWrapperClassName?: string;
35
- fileInputClassName?: string;
36
- onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void;
37
- buttonClassName?: string;
38
- }
32
+ export type FileInputProps = React.HTMLProps<HTMLInputElement>;
39
33
 
40
34
  export type RadioProps = React.HTMLProps<HTMLInputElement>;
41
35
 
@@ -64,7 +58,6 @@ export interface SelectProps extends React.HTMLProps<HTMLSelectElement> {
64
58
  iconSize?: number;
65
59
  error?: FieldError | undefined;
66
60
  required?: boolean;
67
- labelClassName?: string;
68
61
  }
69
62
  export interface IconProps extends React.ComponentPropsWithRef<'i'> {
70
63
  name: string;
@@ -98,20 +91,3 @@ export interface BadgeProps {
98
91
  children: ReactNode;
99
92
  className?: string;
100
93
  }
101
-
102
- export type AccordionProps = {
103
- isCollapse?: boolean;
104
- collapseClassName?: string;
105
- title?: string;
106
- subTitle?: string;
107
- icons?: string[];
108
- iconSize?: number;
109
- iconColor?: string;
110
- children?: ReactNode;
111
- headerClassName?: string;
112
- className?: string;
113
- titleClassName?: string;
114
- subTitleClassName?: string;
115
- dataTestId?: string;
116
- contentClassName?: string;
117
- };
@@ -1,3 +1 @@
1
- export * from './use-contract';
2
- export * from './use-fav-button';
3
1
  export * from './use-add-product-to-basket';
@@ -14,7 +14,5 @@ module.exports = [
14
14
  'pz-akifast',
15
15
  'pz-saved-card',
16
16
  'pz-tabby-extension',
17
- 'pz-tamara-extension',
18
- 'pz-hepsipay',
19
- 'pz-similar-products'
17
+ 'pz-tamara-extension'
20
18
  ];
@@ -1,10 +1,6 @@
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
-
8
4
  const commerceUrl = encodeURI(process.env.SERVICE_BACKEND_URL ?? 'default');
9
5
 
10
6
  /** @type {import('@akinon/next/types').Settings} */
@@ -18,7 +14,6 @@ module.exports = {
18
14
  { translationKey: 'size', key: 'size' }
19
15
  ],
20
16
  localization: {
21
- // If there is one locale in the locales array, the default locale will be hidden in the URL.
22
17
  locales: [
23
18
  {
24
19
  label: 'EN',
@@ -46,7 +41,7 @@ module.exports = {
46
41
  }
47
42
  ],
48
43
  defaultLocaleValue: 'en',
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.
44
+ localeUrlStrategy: LocaleUrlStrategy.HideDefaultLocale,
50
45
  redirectToDefaultLocale: true,
51
46
  defaultCurrencyCode: 'usd'
52
47
  },
@@ -67,6 +62,5 @@ module.exports = {
67
62
  redis: {
68
63
  defaultExpirationTime: 900 // 15 min
69
64
  },
70
- customNotFoundEnabled: false,
71
- commerceRedirectionIgnoreList: ['/users/reset']
65
+ customNotFoundEnabled: false
72
66
  };
@@ -1,58 +1,4 @@
1
1
  export * from '@theme/types/widgets';
2
- import { SignInOptions } from 'next-auth/react';
3
-
4
- /**
5
- * Form types for authentication
6
- */
7
- export enum FormType {
8
- login = 'login',
9
- register = 'register',
10
- loyaltyRegister = 'loyaltyRegister',
11
- otpLogin = 'otpLogin'
12
- }
13
-
14
- /**
15
- * Extended SignInOptions with additional form type support for Project Zero Next authentication
16
- *
17
- * @example
18
- * ```typescript
19
- * // For standard login
20
- * signIn('default', {
21
- * ...data,
22
- * formType: FormType.login
23
- * } as PzSignInOptions);
24
- *
25
- * // For standard user registration
26
- * signIn('default', {
27
- * ...data,
28
- * formType: FormType.register
29
- * } as PzSignInOptions);
30
- *
31
- * // For loyalty program registration
32
- * signIn('default', {
33
- * ...data,
34
- * formType: FormType.loyaltyRegister
35
- * } as PzSignInOptions);
36
- *
37
- * // For OTP login
38
- * signIn('default', {
39
- * ...data,
40
- * formType: FormType.otpLogin
41
- * } as PzSignInOptions);
42
- * ```
43
- */
44
- export interface PzSignInOptions extends SignInOptions {
45
- /**
46
- * Type of the authentication form being submitted
47
- *
48
- * Available options:
49
- * - `FormType.login` - Standard email/password login
50
- * - `FormType.register` - Standard user registration
51
- * - `FormType.loyaltyRegister` - Loyalty program registration (use this for loyalty signups)
52
- * - `FormType.otpLogin` - OTP-based login via SMS
53
- */
54
- formType?: FormType;
55
- }
56
2
 
57
3
  export type RegisterFormType = {
58
4
  email: string;
@@ -64,14 +10,14 @@ export type RegisterFormType = {
64
10
  kvkk_confirm: boolean;
65
11
  email_allowed: boolean;
66
12
  sms_allowed: boolean;
67
- formType: FormType;
13
+ formType: string;
68
14
  locale: string;
69
15
  };
70
16
 
71
17
  export type LoginFormType = {
72
18
  email: string;
73
19
  password: string;
74
- formType: FormType;
20
+ formType: string;
75
21
  locale: string;
76
22
  };
77
23
 
@@ -79,7 +25,7 @@ export type OtpLoginFormType = {
79
25
  phone: string;
80
26
  code?: string;
81
27
  locale: string;
82
- formType: FormType;
28
+ formType: string;
83
29
  };
84
30
 
85
31
  export enum WIDGET_TYPE {
@@ -125,20 +71,3 @@ export interface SeoProps {
125
71
  ogPriceAmount?: string;
126
72
  ogPriceCurrency?: string;
127
73
  }
128
-
129
- export interface ModalProps {
130
- portalId: string;
131
- children?: React.ReactNode;
132
- open?: boolean;
133
- setOpen?: (open: boolean) => void;
134
- title?: React.ReactNode;
135
- showCloseButton?: React.ReactNode;
136
- className?: string;
137
- overlayClassName?: string;
138
- headerWrapperClassName?: string;
139
- titleClassName?: string;
140
- closeButtonClassName?: string;
141
- iconName?: string;
142
- iconSize?: number;
143
- iconClassName?: string;
144
- }
@@ -259,25 +259,23 @@ export const AddressForm = (props: Props) => {
259
259
  {/* TODO: Fix select and textarea components */}
260
260
 
261
261
  <Select
262
- className="w-full border-gray-500 text-sm"
262
+ className="w-full border-gray-500 text-sm mt-2"
263
263
  options={countryOptions}
264
264
  {...register('country')}
265
265
  error={errors.country}
266
266
  data-testid="address-form-country"
267
267
  label={t('account.address_book.form.country.title')}
268
- labelClassName="mb-3"
269
268
  required
270
269
  />
271
270
 
272
271
  {city && (
273
272
  <Select
274
- className="w-full border-gray-500 text-sm"
273
+ className="w-full border-gray-500 text-sm mt-2"
275
274
  options={cityOptions}
276
275
  {...register('city')}
277
276
  error={errors.city}
278
277
  data-testid="address-form-city"
279
278
  label={t('account.address_book.form.province.title')}
280
- labelClassName="mb-3"
281
279
  required
282
280
  />
283
281
  )}
@@ -285,26 +283,24 @@ export const AddressForm = (props: Props) => {
285
283
  <div className="flex gap-4">
286
284
  <div className="flex-1">
287
285
  <Select
288
- className="w-full border-gray-500 text-sm"
286
+ className="w-full border-gray-500 text-sm mt-2"
289
287
  options={townshipOptions}
290
288
  {...register('township')}
291
289
  error={errors.township}
292
290
  data-testid="address-form-township"
293
291
  label={t('account.address_book.form.township.title')}
294
- labelClassName="mb-3"
295
292
  required
296
293
  />
297
294
  </div>
298
295
  {district && (
299
296
  <div className="flex-1">
300
297
  <Select
301
- className="w-full border-gray-500 text-sm"
298
+ className="w-full border-gray-500 text-sm mt-2"
302
299
  options={districtOptions}
303
300
  {...register('district')}
304
301
  error={errors.district}
305
302
  data-testid="address-form-district"
306
303
  label={t('account.address_book.form.district.title')}
307
- labelClassName="mb-3"
308
304
  required
309
305
  />
310
306
  </div>
@@ -31,13 +31,13 @@ export const ContentHeader = (props: Props) => {
31
31
  </h3>
32
32
  <Select
33
33
  onChange={handleChange}
34
- className="w-full mb-4 md:mb-0 md:w-56 text-xs"
34
+ className="w-full mb-4 md:mb-0 md:w-56 md:mr-4 text-xs"
35
35
  options={orders}
36
36
  data-testid="account-orders-header-select"
37
37
  ></Select>
38
38
  <Button
39
39
  className={clsx(
40
- 'w-full md:w-56 md:ms-4',
40
+ 'w-full md:w-56',
41
41
  isButtonDisabled &&
42
42
  'hover:bg-black hover:text-white disabled:opacity-75'
43
43
  )}
@@ -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 { Basket, BasketItem as BasketItemType } from '@akinon/next/types';
6
+ import { 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,12 +19,11 @@ import { pushRemoveFromCart } from '@theme/utils/gtm';
19
19
  interface Props {
20
20
  basketItem?: BasketItemType;
21
21
  namespace?: string;
22
- onBasketUpdate?: (basket: Basket) => void;
23
22
  }
24
23
 
25
24
  export const BasketItem = (props: Props) => {
26
25
  const { t } = useLocalization();
27
- const { basketItem, namespace, onBasketUpdate } = props;
26
+ const { basketItem, namespace } = props;
28
27
  const [updateQuantityMutation] = useUpdateQuantityMutation();
29
28
  const dispatch = useAppDispatch();
30
29
  const [isRemoveBasketModalOpen, setRemoveBasketModalOpen] = useState(false);
@@ -50,21 +49,19 @@ export const BasketItem = (props: Props) => {
50
49
  requestParams.namespace = namespace;
51
50
  }
52
51
 
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
- }
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
+ )
62
63
  )
63
64
  );
64
- onBasketUpdate?.(response.basket);
65
- } catch (error) {
66
- console.error('Error updating quantity:', error);
67
- }
68
65
  };
69
66
 
70
67
  const deleteProduct = async (productPk?: number) => {
@@ -18,7 +18,6 @@ import clsx from 'clsx';
18
18
 
19
19
  interface Props {
20
20
  basket: Basket;
21
- onBasketUpdate?: (basket: Basket) => void;
22
21
  }
23
22
 
24
23
  const voucherCodeFormSchema = (t) =>
@@ -28,7 +27,7 @@ const voucherCodeFormSchema = (t) =>
28
27
 
29
28
  export const Summary = (props: Props) => {
30
29
  const { t } = useLocalization();
31
- const { basket, onBasketUpdate } = props;
30
+ const { basket } = props;
32
31
  const router = useRouter();
33
32
  const {
34
33
  register,
@@ -54,7 +53,7 @@ export const Summary = (props: Props) => {
54
53
  const removeVoucherCode = () => {
55
54
  removeVoucherCodeMutation()
56
55
  .unwrap()
57
- .then((basket) => {
56
+ .then((basket) =>
58
57
  dispatch(
59
58
  basketApi.util.updateQueryData(
60
59
  'getBasket',
@@ -63,9 +62,8 @@ export const Summary = (props: Props) => {
63
62
  Object.assign(draftBasket, basket);
64
63
  }
65
64
  )
66
- );
67
- onBasketUpdate?.(basket);
68
- })
65
+ )
66
+ )
69
67
  .catch((error: Error) => {
70
68
  setError('voucherCode', { message: error.data.non_field_errors });
71
69
  });
@@ -76,7 +74,7 @@ export const Summary = (props: Props) => {
76
74
  voucher_code: data.voucherCode
77
75
  })
78
76
  .unwrap()
79
- .then((basket) => {
77
+ .then((basket) =>
80
78
  dispatch(
81
79
  basketApi.util.updateQueryData(
82
80
  'getBasket',
@@ -85,9 +83,8 @@ export const Summary = (props: Props) => {
85
83
  Object.assign(draftBasket, basket);
86
84
  }
87
85
  )
88
- );
89
- onBasketUpdate?.(basket);
90
- })
86
+ )
87
+ )
91
88
  .catch((error: Error) => {
92
89
  setError('voucherCode', { message: error.data.non_field_errors });
93
90
  });
@@ -51,14 +51,9 @@ const GuestLogin = () => {
51
51
  ).unwrap();
52
52
 
53
53
  Object.keys(response?.errors || {}).forEach((key) => {
54
- const errorValue = response?.errors[key];
55
- const message = Array.isArray(errorValue)
56
- ? errorValue.join(', ')
57
- : errorValue || '';
58
-
59
54
  setError(key as keyof GuestLoginFormParams, {
60
55
  type: 'custom',
61
- message
56
+ message: response?.errors[key]?.join(', ')
62
57
  });
63
58
  });
64
59
  } catch (error) {
@@ -76,7 +76,7 @@ export default function ActionMenu() {
76
76
  : 'bg-secondary-500 text-white'
77
77
  )}
78
78
  >
79
- <span data-testid="header-basket-count">{totalQuantity}</span>
79
+ {totalQuantity}
80
80
  </Badge>
81
81
  ),
82
82
  miniBasket: <MiniBasket />