@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,598 @@
1
+ "use client"
2
+
3
+ import { HttpTypes } from "@medusajs/types"
4
+ import { useMemo, useEffect, useState, useRef } from "react"
5
+ import ProductRating from "../product-rating"
6
+ import { fetchReviewsForProduct } from "@core/data/reviews"
7
+ import { retrieveCustomer } from "@core/data/customer"
8
+ import ReviewModal from "../review-modal"
9
+
10
+ type Review = {
11
+ id: string
12
+ product_id: string
13
+ customer_id: string
14
+ rating: number
15
+ title: string
16
+ description: string
17
+ images: string[]
18
+ verified_purchase: boolean
19
+ status: string
20
+ created_at: string
21
+ updated_at: string
22
+ }
23
+
24
+ type RatingsTabProps = {
25
+ product: HttpTypes.StoreProduct & {
26
+ average_rating?: number | null
27
+ total_rating_count?: number | null
28
+ }
29
+ }
30
+
31
+ const RatingsTab = ({ product }: RatingsTabProps) => {
32
+ const [reviews, setReviews] = useState<Review[]>([])
33
+
34
+ // Collect all images from all reviews for Moments of Love section
35
+ const allReviewImages = useMemo(() => {
36
+ const images: string[] = []
37
+ reviews.forEach((review) => {
38
+ if (review.images && review.images.length > 0) {
39
+ images.push(...review.images)
40
+ }
41
+ })
42
+ return images
43
+ }, [reviews])
44
+ const [isLoading, setIsLoading] = useState(false)
45
+ const [isViewAllOpen, setIsViewAllOpen] = useState(false)
46
+ const scrollContainerRef = useRef<HTMLDivElement>(null)
47
+ const [canScrollLeft, setCanScrollLeft] = useState(false)
48
+ const [canScrollRight, setCanScrollRight] = useState(true)
49
+ const [activeImageIndex, setActiveImageIndex] = useState<number | null>(null)
50
+ const [expandedReviews, setExpandedReviews] = useState<string[]>([])
51
+ const [currentUser, setCurrentUser] = useState<any>(null)
52
+ const [editingReview, setEditingReview] = useState<Review | null>(null)
53
+ const [isEditModalOpen, setIsEditModalOpen] = useState(false)
54
+
55
+ useEffect(() => {
56
+ retrieveCustomer().then((customer) => {
57
+ if (customer) setCurrentUser(customer)
58
+ })
59
+ }, [])
60
+
61
+ // Lock body scroll when any modal is open
62
+ useEffect(() => {
63
+ const isAnyModalOpen = isViewAllOpen || isEditModalOpen || activeImageIndex !== null
64
+ if (isAnyModalOpen) {
65
+ document.body.style.overflow = 'hidden'
66
+ } else {
67
+ document.body.style.overflow = ''
68
+ }
69
+ return () => {
70
+ document.body.style.overflow = ''
71
+ }
72
+ }, [isViewAllOpen, isEditModalOpen, activeImageIndex])
73
+
74
+ const checkScroll = () => {
75
+ if (scrollContainerRef.current) {
76
+ const { scrollLeft, scrollWidth, clientWidth } = scrollContainerRef.current
77
+ setCanScrollLeft(scrollLeft > 0)
78
+ setCanScrollRight(scrollLeft + clientWidth < scrollWidth - 1)
79
+ }
80
+ }
81
+
82
+ useEffect(() => {
83
+ const container = scrollContainerRef.current
84
+ if (container) {
85
+ container.addEventListener("scroll", checkScroll)
86
+ // Initial check
87
+ checkScroll()
88
+ }
89
+ return () => container?.removeEventListener("scroll", checkScroll)
90
+ }, [reviews])
91
+
92
+ const scroll = (direction: "left" | "right") => {
93
+ if (scrollContainerRef.current) {
94
+ const scrollAmount = 300
95
+ scrollContainerRef.current.scrollBy({
96
+ left: direction === "right" ? scrollAmount : -scrollAmount,
97
+ behavior: "smooth",
98
+ })
99
+ }
100
+ }
101
+
102
+ const nextImage = () => {
103
+ if (activeImageIndex !== null) {
104
+ setActiveImageIndex((prev) => (prev! + 1) % allReviewImages.length)
105
+ }
106
+ }
107
+
108
+ const prevImage = () => {
109
+ if (activeImageIndex !== null) {
110
+ setActiveImageIndex((prev) => (prev! - 1 + allReviewImages.length) % allReviewImages.length)
111
+ }
112
+ }
113
+
114
+ useEffect(() => {
115
+ const handleKeyDown = (e: KeyboardEvent) => {
116
+ if (activeImageIndex === null) return
117
+ if (e.key === "ArrowRight") nextImage()
118
+ if (e.key === "ArrowLeft") prevImage()
119
+ if (e.key === "Escape") setActiveImageIndex(null)
120
+ }
121
+ window.addEventListener("keydown", handleKeyDown)
122
+ return () => window.removeEventListener("keydown", handleKeyDown)
123
+ }, [activeImageIndex, allReviewImages.length])
124
+
125
+ // Calculate average rating from reviews
126
+ const calculatedRating = useMemo(() => {
127
+ if (reviews.length === 0) return product.average_rating || 0
128
+ const sum = reviews.reduce((acc, review) => acc + review.rating, 0)
129
+ return sum / reviews.length
130
+ }, [reviews, product.average_rating])
131
+
132
+ const rating = calculatedRating
133
+ const total = reviews.length > 0 ? reviews.length : (product.total_rating_count || 0)
134
+
135
+
136
+
137
+ const fetchReviews = async () => {
138
+ setIsLoading(true)
139
+ try {
140
+ // Call Server Action to get logs in Terminal
141
+ const fetchedReviews = await fetchReviewsForProduct(product.id)
142
+ setReviews(fetchedReviews)
143
+ } catch (error) {
144
+ setReviews([])
145
+ } finally {
146
+ setIsLoading(false)
147
+ }
148
+ }
149
+
150
+ useEffect(() => {
151
+ fetchReviews()
152
+ }, [product.id])
153
+
154
+ // Calculate real distribution from actual reviews
155
+ const distribution = useMemo(() => {
156
+ if (reviews.length === 0) return { 5: 0, 4: 0, 3: 0, 2: 0, 1: 0 }
157
+
158
+ const dist = { 5: 0, 4: 0, 3: 0, 2: 0, 1: 0 }
159
+ reviews.forEach((review) => {
160
+ const star = review.rating as 1 | 2 | 3 | 4 | 5
161
+ if (star >= 1 && star <= 5) {
162
+ dist[star] = (dist[star] || 0) + 1
163
+ }
164
+ })
165
+ return dist
166
+ }, [reviews])
167
+
168
+ const getRatingColor = (rating: number) => {
169
+ if (rating >= 4) return "bg-green-500"
170
+ if (rating >= 3) return "bg-yellow-400"
171
+ return "bg-red-500"
172
+ }
173
+
174
+ const getProgressBarColor = (star: number) => {
175
+ if (star >= 4) return "from-green-400 to-green-600 shadow-[0_0_10px_rgba(34,197,94,0.3)] text-green-500"
176
+ if (star >= 3) return "from-yellow-300 to-yellow-500 shadow-[0_0_10px_rgba(250,204,21,0.3)] text-yellow-500"
177
+ return "from-red-400 to-red-600 shadow-[0_0_10px_rgba(239,68,68,0.3)] text-red-500"
178
+ }
179
+
180
+ return (
181
+ <div className="py-2 sm:py-4 overflow-x-hidden">
182
+ {/* Header Section */}
183
+ <div className="flex items-center justify-between mb-8 sm:mb-10">
184
+ <div className="flex flex-col gap-1">
185
+ <h3 className="text-sm font-bold text-gray-800 uppercase tracking-[0.2em]">Product Ratings</h3>
186
+ <div className="h-1 w-12 bg-[#8B5AB1] rounded-full"></div>
187
+ </div>
188
+ <ProductRating product={product} buttonText="Submit Review" showStars={false} />
189
+ </div>
190
+
191
+ {/* Content Section: Score & Bars */}
192
+ {total > 0 && (
193
+ <div className="flex flex-col lg:flex-row gap-8 lg:gap-12 items-center lg:items-start py-6 sm:py-8">
194
+
195
+ {/* Left: Big Score Card */}
196
+ <div className="flex flex-col items-center gap-2 min-w-[150px]">
197
+ <div className="flex items-center gap-2">
198
+ <span className="text-5xl sm:text-6xl font-bold text-gray-900 tracking-tight">
199
+ {rating.toFixed(1)}
200
+ </span>
201
+ <svg
202
+ width="36"
203
+ height="36"
204
+ viewBox="0 0 24 24"
205
+ fill={rating >= 4 ? "#22c55e" : rating >= 3 ? "#FFD700" : "#ef4444"}
206
+ >
207
+ <path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z" />
208
+ </svg>
209
+ </div>
210
+
211
+ <div className="flex flex-col items-center gap-1.5 mt-2">
212
+ <div className="flex gap-0.5">
213
+ {[1, 2, 3, 4, 5].map((s) => (
214
+ <svg
215
+ key={s}
216
+ width="16"
217
+ height="16"
218
+ viewBox="0 0 24 24"
219
+ fill={s <= Math.round(rating) ? (rating >= 4 ? "#22c55e" : rating >= 3 ? "#FFD700" : "#ef4444") : "#E5E7EB"}
220
+ >
221
+ <path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z" />
222
+ </svg>
223
+ ))}
224
+ </div>
225
+ <p className="text-gray-800 text-sm font-bold mt-2">
226
+ {total >= 1000 ? `${(total / 1000).toFixed(1)}k` : total} Verified Buyers
227
+ </p>
228
+ </div>
229
+ </div>
230
+
231
+ {/* Vertical Separator (Desktop) */}
232
+ <div className="hidden lg:block w-px self-stretch bg-gradient-to-b from-transparent via-gray-200 to-transparent"></div>
233
+
234
+ {/* Right: Progress Bars Table */}
235
+ <div className="flex-1 w-full max-w-lg flex flex-col gap-4">
236
+ {[5, 4, 3, 2, 1].map((star) => {
237
+ const count = distribution[star as keyof typeof distribution]
238
+ const percentage = total > 0 ? (count / total) * 100 : 0
239
+
240
+ return (
241
+ <div key={star} className="grid grid-cols-[40px_1fr_45px] items-center gap-4 group">
242
+ <div className="flex items-center gap-1.5">
243
+ <span className={`text-sm font-bold transition-colors ${star >= 4 ? "group-hover:text-green-700 text-gray-800" : star === 3 ? "group-hover:text-yellow-700 text-gray-800" : "group-hover:text-red-700 text-gray-800"}`}>{star}</span>
244
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="#9CA3AF" className={`transition-colors ${star >= 4 ? "group-hover:fill-green-500" : star === 3 ? "group-hover:fill-yellow-400" : "group-hover:fill-red-500"}`}>
245
+ <path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z" />
246
+ </svg>
247
+ </div>
248
+
249
+ <div className="h-2.5 bg-white rounded-full overflow-hidden border border-gray-100 shadow-inner">
250
+ <div
251
+ className={`h-full rounded-full bg-gradient-to-r ${getProgressBarColor(star).split(' text-')[0]} transition-all duration-1000 ease-out flex justify-end`}
252
+ style={{ width: `${percentage}%` }}
253
+ >
254
+ <div className="w-1.5 h-full bg-white/30 animate-pulse"></div>
255
+ </div>
256
+ </div>
257
+
258
+ <div className="text-right">
259
+ <span className="text-xs font-bold text-gray-800 group-hover:text-black transition-colors">
260
+ {count}
261
+ </span>
262
+ </div>
263
+ </div>
264
+ )
265
+ })}
266
+ </div>
267
+ </div>
268
+ )}
269
+
270
+
271
+ {/* Moments of Love Section */}
272
+ {allReviewImages.length > 0 && (
273
+ <div className="mt-8 border-t pt-6">
274
+ <h4 className="font-semibold text-lg mb-4 text-gray-900">
275
+ Moments Of Love ({allReviewImages.length})
276
+ </h4>
277
+ <div className="relative group/scroll">
278
+ <div
279
+ ref={scrollContainerRef}
280
+ className="flex gap-3 overflow-x-auto pb-2 scrollbar-hide scroll-smooth"
281
+ >
282
+ {allReviewImages.map((imageUrl, idx) => (
283
+ <img
284
+ key={idx}
285
+ src={imageUrl}
286
+ alt={`Moment ${idx + 1}`}
287
+ className="w-32 h-32 object-cover rounded-lg border border-gray-200 flex-shrink-0 cursor-pointer hover:opacity-90 transition-opacity"
288
+ onClick={() => setActiveImageIndex(idx)}
289
+ />
290
+ ))}
291
+ </div>
292
+
293
+ {/* Left Scroll Arrow */}
294
+ <button
295
+ onClick={() => scroll("left")}
296
+ className={`absolute left-2 top-1/2 -translate-y-1/2 bg-white/90 backdrop-blur-sm border border-gray-200 p-2 rounded-full shadow-lg text-[#8B5AB1] hover:bg-white transition-all z-10 hidden sm:flex ${canScrollLeft ? "opacity-100" : "opacity-0 pointer-events-none"}`}
297
+ aria-label="Scroll left"
298
+ >
299
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round">
300
+ <polyline points="15 18 9 12 15 6"></polyline>
301
+ </svg>
302
+ </button>
303
+
304
+ {/* Right Scroll Arrow */}
305
+ <button
306
+ onClick={() => scroll("right")}
307
+ className={`absolute right-2 top-1/2 -translate-y-1/2 bg-white/90 backdrop-blur-sm border border-gray-200 p-2 rounded-full shadow-lg text-[#8B5AB1] hover:bg-white transition-all z-10 hidden sm:flex ${canScrollRight ? "opacity-100" : "opacity-0 pointer-events-none"}`}
308
+ aria-label="Scroll right"
309
+ >
310
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round">
311
+ <polyline points="9 18 15 12 9 6"></polyline>
312
+ </svg>
313
+ </button>
314
+ </div>
315
+ <style jsx>{`
316
+ .scrollbar-hide {
317
+ -ms-overflow-style: none; /* IE and Edge */
318
+ scrollbar-width: none; /* Firefox */
319
+ }
320
+ .scrollbar-hide::-webkit-scrollbar {
321
+ display: none; /* Chrome, Safari and Opera */
322
+ }
323
+ `}</style>
324
+ </div>
325
+ )}
326
+
327
+ {/* Reviews List - Show only 2 latest */}
328
+ <div className="mt-8 border-t pt-6">
329
+ <h4 className="font-semibold text-lg mb-6 text-gray-900">Customer Reviews</h4>
330
+ {isLoading ? (
331
+ <p className="text-gray-500 text-sm">Loading reviews...</p>
332
+ ) : reviews.length === 0 ? (
333
+ <p className="text-gray-500 text-sm">No reviews yet. Be the first to review this product!</p>
334
+ ) : (
335
+ <>
336
+ <div className="space-y-6">
337
+ {reviews.slice(0, 2).map((review) => (
338
+ <div key={review.id} className="bg-white border border-gray-200 rounded-lg p-6 shadow-sm relative group">
339
+ {/* Edit Button */}
340
+ {currentUser && currentUser.id === review.customer_id && (
341
+ <button
342
+ onClick={() => {
343
+ setEditingReview(review)
344
+ setIsEditModalOpen(true)
345
+ }}
346
+ className="absolute top-4 right-4 text-[#8B5AB1] hover:bg-[#8B5AB1] hover:text-white transition-colors flex items-center gap-1.5 bg-[#8B5AB1]/10 px-3 py-1.5 rounded-full border border-[#8B5AB1]/20 text-xs sm:text-sm font-semibold shadow-sm"
347
+ >
348
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
349
+ <path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path>
350
+ <path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path>
351
+ </svg>
352
+ <span className="inline">Edit</span>
353
+ </button>
354
+ )}
355
+
356
+ {/* Review Title */}
357
+ <h5 className="font-bold text-base text-gray-900 mb-3 pr-16">{review.title}</h5>
358
+
359
+ {/* Rating Badge and Review Text */}
360
+ <div className="flex items-start gap-4 mb-4">
361
+ {/* Styled Star Rating Badge */}
362
+ <div className={`${getRatingColor(review.rating)} rounded-md px-3 py-1.5 flex items-center justify-center min-w-[50px] shadow-sm`}>
363
+ <span className="text-white font-semibold text-sm">{review.rating}★</span>
364
+ </div>
365
+
366
+ {/* Review Description */}
367
+ <p className="text-gray-900 text-sm leading-relaxed flex-1 font-medium">{review.description}</p>
368
+ </div>
369
+
370
+ {/* Review Images - Side by Side */}
371
+ {review.images && review.images.length > 0 && (
372
+ <div className="flex flex-wrap gap-2 mb-4 w-full items-center">
373
+ {(expandedReviews.includes(review.id) ? review.images : review.images.slice(0, 2)).map((imageUrl, idx) => (
374
+ <div key={idx} className="relative w-24 h-24">
375
+ <img
376
+ src={imageUrl}
377
+ alt={`Review image ${idx + 1}`}
378
+ className="w-full h-full object-cover rounded border border-gray-200 cursor-pointer hover:opacity-90 transition-opacity"
379
+ onClick={() => {
380
+ const globalIdx = allReviewImages.indexOf(imageUrl);
381
+ if (globalIdx !== -1) setActiveImageIndex(globalIdx);
382
+ }}
383
+ />
384
+ </div>
385
+ ))}
386
+ {!expandedReviews.includes(review.id) && review.images.length > 2 && (
387
+ <button
388
+ className="w-24 h-24 flex flex-col items-center justify-center rounded border border-dashed border-[#8B5AB1]/30 bg-[#8B5AB1]/5 hover:bg-[#8B5AB1]/10 transition-colors text-[#8B5AB1]"
389
+ onClick={() => setExpandedReviews(prev => [...prev, review.id])}
390
+ >
391
+ <span className="font-bold text-sm">+{review.images.length - 2} more</span>
392
+ <span className="text-[10px] font-medium uppercase tracking-tighter">Images</span>
393
+ </button>
394
+ )}
395
+ </div>
396
+ )}
397
+
398
+ {/* Customer Info and Date */}
399
+ <div className="flex items-center gap-2 text-sm text-gray-700 font-medium border-t border-gray-100 pt-3">
400
+ <span className="font-bold">Chocomelon Customer</span>
401
+ <span className="text-gray-400">|</span>
402
+ <span>{new Date(review.created_at).toLocaleDateString('en-GB', { day: 'numeric', month: 'short', year: 'numeric' })}</span>
403
+ </div>
404
+ </div>
405
+ ))}
406
+ </div>
407
+
408
+ {/* View All Button */}
409
+ {reviews.length > 2 && (
410
+ <div className="mt-6 text-left">
411
+ <button
412
+ onClick={() => setIsViewAllOpen(true)}
413
+ className="text-[#8B5AB1] font-semibold text-sm hover:underline"
414
+ >
415
+ View all {reviews.length} reviews
416
+ </button>
417
+ </div>
418
+ )}
419
+ </>
420
+ )}
421
+ </div>
422
+
423
+ {/* View All Reviews Modal */}
424
+ {isViewAllOpen && (
425
+ <div
426
+ className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-sm p-4"
427
+ onClick={(e) => {
428
+ if (e.target === e.currentTarget) setIsViewAllOpen(false)
429
+ }}
430
+ >
431
+ <div className="bg-white rounded-xl shadow-2xl w-full max-w-4xl max-h-[90vh] overflow-hidden flex flex-col">
432
+ {/* Modal Header */}
433
+ <div className="bg-[#8B5AB1] p-4 text-white flex justify-between items-center">
434
+ <h2 className="text-xl font-bold">All Reviews ({reviews.length})</h2>
435
+ <button
436
+ onClick={() => setIsViewAllOpen(false)}
437
+ className="text-white hover:text-gray-200"
438
+ >
439
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
440
+ <line x1="18" y1="6" x2="6" y2="18"></line>
441
+ <line x1="6" y1="6" x2="18" y2="18"></line>
442
+ </svg>
443
+ </button>
444
+ </div>
445
+
446
+ {/* Modal Content - Scrollable */}
447
+ <div className="overflow-y-auto flex-1 p-6">
448
+ <div className="space-y-6">
449
+ {reviews.map((review) => (
450
+ <div key={review.id} className="bg-white border border-gray-200 rounded-lg p-6 shadow-sm relative group">
451
+ {/* Edit Button */}
452
+ {currentUser && currentUser.id === review.customer_id && (
453
+ <button
454
+ onClick={() => {
455
+ setEditingReview(review)
456
+ setIsEditModalOpen(true)
457
+ }}
458
+ className="absolute top-4 right-4 text-[#8B5AB1] hover:bg-[#8B5AB1] hover:text-white transition-colors flex items-center gap-1.5 bg-[#8B5AB1]/10 px-3 py-1.5 rounded-full border border-[#8B5AB1]/20 text-xs sm:text-sm font-semibold shadow-sm"
459
+ >
460
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
461
+ <path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path>
462
+ <path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path>
463
+ </svg>
464
+ <span className="inline">Edit</span>
465
+ </button>
466
+ )}
467
+
468
+ {/* Review Title */}
469
+ <h5 className="font-bold text-base text-gray-900 mb-3 pr-16">{review.title}</h5>
470
+
471
+ {/* Rating Badge and Review Text */}
472
+ <div className="flex items-start gap-4 mb-4">
473
+ {/* Styled Star Rating Badge */}
474
+ <div className={`${getRatingColor(review.rating)} rounded-md px-3 py-1.5 flex items-center justify-center min-w-[50px] shadow-sm`}>
475
+ <span className="text-white font-semibold text-sm">{review.rating}★</span>
476
+ </div>
477
+
478
+ {/* Review Description */}
479
+ <p className="text-gray-900 text-sm leading-relaxed flex-1 font-medium">{review.description}</p>
480
+ </div>
481
+
482
+ {/* Review Images - Mobile Grid (Small) / Desktop Side by Side */}
483
+ {review.images && review.images.length > 0 && (
484
+ <div className="grid grid-cols-2 sm:flex sm:flex-wrap gap-2 sm:gap-3 mb-4 w-fit">
485
+ {review.images.map((imageUrl, idx) => (
486
+ <img
487
+ key={idx}
488
+ src={imageUrl}
489
+ alt={`Review image ${idx + 1}`}
490
+ className="w-24 h-24 sm:w-32 sm:h-32 object-cover rounded-lg border border-gray-200 cursor-pointer hover:opacity-90 transition-opacity shadow-sm"
491
+ onClick={() => {
492
+ const globalIdx = allReviewImages.indexOf(imageUrl);
493
+ if (globalIdx !== -1) setActiveImageIndex(globalIdx);
494
+ }}
495
+ />
496
+ ))}
497
+ </div>
498
+ )}
499
+
500
+ {/* Customer Info and Date */}
501
+ <div className="flex items-center gap-2 text-sm text-gray-600 border-t border-gray-100 pt-3">
502
+ <span className="font-medium">Chocomelon Customer</span>
503
+ <span className="text-gray-300">|</span>
504
+ <span>{new Date(review.created_at).toLocaleDateString('en-GB', { day: 'numeric', month: 'short', year: 'numeric' })}</span>
505
+ </div>
506
+ </div>
507
+ ))}
508
+ </div>
509
+ </div>
510
+ </div>
511
+ </div>
512
+ )}
513
+
514
+ {/* Image Lightbox / Popup */}
515
+ {activeImageIndex !== null && (
516
+ <div
517
+ className="fixed inset-0 z-[60] flex items-center justify-center bg-black/70 backdrop-blur-xl p-4 sm:p-8"
518
+ onClick={() => setActiveImageIndex(null)}
519
+ >
520
+ {/* Close Button */}
521
+ <button
522
+ onClick={(e) => {
523
+ e.stopPropagation();
524
+ setActiveImageIndex(null);
525
+ }}
526
+ className="absolute top-4 right-4 text-white/70 hover:text-white bg-white/10 p-2 rounded-full transition-colors z-20"
527
+ >
528
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5">
529
+ <line x1="18" y1="6" x2="6" y2="18"></line>
530
+ <line x1="6" y1="6" x2="18" y2="18"></line>
531
+ </svg>
532
+ </button>
533
+
534
+ {/* Navigation Buttons */}
535
+ {allReviewImages.length > 1 && (
536
+ <>
537
+ <button
538
+ onClick={(e) => {
539
+ e.stopPropagation();
540
+ prevImage();
541
+ }}
542
+ className="absolute left-4 top-1/2 -translate-y-1/2 bg-white/10 hover:bg-white/20 text-white p-3 rounded-full transition-all z-20"
543
+ >
544
+ <svg width="30" height="30" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round">
545
+ <polyline points="15 18 9 12 15 6"></polyline>
546
+ </svg>
547
+ </button>
548
+ <button
549
+ onClick={(e) => {
550
+ e.stopPropagation();
551
+ nextImage();
552
+ }}
553
+ className="absolute right-4 top-1/2 -translate-y-1/2 bg-white/10 hover:bg-white/20 text-white p-3 rounded-full transition-all z-20"
554
+ >
555
+ <svg width="30" height="30" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round">
556
+ <polyline points="9 18 15 12 9 6"></polyline>
557
+ </svg>
558
+ </button>
559
+ </>
560
+ )}
561
+
562
+ {/* Image Display */}
563
+ <div className="relative max-w-5xl w-full h-full flex flex-col items-center justify-center">
564
+ <img
565
+ src={allReviewImages[activeImageIndex]}
566
+ alt={`Moment ${activeImageIndex + 1}`}
567
+ className="max-w-full max-h-[80vh] object-contain rounded-lg shadow-2xl animate-in zoom-in-95 duration-300"
568
+ onClick={(e) => e.stopPropagation()}
569
+ />
570
+
571
+ {/* Image Counter */}
572
+ <div className="mt-6 px-4 py-1.5 bg-white/10 rounded-full border border-white/20 backdrop-blur-sm text-white/90 text-sm font-bold tracking-widest uppercase">
573
+ {activeImageIndex + 1} / {allReviewImages.length}
574
+ </div>
575
+ </div>
576
+ </div>
577
+ )}
578
+
579
+ {/* Edit Review Modal */}
580
+ {isEditModalOpen && (
581
+ <ReviewModal
582
+ productId={product.id}
583
+ productTitle={product.title || "Product"}
584
+ isOpen={isEditModalOpen}
585
+ close={() => {
586
+ setIsEditModalOpen(false)
587
+ setEditingReview(null)
588
+ }}
589
+ existingReview={editingReview}
590
+ onSuccess={fetchReviews}
591
+ />
592
+ )}
593
+
594
+ </div>
595
+ )
596
+ }
597
+
598
+ export default RatingsTab
@@ -0,0 +1,53 @@
1
+ "use client"
2
+
3
+ import { useEffect } from "react"
4
+ import { trackViewItem } from "@core/analytics/ga4-ecommerce"
5
+
6
+ type ProductViewTrackerProps = {
7
+ product: {
8
+ id: string
9
+ title?: string
10
+ variants?: Array<{
11
+ id: string
12
+ title?: string
13
+ calculated_price?: { calculated_amount?: number; currency_code?: string }
14
+ }>
15
+ }
16
+ region?: { currency_code?: string }
17
+ selectedVariantId?: string | null
18
+ }
19
+
20
+ export default function ProductViewTracker({
21
+ product,
22
+ region,
23
+ selectedVariantId,
24
+ }: ProductViewTrackerProps) {
25
+ useEffect(() => {
26
+ if (!product?.id) return
27
+
28
+ const variant = selectedVariantId
29
+ ? product.variants?.find((v) => v.id === selectedVariantId)
30
+ : product.variants?.[0]
31
+ const currency = (variant as any)?.calculated_price?.currency_code?.toUpperCase()
32
+ ?? region?.currency_code?.toUpperCase()
33
+ ?? "USD"
34
+ const unitPrice = (variant as any)?.calculated_price?.calculated_amount ?? 0
35
+ const value = unitPrice / 100
36
+
37
+ trackViewItem({
38
+ currency,
39
+ value,
40
+ items: [
41
+ {
42
+ item_id: variant?.id ?? product.id,
43
+ item_name: product.title ?? "",
44
+ price: unitPrice / 100,
45
+ quantity: 1,
46
+ item_variant: variant?.title ?? undefined,
47
+ },
48
+ ],
49
+ })
50
+ }, [product?.id, product?.title, product?.variants, region?.currency_code, selectedVariantId])
51
+
52
+ return null
53
+ }