@pradip1995/create-storefront 1.0.1

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 (338) hide show
  1. package/bin/create-storefront.js +239 -0
  2. package/lib/kit-next-config.js +84 -0
  3. package/package.json +32 -0
  4. package/templates/storefront/.eslintrc.json +3 -0
  5. package/templates/storefront/README.md +35 -0
  6. package/templates/storefront/check-env-variables.js +51 -0
  7. package/templates/storefront/kit-next-config.js +71 -0
  8. package/templates/storefront/next-env.d.ts +5 -0
  9. package/templates/storefront/next.config.js +25 -0
  10. package/templates/storefront/package.json +56 -0
  11. package/templates/storefront/postcss.config.js +6 -0
  12. package/templates/storefront/public/favicon.png +0 -0
  13. package/templates/storefront/src/app/[countryCode]/(checkout)/checkout/page.tsx +23 -0
  14. package/templates/storefront/src/app/[countryCode]/(checkout)/checkout/payment/page.tsx +47 -0
  15. package/templates/storefront/src/app/[countryCode]/(checkout)/layout.tsx +31 -0
  16. package/templates/storefront/src/app/[countryCode]/(checkout)/not-found.tsx +19 -0
  17. package/templates/storefront/src/app/[countryCode]/(main)/account/@dashboard/addresses/page.tsx +31 -0
  18. package/templates/storefront/src/app/[countryCode]/(main)/account/@dashboard/loading.tsx +9 -0
  19. package/templates/storefront/src/app/[countryCode]/(main)/account/@dashboard/orders/details/[id]/page.tsx +35 -0
  20. package/templates/storefront/src/app/[countryCode]/(main)/account/@dashboard/orders/exchange/[id]/page.tsx +47 -0
  21. package/templates/storefront/src/app/[countryCode]/(main)/account/@dashboard/orders/page.tsx +28 -0
  22. package/templates/storefront/src/app/[countryCode]/(main)/account/@dashboard/orders/return/[id]/page.tsx +66 -0
  23. package/templates/storefront/src/app/[countryCode]/(main)/account/@dashboard/page.tsx +22 -0
  24. package/templates/storefront/src/app/[countryCode]/(main)/account/@dashboard/payment-methods/page.tsx +23 -0
  25. package/templates/storefront/src/app/[countryCode]/(main)/account/@dashboard/profile/page.tsx +43 -0
  26. package/templates/storefront/src/app/[countryCode]/(main)/account/@login/default.tsx +11 -0
  27. package/templates/storefront/src/app/[countryCode]/(main)/account/@login/page.tsx +18 -0
  28. package/templates/storefront/src/app/[countryCode]/(main)/account/guest-orders/page.tsx +13 -0
  29. package/templates/storefront/src/app/[countryCode]/(main)/account/layout.tsx +22 -0
  30. package/templates/storefront/src/app/[countryCode]/(main)/account/loading.tsx +9 -0
  31. package/templates/storefront/src/app/[countryCode]/(main)/cart/loading.tsx +5 -0
  32. package/templates/storefront/src/app/[countryCode]/(main)/cart/not-found.tsx +21 -0
  33. package/templates/storefront/src/app/[countryCode]/(main)/cart/page.tsx +23 -0
  34. package/templates/storefront/src/app/[countryCode]/(main)/categories/[...category]/page.tsx +11 -0
  35. package/templates/storefront/src/app/[countryCode]/(main)/collections/[handle]/page.tsx +11 -0
  36. package/templates/storefront/src/app/[countryCode]/(main)/contact/page.tsx +21 -0
  37. package/templates/storefront/src/app/[countryCode]/(main)/guest-orders/page.tsx +12 -0
  38. package/templates/storefront/src/app/[countryCode]/(main)/help/page.tsx +28 -0
  39. package/templates/storefront/src/app/[countryCode]/(main)/layout.tsx +21 -0
  40. package/templates/storefront/src/app/[countryCode]/(main)/not-found.tsx +20 -0
  41. package/templates/storefront/src/app/[countryCode]/(main)/order/[id]/confirmed/loading.tsx +5 -0
  42. package/templates/storefront/src/app/[countryCode]/(main)/order/[id]/confirmed/page.tsx +23 -0
  43. package/templates/storefront/src/app/[countryCode]/(main)/order/[id]/transfer/[token]/accept/page.tsx +41 -0
  44. package/templates/storefront/src/app/[countryCode]/(main)/order/[id]/transfer/[token]/decline/page.tsx +41 -0
  45. package/templates/storefront/src/app/[countryCode]/(main)/order/[id]/transfer/[token]/page.tsx +38 -0
  46. package/templates/storefront/src/app/[countryCode]/(main)/order/exchange/[id]/page.tsx +47 -0
  47. package/templates/storefront/src/app/[countryCode]/(main)/order/return/[id]/page.tsx +61 -0
  48. package/templates/storefront/src/app/[countryCode]/(main)/orders/[id]/page.tsx +33 -0
  49. package/templates/storefront/src/app/[countryCode]/(main)/page.tsx +24 -0
  50. package/templates/storefront/src/app/[countryCode]/(main)/privacy-policy/page.tsx +173 -0
  51. package/templates/storefront/src/app/[countryCode]/(main)/products/[handle]/page.tsx +193 -0
  52. package/templates/storefront/src/app/[countryCode]/(main)/reset-password/page.tsx +192 -0
  53. package/templates/storefront/src/app/[countryCode]/(main)/store/page.tsx +72 -0
  54. package/templates/storefront/src/app/[countryCode]/(main)/terms-of-use/page.tsx +179 -0
  55. package/templates/storefront/src/app/[countryCode]/(main)/wishlist/page.tsx +19 -0
  56. package/templates/storefront/src/app/api/meta/event/route.ts +63 -0
  57. package/templates/storefront/src/app/auth/customer/google/callback/page.tsx +126 -0
  58. package/templates/storefront/src/app/layout.tsx +104 -0
  59. package/templates/storefront/src/app/not-found.tsx +30 -0
  60. package/templates/storefront/src/app/opengraph-image.jpg +0 -0
  61. package/templates/storefront/src/app/robots.ts +15 -0
  62. package/templates/storefront/src/app/sitemap.ts +65 -0
  63. package/templates/storefront/src/app/twitter-image.jpg +0 -0
  64. package/templates/storefront/src/modules/account/components/account-deletion/index.tsx +160 -0
  65. package/templates/storefront/src/modules/account/components/account-info/index.tsx +145 -0
  66. package/templates/storefront/src/modules/account/components/account-nav/icons.tsx +43 -0
  67. package/templates/storefront/src/modules/account/components/account-nav/index.tsx +318 -0
  68. package/templates/storefront/src/modules/account/components/account-nav/logout-modal.tsx +92 -0
  69. package/templates/storefront/src/modules/account/components/account-nav/payment-methods-icon.tsx +9 -0
  70. package/templates/storefront/src/modules/account/components/address-book/index.tsx +47 -0
  71. package/templates/storefront/src/modules/account/components/address-card/add-address.tsx +377 -0
  72. package/templates/storefront/src/modules/account/components/address-card/edit-address-modal.tsx +468 -0
  73. package/templates/storefront/src/modules/account/components/deletion-pending-modal/index.tsx +213 -0
  74. package/templates/storefront/src/modules/account/components/forgot-password/index.tsx +1 -0
  75. package/templates/storefront/src/modules/account/components/login/index.tsx +1 -0
  76. package/templates/storefront/src/modules/account/components/order-card/index.tsx +221 -0
  77. package/templates/storefront/src/modules/account/components/order-overview/index.tsx +159 -0
  78. package/templates/storefront/src/modules/account/components/overview/index.tsx +189 -0
  79. package/templates/storefront/src/modules/account/components/profile-billing-address/index.tsx +447 -0
  80. package/templates/storefront/src/modules/account/components/profile-email/index.tsx +75 -0
  81. package/templates/storefront/src/modules/account/components/profile-form/index.tsx +416 -0
  82. package/templates/storefront/src/modules/account/components/profile-name/index.tsx +76 -0
  83. package/templates/storefront/src/modules/account/components/profile-password/index.tsx +70 -0
  84. package/templates/storefront/src/modules/account/components/profile-phone/index.tsx +185 -0
  85. package/templates/storefront/src/modules/account/components/register/index.tsx +1 -0
  86. package/templates/storefront/src/modules/account/components/return-item-selector/index.tsx +187 -0
  87. package/templates/storefront/src/modules/account/components/return-shipping-selector/index.tsx +118 -0
  88. package/templates/storefront/src/modules/account/components/transfer-request-form/index.tsx +81 -0
  89. package/templates/storefront/src/modules/account/templates/account-layout.tsx +38 -0
  90. package/templates/storefront/src/modules/account/templates/exchange-request-template.tsx +389 -0
  91. package/templates/storefront/src/modules/account/templates/guest-orders-template.tsx +123 -0
  92. package/templates/storefront/src/modules/account/templates/login-template.tsx +44 -0
  93. package/templates/storefront/src/modules/account/templates/payment-methods-template.tsx +478 -0
  94. package/templates/storefront/src/modules/account/templates/return-request-template.tsx +300 -0
  95. package/templates/storefront/src/modules/cart/components/abandoned-carts/ScrollToPendingOrdersButton.tsx +21 -0
  96. package/templates/storefront/src/modules/cart/components/abandoned-carts/index.tsx +335 -0
  97. package/templates/storefront/src/modules/cart/components/applied-promotions/index.tsx +121 -0
  98. package/templates/storefront/src/modules/cart/components/cart-delivery-selection/index.tsx +203 -0
  99. package/templates/storefront/src/modules/cart/components/cart-item-card/index.tsx +476 -0
  100. package/templates/storefront/src/modules/cart/components/cart-item-select/index.tsx +73 -0
  101. package/templates/storefront/src/modules/cart/components/cart-view-tracker/index.tsx +44 -0
  102. package/templates/storefront/src/modules/cart/components/delivery-information/index.tsx +89 -0
  103. package/templates/storefront/src/modules/cart/components/empty-cart-message/index.tsx +38 -0
  104. package/templates/storefront/src/modules/cart/components/item/index.tsx +150 -0
  105. package/templates/storefront/src/modules/cart/components/pincode-checker/index.tsx +174 -0
  106. package/templates/storefront/src/modules/cart/components/sign-in-prompt/index.tsx +26 -0
  107. package/templates/storefront/src/modules/cart/components/you-may-also-like/index.tsx +137 -0
  108. package/templates/storefront/src/modules/cart/templates/index.tsx +88 -0
  109. package/templates/storefront/src/modules/cart/templates/items.tsx +49 -0
  110. package/templates/storefront/src/modules/cart/templates/preview.tsx +51 -0
  111. package/templates/storefront/src/modules/cart/templates/summary.tsx +29 -0
  112. package/templates/storefront/src/modules/checkout/components/add-address-modal/index.tsx +390 -0
  113. package/templates/storefront/src/modules/checkout/components/address-card/index.tsx +135 -0
  114. package/templates/storefront/src/modules/checkout/components/address-select/index.tsx +116 -0
  115. package/templates/storefront/src/modules/checkout/components/addresses/index.tsx +605 -0
  116. package/templates/storefront/src/modules/checkout/components/back-link/index.tsx +32 -0
  117. package/templates/storefront/src/modules/checkout/components/billing_address/index.tsx +301 -0
  118. package/templates/storefront/src/modules/checkout/components/checkout-begin-tracker/index.tsx +45 -0
  119. package/templates/storefront/src/modules/checkout/components/checkout-leave-guard/index.tsx +109 -0
  120. package/templates/storefront/src/modules/checkout/components/checkout-shipping-tracker/index.tsx +45 -0
  121. package/templates/storefront/src/modules/checkout/components/country-select/index.tsx +50 -0
  122. package/templates/storefront/src/modules/checkout/components/discount-code/index.tsx +220 -0
  123. package/templates/storefront/src/modules/checkout/components/error-message/index.tsx +13 -0
  124. package/templates/storefront/src/modules/checkout/components/payment/index.tsx +572 -0
  125. package/templates/storefront/src/modules/checkout/components/payment-button/index.tsx +257 -0
  126. package/templates/storefront/src/modules/checkout/components/payment-button/razorpay-payment-button.tsx +136 -0
  127. package/templates/storefront/src/modules/checkout/components/payment-container/index.tsx +129 -0
  128. package/templates/storefront/src/modules/checkout/components/payment-test/index.tsx +12 -0
  129. package/templates/storefront/src/modules/checkout/components/payment-wrapper/index.tsx +50 -0
  130. package/templates/storefront/src/modules/checkout/components/payment-wrapper/stripe-wrapper.tsx +54 -0
  131. package/templates/storefront/src/modules/checkout/components/processing-overlay/index.tsx +83 -0
  132. package/templates/storefront/src/modules/checkout/components/review/index.tsx +60 -0
  133. package/templates/storefront/src/modules/checkout/components/select-address-modal/index.tsx +103 -0
  134. package/templates/storefront/src/modules/checkout/components/shipping/index.tsx +533 -0
  135. package/templates/storefront/src/modules/checkout/components/shipping-address/index.tsx +521 -0
  136. package/templates/storefront/src/modules/checkout/components/submit-button/index.tsx +32 -0
  137. package/templates/storefront/src/modules/checkout/templates/checkout-form/index.tsx +38 -0
  138. package/templates/storefront/src/modules/checkout/templates/checkout-summary/index.tsx +274 -0
  139. package/templates/storefront/src/modules/common/components/breadcrumb/index.tsx +43 -0
  140. package/templates/storefront/src/modules/common/components/cart-totals/index.tsx +473 -0
  141. package/templates/storefront/src/modules/common/components/checkbox/index.tsx +98 -0
  142. package/templates/storefront/src/modules/common/components/delete-button/index.tsx +156 -0
  143. package/templates/storefront/src/modules/common/components/divider/index.tsx +9 -0
  144. package/templates/storefront/src/modules/common/components/filter-checkbox-group/index.tsx +134 -0
  145. package/templates/storefront/src/modules/common/components/filter-radio-group/index.tsx +62 -0
  146. package/templates/storefront/src/modules/common/components/input/index.tsx +79 -0
  147. package/templates/storefront/src/modules/common/components/interactive-link/index.tsx +33 -0
  148. package/templates/storefront/src/modules/common/components/line-item-options/index.tsx +26 -0
  149. package/templates/storefront/src/modules/common/components/line-item-price/index.tsx +64 -0
  150. package/templates/storefront/src/modules/common/components/line-item-unit-price/index.tsx +61 -0
  151. package/templates/storefront/src/modules/common/components/localized-client-link/index.tsx +32 -0
  152. package/templates/storefront/src/modules/common/components/login-popup/index.tsx +78 -0
  153. package/templates/storefront/src/modules/common/components/modal/index.tsx +123 -0
  154. package/templates/storefront/src/modules/common/components/native-select/index.tsx +75 -0
  155. package/templates/storefront/src/modules/common/components/obfuscated-email/index.tsx +30 -0
  156. package/templates/storefront/src/modules/common/components/product/product-rating/index.tsx +172 -0
  157. package/templates/storefront/src/modules/common/components/product/review-modal/index.tsx +333 -0
  158. package/templates/storefront/src/modules/common/components/product/share-button/index.tsx +227 -0
  159. package/templates/storefront/src/modules/common/components/product/wishlist-icon/index.tsx +46 -0
  160. package/templates/storefront/src/modules/common/components/radio/index.tsx +27 -0
  161. package/templates/storefront/src/modules/common/components/select/index.tsx +164 -0
  162. package/templates/storefront/src/modules/common/components/side-panel/index.tsx +65 -0
  163. package/templates/storefront/src/modules/common/icons/arrow-left.tsx +36 -0
  164. package/templates/storefront/src/modules/common/icons/back.tsx +37 -0
  165. package/templates/storefront/src/modules/common/icons/bancontact.tsx +26 -0
  166. package/templates/storefront/src/modules/common/icons/chevron-down.tsx +30 -0
  167. package/templates/storefront/src/modules/common/icons/delivered.tsx +29 -0
  168. package/templates/storefront/src/modules/common/icons/envelope.tsx +27 -0
  169. package/templates/storefront/src/modules/common/icons/eye-off.tsx +37 -0
  170. package/templates/storefront/src/modules/common/icons/eye.tsx +37 -0
  171. package/templates/storefront/src/modules/common/icons/fast-delivery.tsx +65 -0
  172. package/templates/storefront/src/modules/common/icons/ideal.tsx +26 -0
  173. package/templates/storefront/src/modules/common/icons/lock.tsx +31 -0
  174. package/templates/storefront/src/modules/common/icons/map-pin.tsx +37 -0
  175. package/templates/storefront/src/modules/common/icons/medusa.tsx +27 -0
  176. package/templates/storefront/src/modules/common/icons/menu.tsx +45 -0
  177. package/templates/storefront/src/modules/common/icons/nextjs.tsx +27 -0
  178. package/templates/storefront/src/modules/common/icons/package.tsx +44 -0
  179. package/templates/storefront/src/modules/common/icons/paypal.tsx +30 -0
  180. package/templates/storefront/src/modules/common/icons/phone.tsx +30 -0
  181. package/templates/storefront/src/modules/common/icons/placeholder-image.tsx +44 -0
  182. package/templates/storefront/src/modules/common/icons/refresh.tsx +51 -0
  183. package/templates/storefront/src/modules/common/icons/spinner.tsx +37 -0
  184. package/templates/storefront/src/modules/common/icons/trash.tsx +51 -0
  185. package/templates/storefront/src/modules/common/icons/user.tsx +37 -0
  186. package/templates/storefront/src/modules/common/icons/x.tsx +37 -0
  187. package/templates/storefront/src/modules/contact/templates/index.tsx +272 -0
  188. package/templates/storefront/src/modules/help/templates/index.tsx +629 -0
  189. package/templates/storefront/src/modules/home/components/dynamic-banner/index.tsx +190 -0
  190. package/templates/storefront/src/modules/home/components/featured-products/index.tsx +16 -0
  191. package/templates/storefront/src/modules/home/components/featured-products/product-rail/index.tsx +51 -0
  192. package/templates/storefront/src/modules/home/components/features/index.tsx +1 -0
  193. package/templates/storefront/src/modules/home/components/hero/index.tsx +1 -0
  194. package/templates/storefront/src/modules/home/components/loved-by-moms/index.tsx +1 -0
  195. package/templates/storefront/src/modules/home/components/new-arrivals/index.tsx +1 -0
  196. package/templates/storefront/src/modules/home/components/shop-by-age/index.tsx +1 -0
  197. package/templates/storefront/src/modules/home/components/shop-by-category/index.tsx +1 -0
  198. package/templates/storefront/src/modules/home/components/testimonials/index.tsx +1 -0
  199. package/templates/storefront/src/modules/home/components/why-choose-us/dynamic-features.tsx +93 -0
  200. package/templates/storefront/src/modules/home/components/why-choose-us/index.tsx +1 -0
  201. package/templates/storefront/src/modules/layout/components/account-dropdown/index.tsx +56 -0
  202. package/templates/storefront/src/modules/layout/components/cart-button/index.tsx +8 -0
  203. package/templates/storefront/src/modules/layout/components/cart-dropdown/index.tsx +424 -0
  204. package/templates/storefront/src/modules/layout/components/cart-mismatch-banner/index.tsx +57 -0
  205. package/templates/storefront/src/modules/layout/components/cookie-consent/index.tsx +116 -0
  206. package/templates/storefront/src/modules/layout/components/country-select/index.tsx +135 -0
  207. package/templates/storefront/src/modules/layout/components/desktop-search/index.tsx +148 -0
  208. package/templates/storefront/src/modules/layout/components/dynamic-logo/index.tsx +27 -0
  209. package/templates/storefront/src/modules/layout/components/footer-categories/index.tsx +34 -0
  210. package/templates/storefront/src/modules/layout/components/footer-contact/index.tsx +87 -0
  211. package/templates/storefront/src/modules/layout/components/footer-description/index.tsx +12 -0
  212. package/templates/storefront/src/modules/layout/components/footer-logo/index.tsx +22 -0
  213. package/templates/storefront/src/modules/layout/components/footer-newsletter/index.tsx +100 -0
  214. package/templates/storefront/src/modules/layout/components/language-select/index.tsx +192 -0
  215. package/templates/storefront/src/modules/layout/components/medusa-cta/index.tsx +21 -0
  216. package/templates/storefront/src/modules/layout/components/mobile-menu/index.tsx +296 -0
  217. package/templates/storefront/src/modules/layout/components/nav-links/index.tsx +66 -0
  218. package/templates/storefront/src/modules/layout/components/nav-wrapper/index.tsx +14 -0
  219. package/templates/storefront/src/modules/layout/components/promo-bar/index.tsx +7 -0
  220. package/templates/storefront/src/modules/layout/components/promo-bar/promo-bar-content.tsx +174 -0
  221. package/templates/storefront/src/modules/layout/components/push-notification-manager/index.tsx +191 -0
  222. package/templates/storefront/src/modules/layout/components/search-panel/index.tsx +136 -0
  223. package/templates/storefront/src/modules/layout/components/side-menu/index.tsx +144 -0
  224. package/templates/storefront/src/modules/layout/components/verification-banner/index.tsx +217 -0
  225. package/templates/storefront/src/modules/layout/components/wishlist-counter/index.tsx +17 -0
  226. package/templates/storefront/src/modules/layout/templates/footer/index.tsx +7 -0
  227. package/templates/storefront/src/modules/layout/templates/nav/index.tsx +14 -0
  228. package/templates/storefront/src/modules/order/components/cancel-order-modal/index.tsx +168 -0
  229. package/templates/storefront/src/modules/order/components/help/index.tsx +25 -0
  230. package/templates/storefront/src/modules/order/components/item/index.tsx +62 -0
  231. package/templates/storefront/src/modules/order/components/items/index.tsx +44 -0
  232. package/templates/storefront/src/modules/order/components/onboarding-cta/index.tsx +28 -0
  233. package/templates/storefront/src/modules/order/components/order-confirmation-back-handler/index.tsx +28 -0
  234. package/templates/storefront/src/modules/order/components/order-details/index.tsx +63 -0
  235. package/templates/storefront/src/modules/order/components/order-purchase-tracker/index.tsx +48 -0
  236. package/templates/storefront/src/modules/order/components/order-redesign/index.tsx +887 -0
  237. package/templates/storefront/src/modules/order/components/order-summary/index.tsx +60 -0
  238. package/templates/storefront/src/modules/order/components/payment-details/index.tsx +63 -0
  239. package/templates/storefront/src/modules/order/components/shipping-details/index.tsx +73 -0
  240. package/templates/storefront/src/modules/order/components/transfer-actions/index.tsx +81 -0
  241. package/templates/storefront/src/modules/order/components/transfer-image/index.tsx +275 -0
  242. package/templates/storefront/src/modules/order/templates/order-completed-template.tsx +233 -0
  243. package/templates/storefront/src/modules/order/templates/order-details-template.tsx +128 -0
  244. package/templates/storefront/src/modules/products/components/image-gallery/index.tsx +297 -0
  245. package/templates/storefront/src/modules/products/components/product-actions/index.tsx +1400 -0
  246. package/templates/storefront/src/modules/products/components/product-actions/mobile-actions.tsx +217 -0
  247. package/templates/storefront/src/modules/products/components/product-actions/option-select.tsx +62 -0
  248. package/templates/storefront/src/modules/products/components/product-onboarding-cta/index.tsx +30 -0
  249. package/templates/storefront/src/modules/products/components/product-preview/index.tsx +5 -0
  250. package/templates/storefront/src/modules/products/components/product-preview/price.tsx +29 -0
  251. package/templates/storefront/src/modules/products/components/product-price/index.tsx +58 -0
  252. package/templates/storefront/src/modules/products/components/product-rating/index.tsx +1 -0
  253. package/templates/storefront/src/modules/products/components/product-tabs/accordion.tsx +100 -0
  254. package/templates/storefront/src/modules/products/components/product-tabs/index.tsx +127 -0
  255. package/templates/storefront/src/modules/products/components/product-tabs/ratings-tab.tsx +598 -0
  256. package/templates/storefront/src/modules/products/components/product-view-tracker/index.tsx +53 -0
  257. package/templates/storefront/src/modules/products/components/related-products/index.tsx +152 -0
  258. package/templates/storefront/src/modules/products/components/review-modal/index.tsx +1 -0
  259. package/templates/storefront/src/modules/products/components/share-button/index.tsx +1 -0
  260. package/templates/storefront/src/modules/products/components/thumbnail/index.tsx +91 -0
  261. package/templates/storefront/src/modules/products/components/wishlist-icon/index.tsx +1 -0
  262. package/templates/storefront/src/modules/products/context/product-context.tsx +52 -0
  263. package/templates/storefront/src/modules/products/templates/index.tsx +26 -0
  264. package/templates/storefront/src/modules/products/templates/product-actions-wrapper/index.tsx +1 -0
  265. package/templates/storefront/src/modules/products/templates/product-info/index.tsx +2 -0
  266. package/templates/storefront/src/modules/shipping/components/free-shipping-price-nudge/index.tsx +283 -0
  267. package/templates/storefront/src/modules/skeletons/components/skeleton-button/index.tsx +5 -0
  268. package/templates/storefront/src/modules/skeletons/components/skeleton-card-details/index.tsx +10 -0
  269. package/templates/storefront/src/modules/skeletons/components/skeleton-cart-item/index.tsx +35 -0
  270. package/templates/storefront/src/modules/skeletons/components/skeleton-cart-totals/index.tsx +30 -0
  271. package/templates/storefront/src/modules/skeletons/components/skeleton-code-form/index.tsx +13 -0
  272. package/templates/storefront/src/modules/skeletons/components/skeleton-line-item/index.tsx +35 -0
  273. package/templates/storefront/src/modules/skeletons/components/skeleton-order-confirmed-header/index.tsx +14 -0
  274. package/templates/storefront/src/modules/skeletons/components/skeleton-order-information/index.tsx +36 -0
  275. package/templates/storefront/src/modules/skeletons/components/skeleton-order-items/index.tsx +43 -0
  276. package/templates/storefront/src/modules/skeletons/components/skeleton-order-summary/index.tsx +15 -0
  277. package/templates/storefront/src/modules/skeletons/components/skeleton-product-preview/index.tsx +15 -0
  278. package/templates/storefront/src/modules/skeletons/templates/skeleton-cart-page/index.tsx +65 -0
  279. package/templates/storefront/src/modules/skeletons/templates/skeleton-order-confirmed/index.tsx +21 -0
  280. package/templates/storefront/src/modules/skeletons/templates/skeleton-product-grid/index.tsx +23 -0
  281. package/templates/storefront/src/modules/skeletons/templates/skeleton-related-products/index.tsx +25 -0
  282. package/templates/storefront/src/modules/store/components/client-paginated-products.tsx +108 -0
  283. package/templates/storefront/src/modules/store/components/mobile-filters/index.tsx +135 -0
  284. package/templates/storefront/src/modules/store/components/pagination/index.tsx +118 -0
  285. package/templates/storefront/src/modules/store/components/product-list-view-tracker/index.tsx +43 -0
  286. package/templates/storefront/src/modules/store/components/refinement-list/index.tsx +299 -0
  287. package/templates/storefront/src/modules/store/components/refinement-list/sort-products/index.tsx +120 -0
  288. package/templates/storefront/src/modules/store/components/store-header/index.tsx +67 -0
  289. package/templates/storefront/src/modules/store/templates/index.tsx +1 -0
  290. package/templates/storefront/src/modules/store/templates/paginated-products.tsx +175 -0
  291. package/templates/storefront/src/modules/wishlist/components/wishlist-item/index.tsx +797 -0
  292. package/templates/storefront/src/modules/wishlist/templates/index.tsx +176 -0
  293. package/templates/storefront/src/storefront.config.ts +12 -0
  294. package/templates/storefront/src/styles/globals.css +326 -0
  295. package/templates/storefront/src/theme/valero/blocks/home/Features/index.tsx +61 -0
  296. package/templates/storefront/src/theme/valero/blocks/home/Hero/index.tsx +102 -0
  297. package/templates/storefront/src/theme/valero/blocks/home/LovedByMoms/index.tsx +407 -0
  298. package/templates/storefront/src/theme/valero/blocks/home/NewArrivals/index.tsx +48 -0
  299. package/templates/storefront/src/theme/valero/blocks/home/ShopByAge/index.tsx +128 -0
  300. package/templates/storefront/src/theme/valero/blocks/home/ShopByCategory/index.tsx +409 -0
  301. package/templates/storefront/src/theme/valero/blocks/home/Testimonials/index.tsx +697 -0
  302. package/templates/storefront/src/theme/valero/blocks/home/WhyChooseUs/index.tsx +62 -0
  303. package/templates/storefront/src/theme/valero/layouts/MainLayoutShell.tsx +14 -0
  304. package/templates/storefront/src/theme/valero/primitives/Button.tsx +28 -0
  305. package/templates/storefront/src/theme/valero/primitives/Card.tsx +32 -0
  306. package/templates/storefront/src/theme/valero/primitives/index.ts +2 -0
  307. package/templates/storefront/src/theme/valero/slots/account/ForgotPassword/index.tsx +1 -0
  308. package/templates/storefront/src/theme/valero/slots/account/Login/index.tsx +1 -0
  309. package/templates/storefront/src/theme/valero/slots/account/LoginTemplate/index.tsx +44 -0
  310. package/templates/storefront/src/theme/valero/slots/account/Register/index.tsx +1 -0
  311. package/templates/storefront/src/theme/valero/slots/cart/CartItem/index.tsx +11 -0
  312. package/templates/storefront/src/theme/valero/slots/cart/CartSummary/index.tsx +13 -0
  313. package/templates/storefront/src/theme/valero/slots/checkout/CheckoutForm/index.tsx +1 -0
  314. package/templates/storefront/src/theme/valero/slots/checkout/CheckoutSummary/index.tsx +1 -0
  315. package/templates/storefront/src/theme/valero/slots/layout/Footer/index.tsx +104 -0
  316. package/templates/storefront/src/theme/valero/slots/layout/Nav/index.tsx +97 -0
  317. package/templates/storefront/src/theme/valero/slots/layout/PromoBar/index.tsx +19 -0
  318. package/templates/storefront/src/theme/valero/slots/layout/PromoBar/promo-bar-content.tsx +174 -0
  319. package/templates/storefront/src/theme/valero/slots/order/OrderDetails/index.tsx +12 -0
  320. package/templates/storefront/src/theme/valero/slots/product/ProductActions/ProductCTASection.tsx +191 -0
  321. package/templates/storefront/src/theme/valero/slots/product/ProductActions/ProductDetailsSection.tsx +137 -0
  322. package/templates/storefront/src/theme/valero/slots/product/ProductActions/ProductFeaturePanel.tsx +245 -0
  323. package/templates/storefront/src/theme/valero/slots/product/ProductActions/ProductHighlightsSection.tsx +98 -0
  324. package/templates/storefront/src/theme/valero/slots/product/ProductActions/ProductOptionsSection.tsx +233 -0
  325. package/templates/storefront/src/theme/valero/slots/product/ProductActions/ProductPriceSection.tsx +53 -0
  326. package/templates/storefront/src/theme/valero/slots/product/ProductActions/ProductTrustSection.tsx +84 -0
  327. package/templates/storefront/src/theme/valero/slots/product/ProductActions/index.tsx +161 -0
  328. package/templates/storefront/src/theme/valero/slots/product/ProductCard/index.tsx +132 -0
  329. package/templates/storefront/src/theme/valero/slots/product/ProductInfo/index.tsx +40 -0
  330. package/templates/storefront/src/theme/valero/templates/StorePage/index.tsx +154 -0
  331. package/templates/storefront/src/theme/valero/tokens/colors.js +16 -0
  332. package/templates/storefront/src/theme/valero/tokens/colors.ts +21 -0
  333. package/templates/storefront/src/theme/valero/tokens/fonts.ts +13 -0
  334. package/templates/storefront/src/theme/valero/tokens/index.ts +3 -0
  335. package/templates/storefront/src/theme/valero/tokens/spacing.ts +9 -0
  336. package/templates/storefront/src/theme/valero/tokens/theme.css +91 -0
  337. package/templates/storefront/tailwind.config.js +221 -0
  338. package/templates/storefront/tsconfig.json +30 -0
@@ -0,0 +1,233 @@
1
+ import { Heading, Text } from "@medusajs/ui"
2
+ import { cookies as nextCookies } from "next/headers"
3
+ import Image from "next/image"
4
+ import Link from "next/link"
5
+ import LocalizedClientLink from "@modules/common/components/localized-client-link"
6
+
7
+ import OnboardingCta from "@modules/order/components/onboarding-cta"
8
+ import OrderConfirmationBackHandler from "@modules/order/components/order-confirmation-back-handler"
9
+ import { HttpTypes } from "@medusajs/types"
10
+ import Thumbnail from "@modules/products/components/thumbnail"
11
+ import { convertToLocale } from "@core/util/money"
12
+ import OrderPurchaseTracker from "@modules/order/components/order-purchase-tracker"
13
+
14
+ type OrderCompletedTemplateProps = {
15
+ order: HttpTypes.StoreOrder
16
+ }
17
+
18
+ export default async function OrderCompletedTemplate({
19
+ order,
20
+ }: OrderCompletedTemplateProps) {
21
+ const cookies = await nextCookies()
22
+
23
+ const isOnboarding = cookies.get("_medusa_onboarding")?.value === "true"
24
+
25
+ const shippingAddress = order.shipping_address
26
+ const fullName = `${shippingAddress?.first_name || ""} ${shippingAddress?.last_name || ""}`.trim()
27
+ const phone = shippingAddress?.phone ? (shippingAddress.phone.startsWith("+") ? shippingAddress.phone : `+${shippingAddress.phone}`) : ""
28
+ const address = [
29
+ shippingAddress?.address_1,
30
+ shippingAddress?.address_2,
31
+ shippingAddress?.city,
32
+ shippingAddress?.province,
33
+ shippingAddress?.postal_code,
34
+ shippingAddress?.country_code?.toUpperCase(),
35
+ ]
36
+ .filter(Boolean)
37
+ .join(", ")
38
+
39
+ return (
40
+ <div className="min-h-[calc(100vh-64px)] bg-[#FFFAFE] py-12">
41
+ <div className="content-container max-w-4xl mx-auto">
42
+ <OrderPurchaseTracker order={order} />
43
+ {isOnboarding && <OnboardingCta orderId={order.id} />}
44
+
45
+ <div className="flex flex-col gap-8" data-testid="order-complete-container">
46
+ <OrderConfirmationBackHandler />
47
+ {/* Top Illustration Section */}
48
+ <div className="flex justify-center items-center pb-2 pt-2">
49
+ <div className="relative w-full max-w-2xl h-64">
50
+ <Image
51
+ src="/Successful purchase-pana 1.png"
52
+ alt="Order Successful"
53
+ fill
54
+ className="object-contain"
55
+ priority
56
+ />
57
+ </div>
58
+ </div>
59
+
60
+ {/* Order Confirmed Heading */}
61
+ <div className="text-center">
62
+ <Heading
63
+ level="h1"
64
+ className="text-4xl font-bold mb-2"
65
+ style={{ color: '#8B5AB1' }}
66
+ >
67
+ Order Confirmed
68
+ </Heading>
69
+ <Text className="text-gray-700 text-lg max-w-2xl mx-auto">
70
+ Your order is confirmed. You will receive an order confirmation email/SMS shortly with the expected delivery date for your items.
71
+ </Text>
72
+ </div>
73
+
74
+ {/* Delivery Details Card */}
75
+ <div className="bg-[#FFFAFE] rounded-lg p-6 shadow-md border" style={{ borderColor: '#C0C0C0' }}>
76
+ <div className="flex flex-col md:flex-row justify-between items-start gap-6">
77
+ <div className="flex-1">
78
+ <Text className="text-gray-600 text-sm mb-2">Delivering to:</Text>
79
+ <Text className="text-lg font-semibold text-gray-900 mb-1">
80
+ {fullName || "Customer"} {phone && `| ${phone}`}
81
+ </Text>
82
+ <Text className="text-gray-700 mt-2">
83
+ {address || "Address not available"}
84
+ </Text>
85
+ {cookies.get("_medusa_jwt")?.value && (
86
+ <LocalizedClientLink
87
+ href={`/orders/${order.id}`}
88
+ className="inline-block mt-4 px-4 py-2 border text-sm font-medium transition-colors"
89
+ style={{
90
+ borderColor: '#8B5AB1',
91
+ color: '#8B5AB1',
92
+ borderRadius: '30px'
93
+ }}
94
+ >
95
+ Order Details &gt;
96
+ </LocalizedClientLink>
97
+ )}
98
+ </div>
99
+ <div className="flex-shrink-0">
100
+ <div className="relative w-32 h-32">
101
+ <Image
102
+ src="/BIKE.png"
103
+ alt="Delivery"
104
+ fill
105
+ className="object-contain"
106
+ />
107
+ </div>
108
+ </div>
109
+ </div>
110
+ </div>
111
+
112
+ {/* Order Items Card */}
113
+ <div className="bg-[#FFFAFE] rounded-lg p-6 shadow-md border" style={{ borderColor: '#C0C0C0' }}>
114
+ <Heading
115
+ level="h2"
116
+ className="text-2xl font-bold mb-6"
117
+ style={{ color: '#8B5AB1' }}
118
+ >
119
+ Order Items
120
+ </Heading>
121
+ <div className="space-y-6">
122
+ {order.items?.map((item) => (
123
+ <LocalizedClientLink
124
+ key={item.id}
125
+ href={`/products/${item.variant?.product?.handle}`}
126
+ className="flex items-center gap-6 pb-6 border-b last:border-b-0 last:pb-0 group"
127
+ style={{ borderColor: '#F3F4F6' }}
128
+ >
129
+ <div className="w-20 h-20 sm:w-24 sm:h-24 flex-shrink-0 bg-gray-50 rounded-xl overflow-hidden border border-gray-100 group-hover:opacity-80 transition-opacity">
130
+ <Thumbnail
131
+ thumbnail={item.thumbnail}
132
+ variantImages={item.variant?.images}
133
+ images={item.variant?.product?.images}
134
+ size="square"
135
+ />
136
+ </div>
137
+ <div className="flex-1 min-w-0">
138
+ <Text className="text-[10px] uppercase tracking-wider text-gray-400 font-semibold block mb-1">
139
+ Chocomelon Baby
140
+ </Text>
141
+ <Heading level="h3" className="font-bold text-[#04122C] text-lg mb-1 truncate group-hover:text-[#8B5AB1] transition-colors">
142
+ {item.product_title || item.title}
143
+ </Heading>
144
+ <div className="flex flex-wrap items-center gap-x-4 gap-y-1">
145
+ <Text className="text-sm font-medium text-gray-500">
146
+ {item.variant?.title && `Variant: ${item.variant.title}`}
147
+ </Text>
148
+ {item.variant?.title && <Text className="text-sm font-medium text-gray-300">|</Text>}
149
+ <Text className="text-sm font-medium text-gray-500">
150
+ Qty: {item.quantity}
151
+ </Text>
152
+ </div>
153
+ </div>
154
+ <div className="text-right flex flex-col items-end gap-1">
155
+ <Text className="font-bold text-[#04122C] text-lg whitespace-nowrap">
156
+ {convertToLocale({
157
+ amount: item.total || (item.unit_price || 0) * (item.quantity || 0),
158
+ currency_code: order.currency_code,
159
+ })}
160
+ </Text>
161
+ <Text className="text-xs text-gray-400 whitespace-nowrap">
162
+ {convertToLocale({
163
+ amount: item.unit_price || 0,
164
+ currency_code: order.currency_code,
165
+ })} Each
166
+ </Text>
167
+ </div>
168
+ </LocalizedClientLink>
169
+ ))}
170
+ </div>
171
+
172
+ {/* Order Summary */}
173
+ <div className="mt-8 pt-6 border-t border-gray-100 space-y-3">
174
+ <div className="flex justify-between items-center text-gray-600">
175
+ <Text>Subtotal</Text>
176
+ <Text className="font-medium">{convertToLocale({ amount: order.subtotal || 0, currency_code: order.currency_code })}</Text>
177
+ </div>
178
+ <div className="flex justify-between items-center text-gray-600">
179
+ <Text>Shipping</Text>
180
+ <Text className="font-medium">{convertToLocale({ amount: order.shipping_total || 0, currency_code: order.currency_code })}</Text>
181
+ </div>
182
+ {(order.tax_total || 0) > 0 && (
183
+ <div className="flex justify-between items-center text-gray-600">
184
+ <Text>Tax</Text>
185
+ <Text className="font-medium">{convertToLocale({ amount: order.tax_total || 0, currency_code: order.currency_code })}</Text>
186
+ </div>
187
+ )}
188
+ {(order.discount_total || 0) > 0 && (
189
+ <div className="flex justify-between items-center text-red-500">
190
+ <Text>Discount</Text>
191
+ <Text className="font-medium">-{convertToLocale({ amount: order.discount_total || 0, currency_code: order.currency_code })}</Text>
192
+ </div>
193
+ )}
194
+ <div className="flex justify-between items-center mt-6 pt-4 border-t-2" style={{ borderColor: '#F3F4F6' }}>
195
+ <Text className="text-xl font-bold text-gray-900">Total Amount Paid</Text>
196
+ <Text className="text-2xl font-black" style={{ color: '#8B5AB1' }}>
197
+ {convertToLocale({ amount: order.total || 0, currency_code: order.currency_code })}
198
+ </Text>
199
+ </div>
200
+ </div>
201
+ </div>
202
+
203
+ {/* Action Buttons */}
204
+ <div className="flex flex-col sm:flex-row gap-4 justify-center">
205
+ <LocalizedClientLink
206
+ href="/store"
207
+ className="px-16 py-3 border-2 text-center font-semibold transition-colors bg-[#FFFAFE]"
208
+ style={{
209
+ borderColor: '#8B5AB1',
210
+ color: '#8B5AB1',
211
+ borderRadius: '30px',
212
+ minWidth: '250px'
213
+ }}
214
+ >
215
+ Continue Shopping
216
+ </LocalizedClientLink>
217
+ <LocalizedClientLink
218
+ href={cookies.get("_medusa_jwt")?.value ? "/account/orders" : `/account?view=guest&email=${order.email}`}
219
+ className="px-16 py-3 text-center font-semibold text-white transition-colors"
220
+ style={{
221
+ backgroundColor: '#8B5AB1',
222
+ borderRadius: '30px',
223
+ minWidth: '250px'
224
+ }}
225
+ >
226
+ {cookies.get("_medusa_jwt")?.value ? "View Orders" : "Track Guest Order"}
227
+ </LocalizedClientLink>
228
+ </div>
229
+ </div>
230
+ </div>
231
+ </div>
232
+ )
233
+ }
@@ -0,0 +1,128 @@
1
+ "use client"
2
+
3
+ import { HttpTypes } from "@medusajs/types"
4
+ import React from "react"
5
+
6
+ import { downloadInvoice, cancelOrder, reorderOrder } from "@core/data/orders"
7
+ import { hasReturnableItems } from "@core/util/returns"
8
+ import { useRouter, useParams } from "next/navigation"
9
+ import CancelOrderModal from "@modules/order/components/cancel-order-modal"
10
+ import OrderRedesign from "@modules/order/components/order-redesign"
11
+
12
+ type OrderDetailsTemplateProps = {
13
+ order: HttpTypes.StoreOrder
14
+ }
15
+
16
+ const OrderDetailsTemplate: React.FC<OrderDetailsTemplateProps> = ({
17
+ order,
18
+ }) => {
19
+ const router = useRouter()
20
+ const params = useParams()
21
+ const countryCode = params.countryCode as string
22
+ const [isDownloading, setIsDownloading] = React.useState(false)
23
+ const [isCancelling, setIsCancelling] = React.useState(false)
24
+ const [isReordering, setIsReordering] = React.useState(false)
25
+ const [showCancelModal, setShowCancelModal] = React.useState(false)
26
+ const [inventoryIssues, setInventoryIssues] = React.useState<any[] | null>(null)
27
+
28
+ const handleDownloadInvoice = async () => {
29
+ setIsDownloading(true)
30
+ try {
31
+ const result = await downloadInvoice(order.id)
32
+
33
+ if (!result.success) {
34
+ alert(result.error || "Failed to download invoice")
35
+ setIsDownloading(false)
36
+ return
37
+ }
38
+
39
+ if (result.data) {
40
+ // Convert base64 to blob
41
+ const byteCharacters = atob(result.data)
42
+ const byteNumbers = new Array(byteCharacters.length)
43
+ for (let i = 0; i < byteCharacters.length; i++) {
44
+ byteNumbers[i] = byteCharacters.charCodeAt(i)
45
+ }
46
+ const byteArray = new Uint8Array(byteNumbers)
47
+ const blob = new Blob([byteArray], { type: "application/pdf" })
48
+
49
+ const url = window.URL.createObjectURL(blob)
50
+ const a = document.createElement("a")
51
+ a.href = url
52
+ a.download = `invoice-${order.display_id}.pdf`
53
+ document.body.appendChild(a)
54
+ a.click()
55
+ window.URL.revokeObjectURL(url)
56
+ document.body.removeChild(a)
57
+ }
58
+ } catch (error) {
59
+ alert("An error occurred while downloading the invoice.")
60
+ } finally {
61
+ setIsDownloading(false)
62
+ }
63
+ }
64
+
65
+ const handleCancelOrderConfirm = async (reasonId: string) => {
66
+ setIsCancelling(true)
67
+ const result = await cancelOrder(order.id, reasonId)
68
+ if (result.success) {
69
+ alert("Order cancelled successfully")
70
+ setShowCancelModal(false)
71
+ router.refresh()
72
+ } else {
73
+ alert(result.error || "Failed to cancel order")
74
+ }
75
+ setIsCancelling(false)
76
+ }
77
+
78
+ const handleReorder = async (skipVariantIds?: string[], forceReorder: boolean = false) => {
79
+ setIsReordering(true)
80
+ setInventoryIssues(null)
81
+ const result = await reorderOrder(order.id, skipVariantIds, forceReorder)
82
+ if (result.success) {
83
+ const cartId = (result as any).data?.cart?.id || (result as any).data?.id || (result as any).data?.cart_id
84
+ const checkoutUrl = countryCode ? `/${countryCode}/checkout` : "/checkout"
85
+ window.location.assign(cartId ? `${checkoutUrl}?cart_id=${cartId}` : checkoutUrl)
86
+ } else {
87
+ if ((result as any).inventory_issues) {
88
+ setInventoryIssues((result as any).inventory_issues)
89
+ } else {
90
+ alert(result.error || "Failed to reorder")
91
+ }
92
+ }
93
+ setIsReordering(false)
94
+ }
95
+
96
+ const isCancelled = order.status === "canceled"
97
+ const canCancel = !isCancelled && order.status !== "archived"
98
+
99
+ return (
100
+ <div className="py-6 min-h-[calc(100vh-64px)] bg-[#FFFAFE] overflow-x-hidden">
101
+ <div className="w-full max-w-[1400px] mx-auto px-[5px] min-[500px]:px-4 md:px-6 lg:px-8 flex flex-col gap-y-6 overflow-x-hidden">
102
+ <OrderRedesign
103
+ order={order}
104
+ handleDownloadInvoice={handleDownloadInvoice}
105
+ isDownloading={isDownloading}
106
+ handleCancelOrder={async () => setShowCancelModal(true)}
107
+ isCancelling={isCancelling}
108
+ handleReorder={handleReorder}
109
+ isReordering={isReordering}
110
+ canCancel={canCancel}
111
+ isCancelled={isCancelled}
112
+ hasReturnableItems={hasReturnableItems(order)}
113
+ inventoryIssues={inventoryIssues}
114
+ setInventoryIssues={setInventoryIssues}
115
+ />
116
+ </div>
117
+
118
+ <CancelOrderModal
119
+ isOpen={showCancelModal}
120
+ isSubmitting={isCancelling}
121
+ onClose={() => setShowCancelModal(false)}
122
+ onConfirm={handleCancelOrderConfirm}
123
+ />
124
+ </div>
125
+ )
126
+ }
127
+
128
+ export default OrderDetailsTemplate
@@ -0,0 +1,297 @@
1
+ 'use client'
2
+
3
+ import { useState, useMemo, useEffect } from "react"
4
+ import { HttpTypes } from "@medusajs/types"
5
+ import Image from "next/image"
6
+ import { useSearchParams } from "next/navigation"
7
+ import { getDefaultImageIndexForSelection, getImagesForSelection } from "@core/util/product"
8
+ import { clx } from "@medusajs/ui"
9
+
10
+ type ImageGalleryProps = {
11
+ images: HttpTypes.StoreProductImage[]
12
+ product?: HttpTypes.StoreProduct
13
+ }
14
+
15
+ import { useRef } from "react"
16
+ import { useProductContext } from "@modules/products/context/product-context"
17
+
18
+ const ImageGallery = ({ images: initialImages, product }: ImageGalleryProps) => {
19
+ const { selectedOptions } = useProductContext()
20
+ const scrollContainerRef = useRef<HTMLDivElement>(null)
21
+
22
+ const hasOptionSelection = useMemo(
23
+ () => Object.values(selectedOptions).some(Boolean),
24
+ [selectedOptions]
25
+ )
26
+
27
+ // Compute images from full or partial option selection (deduped for color-only picks)
28
+ const images = useMemo(() => {
29
+ if (product && hasOptionSelection) {
30
+ return getImagesForSelection(product, selectedOptions)
31
+ }
32
+ return initialImages
33
+ }, [product, selectedOptions, hasOptionSelection, initialImages])
34
+
35
+ const [selectedImageIndex, setSelectedImageIndex] = useState(0)
36
+ const [showRightButton, setShowRightButton] = useState(false)
37
+ const [showLeftButton, setShowLeftButton] = useState(false)
38
+
39
+ // Check if scrolling is needed
40
+ const checkScroll = () => {
41
+ const container = scrollContainerRef.current
42
+ if (container) {
43
+ const canScrollLeft = container.scrollLeft > 0
44
+ const canScrollRight = Math.ceil(container.scrollLeft + container.clientWidth) < container.scrollWidth
45
+
46
+ setShowLeftButton(canScrollLeft)
47
+ setShowRightButton(canScrollRight)
48
+ }
49
+ }
50
+
51
+ // Select the variant's assigned image by default when the selection changes
52
+ useEffect(() => {
53
+ const defaultIndex = product
54
+ ? getDefaultImageIndexForSelection(product, selectedOptions, images)
55
+ : 0
56
+
57
+ setSelectedImageIndex(defaultIndex)
58
+ setTimeout(checkScroll, 100)
59
+ }, [selectedOptions, images, product])
60
+
61
+ // Handle manual scroll
62
+ const scrollTo = (direction: 'left' | 'right') => {
63
+ const container = scrollContainerRef.current
64
+ if (container) {
65
+ // Scroll by exactly one thumbnail width (container is divided into 4)
66
+ const scrollAmount = container.clientWidth / 4
67
+ const directionMultiplier = direction === 'left' ? -1 : 1
68
+ container.scrollBy({ left: scrollAmount * directionMultiplier, behavior: 'smooth' })
69
+ }
70
+ }
71
+
72
+ const [zoomPosition, setZoomPosition] = useState({ x: 0, y: 0, percentX: 0, percentY: 0 })
73
+ const [isZooming, setIsZooming] = useState(false)
74
+ const [isPageScrolling, setIsPageScrolling] = useState(false)
75
+ const scrollTimeoutRef = useRef<NodeJS.Timeout | null>(null)
76
+ const containerRef = useRef<HTMLDivElement>(null)
77
+
78
+ useEffect(() => {
79
+ const handleScroll = () => {
80
+ setIsPageScrolling(true)
81
+ setIsZooming(false) // Force reset zoom instantly when scrolling starts
82
+
83
+ // If the entire page reaches the top, reset the gallery scroll too
84
+ if (window.scrollY === 0) {
85
+ const scrollContainer = document.getElementById('product-gallery-container')
86
+ if (scrollContainer && scrollContainer.scrollTop > 0) {
87
+ scrollContainer.scrollTo({ top: 0, behavior: 'smooth' })
88
+ }
89
+ }
90
+
91
+ if (scrollTimeoutRef.current) {
92
+ clearTimeout(scrollTimeoutRef.current)
93
+ }
94
+ scrollTimeoutRef.current = setTimeout(() => {
95
+ setIsPageScrolling(false)
96
+ }, 300)
97
+ }
98
+
99
+ window.addEventListener("scroll", handleScroll, { passive: true })
100
+ return () => {
101
+ window.removeEventListener("scroll", handleScroll)
102
+ if (scrollTimeoutRef.current) clearTimeout(scrollTimeoutRef.current)
103
+ }
104
+ }, [])
105
+
106
+ const handleMouseMove = (e: React.MouseEvent<HTMLDivElement>) => {
107
+ if (isPageScrolling) {
108
+ if (isZooming) setIsZooming(false)
109
+ return
110
+ }
111
+ const { left, top, width, height } = e.currentTarget.getBoundingClientRect()
112
+ const pxX = e.clientX - left
113
+ const pxY = e.clientY - top
114
+ const x = (pxX / width) * 100
115
+ const y = (pxY / height) * 100
116
+
117
+ setZoomPosition({
118
+ x: pxX,
119
+ y: pxY,
120
+ percentX: x,
121
+ percentY: y
122
+ })
123
+
124
+ if (!isZooming) setIsZooming(true)
125
+ }
126
+
127
+ const selectedImage = images[selectedImageIndex]
128
+
129
+ const zoomFactor = 2
130
+ const lensSize = 200
131
+
132
+ return (
133
+ <div className="flex flex-col gap-3 sm:gap-4 min-[768px]:gap-4 min-[1023px]:gap-4 min-[1360px]:gap-4">
134
+ {/* Main Image - Top */}
135
+ {selectedImage && (
136
+ <div
137
+ ref={containerRef}
138
+ className="relative z-0 bg-surface-muted w-full aspect-square max-w-full group/main"
139
+ >
140
+ {!!selectedImage.url && (
141
+ <>
142
+ {/* Image Container */}
143
+ <div className="relative w-full h-full overflow-hidden">
144
+ <Image
145
+ src={selectedImage.url}
146
+ priority={true}
147
+ className="w-full h-full object-cover"
148
+ alt="Product main image"
149
+ fill
150
+ sizes="(max-width: 768px) 100vw, 50vw"
151
+ />
152
+ </div>
153
+
154
+ <div
155
+ className="absolute inset-0 z-20 cursor-zoom-in"
156
+ onMouseEnter={() => !isPageScrolling && setIsZooming(true)}
157
+ onMouseLeave={() => setIsZooming(false)}
158
+ onMouseMove={handleMouseMove}
159
+ onWheel={() => setIsZooming(false)}
160
+ />
161
+
162
+ {/* View Similar Button - Expanding on Hover */}
163
+ <button
164
+ onClick={(e) => {
165
+ e.stopPropagation()
166
+ const element = document.getElementById('related-products')
167
+ if (element) {
168
+ element.scrollIntoView({ behavior: 'smooth' })
169
+ }
170
+ }}
171
+ className="absolute bottom-4 right-4 z-30 bg-white text-heading px-3 py-2 flex items-center gap-2 shadow-brand-sm hover:shadow-brand transition-all text-[11px] font-semibold uppercase tracking-[var(--letter-spacing-nav)]"
172
+ >
173
+ <span>View similar</span>
174
+ </button>
175
+
176
+ {/* Lens Zoom - Using a nested Image for maximum reliability */}
177
+ {isZooming && !isPageScrolling && containerRef.current && (
178
+ <div
179
+ key={`lens-${selectedImageIndex}`}
180
+ className="absolute pointer-events-none z-10 rounded-full border-2 border-white/80 shadow-[0_0_30px_rgba(0,0,0,0.4)] overflow-hidden hidden sm:block"
181
+ style={{
182
+ width: `${lensSize}px`,
183
+ height: `${lensSize}px`,
184
+ left: `${zoomPosition.percentX}%`,
185
+ top: `${zoomPosition.percentY}%`,
186
+ transform: 'translate(-50%, -50%)',
187
+ }}
188
+ >
189
+ <div
190
+ className="absolute"
191
+ style={{
192
+ width: `${containerRef.current.offsetWidth * zoomFactor}px`,
193
+ height: `${containerRef.current.offsetHeight * zoomFactor}px`,
194
+ left: `${-zoomPosition.x * zoomFactor + lensSize / 2}px`,
195
+ top: `${-zoomPosition.y * zoomFactor + lensSize / 2}px`,
196
+ }}
197
+ >
198
+ <Image
199
+ src={selectedImage.url}
200
+ fill
201
+ className="object-cover"
202
+ alt="Zoomed view"
203
+ sizes="100vw"
204
+ />
205
+ </div>
206
+ </div>
207
+ )}
208
+ </>
209
+ )}
210
+ </div>
211
+ )}
212
+
213
+ {/* All Images - Bottom Row */}
214
+ {images.length > 0 && (
215
+ <div className="relative group/gallery flex flex-col gap-1">
216
+ {/* Navigation Buttons Overlay */}
217
+ {(showLeftButton || showRightButton) && (
218
+ <>
219
+ <button
220
+ onClick={() => scrollTo('left')}
221
+ disabled={!showLeftButton}
222
+ className={`absolute left-1 top-1/2 -translate-y-1/2 w-8 h-8 bg-black/50 hover:bg-black/70 text-white rounded-full flex items-center justify-center transition-all z-40 opacity-0 group-hover/gallery:opacity-100 shadow-lg ${!showLeftButton ? 'hidden' : ''
223
+ }`}
224
+ aria-label="Scroll left"
225
+ >
226
+ <svg
227
+ className="w-5 h-5"
228
+ fill="none"
229
+ stroke="currentColor"
230
+ viewBox="0 0 24 24"
231
+ >
232
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 19l-7-7 7-7" />
233
+ </svg>
234
+ </button>
235
+ <button
236
+ onClick={() => scrollTo('right')}
237
+ disabled={!showRightButton}
238
+ className={`absolute right-3 top-1/2 -translate-y-1/2 w-8 h-8 bg-black/50 hover:bg-black/70 text-white rounded-full flex items-center justify-center transition-all z-40 opacity-0 group-hover/gallery:opacity-100 shadow-lg ${!showRightButton ? 'hidden' : ''
239
+ }`}
240
+ aria-label="Scroll right"
241
+ >
242
+ <svg
243
+ className="w-5 h-5"
244
+ fill="none"
245
+ stroke="currentColor"
246
+ viewBox="0 0 24 24"
247
+ >
248
+ <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
249
+ </svg>
250
+ </button>
251
+ </>
252
+ )}
253
+
254
+ <div
255
+ ref={scrollContainerRef}
256
+ onScroll={checkScroll}
257
+ className="flex flex-row gap-3 sm:gap-4 min-[768px]:gap-4 min-[1023px]:gap-4 min-[1024px]:gap-5 min-[1360px]:gap-6 overflow-x-auto pb-1 no-scrollbar scroll-smooth px-1 snap-x snap-mandatory"
258
+ >
259
+ {images.map((image, index) => (
260
+ <div
261
+ key={image.id}
262
+ onClick={() => {
263
+ setSelectedImageIndex(index)
264
+ const scrollContainer = document.getElementById('product-gallery-container')
265
+ if (scrollContainer) {
266
+ scrollContainer.scrollTo({ top: 0, behavior: 'smooth' })
267
+ }
268
+ }}
269
+ className="relative overflow-hidden bg-surface-muted cursor-pointer transition-opacity hover:opacity-80 flex-shrink-0 aspect-square w-[calc(25%-9px)] sm:w-[calc(25%-12px)] min-[1024px]:w-[calc(25%-15px)] min-[1360px]:w-[calc(25%-18px)] snap-start"
270
+ id={image.id}
271
+ style={{
272
+ border:
273
+ selectedImageIndex === index
274
+ ? "2px solid var(--color-brand-accent)"
275
+ : "2px solid transparent",
276
+ }}
277
+ >
278
+ {!!image.url && (
279
+ <Image
280
+ src={image.url}
281
+ priority={index <= 1 ? true : false}
282
+ className="w-full h-full object-cover"
283
+ alt={`Product image ${index + 1}`}
284
+ fill
285
+ sizes="(max-width: 640px) 25vw, 15vw"
286
+ />
287
+ )}
288
+ </div>
289
+ ))}
290
+ </div>
291
+ </div>
292
+ )}
293
+ </div>
294
+ )
295
+ }
296
+
297
+ export default ImageGallery