@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
@@ -19,15 +19,44 @@ import {
19
19
  } from '@akinon/next/hooks';
20
20
  import { Image } from '@akinon/next/components/image';
21
21
  import { pushRemoveFromCart } from '@theme/utils/gtm';
22
+ import {
23
+ useHeaderMiniBasket,
24
+ HEADER_MINI_BASKET_PLACEHOLDER_ID,
25
+ HEADER_MINI_BASKET_SECTION_ID
26
+ } from './header-mini-basket-context';
22
27
 
23
28
  interface MiniBasketItemProps {
24
29
  basketItem: BasketItem;
25
30
  highlightedItem: number;
26
31
  miniBasketListRef: MutableRefObject<HTMLUListElement>;
32
+ isDesigner?: boolean;
33
+ selectedBlockId?: string;
34
+ onBlockClick?: (blockId: string, e?: React.MouseEvent) => void;
35
+ itemStyles?: React.CSSProperties;
36
+ imageStyles?: React.CSSProperties;
37
+ nameStyles?: React.CSSProperties;
38
+ priceStyles?: React.CSSProperties;
39
+ removeButtonStyles?: React.CSSProperties;
40
+ removeButtonContent?: string;
41
+ attributeStyles?: React.CSSProperties;
27
42
  }
28
43
 
29
44
  function MiniBasketItem(props: MiniBasketItemProps) {
30
- const { basketItem, highlightedItem, miniBasketListRef } = props;
45
+ const {
46
+ basketItem,
47
+ highlightedItem,
48
+ miniBasketListRef,
49
+ isDesigner = false,
50
+ selectedBlockId = '',
51
+ onBlockClick,
52
+ itemStyles,
53
+ imageStyles,
54
+ nameStyles,
55
+ priceStyles,
56
+ removeButtonStyles,
57
+ removeButtonContent,
58
+ attributeStyles
59
+ } = props;
31
60
  const dispatch = useAppDispatch();
32
61
  const [updateQuantityMutation] = useUpdateQuantityMutation();
33
62
  const commonProductAttributes = useCommonProductAttributes({
@@ -71,24 +100,63 @@ function MiniBasketItem(props: MiniBasketItemProps) {
71
100
  });
72
101
  };
73
102
 
103
+ const imageWidth = imageStyles?.width
104
+ ? parseInt(String(imageStyles.width))
105
+ : isHighlighted
106
+ ? 96
107
+ : 48;
108
+ const imageHeight = imageStyles?.height
109
+ ? parseInt(String(imageStyles.height))
110
+ : isHighlighted
111
+ ? 160
112
+ : 80;
113
+
74
114
  return (
75
115
  <li
76
- style={{ order: isHighlighted ? '-1' : '0' }}
77
- className={clsx('flex gap-3 py-4 border-b border-gray-500')}
116
+ style={{
117
+ order: isHighlighted ? '-1' : '0',
118
+ ...itemStyles
119
+ }}
120
+ className={clsx(
121
+ 'flex gap-3 py-4 border-b border-gray-500 relative',
122
+ isDesigner &&
123
+ selectedBlockId === 'mini-basket-item' &&
124
+ 'ring-2 ring-blue-500 ring-inset',
125
+ isDesigner &&
126
+ 'cursor-pointer hover:ring-2 hover:ring-blue-300 hover:ring-inset'
127
+ )}
128
+ onClick={(e) => isDesigner && onBlockClick?.('mini-basket-item', e)}
78
129
  >
79
130
  <Link
80
131
  href={basketItem.product.absolute_url}
81
132
  className={clsx(
82
- 'block shrink-0 transition-all duration-300',
83
- isHighlighted ? 'w-24 h-40' : 'w-12 h-20'
133
+ 'block shrink-0 transition-all duration-300 relative',
134
+ imageWidth ? `w-[${imageWidth}px] ` : isHighlighted ? 'w-24' : 'w-12',
135
+ imageHeight
136
+ ? `h-[${imageHeight}px]`
137
+ : isHighlighted
138
+ ? 'h-40'
139
+ : 'h-20',
140
+ isDesigner &&
141
+ selectedBlockId === 'mini-basket-item-image' &&
142
+ 'ring-2 ring-blue-500 ring-inset',
143
+ isDesigner &&
144
+ 'cursor-pointer hover:ring-2 hover:ring-blue-300 hover:ring-inset'
84
145
  )}
146
+ onClick={(e) => {
147
+ if (isDesigner) {
148
+ e.preventDefault();
149
+ onBlockClick?.('mini-basket-item-image', e);
150
+ }
151
+ }}
85
152
  >
86
153
  <Image
87
154
  src={basketItem.product.productimage_set?.[0]?.image ?? ''}
88
155
  alt={basketItem.product.name}
89
- width={isHighlighted ? 96 : 48}
90
- height={isHighlighted ? 160 : 80}
156
+ width={imageWidth}
157
+ height={imageHeight}
91
158
  className="transition-all duration-300"
159
+ style={imageStyles}
92
160
  />
93
161
  </Link>
94
162
  <div className="flex flex-col flex-1">
@@ -107,8 +175,22 @@ function MiniBasketItem(props: MiniBasketItemProps) {
107
175
  </div>
108
176
  <Link
109
177
  href={basketItem.product.absolute_url}
110
- className="block"
178
+ className={clsx(
179
+ 'block relative',
180
+ isDesigner &&
181
+ selectedBlockId === 'mini-basket-item-name' &&
182
+ 'ring-2 ring-blue-500 ring-inset',
183
+ isDesigner &&
184
+ 'cursor-pointer hover:ring-2 hover:ring-blue-300 hover:ring-inset'
185
+ )}
111
186
  data-testid="mini-basket-item-name"
187
+ style={nameStyles}
188
+ onClick={(e) => {
189
+ if (isDesigner) {
190
+ e.preventDefault();
191
+ onBlockClick?.('mini-basket-item-name', e);
192
+ }
193
+ }}
112
194
  >
113
195
  {basketItem.product.name}
114
196
  {/* TODO: Get correct variants */}
@@ -116,7 +198,19 @@ function MiniBasketItem(props: MiniBasketItemProps) {
116
198
  {commonProductAttributes.map((attribute, index) => (
117
199
  <span
118
200
  key={index}
201
+ className={clsx(
202
+ 'relative',
203
+ isDesigner &&
204
+ selectedBlockId === 'mini-basket-item-attribute' &&
205
+ 'ring-2 ring-blue-500 ring-inset',
206
+ isDesigner &&
207
+ 'cursor-pointer hover:ring-2 hover:ring-blue-300 hover:ring-inset'
208
+ )}
119
209
  data-testid={`mini-basket-item-${attribute.name.toLowerCase()}`}
210
+ style={attributeStyles}
211
+ onClick={(e) =>
212
+ isDesigner && onBlockClick?.('mini-basket-item-attribute', e)
213
+ }
120
214
  >
121
215
  {attribute.name}: {attribute.value}
122
216
  </span>
@@ -129,16 +223,43 @@ function MiniBasketItem(props: MiniBasketItemProps) {
129
223
  className="line-through"
130
224
  />
131
225
  )}
132
- <Price value={basketItem.total_amount} />
226
+ <div
227
+ className={clsx(
228
+ 'relative',
229
+ isDesigner &&
230
+ selectedBlockId === 'mini-basket-item-price' &&
231
+ 'ring-2 ring-blue-500 ring-inset',
232
+ isDesigner &&
233
+ 'cursor-pointer hover:ring-2 hover:ring-blue-300 hover:ring-inset'
234
+ )}
235
+ onClick={(e) =>
236
+ isDesigner && onBlockClick?.('mini-basket-item-price', e)
237
+ }
238
+ >
239
+ <Price value={basketItem.total_amount} style={priceStyles} />
240
+ </div>
133
241
  <Button
134
242
  appearance="ghost"
135
243
  className={clsx(
136
- 'p-0 h-auto text-xs font-semibold underline transition-all duration-300',
137
- isHighlighted ? 'invisible' : 'visible'
244
+ 'transition-all duration-300 relative',
245
+ isHighlighted ? 'invisible' : 'visible',
246
+ isDesigner &&
247
+ selectedBlockId === 'mini-basket-item-remove-button' &&
248
+ 'ring-2 ring-blue-500 ring-inset',
249
+ isDesigner &&
250
+ 'cursor-pointer hover:ring-2 hover:ring-blue-300 hover:ring-inset'
138
251
  )}
139
- onClick={removeItem}
252
+ style={removeButtonStyles}
253
+ onClick={(e) => {
254
+ if (isDesigner) {
255
+ e.stopPropagation();
256
+ onBlockClick?.('mini-basket-item-remove-button', e);
257
+ } else {
258
+ removeItem();
259
+ }
260
+ }}
140
261
  >
141
- {t('basket.mini_basket.remove')}
262
+ {removeButtonContent || t('basket.mini_basket.remove')}
142
263
  </Button>
143
264
  </footer>
144
265
  </div>
@@ -147,10 +268,38 @@ function MiniBasketItem(props: MiniBasketItemProps) {
147
268
  }
148
269
 
149
270
  export default function MiniBasket() {
271
+ const {
272
+ isDesigner,
273
+ isSectionSelected,
274
+ selectedBlockId,
275
+ getSectionStyles,
276
+ getSectionProperties,
277
+ getBlockData
278
+ } = useHeaderMiniBasket();
150
279
  const { open: miniBasketOpen } = useAppSelector(
151
280
  (state) => state.root.miniBasket
152
281
  );
153
282
  const dispatch = useAppDispatch();
283
+
284
+ const handleBlockClick = (blockId: string, e?: React.MouseEvent) => {
285
+ if (!isDesigner) return;
286
+ e?.stopPropagation();
287
+
288
+ if (typeof window !== 'undefined' && window.parent) {
289
+ window.parent.postMessage(
290
+ {
291
+ type: 'SELECT_BLOCK_FROM_PREVIEW',
292
+ data: {
293
+ placeholderId: HEADER_MINI_BASKET_PLACEHOLDER_ID,
294
+ sectionId: HEADER_MINI_BASKET_SECTION_ID,
295
+ blockId
296
+ }
297
+ },
298
+ '*'
299
+ );
300
+ }
301
+ };
302
+
154
303
  const {
155
304
  data: basket,
156
305
  isLoading,
@@ -159,15 +308,462 @@ export default function MiniBasket() {
159
308
  skip: !miniBasketOpen
160
309
  });
161
310
  const { data: miniBasket } = useGetMiniBasketQuery();
162
- const { t } = useLocalization();
311
+ const { t, locale } = useLocalization();
163
312
  const { highlightedItem } = useAppSelector((state) => state.root.miniBasket);
164
313
  const [highlightedItemPk, setHighlightedItemPk] = useState(0);
165
314
  const [sortedBasket, setSortedBasket] = useState([]);
315
+ const [isMobile, setIsMobile] = useState(false);
316
+
317
+ useEffect(() => {
318
+ const checkMobile = () => {
319
+ setIsMobile(window.innerWidth < 1024);
320
+ };
321
+
322
+ checkMobile();
323
+
324
+ window.addEventListener('resize', checkMobile);
325
+ return () => window.removeEventListener('resize', checkMobile);
326
+ }, []);
327
+
328
+ const sectionStyles = getSectionStyles();
329
+ const sectionProperties = getSectionProperties();
330
+
331
+ const positionProp = sectionProperties?.position;
332
+ const position =
333
+ typeof positionProp === 'object' && positionProp !== null
334
+ ? (positionProp as { desktop?: string }).desktop || 'right'
335
+ : (positionProp as string) || 'right';
336
+
337
+ const headerBlock = getBlockData('mini-basket-header');
338
+ const footerBlock = getBlockData('mini-basket-footer');
339
+ const titleBlock = getBlockData('mini-basket-title');
340
+ const itemCountBlock = getBlockData('mini-basket-item-count');
341
+ const closeIconBlock = getBlockData('mini-basket-close-icon');
342
+ const listBlock = getBlockData('mini-basket-list');
343
+ const itemBlock = getBlockData('mini-basket-item');
344
+ const itemImageBlock = getBlockData('mini-basket-item-image');
345
+ const itemNameBlock = getBlockData('mini-basket-item-name');
346
+ const itemPriceBlock = getBlockData('mini-basket-item-price');
347
+ const itemRemoveButtonBlock = getBlockData('mini-basket-item-remove-button');
348
+ const itemAttributeBlock = getBlockData('mini-basket-item-attribute');
349
+ const subtotalBlock = getBlockData('mini-basket-subtotal');
350
+ const viewBagButtonBlock = getBlockData('mini-basket-view-bag-button');
351
+ const continueShoppingButtonBlock = getBlockData(
352
+ 'mini-basket-continue-shopping-button'
353
+ );
354
+
355
+ const getBlockContent = (block: any, fallback: string): string => {
356
+ if (!block) return fallback;
357
+
358
+ if (block.value) {
359
+ if (typeof block.value === 'string') {
360
+ return block.value;
361
+ }
362
+
363
+ if (typeof block.value === 'object' && block.value[locale]) {
364
+ return block.value[locale];
365
+ }
366
+
367
+ if (typeof block.value === 'object') {
368
+ return block.value.en || Object.values(block.value)[0] || fallback;
369
+ }
370
+ }
371
+
372
+ if (block.properties?.content) {
373
+ return block.properties.content;
374
+ }
375
+
376
+ return fallback;
377
+ };
378
+
379
+ const getResponsiveValue = (
380
+ value: any,
381
+ device: 'mobile' | 'desktop' = 'desktop'
382
+ ): string => {
383
+ if (typeof value === 'object' && value !== null) {
384
+ return String(value[device] || value.desktop || value.mobile || '');
385
+ }
386
+ return String(value || '');
387
+ };
388
+
389
+ const convertStylesToCSS = (
390
+ styles: Record<string, unknown> | undefined,
391
+ device: 'mobile' | 'desktop' = 'desktop'
392
+ ): React.CSSProperties => {
393
+ if (!styles) return {};
394
+
395
+ const cssStyles: React.CSSProperties = {};
396
+ Object.entries(styles).forEach(([key, value]) => {
397
+ const cssValue = getResponsiveValue(value, device);
398
+ const camelKey = key.replace(/-([a-z])/g, (g) => g[1].toUpperCase());
399
+ if (cssValue) {
400
+ (cssStyles as any)[camelKey] = cssValue;
401
+ }
402
+ });
403
+ return cssStyles;
404
+ };
405
+
406
+ const sectionCSSStylesMobile = convertStylesToCSS(sectionStyles, 'mobile');
407
+ const sectionCSSStylesDesktop = convertStylesToCSS(sectionStyles, 'desktop');
408
+ const headerCSSStylesMobile = convertStylesToCSS(
409
+ headerBlock?.styles,
410
+ 'mobile'
411
+ );
412
+ const headerCSSStylesDesktop = convertStylesToCSS(
413
+ headerBlock?.styles,
414
+ 'desktop'
415
+ );
416
+ const footerCSSStylesMobile = convertStylesToCSS(
417
+ footerBlock?.styles,
418
+ 'mobile'
419
+ );
420
+ const footerCSSStylesDesktop = convertStylesToCSS(
421
+ footerBlock?.styles,
422
+ 'desktop'
423
+ );
424
+ const titleCSSStylesMobile = convertStylesToCSS(titleBlock?.styles, 'mobile');
425
+ const titleCSSStylesDesktop = convertStylesToCSS(
426
+ titleBlock?.styles,
427
+ 'desktop'
428
+ );
429
+ const itemCountCSSStylesMobile = convertStylesToCSS(
430
+ itemCountBlock?.styles,
431
+ 'mobile'
432
+ );
433
+
434
+ const itemCountCSSStylesDesktop = convertStylesToCSS(
435
+ itemCountBlock?.styles,
436
+ 'desktop'
437
+ );
438
+
439
+ const closeIconCSSStylesMobile = convertStylesToCSS(
440
+ closeIconBlock?.styles,
441
+ 'mobile'
442
+ );
443
+ const closeIconCSSStylesDesktop = convertStylesToCSS(
444
+ closeIconBlock?.styles,
445
+ 'desktop'
446
+ );
447
+
448
+ const listCSSStylesMobile = convertStylesToCSS(listBlock?.styles, 'mobile');
449
+ const listCSSStylesDesktop = convertStylesToCSS(listBlock?.styles, 'desktop');
450
+ const itemCSSStylesMobile = convertStylesToCSS(itemBlock?.styles, 'mobile');
451
+ const itemCSSStylesDesktop = convertStylesToCSS(itemBlock?.styles, 'desktop');
452
+ const itemImageCSSStylesMobile = convertStylesToCSS(
453
+ itemImageBlock?.styles,
454
+ 'mobile'
455
+ );
456
+ const itemImageCSSStylesDesktop = convertStylesToCSS(
457
+ itemImageBlock?.styles,
458
+ 'desktop'
459
+ );
460
+ const itemNameCSSStylesMobile = convertStylesToCSS(
461
+ itemNameBlock?.styles,
462
+ 'mobile'
463
+ );
464
+ const itemNameCSSStylesDesktop = convertStylesToCSS(
465
+ itemNameBlock?.styles,
466
+ 'desktop'
467
+ );
468
+ const itemPriceCSSStylesMobile = convertStylesToCSS(
469
+ itemPriceBlock?.styles,
470
+ 'mobile'
471
+ );
472
+ const itemPriceCSSStylesDesktop = convertStylesToCSS(
473
+ itemPriceBlock?.styles,
474
+ 'desktop'
475
+ );
476
+ const itemRemoveButtonCSSStylesMobile = convertStylesToCSS(
477
+ itemRemoveButtonBlock?.styles,
478
+ 'mobile'
479
+ );
480
+ const itemRemoveButtonCSSStylesDesktop = convertStylesToCSS(
481
+ itemRemoveButtonBlock?.styles,
482
+ 'desktop'
483
+ );
484
+ const itemAttributeCSSStylesMobile = convertStylesToCSS(
485
+ itemAttributeBlock?.styles,
486
+ 'mobile'
487
+ );
488
+ const itemAttributeCSSStylesDesktop = convertStylesToCSS(
489
+ itemAttributeBlock?.styles,
490
+ 'desktop'
491
+ );
492
+ const subtotalCSSStylesMobile = convertStylesToCSS(
493
+ subtotalBlock?.styles,
494
+ 'mobile'
495
+ );
496
+ const subtotalCSSStylesDesktop = convertStylesToCSS(
497
+ subtotalBlock?.styles,
498
+ 'desktop'
499
+ );
500
+ const viewBagButtonCSSStylesMobile = convertStylesToCSS(
501
+ viewBagButtonBlock?.styles,
502
+ 'mobile'
503
+ );
504
+ const viewBagButtonCSSStylesDesktop = convertStylesToCSS(
505
+ viewBagButtonBlock?.styles,
506
+ 'desktop'
507
+ );
508
+ const continueShoppingButtonCSSStylesMobile = convertStylesToCSS(
509
+ continueShoppingButtonBlock?.styles,
510
+ 'mobile'
511
+ );
512
+ const continueShoppingButtonCSSStylesDesktop = convertStylesToCSS(
513
+ continueShoppingButtonBlock?.styles,
514
+ 'desktop'
515
+ );
516
+
517
+ // Default styles for mini basket section (mobile)
518
+ const defaultSectionStylesMobile: React.CSSProperties = {
519
+ width: '100vw',
520
+ height: '100vh',
521
+ backgroundColor: '#ffffff',
522
+ borderLeftWidth: '0',
523
+ borderTopWidth: '0',
524
+ borderRightWidth: '0',
525
+ borderBottomWidth: '0',
526
+ borderStyle: 'solid',
527
+ padding: '20px',
528
+ zIndex: 50
529
+ };
530
+
531
+ // Default styles for mini basket section (desktop) based on position
532
+ const getPositionStyles = (): React.CSSProperties => {
533
+ switch (position) {
534
+ case 'left':
535
+ return {
536
+ position: 'absolute',
537
+ top: '100%',
538
+ left: '0',
539
+ right: 'auto'
540
+ };
541
+ case 'bottom':
542
+ return {
543
+ position: 'fixed',
544
+ bottom: '0',
545
+ left: '50%',
546
+ transform: 'translateX(-50%)',
547
+ top: 'auto',
548
+ right: 'auto',
549
+ width: '100%',
550
+ maxWidth: '600px'
551
+ };
552
+ case 'right':
553
+ default:
554
+ return {
555
+ position: 'absolute',
556
+ top: '100%',
557
+ right: '0',
558
+ left: 'auto'
559
+ };
560
+ }
561
+ };
562
+
563
+ const defaultSectionStylesDesktop: React.CSSProperties = {
564
+ width: '320px',
565
+ height: 'max-content',
566
+ maxHeight: '600px',
567
+ backgroundColor: '#ffffff',
568
+ borderLeftWidth: '1px',
569
+ borderTopWidth: '1px',
570
+ borderRightWidth: '2px',
571
+ borderBottomWidth: '2px',
572
+ borderColor: '#6b7280',
573
+ borderStyle: 'solid',
574
+ padding: '20px',
575
+ zIndex: 50,
576
+ ...getPositionStyles()
577
+ };
578
+
579
+ // For mobile: merge mobile defaults with mobile theme styles
580
+ // But don't allow theme editor to override height - we need 100vh for full screen
581
+ const { height: _mobileHeight, ...sectionCSSStylesMobileWithoutHeight } =
582
+ sectionCSSStylesMobile;
583
+ const finalSectionStylesMobile = {
584
+ ...defaultSectionStylesMobile,
585
+ ...sectionCSSStylesMobileWithoutHeight
586
+ };
587
+ const finalHeaderStylesMobile = { ...headerCSSStylesMobile };
588
+ const finalFooterStylesMobile = { ...footerCSSStylesMobile };
589
+ const finalTitleStylesMobile = { ...titleCSSStylesMobile };
590
+ const finalItemCountStylesMobile = { ...itemCountCSSStylesMobile };
591
+ const finalCloseIconStylesMobile = { ...closeIconCSSStylesMobile };
592
+ const finalListStylesMobile = { ...listCSSStylesMobile };
593
+ const finalItemStylesMobile = { ...itemCSSStylesMobile };
594
+ const finalItemImageStylesMobile = { ...itemImageCSSStylesMobile };
595
+ const finalItemNameStylesMobile = { ...itemNameCSSStylesMobile };
596
+ const finalItemPriceStylesMobile = { ...itemPriceCSSStylesMobile };
597
+ const finalItemRemoveButtonStylesMobile = {
598
+ ...itemRemoveButtonCSSStylesMobile
599
+ };
600
+ const finalItemAttributeStylesMobile = { ...itemAttributeCSSStylesMobile };
601
+ const finalSubtotalStylesMobile = { ...subtotalCSSStylesMobile };
602
+ const finalViewBagButtonStylesMobile = {
603
+ width: '100%',
604
+ display: 'flex',
605
+ alignItems: 'center',
606
+ justifyContent: 'center',
607
+ backgroundColor: 'var(--primary)',
608
+ color: 'var(--primary-foreground)',
609
+ borderWidth: '1px',
610
+ borderStyle: 'solid',
611
+ borderColor: 'var(--primary)',
612
+ height: '32px',
613
+ fontWeight: '600',
614
+ transition: 'all',
615
+ ...viewBagButtonCSSStylesMobile
616
+ };
617
+ const finalContinueShoppingButtonStylesMobile = {
618
+ height: 'auto',
619
+ padding: '0',
620
+ fontWeight: '600',
621
+ textDecoration: 'underline',
622
+ alignSelf: 'center',
623
+ ...continueShoppingButtonCSSStylesMobile
624
+ };
625
+
626
+ // For desktop: merge desktop defaults with desktop theme styles
627
+ // But don't allow theme editor to override height - we need max-content for dropdown
628
+ const { height: _desktopHeight, ...sectionCSSStylesDesktopWithoutHeight } =
629
+ sectionCSSStylesDesktop;
630
+ const finalSectionStylesDesktop = {
631
+ ...defaultSectionStylesDesktop,
632
+ ...sectionCSSStylesDesktopWithoutHeight
633
+ };
634
+ const finalHeaderStylesDesktop = { ...headerCSSStylesDesktop };
635
+ const finalFooterStylesDesktop = { ...footerCSSStylesDesktop };
636
+ const finalTitleStylesDesktop = { ...titleCSSStylesDesktop };
637
+ const finalItemCountStylesDesktop = { ...itemCountCSSStylesDesktop };
638
+ const finalCloseIconStylesDesktop = { ...closeIconCSSStylesDesktop };
639
+ const finalListStylesDesktop = { ...listCSSStylesDesktop };
640
+ const finalItemStylesDesktop = { ...itemCSSStylesDesktop };
641
+ const finalItemImageStylesDesktop = { ...itemImageCSSStylesDesktop };
642
+ const finalItemNameStylesDesktop = { ...itemNameCSSStylesDesktop };
643
+ const finalItemPriceStylesDesktop = { ...itemPriceCSSStylesDesktop };
644
+ const finalItemRemoveButtonStylesDesktop = {
645
+ ...itemRemoveButtonCSSStylesDesktop
646
+ };
647
+ const finalItemAttributeStylesDesktop = { ...itemAttributeCSSStylesDesktop };
648
+ const finalSubtotalStylesDesktop = { ...subtotalCSSStylesDesktop };
649
+ const finalViewBagButtonStylesDesktop = {
650
+ width: '100%',
651
+ display: 'flex',
652
+ alignItems: 'center',
653
+ justifyContent: 'center',
654
+ backgroundColor: 'var(--primary)',
655
+ color: 'var(--primary-foreground)',
656
+ borderWidth: '1px',
657
+ borderStyle: 'solid',
658
+ borderColor: 'var(--primary)',
659
+ height: '32px',
660
+ fontWeight: '600',
661
+ transition: 'all',
662
+ ...viewBagButtonCSSStylesDesktop
663
+ };
664
+ const finalContinueShoppingButtonStylesDesktop = {
665
+ height: 'auto',
666
+ padding: '0',
667
+ fontWeight: '600',
668
+ textDecoration: 'underline',
669
+ alignSelf: 'center',
670
+ ...continueShoppingButtonCSSStylesDesktop
671
+ };
672
+
673
+ // Use appropriate styles based on viewport
674
+ const finalSectionStyles = isMobile
675
+ ? finalSectionStylesMobile
676
+ : finalSectionStylesDesktop;
677
+ const finalHeaderStyles = isMobile
678
+ ? finalHeaderStylesMobile
679
+ : finalHeaderStylesDesktop;
680
+ const finalFooterStyles = isMobile
681
+ ? finalFooterStylesMobile
682
+ : finalFooterStylesDesktop;
683
+ const finalTitleStyles = isMobile
684
+ ? finalTitleStylesMobile
685
+ : finalTitleStylesDesktop;
686
+ const finalItemCountStyles = isMobile
687
+ ? finalItemCountStylesMobile
688
+ : finalItemCountStylesDesktop;
689
+ const finalCloseIconStyles = isMobile
690
+ ? finalCloseIconStylesMobile
691
+ : finalCloseIconStylesDesktop;
692
+ const finalListStyles = isMobile
693
+ ? finalListStylesMobile
694
+ : finalListStylesDesktop;
695
+ const finalItemStyles = isMobile
696
+ ? finalItemStylesMobile
697
+ : finalItemStylesDesktop;
698
+ const finalItemImageStyles = isMobile
699
+ ? finalItemImageStylesMobile
700
+ : finalItemImageStylesDesktop;
701
+ const finalItemNameStyles = isMobile
702
+ ? finalItemNameStylesMobile
703
+ : finalItemNameStylesDesktop;
704
+ const finalItemPriceStyles = isMobile
705
+ ? finalItemPriceStylesMobile
706
+ : finalItemPriceStylesDesktop;
707
+ const finalItemRemoveButtonStyles = isMobile
708
+ ? finalItemRemoveButtonStylesMobile
709
+ : finalItemRemoveButtonStylesDesktop;
710
+ const finalItemAttributeStyles = isMobile
711
+ ? finalItemAttributeStylesMobile
712
+ : finalItemAttributeStylesDesktop;
713
+ const finalSubtotalStyles = isMobile
714
+ ? finalSubtotalStylesMobile
715
+ : finalSubtotalStylesDesktop;
716
+ const finalViewBagButtonStyles = isMobile
717
+ ? finalViewBagButtonStylesMobile
718
+ : finalViewBagButtonStylesDesktop;
719
+ const finalContinueShoppingButtonStyles = isMobile
720
+ ? finalContinueShoppingButtonStylesMobile
721
+ : finalContinueShoppingButtonStylesDesktop;
166
722
 
167
723
  const totalQuantity = useMemo(
168
724
  () => miniBasket?.total_quantity ?? 0,
169
725
  [miniBasket]
170
726
  );
727
+
728
+ const titleContent = getBlockContent(
729
+ titleBlock,
730
+ t('basket.mini_basket.my_bag')
731
+ );
732
+ const itemCountContent = getBlockContent(
733
+ itemCountBlock,
734
+ `${totalQuantity} ${t('basket.mini_basket.items')}`
735
+ );
736
+ const subtotalContent = getBlockContent(
737
+ subtotalBlock,
738
+ t('basket.mini_basket.subtotal')
739
+ );
740
+ const viewBagButtonContent = getBlockContent(
741
+ viewBagButtonBlock,
742
+ t('basket.mini_basket.view_bag')
743
+ );
744
+ const continueShoppingButtonContent = getBlockContent(
745
+ continueShoppingButtonBlock,
746
+ t('basket.mini_basket.continue_shopping')
747
+ );
748
+ const itemRemoveButtonContent = getBlockContent(
749
+ itemRemoveButtonBlock,
750
+ t('basket.mini_basket.remove')
751
+ );
752
+
753
+ // Get custom close icon from block properties (supports SVG)
754
+ const closeIconProp = closeIconBlock?.properties?.icon;
755
+ const closeIconValue =
756
+ typeof closeIconProp === 'object' && closeIconProp !== null
757
+ ? (closeIconProp as { desktop?: string }).desktop
758
+ : closeIconProp;
759
+ const hasCustomSvgIcon =
760
+ closeIconValue &&
761
+ typeof closeIconValue === 'string' &&
762
+ closeIconValue.includes('<svg');
763
+ // Use custom icon name or default to 'close'
764
+ const closeIconName: string =
765
+ hasCustomSvgIcon || !closeIconValue ? 'close' : String(closeIconValue);
766
+
171
767
  const miniBasketList = useRef(null);
172
768
 
173
769
  useEffect(() => {
@@ -205,38 +801,151 @@ export default function MiniBasket() {
205
801
  : 'opacity-0 invisible',
206
802
  'fixed top-0 left-0 z-50 w-screen h-screen bg-black bg-opacity-80 transition-all duration-300'
207
803
  )}
208
- onClick={() => {
209
- dispatch(closeMiniBasket());
210
- }}
804
+ onClick={() => dispatch(closeMiniBasket())}
211
805
  />
212
806
  <div
213
807
  className={clsx(
808
+ 'flex-col transition-all duration-300',
214
809
  miniBasketOpen
215
- ? 'flex flex-col opacity-100 visible lg:translate-y-[calc(100%)]'
216
- : 'opacity-0 invisible translate-x-full lg:translate-x-0 lg:translate-y-[calc(100%+16px)]',
217
- 'fixed lg:absolute bottom-0 lg:-bottom-1 right-0 w-80 h-screen lg:h-auto bg-white lg:border-l lg:border-t lg:border-r-2 lg:border-b-2 lg:border-gray-500 p-5 z-50 transition-all duration-300'
810
+ ? 'flex opacity-100 visible'
811
+ : 'hidden opacity-0 invisible',
812
+ 'fixed lg:absolute',
813
+ 'bottom-0 lg:top-full right-0',
814
+ 'w-screen h-screen lg:w-auto',
815
+ isDesigner &&
816
+ isSectionSelected &&
817
+ 'ring-4 ring-blue-500 ring-offset-2',
818
+ isDesigner &&
819
+ 'cursor-pointer hover:ring-4 hover:ring-blue-300 hover:ring-offset-2'
218
820
  )}
821
+ style={finalSectionStyles}
822
+ onClick={(e) => {
823
+ if (e.target === e.currentTarget && isDesigner) {
824
+ if (typeof window !== 'undefined' && window.parent) {
825
+ window.parent.postMessage(
826
+ {
827
+ type: 'SELECT_BLOCK_FROM_PREVIEW',
828
+ data: {
829
+ placeholderId: HEADER_MINI_BASKET_PLACEHOLDER_ID,
830
+ sectionId: HEADER_MINI_BASKET_SECTION_ID,
831
+ blockId: ''
832
+ }
833
+ },
834
+ '*'
835
+ );
836
+ }
837
+ }
838
+ }}
219
839
  >
220
- <header className="flex items-center gap-2 pb-3 border-b">
221
- <h3 className="text-xl font-bold">
222
- {t('basket.mini_basket.my_bag')}
840
+ <header
841
+ className={clsx(
842
+ 'flex items-center gap-2 pb-3 border-b relative',
843
+ isDesigner &&
844
+ selectedBlockId === 'mini-basket-header' &&
845
+ 'ring-2 ring-blue-500 ring-inset',
846
+ isDesigner &&
847
+ 'cursor-pointer hover:ring-2 hover:ring-blue-300 hover:ring-inset'
848
+ )}
849
+ style={finalHeaderStyles}
850
+ onClick={(e) =>
851
+ isDesigner && handleBlockClick('mini-basket-header', e)
852
+ }
853
+ >
854
+ <h3
855
+ className={clsx(
856
+ 'relative',
857
+ isDesigner &&
858
+ selectedBlockId === 'mini-basket-title' &&
859
+ 'ring-2 ring-blue-500 ring-inset',
860
+ isDesigner &&
861
+ 'cursor-pointer hover:ring-2 hover:ring-blue-300 hover:ring-inset'
862
+ )}
863
+ style={finalTitleStyles}
864
+ onClick={(e) =>
865
+ isDesigner && handleBlockClick('mini-basket-title', e)
866
+ }
867
+ >
868
+ {titleContent}
223
869
  </h3>
224
- <span>
225
- {totalQuantity}
226
- {t('basket.mini_basket.items')}
870
+ <span
871
+ className={clsx(
872
+ 'relative',
873
+ isDesigner &&
874
+ selectedBlockId === 'mini-basket-item-count' &&
875
+ 'ring-2 ring-blue-500 ring-inset',
876
+ isDesigner &&
877
+ 'cursor-pointer hover:ring-2 hover:ring-blue-300 hover:ring-inset'
878
+ )}
879
+ style={finalItemCountStyles}
880
+ onClick={(e) =>
881
+ isDesigner && handleBlockClick('mini-basket-item-count', e)
882
+ }
883
+ >
884
+ {totalQuantity} {t('basket.mini_basket.items')}
227
885
  </span>
228
- <Icon
229
- name="close"
230
- size={16}
231
- className="ml-auto fill-primary hover:cursor-pointer"
232
- onClick={() => dispatch(closeMiniBasket())}
233
- />
886
+ {hasCustomSvgIcon ? (
887
+ <div
888
+ className={clsx(
889
+ 'ml-auto hover:cursor-pointer flex items-center justify-center relative',
890
+ isDesigner &&
891
+ selectedBlockId === 'mini-basket-close-icon' &&
892
+ 'ring-2 ring-blue-500 ring-inset'
893
+ )}
894
+ style={{
895
+ width: 16,
896
+ height: 16,
897
+ ...finalCloseIconStyles
898
+ }}
899
+ dangerouslySetInnerHTML={{ __html: closeIconValue }}
900
+ onClick={(e) => {
901
+ e.stopPropagation();
902
+ if (isDesigner) {
903
+ handleBlockClick('mini-basket-close-icon', e);
904
+ } else {
905
+ dispatch(closeMiniBasket());
906
+ }
907
+ }}
908
+ />
909
+ ) : (
910
+ <Icon
911
+ name={closeIconName}
912
+ size={16}
913
+ className={clsx(
914
+ 'ml-auto hover:cursor-pointer relative',
915
+ isDesigner &&
916
+ selectedBlockId === 'mini-basket-close-icon' &&
917
+ 'ring-2 ring-blue-500 ring-inset'
918
+ )}
919
+ style={finalCloseIconStyles}
920
+ onClick={(e) => {
921
+ e.stopPropagation();
922
+ if (isDesigner) {
923
+ handleBlockClick('mini-basket-close-icon', e);
924
+ } else {
925
+ dispatch(closeMiniBasket());
926
+ }
927
+ }}
928
+ />
929
+ )}
234
930
  </header>
235
- {isLoading && <LoaderSpinner />} {/* TODO: Fix spinner position */}
931
+ {isLoading && <LoaderSpinner />}
236
932
  {isSuccess && (
237
933
  <ul
238
934
  ref={miniBasketList}
239
- className="overflow-y-auto lg:max-h-64 flex flex-col"
935
+ className={clsx(
936
+ 'overflow-y-auto lg:max-h-64 flex flex-col relative',
937
+ isDesigner &&
938
+ selectedBlockId === 'mini-basket-list' &&
939
+ 'ring-2 ring-blue-500 ring-inset',
940
+ isDesigner &&
941
+ 'cursor-pointer hover:ring-2 hover:ring-blue-300 hover:ring-inset'
942
+ )}
943
+ style={finalListStyles}
944
+ onClick={(e) => {
945
+ if (e.target === e.currentTarget && isDesigner) {
946
+ handleBlockClick('mini-basket-list', e);
947
+ }
948
+ }}
240
949
  >
241
950
  {sortedBasket.map((basketItem) => (
242
951
  <MiniBasketItem
@@ -244,32 +953,98 @@ export default function MiniBasket() {
244
953
  basketItem={basketItem}
245
954
  highlightedItem={highlightedItem}
246
955
  miniBasketListRef={miniBasketList}
956
+ isDesigner={isDesigner}
957
+ selectedBlockId={selectedBlockId}
958
+ onBlockClick={handleBlockClick}
959
+ itemStyles={finalItemStyles}
960
+ imageStyles={finalItemImageStyles}
961
+ nameStyles={finalItemNameStyles}
962
+ priceStyles={finalItemPriceStyles}
963
+ removeButtonStyles={finalItemRemoveButtonStyles}
964
+ removeButtonContent={itemRemoveButtonContent}
965
+ attributeStyles={finalItemAttributeStyles}
247
966
  />
248
967
  ))}
249
968
  </ul>
250
969
  )}
251
- <footer className="flex flex-col gap-3 mt-auto lg:mt-3 lg:flex-1">
252
- <div className="flex self-center gap-1 text-xs font-semibold">
253
- <span>{t('basket.mini_basket.subtotal')}</span>
970
+ <footer
971
+ className={clsx(
972
+ 'flex flex-col gap-3 mt-auto lg:mt-3 lg:flex-1 relative',
973
+ isDesigner &&
974
+ selectedBlockId === 'mini-basket-footer' &&
975
+ 'ring-2 ring-blue-500 ring-inset',
976
+ isDesigner &&
977
+ 'cursor-pointer hover:ring-2 hover:ring-blue-300 hover:ring-inset'
978
+ )}
979
+ style={finalFooterStyles}
980
+ onClick={(e) => {
981
+ if (e.target === e.currentTarget && isDesigner) {
982
+ handleBlockClick('mini-basket-footer', e);
983
+ }
984
+ }}
985
+ >
986
+ <div
987
+ className={clsx(
988
+ 'flex self-center gap-1 text-xs font-semibold relative',
989
+ isDesigner &&
990
+ selectedBlockId === 'mini-basket-subtotal' &&
991
+ 'ring-2 ring-blue-500 ring-inset',
992
+ isDesigner &&
993
+ 'cursor-pointer hover:ring-2 hover:ring-blue-300 hover:ring-inset'
994
+ )}
995
+ style={finalSubtotalStyles}
996
+ onClick={(e) =>
997
+ isDesigner && handleBlockClick('mini-basket-subtotal', e)
998
+ }
999
+ >
1000
+ <span>{subtotalContent}</span>
254
1001
  <Price value={basket?.total_amount} />
255
1002
  </div>
256
- {/* TODO: Fix link styles */}
257
1003
  <Link
258
- onClick={() => {
259
- dispatch(closeMiniBasket());
1004
+ onClick={(e) => {
1005
+ if (isDesigner) {
1006
+ e.preventDefault();
1007
+ e.stopPropagation();
1008
+ handleBlockClick('mini-basket-view-bag-button', e);
1009
+ } else {
1010
+ dispatch(closeMiniBasket());
1011
+ }
260
1012
  }}
261
1013
  href={ROUTES.BASKET}
262
1014
  data-testid="mini-basket-view-bag"
263
- className="w-full flex items-center justify-center bg-primary text-primary-foreground border border-primary h-8 font-semibold transition-all hover:bg-primary-foreground hover:text-primary"
1015
+ className={clsx(
1016
+ 'w-full flex items-center justify-center transition-all relative',
1017
+ isDesigner &&
1018
+ selectedBlockId === 'mini-basket-view-bag-button' &&
1019
+ 'ring-2 ring-blue-500 ring-inset',
1020
+ isDesigner &&
1021
+ 'cursor-pointer hover:ring-2 hover:ring-blue-300 hover:ring-inset'
1022
+ )}
1023
+ style={finalViewBagButtonStyles}
264
1024
  >
265
- {t('basket.mini_basket.view_bag')}
1025
+ {viewBagButtonContent}
266
1026
  </Link>
267
1027
  <Link
268
1028
  href="/"
269
- className="h-auto p-0 font-semibold underline self-center"
1029
+ className={clsx(
1030
+ 'self-center relative',
1031
+ isDesigner &&
1032
+ selectedBlockId === 'mini-basket-continue-shopping-button' &&
1033
+ 'ring-2 ring-blue-500 ring-inset',
1034
+ isDesigner &&
1035
+ 'cursor-pointer hover:ring-2 hover:ring-blue-300 hover:ring-inset'
1036
+ )}
270
1037
  data-testid="mini-basket-continue-shopping"
1038
+ style={finalContinueShoppingButtonStyles}
1039
+ onClick={(e) => {
1040
+ if (isDesigner) {
1041
+ e.preventDefault();
1042
+ e.stopPropagation();
1043
+ handleBlockClick('mini-basket-continue-shopping-button', e);
1044
+ }
1045
+ }}
271
1046
  >
272
- {t('basket.mini_basket.continue_shopping')}
1047
+ {continueShoppingButtonContent}
273
1048
  </Link>
274
1049
  </footer>
275
1050
  </div>