@akinon/projectzero 2.0.0-beta.19 → 2.0.0-beta.20

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 (223) hide show
  1. package/CHANGELOG.md +9 -7
  2. package/app-template/CHANGELOG.md +251 -204
  3. package/app-template/akinon.json +1 -1
  4. package/app-template/package.json +28 -28
  5. package/app-template/public/amex.svg +12 -0
  6. package/app-template/public/apple-pay.svg +16 -0
  7. package/app-template/public/assets/images/product-placeholder-1.jpg +0 -0
  8. package/app-template/public/assets/images/product-placeholder-2.jpg +0 -0
  9. package/app-template/public/assets/images/product-placeholder-3.jpg +0 -0
  10. package/app-template/public/assets/images/product-placeholder-4.jpg +0 -0
  11. package/app-template/public/google-pay.svg +16 -0
  12. package/app-template/public/locales/en/account.json +6 -3
  13. package/app-template/public/locales/en/auth.json +6 -7
  14. package/app-template/public/locales/en/basket.json +6 -6
  15. package/app-template/public/locales/en/blog.json +7 -0
  16. package/app-template/public/locales/en/category.json +3 -1
  17. package/app-template/public/locales/en/checkout.json +5 -4
  18. package/app-template/public/locales/en/common.json +11 -2
  19. package/app-template/public/locales/en/forgot_password.json +6 -7
  20. package/app-template/public/locales/en/product.json +4 -3
  21. package/app-template/public/locales/tr/account.json +6 -3
  22. package/app-template/public/locales/tr/auth.json +16 -17
  23. package/app-template/public/locales/tr/basket.json +4 -4
  24. package/app-template/public/locales/tr/blog.json +7 -0
  25. package/app-template/public/locales/tr/category.json +3 -1
  26. package/app-template/public/locales/tr/checkout.json +39 -38
  27. package/app-template/public/locales/tr/common.json +10 -1
  28. package/app-template/public/locales/tr/forgot_password.json +12 -13
  29. package/app-template/public/locales/tr/product.json +1 -0
  30. package/app-template/public/logo.svg +3 -27
  31. package/app-template/public/mastercard.svg +14 -0
  32. package/app-template/public/promotion-banner.jpg +0 -0
  33. package/app-template/public/shop-pay.svg +12 -0
  34. package/app-template/public/visa.svg +12 -0
  35. package/app-template/src/app/[commerce]/[locale]/[currency]/blog/[slug]/page.tsx +118 -0
  36. package/app-template/src/app/[commerce]/[locale]/[currency]/pages/[slug]/page.tsx +15 -0
  37. package/app-template/src/app/api/theme-settings/route.ts +12 -0
  38. package/app-template/src/assets/fonts/pz-icon.css +211 -49
  39. package/app-template/src/assets/fonts/pz-icon.eot +0 -0
  40. package/app-template/src/assets/fonts/pz-icon.html +486 -0
  41. package/app-template/src/assets/fonts/pz-icon.scss +373 -49
  42. package/app-template/src/assets/fonts/pz-icon.svg +215 -53
  43. package/app-template/src/assets/fonts/pz-icon.ttf +0 -0
  44. package/app-template/src/assets/fonts/pz-icon.woff +0 -0
  45. package/app-template/src/assets/fonts/pz-icon.woff2 +0 -0
  46. package/app-template/src/assets/globals.scss +4 -0
  47. package/app-template/src/assets/icons/arrow-right.svg +3 -0
  48. package/app-template/src/assets/icons/cart.svg +4 -12
  49. package/app-template/src/assets/icons/check.svg +2 -18
  50. package/app-template/src/assets/icons/chevron-down.svg +2 -7
  51. package/app-template/src/assets/icons/delete.svg +3 -0
  52. package/app-template/src/assets/icons/facebook.svg +2 -8
  53. package/app-template/src/assets/icons/fav-off.svg +5 -0
  54. package/app-template/src/assets/icons/fav-on.svg +5 -0
  55. package/app-template/src/assets/icons/filter-and-sort.svg +3 -0
  56. package/app-template/src/assets/icons/heart.svg +3 -0
  57. package/app-template/src/assets/icons/instagram.svg +2 -13
  58. package/app-template/src/assets/icons/materials.svg +3 -0
  59. package/app-template/src/assets/icons/person.svg +4 -0
  60. package/app-template/src/assets/icons/pinterest.svg +5 -11
  61. package/app-template/src/assets/icons/ruler.svg +3 -0
  62. package/app-template/src/assets/icons/search.svg +8 -11
  63. package/app-template/src/assets/icons/share.svg +2 -9
  64. package/app-template/src/assets/icons/snapchat.svg +3 -0
  65. package/app-template/src/assets/icons/tiktok.svg +3 -0
  66. package/app-template/src/assets/icons/tumblr.svg +6 -0
  67. package/app-template/src/assets/icons/twitter.svg +2 -10
  68. package/app-template/src/assets/icons/vimeo.svg +3 -0
  69. package/app-template/src/assets/icons/youtube.svg +3 -0
  70. package/app-template/src/assets/icons/zoom.svg +8 -0
  71. package/app-template/src/components/accordion.tsx +33 -11
  72. package/app-template/src/components/action-tooltip.tsx +160 -0
  73. package/app-template/src/components/currency-select.tsx +149 -4
  74. package/app-template/src/components/icon.tsx +5 -6
  75. package/app-template/src/components/index.ts +4 -1
  76. package/app-template/src/components/language-select.tsx +88 -2
  77. package/app-template/src/components/pagination.tsx +132 -20
  78. package/app-template/src/components/quantity-input.tsx +63 -0
  79. package/app-template/src/components/quantity-selector.tsx +203 -0
  80. package/app-template/src/components/route-handler.tsx +50 -0
  81. package/app-template/src/components/select.tsx +89 -69
  82. package/app-template/src/components/types/index.ts +26 -0
  83. package/app-template/src/components/widget-content.tsx +323 -0
  84. package/app-template/src/data/server/theme.ts +70 -0
  85. package/app-template/src/hooks/use-fav-button.tsx +5 -2
  86. package/app-template/src/hooks/use-product-cart.ts +11 -8
  87. package/app-template/src/hooks/use-theme-settings.ts +42 -0
  88. package/app-template/src/lib/fonts.ts +149 -0
  89. package/app-template/src/settings.js +2 -2
  90. package/app-template/src/types/hookform-resolvers-yup.d.ts +28 -0
  91. package/app-template/src/types/widget.ts +169 -0
  92. package/app-template/src/utils/formatDate.ts +48 -0
  93. package/app-template/src/utils/styles.ts +71 -0
  94. package/app-template/src/views/account/contact-form.tsx +147 -130
  95. package/app-template/src/views/basket/basket-item.tsx +691 -107
  96. package/app-template/src/views/basket/basket-summary-context.tsx +560 -0
  97. package/app-template/src/views/basket/designer-context.tsx +617 -0
  98. package/app-template/src/views/basket/index.ts +2 -0
  99. package/app-template/src/views/basket/summary.tsx +496 -75
  100. package/app-template/src/views/breadcrumb/breadcrumb-client.tsx +190 -0
  101. package/app-template/src/views/breadcrumb/breadcrumb-registrar.tsx +286 -0
  102. package/app-template/src/views/breadcrumb/constants.ts +15 -0
  103. package/app-template/src/views/breadcrumb/index.tsx +127 -0
  104. package/app-template/src/views/breadcrumb.tsx +13 -38
  105. package/app-template/src/views/category/category-banner.tsx +4 -23
  106. package/app-template/src/views/category/category-header.tsx +289 -66
  107. package/app-template/src/views/category/category-info.tsx +173 -24
  108. package/app-template/src/views/category/filters/filter-item.tsx +138 -42
  109. package/app-template/src/views/category/filters/index.tsx +208 -48
  110. package/app-template/src/views/category/layout.tsx +7 -4
  111. package/app-template/src/views/category/native-widget-context.tsx +257 -0
  112. package/app-template/src/views/category/product-list-registrar.tsx +665 -0
  113. package/app-template/src/views/checkout/auth.tsx +64 -40
  114. package/app-template/src/views/checkout/checkout-address-registrar.tsx +254 -0
  115. package/app-template/src/views/checkout/checkout-buttons-registrar.tsx +183 -0
  116. package/app-template/src/views/checkout/checkout-delivery-method-registrar.tsx +259 -0
  117. package/app-template/src/views/checkout/checkout-payment-options-registrar.tsx +253 -0
  118. package/app-template/src/views/checkout/checkout-summary-registrar.tsx +183 -0
  119. package/app-template/src/views/checkout/constants.ts +5 -0
  120. package/app-template/src/views/checkout/index.tsx +5 -0
  121. package/app-template/src/views/checkout/layout/header.tsx +9 -5
  122. package/app-template/src/views/checkout/steps/payment/index.tsx +5 -2
  123. package/app-template/src/views/checkout/steps/payment/options/credit-card/index.tsx +72 -1
  124. package/app-template/src/views/checkout/steps/payment/options/masterpass-rest.tsx +15 -0
  125. package/app-template/src/views/checkout/steps/payment/options/saved-card.tsx +18 -0
  126. package/app-template/src/views/checkout/steps/payment/payment-option-buttons.tsx +171 -40
  127. package/app-template/src/views/checkout/steps/shipping/address-box.tsx +74 -12
  128. package/app-template/src/views/checkout/steps/shipping/addresses.tsx +128 -45
  129. package/app-template/src/views/checkout/steps/shipping/shipping-options.tsx +232 -27
  130. package/app-template/src/views/checkout/summary.tsx +303 -29
  131. package/app-template/src/views/footer/footer-app-banner-context.tsx +326 -0
  132. package/app-template/src/views/footer/footer-bottom-context.tsx +215 -0
  133. package/app-template/src/views/footer/footer-bottom-wrapper.tsx +74 -0
  134. package/app-template/src/views/footer/footer-layout-constants.ts +35 -0
  135. package/app-template/src/views/footer/footer-layout-registrar.tsx +342 -0
  136. package/app-template/src/views/footer/footer-layout-switcher.tsx +110 -0
  137. package/app-template/src/views/footer/footer-menu-context.tsx +211 -0
  138. package/app-template/src/views/footer/footer-native-widgets.tsx +60 -0
  139. package/app-template/src/views/footer/footer-social-context.tsx +254 -0
  140. package/app-template/src/views/footer/footer-subscription-context.tsx +210 -0
  141. package/app-template/src/views/footer/footer-utils.ts +43 -0
  142. package/app-template/src/views/footer/footer-value-props-context.tsx +326 -0
  143. package/app-template/src/views/footer/logo-settings.ts +183 -0
  144. package/app-template/src/views/footer/native-widget-config.ts +262 -0
  145. package/app-template/src/views/footer/subscription-settings.ts +122 -0
  146. package/app-template/src/views/footer/use-footer-logo.ts +162 -0
  147. package/app-template/src/views/footer.tsx +415 -13
  148. package/app-template/src/views/guest-login/index.tsx +62 -58
  149. package/app-template/src/views/header/action-menu.tsx +277 -45
  150. package/app-template/src/views/header/band.tsx +6 -21
  151. package/app-template/src/views/header/designer-context.tsx +261 -0
  152. package/app-template/src/views/header/header-announcement-registrar.tsx +267 -0
  153. package/app-template/src/views/header/header-client-wrapper.tsx +496 -0
  154. package/app-template/src/views/header/header-content.tsx +1026 -0
  155. package/app-template/src/views/header/header-currency-registrar.tsx +348 -0
  156. package/app-template/src/views/header/header-icons-context.tsx +262 -0
  157. package/app-template/src/views/header/header-language-registrar.tsx +348 -0
  158. package/app-template/src/views/header/header-layout-context.tsx +143 -0
  159. package/app-template/src/views/header/header-layout-registrar.tsx +658 -0
  160. package/app-template/src/views/header/header-logo-context.tsx +228 -0
  161. package/app-template/src/views/header/header-logo.tsx +118 -0
  162. package/app-template/src/views/header/header-mini-basket-context.tsx +524 -0
  163. package/app-template/src/views/header/header-search-registrar.tsx +511 -0
  164. package/app-template/src/views/header/header-text-slider-registrar.tsx +382 -0
  165. package/app-template/src/views/header/index.tsx +109 -47
  166. package/app-template/src/views/header/inline-search.tsx +262 -0
  167. package/app-template/src/views/header/mini-basket.tsx +819 -44
  168. package/app-template/src/views/header/mobile-hamburger-button.tsx +5 -8
  169. package/app-template/src/views/header/mobile-menu.tsx +12 -0
  170. package/app-template/src/views/header/navbar-menu-context.tsx +219 -0
  171. package/app-template/src/views/header/navbar.tsx +178 -111
  172. package/app-template/src/views/header/search/index.tsx +71 -32
  173. package/app-template/src/views/header/search/results.tsx +127 -65
  174. package/app-template/src/views/header/search/search-input.tsx +61 -0
  175. package/app-template/src/views/header/server-settings-parser.ts +1105 -0
  176. package/app-template/src/views/header/use-header-icons.ts +241 -0
  177. package/app-template/src/views/header/use-header-logo.ts +213 -0
  178. package/app-template/src/views/header/use-navbar-menu.ts +179 -0
  179. package/app-template/src/views/login/index.tsx +54 -46
  180. package/app-template/src/views/product/accordion-section.tsx +61 -0
  181. package/app-template/src/views/product/accordion-wrapper.tsx +135 -43
  182. package/app-template/src/views/product/custom-button-group.tsx +69 -0
  183. package/app-template/src/views/product/favorites-button-section.tsx +69 -0
  184. package/app-template/src/views/product/find-in-store-section.tsx +60 -0
  185. package/app-template/src/views/product/index.ts +1 -0
  186. package/app-template/src/views/product/layout.tsx +6 -5
  187. package/app-template/src/views/product/misc-buttons.tsx +339 -25
  188. package/app-template/src/views/product/price-wrapper.tsx +3 -29
  189. package/app-template/src/views/product/product-actions.tsx +137 -8
  190. package/app-template/src/views/product/product-info-section.tsx +140 -0
  191. package/app-template/src/views/product/product-info.tsx +69 -31
  192. package/app-template/src/views/product/product-share.tsx +13 -8
  193. package/app-template/src/views/product/product-variants.tsx +2 -2
  194. package/app-template/src/views/product/quantity-section.tsx +73 -0
  195. package/app-template/src/views/product/sale-tag.tsx +10 -0
  196. package/app-template/src/views/product/share-section.tsx +357 -0
  197. package/app-template/src/views/product/slider.tsx +117 -79
  198. package/app-template/src/views/product/variant.tsx +69 -41
  199. package/app-template/src/views/product/variants-section.tsx +126 -0
  200. package/app-template/src/views/product-detail/constants.ts +272 -0
  201. package/app-template/src/views/product-detail/index.ts +10 -0
  202. package/app-template/src/views/product-detail/product-detail-registrar.tsx +616 -0
  203. package/app-template/src/views/product-item/index.tsx +119 -46
  204. package/app-template/src/views/register/index.tsx +14 -25
  205. package/app-template/src/views/share/index.tsx +9 -6
  206. package/app-template/src/views/widgets/home-hero-slider-content.tsx +41 -39
  207. package/app-template/src/widgets/flatpages/about-us/index.tsx +78 -0
  208. package/app-template/src/widgets/flatpages/blog-list/index.tsx +129 -0
  209. package/app-template/src/widgets/footer-app-banner.tsx +444 -0
  210. package/app-template/src/widgets/footer-bottom.tsx +127 -0
  211. package/app-template/src/widgets/footer-menu-compact.tsx +238 -0
  212. package/app-template/src/widgets/footer-menu-two.tsx +298 -0
  213. package/app-template/src/widgets/footer-social-client.tsx +251 -0
  214. package/app-template/src/widgets/footer-social.tsx +47 -16
  215. package/app-template/src/widgets/footer-subscription/footer-subscription-form.tsx +17 -14
  216. package/app-template/src/widgets/footer-subscription/index.tsx +183 -17
  217. package/app-template/src/widgets/footer-value-props.tsx +201 -0
  218. package/app-template/src/widgets/index.ts +7 -0
  219. package/app-template/src/widgets/schemas/about-us.json +46 -0
  220. package/app-template/src/widgets/schemas/blog-list.json +37 -0
  221. package/app-template/src/widgets/schemas/blog.json +29 -0
  222. package/app-template/tailwind.config.js +18 -2
  223. package/package.json +1 -1
@@ -4,8 +4,15 @@ import {
4
4
  } from '@akinon/next/data/client/basket';
5
5
  import { useAppDispatch } from '@akinon/next/redux/hooks';
6
6
  import { BasketItem as BasketItemType } from '@akinon/next/types';
7
- import { Price, Button, Icon, Modal, Select, Link } from '@theme/components';
8
- import { useState } from 'react';
7
+ import {
8
+ Price,
9
+ Button,
10
+ Icon,
11
+ LoaderSpinner,
12
+ Modal,
13
+ Link
14
+ } from '@theme/components';
15
+ import { ComponentProps, useState } from 'react';
9
16
  import { useAddFavoriteMutation } from '@akinon/next/data/client/wishlist';
10
17
  import {
11
18
  useCommonProductAttributes,
@@ -13,14 +20,89 @@ import {
13
20
  } from '@akinon/next/hooks';
14
21
  import PluginModule, { Component } from '@akinon/next/components/plugin-module';
15
22
  import { Image } from '@akinon/next/components/image';
16
- import clsx from 'clsx';
17
23
  import { pushRemoveFromCart } from '@theme/utils/gtm';
24
+ import { WithDesignerFeatures } from '@akinon/next/components/theme-editor/components/with-designer-features';
25
+ import {
26
+ BASKET_ITEM_BLOCKS,
27
+ BASKET_ITEMS_SECTION_ID,
28
+ BASKET_PLACEHOLDER_ID,
29
+ useBasketDesigner
30
+ } from './designer-context';
18
31
 
19
32
  interface Props {
20
33
  basketItem?: BasketItemType;
21
34
  namespace?: string;
22
35
  }
23
36
 
37
+ const convertStylesToCSS = (
38
+ styles: Record<string, unknown> | undefined
39
+ ): React.CSSProperties => {
40
+ if (!styles) return {};
41
+
42
+ const cssStyles: React.CSSProperties = {};
43
+
44
+ Object.entries(styles).forEach(([key, value]) => {
45
+ const rawValue =
46
+ typeof value === 'object' && value !== null && 'desktop' in value
47
+ ? (value as Record<string, unknown>).desktop
48
+ : value;
49
+
50
+ const cssValue =
51
+ typeof rawValue === 'number'
52
+ ? rawValue
53
+ : typeof rawValue === 'string'
54
+ ? rawValue
55
+ : '';
56
+
57
+ if (cssValue !== '' && cssValue !== null && cssValue !== undefined) {
58
+ const camelKey = key.replace(/-([a-z])/g, (_, letter) =>
59
+ letter.toUpperCase()
60
+ );
61
+ (cssStyles as Record<string, unknown>)[camelKey] = cssValue;
62
+ }
63
+ });
64
+
65
+ return cssStyles;
66
+ };
67
+
68
+ const pickStyles = (
69
+ styles: React.CSSProperties,
70
+ keys: Array<keyof React.CSSProperties>
71
+ ): React.CSSProperties => {
72
+ const picked: React.CSSProperties = {};
73
+
74
+ keys.forEach((key) => {
75
+ const value = styles[key];
76
+
77
+ if (value !== undefined && value !== null && value !== '') {
78
+ (picked as Record<string, unknown>)[key as string] = value;
79
+ }
80
+ });
81
+
82
+ return picked;
83
+ };
84
+
85
+ const omitStyles = (
86
+ styles: React.CSSProperties,
87
+ keys: Array<keyof React.CSSProperties>
88
+ ): React.CSSProperties => {
89
+ const omitted = { ...styles };
90
+
91
+ keys.forEach((key) => {
92
+ delete omitted[key];
93
+ });
94
+
95
+ return omitted;
96
+ };
97
+
98
+ const getResponsiveValue = (value: unknown) => {
99
+ if (typeof value === 'object' && value !== null && 'desktop' in value) {
100
+ return (value as Record<string, unknown>).desktop;
101
+ }
102
+
103
+ return value;
104
+ };
105
+
24
106
  export const BasketItem = (props: Props) => {
25
107
  const { t } = useLocalization();
26
108
  const { basketItem, namespace } = props;
@@ -29,15 +111,228 @@ export const BasketItem = (props: Props) => {
29
111
  const [isRemoveBasketModalOpen, setRemoveBasketModalOpen] = useState(false);
30
112
  const [addFavorite, { isLoading: addFavoriteLoading }] =
31
113
  useAddFavoriteMutation();
32
- const [updateQuantityLoading, setUpdateQuantityLoading] = useState(false);
33
114
  const commonProductAttributes = useCommonProductAttributes({
34
115
  attributes: basketItem.product.attributes_kwargs
35
116
  });
117
+ const [updateQuantityLoading, setUpdateQuantityLoading] = useState(false);
118
+ const { isDesigner, selectedBlockId, getBlockStyles, getBlockProperties } =
119
+ useBasketDesigner();
120
+ const rowStyles = convertStylesToCSS(
121
+ getBlockStyles(BASKET_ITEM_BLOCKS.ITEM_ROW.id)
122
+ );
123
+ const imageStyles = convertStylesToCSS(
124
+ getBlockStyles(BASKET_ITEM_BLOCKS.IMAGE.id)
125
+ );
126
+ const nameStyles = convertStylesToCSS(
127
+ getBlockStyles(BASKET_ITEM_BLOCKS.NAME.id)
128
+ );
129
+ const attributesStyles = convertStylesToCSS(
130
+ getBlockStyles(BASKET_ITEM_BLOCKS.ATTRIBUTES.id)
131
+ );
132
+ const attributeLabelStyles = convertStylesToCSS(
133
+ getBlockStyles(BASKET_ITEM_BLOCKS.ATTRIBUTE_LABEL.id)
134
+ );
135
+ const attributeValueStyles = convertStylesToCSS(
136
+ getBlockStyles(BASKET_ITEM_BLOCKS.ATTRIBUTE_VALUE.id)
137
+ );
138
+ const quantityStyles = convertStylesToCSS(
139
+ getBlockStyles(BASKET_ITEM_BLOCKS.QUANTITY.id)
140
+ );
141
+ const quantityWrapperStyles = convertStylesToCSS(
142
+ getBlockStyles(BASKET_ITEM_BLOCKS.QUANTITY_WRAPPER.id)
143
+ );
144
+ const quantityMinusButtonStyles = convertStylesToCSS(
145
+ getBlockStyles(BASKET_ITEM_BLOCKS.QUANTITY_MINUS_BUTTON.id)
146
+ );
147
+ const quantityMinusIconStyles = convertStylesToCSS(
148
+ getBlockStyles(BASKET_ITEM_BLOCKS.QUANTITY_MINUS_ICON.id)
149
+ );
150
+ const quantityValueStyles = convertStylesToCSS(
151
+ getBlockStyles(BASKET_ITEM_BLOCKS.QUANTITY_VALUE.id)
152
+ );
153
+ const quantityPlusButtonStyles = convertStylesToCSS(
154
+ getBlockStyles(BASKET_ITEM_BLOCKS.QUANTITY_PLUS_BUTTON.id)
155
+ );
156
+ const quantityPlusIconStyles = convertStylesToCSS(
157
+ getBlockStyles(BASKET_ITEM_BLOCKS.QUANTITY_PLUS_ICON.id)
158
+ );
159
+
160
+ const quantityMinusIconProps =
161
+ getBlockProperties(BASKET_ITEM_BLOCKS.QUANTITY_MINUS_ICON.id) || {};
162
+ const quantityPlusIconProps =
163
+ getBlockProperties(BASKET_ITEM_BLOCKS.QUANTITY_PLUS_ICON.id) || {};
164
+ const priceStyles = convertStylesToCSS(
165
+ getBlockStyles(BASKET_ITEM_BLOCKS.PRICE.id)
166
+ );
167
+ const priceTextBlockStyles = convertStylesToCSS(
168
+ getBlockStyles(BASKET_ITEM_BLOCKS.PRICE_TEXT.id)
169
+ );
170
+ const removeStyles = convertStylesToCSS(
171
+ getBlockStyles(BASKET_ITEM_BLOCKS.REMOVE.id)
172
+ );
173
+ const removeProperties = getBlockProperties(BASKET_ITEM_BLOCKS.REMOVE.id);
174
+ const removeIconRaw = getResponsiveValue(removeProperties?.icon);
175
+ const removeIconValue = removeIconRaw;
176
+ const hasCustomRemoveSvgIcon =
177
+ typeof removeIconValue === 'string' && removeIconValue.includes('<svg');
178
+ const removeIconName = (
179
+ typeof removeIconValue === 'string' ? removeIconValue : 'close'
180
+ ) as ComponentProps<typeof Icon>['name'];
181
+ const giftPackStyles = convertStylesToCSS(
182
+ getBlockStyles(BASKET_ITEM_BLOCKS.GIFT_PACK.id)
183
+ );
184
+
185
+ const imageWrapperKeys: Array<keyof React.CSSProperties> = [
186
+ 'width',
187
+ 'height',
188
+ 'marginRight',
189
+ 'marginLeft',
190
+ 'marginTop',
191
+ 'marginBottom',
192
+ 'alignSelf',
193
+ 'justifySelf',
194
+ 'flexGrow',
195
+ 'flexShrink'
196
+ ];
197
+ const priceWrapperKeys: Array<keyof React.CSSProperties> = [
198
+ 'gap',
199
+ 'marginRight',
200
+ 'marginLeft',
201
+ 'marginTop',
202
+ 'marginBottom',
203
+ 'alignSelf',
204
+ 'justifySelf',
205
+ 'justifyContent',
206
+ 'alignItems'
207
+ ];
208
+ const attributesWrapperKeys: Array<keyof React.CSSProperties> = [
209
+ 'gap',
210
+ 'marginRight',
211
+ 'marginLeft',
212
+ 'marginTop',
213
+ 'marginBottom',
214
+ 'alignSelf',
215
+ 'justifySelf',
216
+ 'justifyContent',
217
+ 'alignItems'
218
+ ];
219
+ const quantityContainerKeys: Array<keyof React.CSSProperties> = [
220
+ 'gap',
221
+ 'marginRight',
222
+ 'marginLeft',
223
+ 'marginTop',
224
+ 'marginBottom',
225
+ 'alignSelf',
226
+ 'justifySelf',
227
+ 'justifyContent',
228
+ 'alignItems'
229
+ ];
230
+
231
+ const imageWrapperStyles = pickStyles(imageStyles, imageWrapperKeys);
232
+ const imageElementStyles = {
233
+ ...omitStyles(imageStyles, [
234
+ 'marginRight',
235
+ 'marginLeft',
236
+ 'marginTop',
237
+ 'marginBottom',
238
+ 'alignSelf',
239
+ 'justifySelf',
240
+ 'flexGrow',
241
+ 'flexShrink'
242
+ ]),
243
+ width: imageStyles.width || undefined,
244
+ height: imageStyles.height || undefined
245
+ };
246
+ const priceWrapperStyles = pickStyles(priceStyles, priceWrapperKeys);
247
+ const priceTextStyles = priceTextBlockStyles;
248
+ const attributesWrapperStyles = pickStyles(
249
+ attributesStyles,
250
+ attributesWrapperKeys
251
+ );
252
+ const attributesTextStyles = omitStyles(
253
+ attributesStyles,
254
+ attributesWrapperKeys
255
+ );
256
+ const finalAttributeLabelStyles = {
257
+ ...attributesTextStyles,
258
+ ...attributeLabelStyles
259
+ };
260
+ const finalAttributeValueStyles = {
261
+ ...attributesTextStyles,
262
+ ...attributeValueStyles
263
+ };
264
+ const quantityContainerStyles = pickStyles(
265
+ quantityStyles,
266
+ quantityContainerKeys
267
+ );
268
+ const minusIconRaw = getResponsiveValue(quantityMinusIconProps.icon);
269
+ const plusIconRaw = getResponsiveValue(quantityPlusIconProps.icon);
270
+ const minusIcon = typeof minusIconRaw === 'string' ? minusIconRaw : 'minus';
271
+ const plusIcon = typeof plusIconRaw === 'string' ? plusIconRaw : 'plus';
272
+
273
+ const toNumber = (value: unknown, fallback = 12) => {
274
+ if (typeof value === 'number') return value;
275
+ if (typeof value === 'string') {
276
+ const parsed = parseInt(value, 10);
277
+ return Number.isNaN(parsed) ? fallback : parsed;
278
+ }
279
+
280
+ return fallback;
281
+ };
282
+
283
+ const minusIconSize = toNumber(
284
+ quantityMinusIconStyles.width ||
285
+ quantityMinusIconStyles.height ||
286
+ quantityMinusIconStyles.fontSize,
287
+ 12
288
+ );
289
+ const plusIconSize = toNumber(
290
+ quantityPlusIconStyles.width ||
291
+ quantityPlusIconStyles.height ||
292
+ quantityPlusIconStyles.fontSize,
293
+ 12
294
+ );
295
+
296
+ const isCustomMinusSvg =
297
+ typeof minusIcon === 'string' && minusIcon.includes('<svg');
298
+ const isCustomPlusSvg =
299
+ typeof plusIcon === 'string' && plusIcon.includes('<svg');
300
+
301
+ const handleDesignerClick = (event: React.MouseEvent) => {
302
+ if (isDesigner) {
303
+ event.preventDefault();
304
+ }
305
+ };
306
+
307
+ // Check if any instance of this block type is selected
308
+ const isBlockSelected = (
309
+ block: typeof BASKET_ITEM_BLOCKS[keyof typeof BASKET_ITEM_BLOCKS]
310
+ ) => {
311
+ if (selectedBlockId === block.id) return true;
312
+ // Match only numbered instances (e.g., basket-item-price-1), not sibling blocks
313
+ if (selectedBlockId?.match(new RegExp(`^${block.id}-\\d+$`))) return true;
314
+ return false;
315
+ };
316
+
317
+ // Block instance - uses base ID for styles (shared across all items)
318
+ const getBlockInstance = (
319
+ block: typeof BASKET_ITEM_BLOCKS[keyof typeof BASKET_ITEM_BLOCKS]
320
+ ) => {
321
+ // Always get styles from base block ID (shared across all items)
322
+ const styles = getBlockStyles(block.id);
323
+ return {
324
+ ...block,
325
+ // Use base ID so all items share the same block in theme editor
326
+ id: block.id,
327
+ styles
328
+ };
329
+ };
36
330
 
37
331
  const updateQuantity = async (
38
332
  productPk: number,
39
333
  quantity: number,
40
- attributes: object = {}
334
+ attributes: object = {},
335
+ namespace?: string
41
336
  ) => {
42
337
  const requestParams: {
43
338
  product: number;
@@ -54,9 +349,11 @@ export const BasketItem = (props: Props) => {
54
349
  requestParams.namespace = namespace;
55
350
  }
56
351
 
352
+ setUpdateQuantityLoading(true);
353
+
57
354
  await updateQuantityMutation(requestParams)
58
355
  .unwrap()
59
- .then((data) =>
356
+ .then((data) => {
60
357
  dispatch(
61
358
  basketApi.util.updateQueryData(
62
359
  'getBasket',
@@ -65,15 +362,34 @@ export const BasketItem = (props: Props) => {
65
362
  Object.assign(draftBasket, data.basket);
66
363
  }
67
364
  )
68
- )
69
- );
365
+ );
366
+ })
367
+ .catch((err) => {
368
+ const formattedError =
369
+ err?.data?.non_field_errors ||
370
+ Object.keys(err?.data || {}).map(
371
+ (key) => `${key}: ${err?.data[key].join(', ')}`
372
+ );
373
+
374
+ console.error('Error in operation:', formattedError);
375
+ })
376
+ .finally(() => {
377
+ setTimeout(() => {
378
+ setUpdateQuantityLoading(false);
379
+ }, 200);
380
+ });
70
381
  };
71
382
 
72
383
  const deleteProduct = async (productPk?: number) => {
73
384
  setUpdateQuantityLoading(true);
74
385
 
75
386
  try {
76
- await updateQuantity(basketItem.product.pk, 0, basketItem.attributes);
387
+ await updateQuantity(
388
+ basketItem.product.pk,
389
+ 0,
390
+ basketItem.attributes,
391
+ namespace
392
+ );
77
393
  pushRemoveFromCart(basketItem?.product);
78
394
 
79
395
  if (productPk) {
@@ -87,117 +403,385 @@ export const BasketItem = (props: Props) => {
87
403
  }
88
404
  };
89
405
 
406
+ const handleQuantityChange = (newQuantity: number) => {
407
+ if (newQuantity === 0) {
408
+ setRemoveBasketModalOpen(true);
409
+ } else {
410
+ updateQuantity(
411
+ basketItem.product.pk,
412
+ newQuantity,
413
+ basketItem.attributes,
414
+ namespace
415
+ );
416
+ }
417
+ };
418
+
419
+ const handleDecrease = () => {
420
+ if (isDesigner) return;
421
+ if (updateQuantityLoading) return;
422
+ handleQuantityChange(basketItem.quantity - 1);
423
+ };
424
+
425
+ const handleIncrease = () => {
426
+ if (isDesigner) return;
427
+ if (updateQuantityLoading) return;
428
+ if (basketItem.quantity >= 999) return;
429
+ handleQuantityChange(basketItem.quantity + 1);
430
+ };
431
+
90
432
  return (
91
433
  <>
92
- <li
93
- key={basketItem.id}
94
- className="flex border-b border-gray-200 py-3 relative"
95
- data-testid="basket-item"
96
- >
97
- <div className="w-20 lg:w-[105px] mr-4 shrink-0">
98
- <Link href={basketItem.product.absolute_url} passHref>
99
- <Image
100
- src={basketItem.product.productimage_set[0]?.image}
101
- alt={basketItem.product.name}
102
- width={80}
103
- height={128}
104
- className="md:hidden"
105
- />
106
-
107
- <Image
108
- src={basketItem.product.productimage_set[0]?.image}
109
- alt={basketItem.product.name}
110
- width={105}
111
- height={158}
112
- className="hidden md:block"
113
- />
114
- </Link>
115
- </div>
116
- <div className="w-full flex flex-col justify-between">
117
- <div className="flex h-full">
118
- <div className="flex flex-1 flex-col gap-3 sm:flex-row sm:gap-1">
119
- <div className="flex-1">
120
- <Link
121
- href={basketItem.product.absolute_url}
122
- data-testid="basket-product-name"
123
- passHref
124
- >
125
- <span className="text-xs">{basketItem.product.name}</span>
126
- </Link>
127
- <div className="flex flex-col gap-1">
128
- {commonProductAttributes.map((attribute, index) => (
129
- <span className="text-xs" key={index}>
130
- <span>{attribute.name}</span>:{' '}
131
- <span
132
- data-testid={`basket-item-${attribute.name.toLowerCase()}`}
434
+ <li key={basketItem.id} data-testid="basket-item">
435
+ <WithDesignerFeatures
436
+ block={getBlockInstance(BASKET_ITEM_BLOCKS.ITEM_ROW)}
437
+ placeholderId={BASKET_PLACEHOLDER_ID}
438
+ sectionId={BASKET_ITEMS_SECTION_ID}
439
+ isDesigner={isDesigner}
440
+ isSelected={isBlockSelected(BASKET_ITEM_BLOCKS.ITEM_ROW)}
441
+ className="flex border-b border-gray-200 py-3 relative flex-col gap-4 sm:flex-row"
442
+ style={rowStyles}
443
+ >
444
+ <WithDesignerFeatures
445
+ block={getBlockInstance(BASKET_ITEM_BLOCKS.IMAGE)}
446
+ placeholderId={BASKET_PLACEHOLDER_ID}
447
+ sectionId={BASKET_ITEMS_SECTION_ID}
448
+ isDesigner={isDesigner}
449
+ isSelected={isBlockSelected(BASKET_ITEM_BLOCKS.IMAGE)}
450
+ className="mr-4 shrink-0"
451
+ style={imageWrapperStyles}
452
+ >
453
+ <Link
454
+ href={basketItem.product.absolute_url}
455
+ passHref
456
+ onClick={handleDesignerClick}
457
+ >
458
+ <Image
459
+ src={basketItem.product.productimage_set[0]?.image}
460
+ alt={basketItem.product.name}
461
+ width={80}
462
+ height={128}
463
+ className="md:hidden"
464
+ style={imageElementStyles}
465
+ />
466
+
467
+ <Image
468
+ src={basketItem.product.productimage_set[0]?.image}
469
+ alt={basketItem.product.name}
470
+ width={105}
471
+ height={158}
472
+ className="hidden md:block"
473
+ style={imageElementStyles}
474
+ />
475
+ </Link>
476
+ </WithDesignerFeatures>
477
+ <div className="w-full flex flex-col justify-between">
478
+ <div className="flex h-full gap-4">
479
+ <div className="flex flex-1 flex-col gap-3 sm:flex-row sm:gap-4">
480
+ <div className="flex-1 space-y-2">
481
+ <WithDesignerFeatures
482
+ block={getBlockInstance(BASKET_ITEM_BLOCKS.NAME)}
483
+ placeholderId={BASKET_PLACEHOLDER_ID}
484
+ sectionId={BASKET_ITEMS_SECTION_ID}
485
+ isDesigner={isDesigner}
486
+ isSelected={isBlockSelected(BASKET_ITEM_BLOCKS.NAME)}
487
+ className="inline-block w-full text-xs"
488
+ style={nameStyles}
489
+ >
490
+ <Link
491
+ href={basketItem.product.absolute_url}
492
+ data-testid="basket-product-name"
493
+ passHref
494
+ onClick={handleDesignerClick}
495
+ >
496
+ <span>{basketItem.product.name}</span>
497
+ </Link>
498
+ </WithDesignerFeatures>
499
+ <WithDesignerFeatures
500
+ block={getBlockInstance(BASKET_ITEM_BLOCKS.ATTRIBUTES)}
501
+ placeholderId={BASKET_PLACEHOLDER_ID}
502
+ sectionId={BASKET_ITEMS_SECTION_ID}
503
+ isDesigner={isDesigner}
504
+ isSelected={isBlockSelected(BASKET_ITEM_BLOCKS.ATTRIBUTES)}
505
+ className="flex flex-col gap-1"
506
+ style={attributesWrapperStyles}
507
+ >
508
+ {commonProductAttributes.map((attribute, index) => (
509
+ <div
510
+ className="text-xs"
511
+ key={index}
512
+ style={attributesTextStyles}
133
513
  >
134
- {attribute.value}
135
- </span>
136
- </span>
137
- ))}
514
+ <WithDesignerFeatures
515
+ block={getBlockInstance(
516
+ BASKET_ITEM_BLOCKS.ATTRIBUTE_LABEL
517
+ )}
518
+ placeholderId={BASKET_PLACEHOLDER_ID}
519
+ sectionId={BASKET_ITEMS_SECTION_ID}
520
+ isDesigner={isDesigner}
521
+ isSelected={isBlockSelected(
522
+ BASKET_ITEM_BLOCKS.ATTRIBUTE_LABEL
523
+ )}
524
+ className="inline-block"
525
+ >
526
+ <span style={finalAttributeLabelStyles}>
527
+ {attribute.name}
528
+ </span>
529
+ </WithDesignerFeatures>
530
+ :{' '}
531
+ <WithDesignerFeatures
532
+ block={getBlockInstance(
533
+ BASKET_ITEM_BLOCKS.ATTRIBUTE_VALUE
534
+ )}
535
+ placeholderId={BASKET_PLACEHOLDER_ID}
536
+ sectionId={BASKET_ITEMS_SECTION_ID}
537
+ isDesigner={isDesigner}
538
+ isSelected={isBlockSelected(
539
+ BASKET_ITEM_BLOCKS.ATTRIBUTE_VALUE
540
+ )}
541
+ className="inline-block"
542
+ >
543
+ <span
544
+ style={finalAttributeValueStyles}
545
+ data-testid={`basket-item-${attribute.name.toLowerCase()}`}
546
+ >
547
+ {attribute.value}
548
+ </span>
549
+ </WithDesignerFeatures>
550
+ </div>
551
+ ))}
552
+ </WithDesignerFeatures>
138
553
  </div>
554
+ <WithDesignerFeatures
555
+ block={getBlockInstance(BASKET_ITEM_BLOCKS.QUANTITY)}
556
+ placeholderId={BASKET_PLACEHOLDER_ID}
557
+ sectionId={BASKET_ITEMS_SECTION_ID}
558
+ isDesigner={isDesigner}
559
+ isSelected={isBlockSelected(BASKET_ITEM_BLOCKS.QUANTITY)}
560
+ className="flex flex-col justify-center md:flex-row md:items-center"
561
+ style={quantityContainerStyles}
562
+ >
563
+ <WithDesignerFeatures
564
+ block={getBlockInstance(
565
+ BASKET_ITEM_BLOCKS.QUANTITY_WRAPPER
566
+ )}
567
+ placeholderId={BASKET_PLACEHOLDER_ID}
568
+ sectionId={BASKET_ITEMS_SECTION_ID}
569
+ isDesigner={isDesigner}
570
+ isSelected={isBlockSelected(
571
+ BASKET_ITEM_BLOCKS.QUANTITY_WRAPPER
572
+ )}
573
+ className="w-[138px] h-11 flex items-center justify-between border p-4"
574
+ style={quantityWrapperStyles}
575
+ >
576
+ <WithDesignerFeatures
577
+ block={getBlockInstance(
578
+ BASKET_ITEM_BLOCKS.QUANTITY_MINUS_BUTTON
579
+ )}
580
+ placeholderId={BASKET_PLACEHOLDER_ID}
581
+ sectionId={BASKET_ITEMS_SECTION_ID}
582
+ isDesigner={isDesigner}
583
+ isSelected={isBlockSelected(
584
+ BASKET_ITEM_BLOCKS.QUANTITY_MINUS_BUTTON
585
+ )}
586
+ className="inline-flex"
587
+ >
588
+ <Button
589
+ className="h-auto p-0 hover:bg-transparent hover:text-black"
590
+ appearance="ghost"
591
+ onClick={handleDecrease}
592
+ disabled={
593
+ updateQuantityLoading || basketItem.quantity <= 0
594
+ }
595
+ style={quantityMinusButtonStyles}
596
+ >
597
+ <WithDesignerFeatures
598
+ block={getBlockInstance(
599
+ BASKET_ITEM_BLOCKS.QUANTITY_MINUS_ICON
600
+ )}
601
+ placeholderId={BASKET_PLACEHOLDER_ID}
602
+ sectionId={BASKET_ITEMS_SECTION_ID}
603
+ isDesigner={isDesigner}
604
+ isSelected={isBlockSelected(
605
+ BASKET_ITEM_BLOCKS.QUANTITY_MINUS_ICON
606
+ )}
607
+ className="inline-flex"
608
+ style={quantityMinusIconStyles}
609
+ >
610
+ {isCustomMinusSvg ? (
611
+ <div
612
+ style={{
613
+ width: minusIconSize,
614
+ height: minusIconSize
615
+ }}
616
+ dangerouslySetInnerHTML={{ __html: minusIcon }}
617
+ />
618
+ ) : (
619
+ <Icon
620
+ name={
621
+ minusIcon as ComponentProps<typeof Icon>['name']
622
+ }
623
+ size={minusIconSize}
624
+ style={quantityMinusIconStyles}
625
+ />
626
+ )}
627
+ </WithDesignerFeatures>
628
+ </Button>
629
+ </WithDesignerFeatures>
630
+
631
+ <WithDesignerFeatures
632
+ block={getBlockInstance(
633
+ BASKET_ITEM_BLOCKS.QUANTITY_VALUE
634
+ )}
635
+ placeholderId={BASKET_PLACEHOLDER_ID}
636
+ sectionId={BASKET_ITEMS_SECTION_ID}
637
+ isDesigner={isDesigner}
638
+ isSelected={isBlockSelected(
639
+ BASKET_ITEM_BLOCKS.QUANTITY_VALUE
640
+ )}
641
+ className="inline-flex"
642
+ style={quantityValueStyles}
643
+ >
644
+ {updateQuantityLoading ? (
645
+ <LoaderSpinner className="w-4 h-4" />
646
+ ) : (
647
+ <span style={quantityValueStyles}>
648
+ {basketItem.quantity}
649
+ </span>
650
+ )}
651
+ </WithDesignerFeatures>
652
+
653
+ <WithDesignerFeatures
654
+ block={getBlockInstance(
655
+ BASKET_ITEM_BLOCKS.QUANTITY_PLUS_BUTTON
656
+ )}
657
+ placeholderId={BASKET_PLACEHOLDER_ID}
658
+ sectionId={BASKET_ITEMS_SECTION_ID}
659
+ isDesigner={isDesigner}
660
+ isSelected={isBlockSelected(
661
+ BASKET_ITEM_BLOCKS.QUANTITY_PLUS_BUTTON
662
+ )}
663
+ className="inline-flex"
664
+ >
665
+ <Button
666
+ className="h-auto p-0 hover:bg-transparent hover:text-black"
667
+ appearance="ghost"
668
+ onClick={handleIncrease}
669
+ disabled={
670
+ updateQuantityLoading || basketItem.quantity >= 999
671
+ }
672
+ style={quantityPlusButtonStyles}
673
+ >
674
+ <WithDesignerFeatures
675
+ block={getBlockInstance(
676
+ BASKET_ITEM_BLOCKS.QUANTITY_PLUS_ICON
677
+ )}
678
+ placeholderId={BASKET_PLACEHOLDER_ID}
679
+ sectionId={BASKET_ITEMS_SECTION_ID}
680
+ isDesigner={isDesigner}
681
+ isSelected={isBlockSelected(
682
+ BASKET_ITEM_BLOCKS.QUANTITY_PLUS_ICON
683
+ )}
684
+ className="inline-flex"
685
+ style={quantityPlusIconStyles}
686
+ >
687
+ {isCustomPlusSvg ? (
688
+ <div
689
+ style={{
690
+ width: plusIconSize,
691
+ height: plusIconSize
692
+ }}
693
+ dangerouslySetInnerHTML={{ __html: plusIcon }}
694
+ />
695
+ ) : (
696
+ <Icon
697
+ name={
698
+ plusIcon as ComponentProps<typeof Icon>['name']
699
+ }
700
+ size={plusIconSize}
701
+ style={quantityPlusIconStyles}
702
+ />
703
+ )}
704
+ </WithDesignerFeatures>
705
+ </Button>
706
+ </WithDesignerFeatures>
707
+ </WithDesignerFeatures>
708
+ </WithDesignerFeatures>
709
+ <WithDesignerFeatures
710
+ block={getBlockInstance(BASKET_ITEM_BLOCKS.PRICE)}
711
+ placeholderId={BASKET_PLACEHOLDER_ID}
712
+ sectionId={BASKET_ITEMS_SECTION_ID}
713
+ isDesigner={isDesigner}
714
+ isSelected={isBlockSelected(BASKET_ITEM_BLOCKS.PRICE)}
715
+ className="flex flex-col shrink-0 text-sm gap-2 items-start justify-center lg:flex-row lg:mr-6 lg:gap-6 sm:items-center lg:justify-start"
716
+ style={priceWrapperStyles}
717
+ >
718
+ <WithDesignerFeatures
719
+ block={getBlockInstance(BASKET_ITEM_BLOCKS.PRICE_TEXT)}
720
+ placeholderId={BASKET_PLACEHOLDER_ID}
721
+ sectionId={BASKET_ITEMS_SECTION_ID}
722
+ isDesigner={isDesigner}
723
+ isSelected={isBlockSelected(BASKET_ITEM_BLOCKS.PRICE_TEXT)}
724
+ className="inline-block"
725
+ >
726
+ <Price
727
+ value={basketItem.product.price}
728
+ data-testid="basket-product-price"
729
+ style={priceTextStyles}
730
+ />
731
+ </WithDesignerFeatures>
732
+ </WithDesignerFeatures>
139
733
  </div>
140
- <div className="flex flex-col justify-center md:flex-row md:items-center lg:w-52">
141
- <Select
142
- className="px-2"
143
- defaultValue={basketItem.quantity}
144
- onChange={(event) => {
145
- updateQuantity(
146
- basketItem.product.pk,
147
- Number(event.currentTarget.value)
148
- );
149
- }}
150
- options={[
151
- ...Array.from({ length: 10 }, (_, i) => i + 1),
152
- basketItem.quantity > 10 && basketItem.quantity
153
- ]
154
- .filter((i) => i)
155
- .map((i) => ({
156
- label: `${t('basket.card.qty')} ${i}`,
157
- value: `${i}`
158
- }))}
159
- data-testid="basket-product-quantity"
160
- ></Select>
161
- </div>
162
- <div className="flex flex-col shrink-0 text-sm gap-2 items-start justify-center w-48 lg:flex-row lg:mr-6 lg:gap-6 sm:items-center lg:justify-start">
163
- {parseFloat(basketItem.product.retail_price) >
164
- parseFloat(basketItem.product.price) && (
165
- <Price
166
- className="line-through"
167
- value={basketItem.product.retail_price}
734
+ <WithDesignerFeatures
735
+ block={getBlockInstance(BASKET_ITEM_BLOCKS.REMOVE)}
736
+ placeholderId={BASKET_PLACEHOLDER_ID}
737
+ sectionId={BASKET_ITEMS_SECTION_ID}
738
+ isDesigner={isDesigner}
739
+ isSelected={isBlockSelected(BASKET_ITEM_BLOCKS.REMOVE)}
740
+ className="self-center"
741
+ style={removeStyles}
742
+ >
743
+ {hasCustomRemoveSvgIcon ? (
744
+ <div
745
+ className="cursor-pointer"
746
+ style={{ width: 16, height: 16, ...removeStyles }}
747
+ dangerouslySetInnerHTML={{ __html: removeIconValue }}
748
+ onClick={() => setRemoveBasketModalOpen(true)}
749
+ data-testid="basket-product-remove"
750
+ />
751
+ ) : (
752
+ <Icon
753
+ name={removeIconName}
754
+ size={16}
755
+ className="cursor-pointer hover:fill-secondary-500"
756
+ style={removeStyles}
757
+ onClick={() => setRemoveBasketModalOpen(true)}
758
+ data-testid="basket-product-remove"
168
759
  />
169
760
  )}
170
- <Price
171
- className={clsx(
172
- parseFloat(basketItem.product.retail_price) >
173
- parseFloat(basketItem.product.price)
174
- ? 'text-secondary-500'
175
- : 'text-primary'
176
- )}
177
- value={basketItem.product.price}
178
- data-testid="basket-product-price"
179
- />
180
- </div>
761
+ </WithDesignerFeatures>
181
762
  </div>
182
- <Icon
183
- name="close"
184
- size={16}
185
- className="self-center cursor-pointer hover:fill-secondary-500" // TODO: Add hover color. Fill not working
186
- onClick={() => setRemoveBasketModalOpen(true)}
187
- data-testid="basket-product-remove"
188
- />
189
- </div>
190
763
 
191
- <PluginModule
192
- component={Component.BasketGiftPack}
193
- props={{ basketItem }}
194
- />
195
- </div>
764
+ <WithDesignerFeatures
765
+ block={getBlockInstance(BASKET_ITEM_BLOCKS.GIFT_PACK)}
766
+ placeholderId={BASKET_PLACEHOLDER_ID}
767
+ sectionId={BASKET_ITEMS_SECTION_ID}
768
+ isDesigner={isDesigner}
769
+ isSelected={isBlockSelected(BASKET_ITEM_BLOCKS.GIFT_PACK)}
770
+ className="mt-3"
771
+ style={giftPackStyles}
772
+ >
773
+ <PluginModule
774
+ component={Component.BasketGiftPack}
775
+ props={{ basketItem }}
776
+ />
777
+ </WithDesignerFeatures>
778
+ </div>
779
+ </WithDesignerFeatures>
196
780
  </li>
197
781
  <Modal
198
782
  portalId="remove-basket-item"
199
783
  title={t('basket.card.modal.title')}
200
- className="w-full sm:w-[28rem] max-h-[90vh] overflow-y-auto"
784
+ className="w-full md:w-[28rem] max-h-[90vh] overflow-y-auto"
201
785
  open={isRemoveBasketModalOpen}
202
786
  setOpen={setRemoveBasketModalOpen}
203
787
  >