@akinon/projectzero 2.0.0-beta.2 → 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 (377) hide show
  1. package/CHANGELOG.md +147 -6
  2. package/app-template/.env.example +8 -0
  3. package/app-template/.github/instructions/account.instructions.md +749 -0
  4. package/app-template/.github/instructions/checkout.instructions.md +678 -0
  5. package/app-template/.github/instructions/default.instructions.md +279 -0
  6. package/app-template/.github/instructions/edge-cases.instructions.md +73 -0
  7. package/app-template/.github/instructions/routing.instructions.md +603 -0
  8. package/app-template/.github/instructions/settings.instructions.md +338 -0
  9. package/app-template/.gitignore +5 -0
  10. package/app-template/AGENTS.md +7 -0
  11. package/app-template/CHANGELOG.md +1645 -61
  12. package/app-template/Procfile +1 -1
  13. package/app-template/README.md +6 -0
  14. package/app-template/akinon.json +1 -4
  15. package/app-template/build.sh +10 -0
  16. package/app-template/config/prebuild-tests.json +5 -0
  17. package/app-template/docs/advanced-usage.md +111 -0
  18. package/app-template/docs/plugins.md +60 -25
  19. package/app-template/docs/sentry-usage.md +35 -0
  20. package/app-template/jest.config.ts +2 -2
  21. package/app-template/next-env.d.ts +1 -0
  22. package/app-template/next.config.mjs +8 -5
  23. package/app-template/package.json +60 -50
  24. package/app-template/postcss.config.mjs +5 -0
  25. package/app-template/public/amex.svg +12 -0
  26. package/app-template/public/apple-pay.svg +16 -0
  27. package/app-template/public/assets/images/product-placeholder-1.jpg +0 -0
  28. package/app-template/public/assets/images/product-placeholder-2.jpg +0 -0
  29. package/app-template/public/assets/images/product-placeholder-3.jpg +0 -0
  30. package/app-template/public/assets/images/product-placeholder-4.jpg +0 -0
  31. package/app-template/public/google-pay.svg +16 -0
  32. package/app-template/public/locales/en/account.json +13 -4
  33. package/app-template/public/locales/en/auth.json +6 -7
  34. package/app-template/public/locales/en/basket.json +6 -6
  35. package/app-template/public/locales/en/blog.json +7 -0
  36. package/app-template/public/locales/en/category.json +3 -1
  37. package/app-template/public/locales/en/checkout.json +17 -4
  38. package/app-template/public/locales/en/common.json +71 -3
  39. package/app-template/public/locales/en/forgot_password.json +6 -7
  40. package/app-template/public/locales/en/product.json +84 -4
  41. package/app-template/public/locales/tr/account.json +13 -4
  42. package/app-template/public/locales/tr/auth.json +16 -17
  43. package/app-template/public/locales/tr/basket.json +4 -4
  44. package/app-template/public/locales/tr/blog.json +7 -0
  45. package/app-template/public/locales/tr/category.json +3 -1
  46. package/app-template/public/locales/tr/checkout.json +48 -36
  47. package/app-template/public/locales/tr/common.json +70 -2
  48. package/app-template/public/locales/tr/forgot_password.json +12 -13
  49. package/app-template/public/locales/tr/product.json +82 -0
  50. package/app-template/public/logo.svg +3 -27
  51. package/app-template/public/mastercard.svg +14 -0
  52. package/app-template/public/masterpass-javascript-sdk-web.min.js +1 -0
  53. package/app-template/public/promotion-banner.jpg +0 -0
  54. package/app-template/public/shop-pay.svg +12 -0
  55. package/app-template/public/visa.svg +12 -0
  56. package/app-template/src/__tests__/middleware-matcher.test.ts +135 -0
  57. package/app-template/src/app/[commerce]/[locale]/[currency]/blog/[slug]/page.tsx +118 -0
  58. package/app-template/src/app/[commerce]/[locale]/[currency]/pages/[slug]/page.tsx +15 -0
  59. package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/[...prettyurl]/page.tsx +9 -9
  60. package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/account/layout.tsx +2 -2
  61. package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/account/orders/[id]/cancellation/page.tsx +105 -13
  62. package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/account/orders/[id]/page.tsx +136 -52
  63. package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/account/profile/page.tsx +2 -2
  64. package/app-template/src/app/[pz]/category/[pk]/page.tsx +27 -0
  65. package/app-template/src/app/[pz]/error.tsx +17 -0
  66. package/app-template/src/app/[pz]/flat-page/[pk]/page.tsx +23 -0
  67. package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/forms/[pk]/generate/page.tsx +1 -2
  68. package/app-template/src/app/[pz]/group-product/[pk]/page.tsx +93 -0
  69. package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/landing-page/[pk]/page.tsx +2 -4
  70. package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/layout.tsx +3 -10
  71. package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/list/page.tsx +2 -4
  72. package/app-template/src/app/{[commerce]/[locale]/[currency]/pz-not-found/page.tsx → [pz]/not-found.tsx} +5 -7
  73. package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/orders/checkout/page.tsx +7 -4
  74. package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/orders/completed/[token]/page.tsx +6 -4
  75. package/app-template/src/app/[pz]/product/[pk]/page.tsx +102 -0
  76. package/app-template/src/app/[pz]/special-page/[pk]/page.tsx +35 -0
  77. package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/users/email-set-primary/[[...id]]/page.tsx +3 -4
  78. package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/users/registration/account-confirm-email/[[...id]]/page.tsx +3 -3
  79. package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/users/reset/[[...id]]/page.tsx +41 -5
  80. package/app-template/src/app/[pz]/xml-sitemap/[node]/route.ts +73 -0
  81. package/app-template/src/app/api/auth/[...nextauth]/route.ts +3 -0
  82. package/app-template/src/app/api/barcode-search/route.ts +1 -0
  83. package/app-template/src/app/api/form/[...id]/route.ts +1 -7
  84. package/app-template/src/app/api/image-proxy/route.ts +1 -0
  85. package/app-template/src/app/api/product-categories/route.ts +1 -0
  86. package/app-template/src/app/api/similar-product-list/route.ts +1 -0
  87. package/app-template/src/app/api/similar-products/route.ts +1 -0
  88. package/app-template/src/app/api/theme-settings/route.ts +12 -0
  89. package/app-template/src/app/api/virtual-try-on/limited-categories/route.ts +1 -0
  90. package/app-template/src/app/api/virtual-try-on/route.ts +1 -0
  91. package/app-template/src/assets/fonts/pz-icon.css +211 -49
  92. package/app-template/src/assets/fonts/pz-icon.eot +0 -0
  93. package/app-template/src/assets/fonts/pz-icon.html +486 -0
  94. package/app-template/src/assets/fonts/pz-icon.scss +373 -49
  95. package/app-template/src/assets/fonts/pz-icon.svg +215 -53
  96. package/app-template/src/assets/fonts/pz-icon.ttf +0 -0
  97. package/app-template/src/assets/fonts/pz-icon.woff +0 -0
  98. package/app-template/src/assets/fonts/pz-icon.woff2 +0 -0
  99. package/app-template/src/assets/globals.scss +37 -34
  100. package/app-template/src/assets/icons/arrow-right.svg +3 -0
  101. package/app-template/src/assets/icons/cart.svg +4 -12
  102. package/app-template/src/assets/icons/check.svg +2 -18
  103. package/app-template/src/assets/icons/chevron-down.svg +2 -7
  104. package/app-template/src/assets/icons/delete.svg +3 -0
  105. package/app-template/src/assets/icons/facebook.svg +2 -8
  106. package/app-template/src/assets/icons/fav-off.svg +5 -0
  107. package/app-template/src/assets/icons/fav-on.svg +5 -0
  108. package/app-template/src/assets/icons/filter-and-sort.svg +3 -0
  109. package/app-template/src/assets/icons/heart.svg +3 -0
  110. package/app-template/src/assets/icons/instagram.svg +2 -13
  111. package/app-template/src/assets/icons/materials.svg +3 -0
  112. package/app-template/src/assets/icons/person.svg +4 -0
  113. package/app-template/src/assets/icons/pinterest.svg +5 -11
  114. package/app-template/src/assets/icons/ruler.svg +3 -0
  115. package/app-template/src/assets/icons/search.svg +8 -11
  116. package/app-template/src/assets/icons/share.svg +2 -9
  117. package/app-template/src/assets/icons/snapchat.svg +3 -0
  118. package/app-template/src/assets/icons/tiktok.svg +3 -0
  119. package/app-template/src/assets/icons/tumblr.svg +6 -0
  120. package/app-template/src/assets/icons/twitter.svg +2 -10
  121. package/app-template/src/assets/icons/vimeo.svg +3 -0
  122. package/app-template/src/assets/icons/youtube.svg +3 -0
  123. package/app-template/src/assets/icons/zoom.svg +8 -0
  124. package/app-template/src/auth.ts +3 -0
  125. package/app-template/src/components/__tests__/link.test.tsx +2 -0
  126. package/app-template/src/components/accordion.tsx +48 -23
  127. package/app-template/src/components/action-tooltip.tsx +160 -0
  128. package/app-template/src/components/button.tsx +50 -35
  129. package/app-template/src/components/carousel-core.tsx +4 -11
  130. package/app-template/src/components/checkbox.tsx +2 -1
  131. package/app-template/src/components/currency-select.tsx +150 -4
  132. package/app-template/src/components/file-input.tsx +64 -2
  133. package/app-template/src/components/generate-form-fields.tsx +49 -10
  134. package/app-template/src/components/icon.tsx +5 -6
  135. package/app-template/src/components/index.ts +4 -1
  136. package/app-template/src/components/input.tsx +8 -2
  137. package/app-template/src/components/language-select.tsx +88 -2
  138. package/app-template/src/components/modal.tsx +34 -16
  139. package/app-template/src/components/pagination.tsx +133 -20
  140. package/app-template/src/components/price.tsx +1 -1
  141. package/app-template/src/components/pwa-tags.tsx +1 -0
  142. package/app-template/src/components/quantity-input.tsx +63 -0
  143. package/app-template/src/components/quantity-selector.tsx +203 -0
  144. package/app-template/src/components/route-handler.tsx +50 -0
  145. package/app-template/src/components/select.tsx +86 -54
  146. package/app-template/src/components/tabs.tsx +2 -2
  147. package/app-template/src/components/types/index.ts +55 -2
  148. package/app-template/src/components/widget-content.tsx +323 -0
  149. package/app-template/src/data/server/theme.ts +70 -0
  150. package/app-template/src/hooks/use-fav-button.tsx +9 -10
  151. package/app-template/src/hooks/use-product-cart.ts +80 -0
  152. package/app-template/src/hooks/use-stock-alert.ts +74 -0
  153. package/app-template/src/hooks/use-theme-settings.ts +42 -0
  154. package/app-template/src/lib/fonts.ts +149 -0
  155. package/app-template/src/middleware.ts +1 -0
  156. package/app-template/src/plugins.js +13 -2
  157. package/app-template/src/redux/middlewares/category.ts +6 -5
  158. package/app-template/src/redux/reducers/category.ts +1 -1
  159. package/app-template/src/redux/store.ts +21 -1
  160. package/app-template/src/routes/index.ts +2 -1
  161. package/app-template/src/settings.js +5 -3
  162. package/app-template/src/types/hookform-resolvers-yup.d.ts +28 -0
  163. package/app-template/src/types/index.ts +74 -3
  164. package/app-template/src/types/next-auth.d.ts +2 -2
  165. package/app-template/src/types/widget.ts +169 -0
  166. package/app-template/src/utils/convert-facet-search-params.ts +1 -1
  167. package/app-template/src/utils/formatDate.ts +48 -0
  168. package/app-template/src/utils/styles.ts +71 -0
  169. package/app-template/src/utils/variant-validation.ts +41 -0
  170. package/app-template/src/views/account/address-form.tsx +8 -4
  171. package/app-template/src/views/account/contact-form.tsx +148 -136
  172. package/app-template/src/views/account/content-header.tsx +2 -2
  173. package/app-template/src/views/account/faq/faq-tabs.tsx +8 -2
  174. package/app-template/src/views/account/favorite-item.tsx +1 -1
  175. package/app-template/src/views/account/order.tsx +10 -8
  176. package/app-template/src/views/account/orders/order-cancellation-item.tsx +4 -3
  177. package/app-template/src/views/account/orders/order-detail-header.tsx +1 -1
  178. package/app-template/src/views/anonymous-tracking/order-detail/index.tsx +44 -37
  179. package/app-template/src/views/basket/basket-item.tsx +697 -107
  180. package/app-template/src/views/basket/basket-summary-context.tsx +560 -0
  181. package/app-template/src/views/basket/designer-context.tsx +617 -0
  182. package/app-template/src/views/basket/index.ts +2 -0
  183. package/app-template/src/views/basket/summary.tsx +497 -60
  184. package/app-template/src/views/breadcrumb/breadcrumb-client.tsx +190 -0
  185. package/app-template/src/views/breadcrumb/breadcrumb-registrar.tsx +286 -0
  186. package/app-template/src/views/breadcrumb/constants.ts +15 -0
  187. package/app-template/src/views/breadcrumb/index.tsx +127 -0
  188. package/app-template/src/views/breadcrumb.tsx +13 -38
  189. package/app-template/src/views/category/category-active-filters.tsx +1 -1
  190. package/app-template/src/views/category/category-banner.tsx +4 -23
  191. package/app-template/src/views/category/category-header.tsx +289 -60
  192. package/app-template/src/views/category/category-info.tsx +177 -27
  193. package/app-template/src/views/category/filters/filter-item.tsx +138 -42
  194. package/app-template/src/views/category/filters/index.tsx +209 -49
  195. package/app-template/src/views/category/layout.tsx +7 -4
  196. package/app-template/src/views/category/native-widget-context.tsx +257 -0
  197. package/app-template/src/views/category/product-list-registrar.tsx +665 -0
  198. package/app-template/src/views/checkout/auth.tsx +64 -40
  199. package/app-template/src/views/checkout/checkout-address-registrar.tsx +254 -0
  200. package/app-template/src/views/checkout/checkout-buttons-registrar.tsx +183 -0
  201. package/app-template/src/views/checkout/checkout-delivery-method-registrar.tsx +259 -0
  202. package/app-template/src/views/checkout/checkout-payment-options-registrar.tsx +253 -0
  203. package/app-template/src/views/checkout/checkout-summary-registrar.tsx +183 -0
  204. package/app-template/src/views/checkout/constants.ts +5 -0
  205. package/app-template/src/views/checkout/index.tsx +5 -0
  206. package/app-template/src/views/checkout/layout/header.tsx +9 -5
  207. package/app-template/src/views/checkout/steps/payment/index.tsx +5 -2
  208. package/app-template/src/views/checkout/steps/payment/options/credit-card/index.tsx +93 -6
  209. package/app-template/src/views/checkout/steps/payment/options/funds-transfer.tsx +25 -5
  210. package/app-template/src/views/checkout/steps/payment/options/loyalty.tsx +21 -2
  211. package/app-template/src/views/checkout/steps/payment/options/masterpass-rest.tsx +15 -0
  212. package/app-template/src/views/checkout/steps/payment/options/redirection.tsx +27 -5
  213. package/app-template/src/views/checkout/steps/payment/options/saved-card.tsx +18 -0
  214. package/app-template/src/views/checkout/steps/payment/options/store-credit.tsx +464 -0
  215. package/app-template/src/views/checkout/steps/payment/payment-option-buttons.tsx +171 -40
  216. package/app-template/src/views/checkout/steps/shipping/address-box.tsx +104 -29
  217. package/app-template/src/views/checkout/steps/shipping/addresses.tsx +129 -46
  218. package/app-template/src/views/checkout/steps/shipping/shipping-options.tsx +232 -27
  219. package/app-template/src/views/checkout/summary.tsx +310 -26
  220. package/app-template/src/views/find-in-store/index.tsx +2 -2
  221. package/app-template/src/views/footer/footer-app-banner-context.tsx +326 -0
  222. package/app-template/src/views/footer/footer-bottom-context.tsx +215 -0
  223. package/app-template/src/views/footer/footer-bottom-wrapper.tsx +74 -0
  224. package/app-template/src/views/footer/footer-layout-constants.ts +35 -0
  225. package/app-template/src/views/footer/footer-layout-registrar.tsx +342 -0
  226. package/app-template/src/views/footer/footer-layout-switcher.tsx +110 -0
  227. package/app-template/src/views/footer/footer-menu-context.tsx +211 -0
  228. package/app-template/src/views/footer/footer-native-widgets.tsx +60 -0
  229. package/app-template/src/views/footer/footer-social-context.tsx +254 -0
  230. package/app-template/src/views/footer/footer-subscription-context.tsx +210 -0
  231. package/app-template/src/views/footer/footer-utils.ts +43 -0
  232. package/app-template/src/views/footer/footer-value-props-context.tsx +326 -0
  233. package/app-template/src/views/footer/logo-settings.ts +183 -0
  234. package/app-template/src/views/footer/native-widget-config.ts +262 -0
  235. package/app-template/src/views/footer/subscription-settings.ts +122 -0
  236. package/app-template/src/views/footer/use-footer-logo.ts +162 -0
  237. package/app-template/src/views/footer.tsx +415 -13
  238. package/app-template/src/views/guest-login/index.tsx +62 -58
  239. package/app-template/src/views/header/action-menu.tsx +284 -49
  240. package/app-template/src/views/header/band.tsx +6 -21
  241. package/app-template/src/views/header/designer-context.tsx +261 -0
  242. package/app-template/src/views/header/header-announcement-registrar.tsx +267 -0
  243. package/app-template/src/views/header/header-client-wrapper.tsx +496 -0
  244. package/app-template/src/views/header/header-content.tsx +1026 -0
  245. package/app-template/src/views/header/header-currency-registrar.tsx +348 -0
  246. package/app-template/src/views/header/header-icons-context.tsx +262 -0
  247. package/app-template/src/views/header/header-language-registrar.tsx +348 -0
  248. package/app-template/src/views/header/header-layout-context.tsx +143 -0
  249. package/app-template/src/views/header/header-layout-registrar.tsx +658 -0
  250. package/app-template/src/views/header/header-logo-context.tsx +228 -0
  251. package/app-template/src/views/header/header-logo.tsx +118 -0
  252. package/app-template/src/views/header/header-mini-basket-context.tsx +524 -0
  253. package/app-template/src/views/header/header-search-registrar.tsx +511 -0
  254. package/app-template/src/views/header/header-text-slider-registrar.tsx +382 -0
  255. package/app-template/src/views/header/index.tsx +110 -48
  256. package/app-template/src/views/header/inline-search.tsx +262 -0
  257. package/app-template/src/views/header/mini-basket.tsx +832 -46
  258. package/app-template/src/views/header/mobile-hamburger-button.tsx +5 -8
  259. package/app-template/src/views/header/mobile-menu.tsx +12 -0
  260. package/app-template/src/views/header/navbar-menu-context.tsx +219 -0
  261. package/app-template/src/views/header/navbar.tsx +178 -111
  262. package/app-template/src/views/header/search/index.tsx +85 -24
  263. package/app-template/src/views/header/search/results.tsx +127 -65
  264. package/app-template/src/views/header/search/search-input.tsx +61 -0
  265. package/app-template/src/views/header/server-settings-parser.ts +1105 -0
  266. package/app-template/src/views/header/use-header-icons.ts +241 -0
  267. package/app-template/src/views/header/use-header-logo.ts +213 -0
  268. package/app-template/src/views/header/use-navbar-menu.ts +179 -0
  269. package/app-template/src/views/installment-options/index.tsx +1 -1
  270. package/app-template/src/views/login/index.tsx +89 -56
  271. package/app-template/src/views/otp-login/index.tsx +23 -20
  272. package/app-template/src/views/product/accordion-section.tsx +61 -0
  273. package/app-template/src/views/product/accordion-wrapper.tsx +135 -43
  274. package/app-template/src/views/product/custom-button-group.tsx +69 -0
  275. package/app-template/src/views/product/favorites-button-section.tsx +69 -0
  276. package/app-template/src/views/product/find-in-store-section.tsx +60 -0
  277. package/app-template/src/views/product/index.ts +1 -0
  278. package/app-template/src/views/product/layout.tsx +21 -6
  279. package/app-template/src/views/product/misc-buttons.tsx +339 -25
  280. package/app-template/src/views/product/price-wrapper.tsx +3 -24
  281. package/app-template/src/views/product/product-actions.tsx +294 -0
  282. package/app-template/src/views/product/product-info-section.tsx +140 -0
  283. package/app-template/src/views/product/product-info.tsx +130 -254
  284. package/app-template/src/views/product/product-share.tsx +61 -0
  285. package/app-template/src/views/product/product-variants.tsx +26 -0
  286. package/app-template/src/views/product/quantity-section.tsx +73 -0
  287. package/app-template/src/views/product/sale-tag.tsx +10 -0
  288. package/app-template/src/views/product/share-section.tsx +357 -0
  289. package/app-template/src/views/product/slider.tsx +135 -76
  290. package/app-template/src/views/product/variant.tsx +69 -41
  291. package/app-template/src/views/product/variants-section.tsx +126 -0
  292. package/app-template/src/views/product-detail/constants.ts +272 -0
  293. package/app-template/src/views/product-detail/index.ts +10 -0
  294. package/app-template/src/views/product-detail/product-detail-registrar.tsx +616 -0
  295. package/app-template/src/views/product-item/index.tsx +119 -46
  296. package/app-template/src/views/register/index.tsx +54 -44
  297. package/app-template/src/views/share/index.tsx +9 -6
  298. package/app-template/src/views/widgets/home-hero-slider-content.tsx +41 -39
  299. package/app-template/src/widgets/flatpages/about-us/index.tsx +78 -0
  300. package/app-template/src/widgets/flatpages/blog-list/index.tsx +129 -0
  301. package/app-template/src/widgets/footer-app-banner.tsx +444 -0
  302. package/app-template/src/widgets/footer-bottom.tsx +127 -0
  303. package/app-template/src/widgets/footer-menu-compact.tsx +238 -0
  304. package/app-template/src/widgets/footer-menu-two.tsx +298 -0
  305. package/app-template/src/widgets/footer-menu.tsx +6 -2
  306. package/app-template/src/widgets/footer-social-client.tsx +251 -0
  307. package/app-template/src/widgets/footer-social.tsx +47 -16
  308. package/app-template/src/widgets/footer-subscription/footer-subscription-form.tsx +17 -14
  309. package/app-template/src/widgets/footer-subscription/index.tsx +183 -17
  310. package/app-template/src/widgets/footer-value-props.tsx +201 -0
  311. package/app-template/src/widgets/home-stories-eng.tsx +42 -34
  312. package/app-template/src/widgets/index.ts +7 -0
  313. package/app-template/src/widgets/schemas/about-us.json +46 -0
  314. package/app-template/src/widgets/schemas/blog-list.json +37 -0
  315. package/app-template/src/widgets/schemas/blog.json +29 -0
  316. package/app-template/tailwind.config.js +19 -7
  317. package/app-template/tsconfig.json +29 -11
  318. package/codemods/migrate-segments/index.js +591 -0
  319. package/codemods/sentry-9/index.js +30 -0
  320. package/codemods/sentry-9/remove-sentry-configs.js +14 -0
  321. package/codemods/sentry-9/remove-sentry-dependency.js +25 -0
  322. package/codemods/sentry-9/replace-error-page.js +32 -0
  323. package/codemods/update-tailwind-config/index.js +30 -0
  324. package/codemods/update-tailwind-config/transform.js +102 -0
  325. package/commands/codemod.ts +17 -0
  326. package/commands/index.ts +3 -1
  327. package/commands/plugins.ts +115 -46
  328. package/dist/codemods/sentry-9/templates/error.js +14 -0
  329. package/dist/commands/codemod.js +15 -0
  330. package/dist/commands/index.js +3 -1
  331. package/dist/commands/plugins.js +85 -34
  332. package/package.json +3 -2
  333. package/app-template/postcss.config.js +0 -6
  334. package/app-template/sentry.client.config.ts +0 -16
  335. package/app-template/sentry.edge.config.ts +0 -3
  336. package/app-template/sentry.properties +0 -4
  337. package/app-template/sentry.server.config.ts +0 -3
  338. package/app-template/src/app/[commerce]/[locale]/[currency]/category/[pk]/page.tsx +0 -22
  339. package/app-template/src/app/[commerce]/[locale]/[currency]/error.tsx +0 -20
  340. package/app-template/src/app/[commerce]/[locale]/[currency]/flat-page/[pk]/page.tsx +0 -20
  341. package/app-template/src/app/[commerce]/[locale]/[currency]/group-product/[pk]/page.tsx +0 -74
  342. package/app-template/src/app/[commerce]/[locale]/[currency]/product/[pk]/loading.tsx +0 -67
  343. package/app-template/src/app/[commerce]/[locale]/[currency]/product/[pk]/page.tsx +0 -84
  344. package/app-template/src/app/[commerce]/[locale]/[currency]/special-page/[pk]/page.tsx +0 -27
  345. package/app-template/src/app/[commerce]/[locale]/[currency]/xml-sitemap/[node]/route.ts +0 -25
  346. package/app-template/src/pages/api/auth/[...nextauth].ts +0 -3
  347. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/account/address/page.tsx +0 -0
  348. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/account/change-email/page.tsx +0 -0
  349. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/account/change-password/page.tsx +0 -0
  350. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/account/contact/page.tsx +0 -0
  351. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/account/coupons/page.tsx +0 -0
  352. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/account/email-verification/page.tsx +0 -0
  353. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/account/faq/page.tsx +0 -0
  354. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/account/favourite-products/page.tsx +0 -0
  355. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/account/my-quotations/page.tsx +0 -0
  356. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/account/orders/[id]/layout.tsx +0 -0
  357. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/account/orders/page.tsx +0 -0
  358. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/account/page.tsx +0 -0
  359. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/address/stores/page.tsx +0 -0
  360. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/anonymous-tracking/page.tsx +0 -0
  361. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/auth/oauth-login/page.tsx +0 -0
  362. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/auth/page.tsx +0 -0
  363. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/basket/page.tsx +0 -0
  364. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/basket-b2b/page.tsx +0 -0
  365. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/category/[pk]/loading.tsx +0 -0
  366. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/client-root.tsx +0 -0
  367. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/contact-us/page.tsx +0 -0
  368. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/flat-page/[pk]/loading.tsx +0 -0
  369. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/group-product/[pk]/loading.tsx +0 -0
  370. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/landing-page/[pk]/loading.tsx +0 -0
  371. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/list/loading.tsx +0 -0
  372. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/orders/completed/[token]/layout.tsx +0 -0
  373. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/page.tsx +0 -0
  374. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/special-page/[pk]/loading.tsx +0 -0
  375. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/template.tsx +0 -0
  376. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/users/password/reset/page.tsx +0 -0
  377. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/xml-sitemap/route.ts +0 -0
@@ -5,6 +5,7 @@ import clsx from 'clsx';
5
5
  import {
6
6
  basketApi,
7
7
  useGetBasketQuery,
8
+ useGetMiniBasketQuery,
8
9
  useUpdateQuantityMutation
9
10
  } from '@akinon/next/data/client/basket';
10
11
  import { useAppDispatch, useAppSelector } from '@akinon/next/redux/hooks';
@@ -18,15 +19,44 @@ import {
18
19
  } from '@akinon/next/hooks';
19
20
  import { Image } from '@akinon/next/components/image';
20
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';
21
27
 
22
28
  interface MiniBasketItemProps {
23
29
  basketItem: BasketItem;
24
30
  highlightedItem: number;
25
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;
26
42
  }
27
43
 
28
44
  function MiniBasketItem(props: MiniBasketItemProps) {
29
- 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;
30
60
  const dispatch = useAppDispatch();
31
61
  const [updateQuantityMutation] = useUpdateQuantityMutation();
32
62
  const commonProductAttributes = useCommonProductAttributes({
@@ -70,24 +100,63 @@ function MiniBasketItem(props: MiniBasketItemProps) {
70
100
  });
71
101
  };
72
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
+
73
114
  return (
74
115
  <li
75
- style={{ order: isHighlighted ? '-1' : '0' }}
76
- 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)}
77
129
  >
78
130
  <Link
79
131
  href={basketItem.product.absolute_url}
80
132
  className={clsx(
81
- 'block shrink-0 transition-all duration-300',
82
- 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'
83
145
  )}
146
+ onClick={(e) => {
147
+ if (isDesigner) {
148
+ e.preventDefault();
149
+ onBlockClick?.('mini-basket-item-image', e);
150
+ }
151
+ }}
84
152
  >
85
153
  <Image
86
154
  src={basketItem.product.productimage_set?.[0]?.image ?? ''}
87
155
  alt={basketItem.product.name}
88
- width={isHighlighted ? 96 : 48}
89
- height={isHighlighted ? 160 : 80}
156
+ width={imageWidth}
157
+ height={imageHeight}
90
158
  className="transition-all duration-300"
159
+ style={imageStyles}
91
160
  />
92
161
  </Link>
93
162
  <div className="flex flex-col flex-1">
@@ -106,8 +175,22 @@ function MiniBasketItem(props: MiniBasketItemProps) {
106
175
  </div>
107
176
  <Link
108
177
  href={basketItem.product.absolute_url}
109
- 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
+ )}
110
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
+ }}
111
194
  >
112
195
  {basketItem.product.name}
113
196
  {/* TODO: Get correct variants */}
@@ -115,7 +198,19 @@ function MiniBasketItem(props: MiniBasketItemProps) {
115
198
  {commonProductAttributes.map((attribute, index) => (
116
199
  <span
117
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
+ )}
118
209
  data-testid={`mini-basket-item-${attribute.name.toLowerCase()}`}
210
+ style={attributeStyles}
211
+ onClick={(e) =>
212
+ isDesigner && onBlockClick?.('mini-basket-item-attribute', e)
213
+ }
119
214
  >
120
215
  {attribute.name}: {attribute.value}
121
216
  </span>
@@ -128,16 +223,43 @@ function MiniBasketItem(props: MiniBasketItemProps) {
128
223
  className="line-through"
129
224
  />
130
225
  )}
131
- <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>
132
241
  <Button
133
242
  appearance="ghost"
134
243
  className={clsx(
135
- 'p-0 h-auto text-xs font-semibold underline transition-all duration-300',
136
- 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'
137
251
  )}
138
- 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
+ }}
139
261
  >
140
- {t('basket.mini_basket.remove')}
262
+ {removeButtonContent || t('basket.mini_basket.remove')}
141
263
  </Button>
142
264
  </footer>
143
265
  </div>
@@ -146,17 +268,502 @@ function MiniBasketItem(props: MiniBasketItemProps) {
146
268
  }
147
269
 
148
270
  export default function MiniBasket() {
271
+ const {
272
+ isDesigner,
273
+ isSectionSelected,
274
+ selectedBlockId,
275
+ getSectionStyles,
276
+ getSectionProperties,
277
+ getBlockData
278
+ } = useHeaderMiniBasket();
149
279
  const { open: miniBasketOpen } = useAppSelector(
150
280
  (state) => state.root.miniBasket
151
281
  );
152
282
  const dispatch = useAppDispatch();
153
- const { data: basket, isLoading, isSuccess } = useGetBasketQuery();
154
- const { t } = useLocalization();
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
+
303
+ const {
304
+ data: basket,
305
+ isLoading,
306
+ isSuccess
307
+ } = useGetBasketQuery(undefined, {
308
+ skip: !miniBasketOpen
309
+ });
310
+ const { data: miniBasket } = useGetMiniBasketQuery();
311
+ const { t, locale } = useLocalization();
155
312
  const { highlightedItem } = useAppSelector((state) => state.root.miniBasket);
156
313
  const [highlightedItemPk, setHighlightedItemPk] = useState(0);
157
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;
722
+
723
+ const totalQuantity = useMemo(
724
+ () => miniBasket?.total_quantity ?? 0,
725
+ [miniBasket]
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);
158
766
 
159
- const totalQuantity = useMemo(() => basket?.total_quantity ?? 0, [basket]);
160
767
  const miniBasketList = useRef(null);
161
768
 
162
769
  useEffect(() => {
@@ -194,38 +801,151 @@ export default function MiniBasket() {
194
801
  : 'opacity-0 invisible',
195
802
  'fixed top-0 left-0 z-50 w-screen h-screen bg-black bg-opacity-80 transition-all duration-300'
196
803
  )}
197
- onClick={() => {
198
- dispatch(closeMiniBasket());
199
- }}
804
+ onClick={() => dispatch(closeMiniBasket())}
200
805
  />
201
806
  <div
202
807
  className={clsx(
808
+ 'flex-col transition-all duration-300',
203
809
  miniBasketOpen
204
- ? 'flex flex-col opacity-100 visible lg:translate-y-[calc(100%)]'
205
- : 'opacity-0 invisible translate-x-full lg:translate-x-0 lg:translate-y-[calc(100%+16px)]',
206
- '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'
207
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
+ }}
208
839
  >
209
- <header className="flex items-center gap-2 pb-3 border-b">
210
- <h3 className="text-xl font-bold">
211
- {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}
212
869
  </h3>
213
- <span>
214
- {totalQuantity}
215
- {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')}
216
885
  </span>
217
- <Icon
218
- name="close"
219
- size={16}
220
- className="ml-auto fill-primary hover:cursor-pointer"
221
- onClick={() => dispatch(closeMiniBasket())}
222
- />
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
+ )}
223
930
  </header>
224
- {isLoading && <LoaderSpinner />} {/* TODO: Fix spinner position */}
931
+ {isLoading && <LoaderSpinner />}
225
932
  {isSuccess && (
226
933
  <ul
227
934
  ref={miniBasketList}
228
- 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
+ }}
229
949
  >
230
950
  {sortedBasket.map((basketItem) => (
231
951
  <MiniBasketItem
@@ -233,32 +953,98 @@ export default function MiniBasket() {
233
953
  basketItem={basketItem}
234
954
  highlightedItem={highlightedItem}
235
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}
236
966
  />
237
967
  ))}
238
968
  </ul>
239
969
  )}
240
- <footer className="flex flex-col gap-3 mt-auto lg:mt-3 lg:flex-1">
241
- <div className="flex self-center gap-1 text-xs font-semibold">
242
- <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>
243
1001
  <Price value={basket?.total_amount} />
244
1002
  </div>
245
- {/* TODO: Fix link styles */}
246
1003
  <Link
247
- onClick={() => {
248
- 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
+ }
249
1012
  }}
250
1013
  href={ROUTES.BASKET}
251
1014
  data-testid="mini-basket-view-bag"
252
- 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}
253
1024
  >
254
- {t('basket.mini_basket.view_bag')}
1025
+ {viewBagButtonContent}
255
1026
  </Link>
256
1027
  <Link
257
1028
  href="/"
258
- 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
+ )}
259
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
+ }}
260
1046
  >
261
- {t('basket.mini_basket.continue_shopping')}
1047
+ {continueShoppingButtonContent}
262
1048
  </Link>
263
1049
  </footer>
264
1050
  </div>