@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
+ "use client"
2
+
3
+ import Image from "next/image"
4
+ import Color from "color"
5
+ import { HttpTypes } from "@medusajs/types"
6
+ import OptionSelect from "@modules/products/components/product-actions/option-select"
7
+ import type { ProductOptions } from "@core/domain/product/variant-selection"
8
+
9
+ type ProductOptionsSectionProps = {
10
+ product: HttpTypes.StoreProduct
11
+ disabled?: boolean
12
+ isAdding: boolean
13
+ options: ProductOptions
14
+ setOptionValue: (optionId: string, value: string) => void
15
+ setColorValue: (optionId: string, value: string) => void
16
+ validationErrors: Record<string, string>
17
+ colorOption?: HttpTypes.StoreProductOption
18
+ sizeOption?: HttpTypes.StoreProductOption
19
+ otherOptions: HttpTypes.StoreProductOption[]
20
+ colorVariants: HttpTypes.StoreProductVariant[]
21
+ selectedColorValue?: string
22
+ quantity: number
23
+ setQuantity: (value: number) => void
24
+ inventoryLimit: number | null
25
+ onOpenSizeChart: () => void
26
+ }
27
+
28
+ export function ProductOptionsSection({
29
+ product,
30
+ disabled,
31
+ isAdding,
32
+ options,
33
+ setOptionValue,
34
+ setColorValue,
35
+ validationErrors,
36
+ colorOption,
37
+ sizeOption,
38
+ otherOptions,
39
+ colorVariants,
40
+ selectedColorValue,
41
+ quantity,
42
+ setQuantity,
43
+ inventoryLimit,
44
+ onOpenSizeChart,
45
+ }: ProductOptionsSectionProps) {
46
+ return (
47
+ <>
48
+ {colorOption && (
49
+ <div className="flex flex-col gap-3">
50
+ <div className="flex items-center gap-2">
51
+ <h3 className="text-[11px] font-semibold text-heading uppercase tracking-[var(--letter-spacing-nav)]">
52
+ {colorVariants.length > 0
53
+ ? "More Colors"
54
+ : `Select ${colorOption.title}`}
55
+ </h3>
56
+ {validationErrors[colorOption.id] && (
57
+ <span className="text-[10px] text-red-600 font-black uppercase tracking-wider animate-pulse border-l-2 border-red-600 pl-2">
58
+ Please Select {String(colorOption.title || "")}
59
+ </span>
60
+ )}
61
+ </div>
62
+ {colorVariants.length > 0 ? (
63
+ <div className="flex gap-2 sm:gap-3 overflow-x-auto pb-2">
64
+ {colorVariants.map((variant) => {
65
+ const variantImage =
66
+ (variant.metadata?.image as string) ||
67
+ variant.thumbnail ||
68
+ variant.images?.[0]?.url ||
69
+ product.thumbnail
70
+ const variantColorValue = variant.options?.find(
71
+ (opt) => opt.option_id === colorOption.id
72
+ )?.value
73
+ const isSelected = variantColorValue === selectedColorValue
74
+ let stripColor = (variant.metadata?.color_hex as string) || null
75
+ if (!stripColor && variantColorValue) {
76
+ try {
77
+ stripColor = Color(
78
+ variantColorValue.toLowerCase().replace(/\s+/g, "")
79
+ ).hex()
80
+ } catch {
81
+ stripColor = null
82
+ }
83
+ }
84
+ return (
85
+ <button
86
+ key={variant.id}
87
+ type="button"
88
+ className={`w-12 h-14 sm:w-16 sm:h-[4.5rem] overflow-hidden border flex-shrink-0 flex flex-col ${
89
+ isSelected
90
+ ? "border-brand-accent ring-1 ring-brand-accent"
91
+ : "border-[var(--color-header-border)] hover:border-brand-accent-light"
92
+ }`}
93
+ onClick={() => {
94
+ if (colorOption && variantColorValue) {
95
+ setColorValue(colorOption.id, variantColorValue)
96
+ }
97
+ }}
98
+ >
99
+ <div className="flex-1 w-full relative overflow-hidden">
100
+ {variantImage && (
101
+ <Image
102
+ src={variantImage}
103
+ alt={`Color variant: ${variantColorValue || "Unknown"}`}
104
+ fill
105
+ sizes="64px"
106
+ className="object-cover"
107
+ />
108
+ )}
109
+ </div>
110
+ {stripColor && (
111
+ <div
112
+ className="h-2 w-full border-t border-gray-100"
113
+ style={{ backgroundColor: stripColor }}
114
+ />
115
+ )}
116
+ </button>
117
+ )
118
+ })}
119
+ </div>
120
+ ) : (
121
+ <OptionSelect
122
+ option={colorOption}
123
+ current={options[colorOption.id]}
124
+ updateOption={setOptionValue}
125
+ title={colorOption.title ?? ""}
126
+ data-testid="product-options"
127
+ disabled={!!disabled || isAdding}
128
+ error={validationErrors[colorOption.id]}
129
+ />
130
+ )}
131
+ </div>
132
+ )}
133
+
134
+ {sizeOption && (
135
+ <div className="flex flex-col gap-3">
136
+ <div className="flex items-center justify-between">
137
+ <div className="flex items-center gap-2">
138
+ <h3 className="text-[11px] font-semibold text-heading uppercase tracking-[var(--letter-spacing-nav)]">
139
+ Select Size
140
+ </h3>
141
+ {validationErrors[sizeOption.id] && (
142
+ <span className="text-[10px] text-red-600 font-black uppercase tracking-wider animate-pulse border-l-2 border-red-600 pl-2">
143
+ Please Select {String(sizeOption.title || "")}
144
+ </span>
145
+ )}
146
+ </div>
147
+ <button
148
+ type="button"
149
+ onClick={onOpenSizeChart}
150
+ className="flex items-center gap-1.5 text-xs sm:text-sm font-medium hover:opacity-80 text-brand-accent"
151
+ >
152
+ <Image src="/Size Chart.svg" alt="Size Chart" width={16} height={16} />
153
+ Size Chart
154
+ </button>
155
+ </div>
156
+ <OptionSelect
157
+ option={sizeOption}
158
+ current={options[sizeOption.id]}
159
+ updateOption={setOptionValue}
160
+ title={sizeOption.title ?? ""}
161
+ data-testid="product-options"
162
+ disabled={!!disabled || isAdding}
163
+ error={validationErrors[sizeOption.id]}
164
+ />
165
+ </div>
166
+ )}
167
+
168
+ {otherOptions
169
+ .sort((a, b) => (a.title || "").localeCompare(b.title || ""))
170
+ .map((option) => (
171
+ <div key={option.id} className="flex flex-col gap-3">
172
+ <div className="flex items-center gap-2">
173
+ <h3 className="text-[11px] font-semibold text-heading uppercase tracking-[var(--letter-spacing-nav)]">
174
+ Select {String(option.title || "")}
175
+ </h3>
176
+ {validationErrors[option.id] && (
177
+ <span className="text-[10px] text-red-600 font-black uppercase tracking-wider animate-pulse border-l-2 border-red-600 pl-2">
178
+ Please Select {String(option.title || "")}
179
+ </span>
180
+ )}
181
+ </div>
182
+ <OptionSelect
183
+ option={option}
184
+ current={options[option.id]}
185
+ updateOption={setOptionValue}
186
+ title={option.title ?? ""}
187
+ data-testid="product-options"
188
+ disabled={!!disabled || isAdding}
189
+ error={validationErrors[option.id]}
190
+ />
191
+ </div>
192
+ ))}
193
+
194
+ <div className="flex flex-col gap-3">
195
+ <div className="flex items-center justify-between">
196
+ <h3 className="text-xs sm:text-sm font-semibold text-gray-900 uppercase">
197
+ Quantity
198
+ </h3>
199
+ {inventoryLimit !== null && inventoryLimit > 0 && inventoryLimit <= 5 && (
200
+ <span className="text-[10px] sm:text-xs font-bold uppercase tracking-wider text-orange-600">
201
+ Hurry! Only {inventoryLimit} left
202
+ </span>
203
+ )}
204
+ </div>
205
+ <div className="flex items-center border border-[var(--color-header-border)] overflow-hidden bg-surface w-[120px]">
206
+ <button
207
+ type="button"
208
+ onClick={() => setQuantity(Math.max(1, quantity - 1))}
209
+ disabled={quantity <= 1}
210
+ className="w-10 h-10 flex items-center justify-center hover:bg-gray-50 disabled:opacity-50"
211
+ >
212
+
213
+ </button>
214
+ <span className="flex-1 text-center font-semibold text-gray-900 py-2">
215
+ {quantity}
216
+ </span>
217
+ <button
218
+ type="button"
219
+ onClick={() => {
220
+ if (inventoryLimit === null || quantity < inventoryLimit) {
221
+ setQuantity(quantity + 1)
222
+ }
223
+ }}
224
+ disabled={inventoryLimit !== null && quantity >= inventoryLimit}
225
+ className="w-10 h-10 flex items-center justify-center hover:bg-gray-50 disabled:opacity-50"
226
+ >
227
+ +
228
+ </button>
229
+ </div>
230
+ </div>
231
+ </>
232
+ )
233
+ }
@@ -0,0 +1,53 @@
1
+ import {
2
+ formatDisplayPrice,
3
+ formatINRPrice,
4
+ type VariantPrice,
5
+ } from "@core/domain/product/pricing"
6
+
7
+ type ProductPriceSectionProps = {
8
+ displayPrice: VariantPrice
9
+ quantityInCart: number
10
+ }
11
+
12
+ export function ProductPriceSection({
13
+ displayPrice,
14
+ quantityInCart,
15
+ }: ProductPriceSectionProps) {
16
+ const originalPrice = displayPrice?.original_price_number
17
+ const currentPrice = displayPrice?.calculated_price_number
18
+ const discountPercentage =
19
+ originalPrice && currentPrice && originalPrice > currentPrice
20
+ ? Math.round(((originalPrice - currentPrice) / originalPrice) * 100)
21
+ : null
22
+
23
+ return (
24
+ <div className="flex flex-col gap-1">
25
+ <div className="flex items-baseline justify-between flex-wrap gap-2">
26
+ <div className="flex items-baseline gap-3 flex-wrap">
27
+ <span className="text-xl sm:text-2xl font-semibold text-heading">
28
+ {formatDisplayPrice(displayPrice, currentPrice)}
29
+ </span>
30
+ {originalPrice && currentPrice && originalPrice > currentPrice && (
31
+ <>
32
+ <span className="text-base text-[var(--color-text-muted)] line-through">
33
+ {formatINRPrice(originalPrice)}
34
+ </span>
35
+ {discountPercentage && (
36
+ <span className="text-sm font-medium text-brand-sale">
37
+ {discountPercentage}% off
38
+ </span>
39
+ )}
40
+ </>
41
+ )}
42
+ </div>
43
+
44
+ {quantityInCart > 0 && (
45
+ <span className="text-[11px] font-semibold uppercase tracking-[var(--letter-spacing-nav)] text-brand-success">
46
+ {quantityInCart} in bag
47
+ </span>
48
+ )}
49
+ </div>
50
+ <p className="text-xs text-[var(--color-text-muted)]">Tax included.</p>
51
+ </div>
52
+ )
53
+ }
@@ -0,0 +1,84 @@
1
+ "use client"
2
+
3
+ import Image from "next/image"
4
+ import PincodeChecker from "@modules/cart/components/pincode-checker"
5
+
6
+ type ProductTrustSectionProps = {
7
+ selectedVariantId?: string
8
+ onOpenFeature: (feature: string) => void
9
+ }
10
+
11
+ export function ProductTrustSection({ selectedVariantId, onOpenFeature }: ProductTrustSectionProps) {
12
+ return (
13
+ <>
14
+ <div className="flex items-center justify-between py-2 px-4 bg-gray-50/60 rounded-xl border border-gray-100/80 mt-0.5 sm:-mt-1.5 mb-1 sm:mb-0">
15
+ <div className="flex items-center gap-3 sm:gap-4">
16
+ <Image src="/Item.svg" alt="Mastercard" width={40} height={28} className="h-6 sm:h-7 w-auto grayscale-[0.2]" />
17
+ <Image src="/Item (1).svg" alt="Amex" width={40} height={28} className="h-6 sm:h-7 w-auto grayscale-[0.2]" />
18
+ <Image src="/Item (2).svg" alt="Diners" width={40} height={28} className="h-7 sm:h-7 w-auto grayscale-[0.2]" />
19
+ <Image src="/visa.svg" alt="Visa" width={40} height={28} className="h-5 sm:h-6 w-auto grayscale-[0.2]" />
20
+ </div>
21
+ <div className="flex items-center gap-1.5 sm:gap-2 ml-2 sm:ml-0">
22
+ <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#156229" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round">
23
+ <rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect>
24
+ <path d="M7 11V7a5 5 0 0 1 10 0v4"></path>
25
+ </svg>
26
+ <span className="text-[9px] sm:text-[11px] font-bold text-gray-700 uppercase tracking-wider whitespace-nowrap">Secure Payments</span>
27
+ </div>
28
+ </div>
29
+
30
+ {/* Pincode Checker Section */}
31
+ <div className="mt-0.5 sm:-mt-1.5 mb-0">
32
+ <PincodeChecker variantId={selectedVariantId} />
33
+ </div>
34
+
35
+ {/* Service Features Section */}
36
+ <div className="flex flex-row overflow-x-auto no-scrollbar py-4 border-t border-gray-200 gap-4 min-[500px]:gap-0 min-[500px]:justify-between min-[500px]:overflow-visible">
37
+ {/* 7 Days Easy Return */}
38
+ <div className="flex flex-col items-center gap-2 flex-shrink-0 min-w-[120px] min-[500px]:flex-1 min-[500px]:min-w-0 group">
39
+ <div className="w-8 h-8 flex items-center justify-center">
40
+ <Image src="/Return.svg" alt="Return" width={28} height={26} />
41
+ </div>
42
+ <div className="flex items-center gap-1.5 cursor-pointer hover:opacity-80 transition-opacity" onClick={() => onOpenFeature('return')}>
43
+ <span className="text-xs text-gray-800 font-medium text-center whitespace-nowrap leading-none">7 Days Easy Return</span>
44
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3.5" className="text-gray-600 group-hover:text-purple-600 transition-colors relative top-[-0.5px]">
45
+ <polyline points="9 18 15 12 9 6"></polyline>
46
+ </svg>
47
+ </div>
48
+ </div>
49
+
50
+ {/* Separator */}
51
+ <div className="hidden min-[500px]:block w-px h-12 bg-gray-200 flex-shrink-0"></div>
52
+
53
+ {/* Fast Delivery */}
54
+ <div className="flex flex-col items-center gap-2 flex-shrink-0 min-w-[120px] min-[500px]:flex-1 min-[500px]:min-w-0 group">
55
+ <div className="w-8 h-8 flex items-center justify-center">
56
+ <Image src="/delivery.svg" alt="Delivery" width={30} height={18} />
57
+ </div>
58
+ <div className="flex items-center gap-1.5 cursor-pointer hover:opacity-80 transition-opacity" onClick={() => onOpenFeature('delivery')}>
59
+ <span className="text-xs text-gray-800 font-medium text-center whitespace-nowrap leading-none">Fast Delivery</span>
60
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3.5" className="text-gray-600 group-hover:text-purple-600 transition-colors relative top-[-0.5px]">
61
+ <polyline points="9 18 15 12 9 6"></polyline>
62
+ </svg>
63
+ </div>
64
+ </div>
65
+
66
+ {/* Separator */}
67
+ <div className="hidden min-[500px]:block w-px h-12 bg-gray-200 flex-shrink-0"></div>
68
+
69
+ {/* Cash On Delivery Available */}
70
+ <div className="flex flex-col items-center gap-2 flex-shrink-0 min-w-[120px] min-[500px]:flex-1 min-[500px]:min-w-0 group">
71
+ <div className="w-8 h-8 flex items-center justify-center">
72
+ <Image src="/Cod.svg" alt="COD" width={30} height={18} />
73
+ </div>
74
+ <div className="flex items-center gap-1.5 cursor-pointer hover:opacity-80 transition-opacity" onClick={() => onOpenFeature('cod')}>
75
+ <span className="text-xs text-gray-800 font-medium text-center whitespace-nowrap leading-none">Cash On Delivery Available</span>
76
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3.5" className="text-gray-600 group-hover:text-purple-600 transition-colors relative top-[-0.5px]">
77
+ <polyline points="9 18 15 12 9 6"></polyline>
78
+ </svg>
79
+ </div>
80
+ </div>
81
+ </div>
82
+ </>
83
+ )
84
+ }
@@ -0,0 +1,161 @@
1
+ "use client"
2
+
3
+ import { useEffect, useMemo, useRef, useState } from "react"
4
+ import { useParams } from "next/navigation"
5
+ import { HttpTypes } from "@medusajs/types"
6
+ import { useProductVariant } from "@core/hooks/use-product-variant"
7
+ import { useProductActions } from "@core/hooks/use-product-actions"
8
+ import { getDisplayPrice } from "@core/domain/product/pricing"
9
+ import LoginPopup from "@modules/common/components/login-popup"
10
+ import { ProductPriceSection } from "./ProductPriceSection"
11
+ import { ProductOptionsSection } from "./ProductOptionsSection"
12
+ import { ProductCTASection } from "./ProductCTASection"
13
+ import { ProductTrustSection } from "./ProductTrustSection"
14
+ import { ProductHighlightsSection } from "./ProductHighlightsSection"
15
+ import { ProductFeaturePanel } from "./ProductFeaturePanel"
16
+ import { ProductDetailsSection } from "./ProductDetailsSection"
17
+
18
+ type ProductActionsProps = {
19
+ product: HttpTypes.StoreProduct
20
+ region?: HttpTypes.StoreRegion
21
+ disabled?: boolean
22
+ cart?: HttpTypes.StoreCart | null
23
+ }
24
+
25
+ export default function ProductActions({
26
+ product,
27
+ region,
28
+ disabled,
29
+ cart,
30
+ }: ProductActionsProps) {
31
+ const countryCode = useParams().countryCode as string
32
+ const actionsRef = useRef<HTMLDivElement>(null)
33
+
34
+ const {
35
+ options,
36
+ setOptionValue,
37
+ setColorValue,
38
+ selectedVariant,
39
+ isValidVariant,
40
+ validationErrors,
41
+ validateOptions,
42
+ colorOption,
43
+ sizeOption,
44
+ otherOptions,
45
+ colorVariants,
46
+ selectedColorValue,
47
+ } = useProductVariant({ product })
48
+
49
+ const displayPrice = useMemo(
50
+ () => getDisplayPrice(product, selectedVariant),
51
+ [product, selectedVariant]
52
+ )
53
+
54
+ const {
55
+ quantity,
56
+ setQuantity,
57
+ isAdding,
58
+ isBuyingNow,
59
+ variantInCart,
60
+ quantityInCart,
61
+ inStock,
62
+ inventoryLimit,
63
+ handleAddToCart,
64
+ handleBuyNow,
65
+ handleIncreaseQuantity,
66
+ handleDecreaseQuantity,
67
+ } = useProductActions({
68
+ product,
69
+ region,
70
+ cart,
71
+ selectedVariant,
72
+ options,
73
+ isValidVariant,
74
+ displayPrice,
75
+ validateOptions,
76
+ })
77
+
78
+ const [activeFeature, setActiveFeature] = useState<string | null>(null)
79
+ const [showLoginPopup, setShowLoginPopup] = useState(false)
80
+ const [showNotifyMessage, setShowNotifyMessage] = useState(false)
81
+
82
+ useEffect(() => {
83
+ setShowNotifyMessage(false)
84
+ }, [options])
85
+
86
+ return (
87
+ <>
88
+ <div
89
+ className="flex flex-col gap-4 sm:gap-5 w-full"
90
+ ref={actionsRef}
91
+ >
92
+ {displayPrice && (
93
+ <ProductPriceSection
94
+ displayPrice={displayPrice}
95
+ quantityInCart={quantityInCart}
96
+ />
97
+ )}
98
+
99
+ <ProductOptionsSection
100
+ product={product}
101
+ disabled={disabled}
102
+ isAdding={isAdding}
103
+ options={options}
104
+ setOptionValue={setOptionValue}
105
+ setColorValue={setColorValue}
106
+ validationErrors={validationErrors}
107
+ colorOption={colorOption}
108
+ sizeOption={sizeOption}
109
+ otherOptions={otherOptions}
110
+ colorVariants={colorVariants}
111
+ selectedColorValue={selectedColorValue}
112
+ quantity={quantity}
113
+ setQuantity={setQuantity}
114
+ inventoryLimit={inventoryLimit}
115
+ onOpenSizeChart={() => setActiveFeature("size_chart")}
116
+ />
117
+
118
+ <ProductCTASection
119
+ product={product}
120
+ selectedVariant={selectedVariant}
121
+ options={options}
122
+ isValidVariant={isValidVariant}
123
+ disabled={disabled}
124
+ inStock={inStock}
125
+ inventoryLimit={inventoryLimit}
126
+ quantity={quantity}
127
+ quantityInCart={quantityInCart}
128
+ variantInCart={variantInCart}
129
+ isAdding={isAdding}
130
+ isBuyingNow={isBuyingNow}
131
+ showNotifyMessage={showNotifyMessage}
132
+ setShowNotifyMessage={setShowNotifyMessage}
133
+ handleAddToCart={handleAddToCart}
134
+ handleBuyNow={handleBuyNow}
135
+ handleIncreaseQuantity={handleIncreaseQuantity}
136
+ handleDecreaseQuantity={handleDecreaseQuantity}
137
+ />
138
+
139
+ <ProductTrustSection
140
+ selectedVariantId={selectedVariant?.id}
141
+ onOpenFeature={setActiveFeature}
142
+ />
143
+
144
+ <ProductHighlightsSection product={product} />
145
+
146
+ <ProductFeaturePanel
147
+ activeFeature={activeFeature}
148
+ onClose={() => setActiveFeature(null)}
149
+ />
150
+
151
+ <ProductDetailsSection product={product} />
152
+ </div>
153
+
154
+ <LoginPopup
155
+ isOpen={showLoginPopup}
156
+ onClose={() => setShowLoginPopup(false)}
157
+ countryCode={countryCode}
158
+ />
159
+ </>
160
+ )
161
+ }
@@ -0,0 +1,132 @@
1
+ "use client"
2
+
3
+ import { useMemo, useState } from "react"
4
+ import Image from "next/image"
5
+ import LocalizedClientLink from "@modules/common/components/localized-client-link"
6
+ import { getProductPrice } from "@core/util/get-product-price"
7
+ import { convertToLocale } from "@core/util/money"
8
+ import { useAddToCart } from "@core/hooks/use-add-to-cart"
9
+ import PlaceholderImage from "@modules/common/icons/placeholder-image"
10
+ import { clx } from "@medusajs/ui"
11
+ import type { ProductCardProps } from "@core/types/product-card"
12
+
13
+ export default function ProductCard({
14
+ product,
15
+ region,
16
+ className,
17
+ }: ProductCardProps) {
18
+ const { isAdding, handleAddToCart } = useAddToCart({ region })
19
+ const [imageHover, setImageHover] = useState(false)
20
+
21
+ const { cheapestPrice } = getProductPrice({ product })
22
+ const thumbnail = product.thumbnail
23
+ const secondImage = product.images?.[1]?.url
24
+
25
+ const discount = cheapestPrice?.percentage_diff
26
+ const hasSale =
27
+ discount && Number.parseInt(String(discount).replace(/\D/g, ""), 10) > 0
28
+
29
+ const priceLabel = useMemo(() => {
30
+ if (!cheapestPrice) return null
31
+ return convertToLocale({
32
+ amount: cheapestPrice.calculated_price_number,
33
+ currency_code: cheapestPrice.currency_code,
34
+ })
35
+ }, [cheapestPrice])
36
+
37
+ const compareLabel = useMemo(() => {
38
+ if (!cheapestPrice?.original_price_number) return null
39
+ if (cheapestPrice.calculated_price_number >= cheapestPrice.original_price_number)
40
+ return null
41
+ return convertToLocale({
42
+ amount: cheapestPrice.original_price_number,
43
+ currency_code: cheapestPrice.currency_code,
44
+ })
45
+ }, [cheapestPrice])
46
+
47
+ const onQuickAdd = (e: React.MouseEvent) => {
48
+ e.preventDefault()
49
+ e.stopPropagation()
50
+ handleAddToCart(product)
51
+ }
52
+
53
+ return (
54
+ <article className={clx("group flex flex-col w-full", className)}>
55
+ <LocalizedClientLink
56
+ href={`/products/${product.handle}`}
57
+ className="block"
58
+ data-testid="product-card"
59
+ >
60
+ <div
61
+ className="relative aspect-[3/4] overflow-hidden bg-surface-muted mb-4"
62
+ onMouseEnter={() => setImageHover(true)}
63
+ onMouseLeave={() => setImageHover(false)}
64
+ >
65
+ {thumbnail ? (
66
+ <>
67
+ <Image
68
+ src={thumbnail}
69
+ alt={product.title || "Product"}
70
+ fill
71
+ sizes="(max-width:640px) 50vw, (max-width:1024px) 33vw, 25vw"
72
+ className={clx(
73
+ "object-cover transition-opacity duration-500",
74
+ imageHover && secondImage ? "opacity-0" : "opacity-100"
75
+ )}
76
+ />
77
+ {secondImage && (
78
+ <Image
79
+ src={secondImage}
80
+ alt=""
81
+ fill
82
+ sizes="(max-width:640px) 50vw, (max-width:1024px) 33vw, 25vw"
83
+ className={clx(
84
+ "object-cover transition-opacity duration-500",
85
+ imageHover ? "opacity-100" : "opacity-0"
86
+ )}
87
+ />
88
+ )}
89
+ </>
90
+ ) : (
91
+ <div className="absolute inset-0 flex items-center justify-center">
92
+ <PlaceholderImage size={48} />
93
+ </div>
94
+ )}
95
+
96
+ {hasSale && (
97
+ <span className="absolute top-3 left-3 bg-brand-sale text-white text-[10px] font-semibold uppercase tracking-wider px-2.5 py-1">
98
+ Sale
99
+ </span>
100
+ )}
101
+
102
+ <div className="absolute inset-x-0 bottom-0 p-3 translate-y-full group-hover:translate-y-0 transition-transform duration-300">
103
+ <button
104
+ type="button"
105
+ onClick={onQuickAdd}
106
+ disabled={isAdding === product.id}
107
+ className="w-full bg-brand-accent text-[var(--color-text-inverse)] py-3 text-[11px] font-semibold uppercase tracking-[var(--letter-spacing-nav)] hover:bg-brand-accent-hover transition-colors disabled:opacity-60"
108
+ >
109
+ {isAdding === product.id ? "Adding…" : "Quick add"}
110
+ </button>
111
+ </div>
112
+ </div>
113
+
114
+ <div className="space-y-1.5">
115
+ <h3 className="text-sm font-medium text-heading leading-snug line-clamp-2 group-hover:underline underline-offset-2">
116
+ {product.title}
117
+ </h3>
118
+ <div className="flex items-baseline gap-2">
119
+ {priceLabel && (
120
+ <span className="text-sm font-semibold text-heading">{priceLabel}</span>
121
+ )}
122
+ {compareLabel && (
123
+ <span className="text-sm text-[var(--color-text-muted)] line-through">
124
+ {compareLabel}
125
+ </span>
126
+ )}
127
+ </div>
128
+ </div>
129
+ </LocalizedClientLink>
130
+ </article>
131
+ )
132
+ }