@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,1400 @@
1
+ "use client"
2
+
3
+ import { addToCart } from "@core/data/cart"
4
+ import { trackAddToCart } from "@core/analytics/ga4-ecommerce"
5
+ import { useIntersection } from "@core/hooks/use-in-view"
6
+ import { HttpTypes } from "@medusajs/types"
7
+ import { clx } from "@medusajs/ui"
8
+ // import { Button } from "@medusajs/ui" // Removed - unused import causing badge issue
9
+ import OptionSelect from "@modules/products/components/product-actions/option-select"
10
+ import { isEqual } from "lodash"
11
+ import { useParams, usePathname, useSearchParams } from "next/navigation"
12
+ import React, { useEffect, useMemo, useRef, useState } from "react"
13
+ import Color from "color"
14
+
15
+ import { useRouter } from "next/navigation"
16
+ import Image from "next/image"
17
+ import { getProductPrice, getPricesForVariant } from "@core/util/get-product-price"
18
+ import { convertToLocale } from "@core/util/money"
19
+ import RatingsTab from "../product-tabs/ratings-tab"
20
+ import PincodeChecker from "@modules/cart/components/pincode-checker"
21
+ import { addToWishlist, removeFromWishlist, getWishlistProductIds } from "@core/data/wishlist"
22
+ import LoginPopup from "@modules/common/components/login-popup"
23
+ import { updateLineItem, deleteLineItem, buyNow } from "@core/data/cart"
24
+
25
+ type ProductActionsProps = {
26
+ product: HttpTypes.StoreProduct
27
+ region?: HttpTypes.StoreRegion
28
+ disabled?: boolean
29
+ cart?: HttpTypes.StoreCart | null
30
+ }
31
+
32
+ const optionsAsKeymap = (
33
+ variantOptions: HttpTypes.StoreProductVariant["options"]
34
+ ) => {
35
+ return variantOptions?.reduce((acc: Record<string, string>, varopt: any) => {
36
+ acc[varopt.option_id] = varopt.value
37
+ return acc
38
+ }, {})
39
+ }
40
+
41
+ import SidePanel from "@modules/common/components/side-panel"
42
+
43
+ import { useProductContext } from "@modules/products/context/product-context"
44
+ import { useWishlist } from "@core/context/wishlist-context"
45
+
46
+ // ... existing imports ...
47
+
48
+ export default function ProductActions({
49
+ product,
50
+ region,
51
+ disabled,
52
+ cart,
53
+ }: ProductActionsProps) {
54
+ const router = useRouter()
55
+ const pathname = usePathname()
56
+ const searchParams = useSearchParams()
57
+ const { setSelectedVariantId, setSelectedOptions } = useProductContext()
58
+
59
+ const [options, setOptions] = useState<Record<string, string | undefined>>(() => {
60
+ const variantIdFromUrl = searchParams.get("v_id")
61
+
62
+ // If there's a variant ID in URL, find and set its options
63
+ if (variantIdFromUrl && product.variants) {
64
+ const variantFromUrl = product.variants.find((v) => v.id === variantIdFromUrl)
65
+ if (variantFromUrl) {
66
+ return optionsAsKeymap(variantFromUrl.options) ?? {}
67
+ }
68
+ }
69
+
70
+ // If there is only 1 variant, preselect the options
71
+ if (product.variants?.length === 1) {
72
+ return optionsAsKeymap(product.variants[0].options) ?? {}
73
+ }
74
+
75
+ // If there are multiple variants, check if any option has only one possible value
76
+ // and auto-select it (e.g., if only one color is available)
77
+ if (product.options) {
78
+ const partialOptions: Record<string, string> = {}
79
+
80
+ product.options.forEach((option) => {
81
+ // Check unique values from option definition (what is shown to user)
82
+ const uniqueValues = new Set(option.values?.map(v => v.value))
83
+
84
+ if (uniqueValues.size === 1) {
85
+ const uniqueValue = Array.from(uniqueValues)[0]
86
+ if (uniqueValue) {
87
+ partialOptions[option.id] = uniqueValue
88
+ }
89
+ }
90
+ })
91
+
92
+ if (Object.keys(partialOptions).length > 0) {
93
+ return partialOptions
94
+ }
95
+ }
96
+
97
+ return {}
98
+ })
99
+ const [isAdding, setIsAdding] = useState(false)
100
+ const [isBuyingNow, setIsBuyingNow] = useState(false)
101
+ const [quantity, setQuantity] = useState(1)
102
+ const [isDescriptionOpen, setIsDescriptionOpen] = useState(true)
103
+ const [isMaterialOpen, setIsMaterialOpen] = useState(false)
104
+ const [isCareGuideOpen, setIsCareGuideOpen] = useState(false)
105
+ const [isProductTypeOpen, setIsProductTypeOpen] = useState(false)
106
+ const [activeFeature, setActiveFeature] = useState<string | null>(null)
107
+ const [showLoginPopup, setShowLoginPopup] = useState(false)
108
+ const [validationErrors, setValidationErrors] = useState<Record<string, boolean>>({})
109
+ const [showNotifyMessage, setShowNotifyMessage] = useState(false)
110
+ const countryCode = useParams().countryCode as string
111
+
112
+ // Keep effect to sync if URL params change externally
113
+ useEffect(() => {
114
+ const variantIdFromUrl = searchParams.get("v_id")
115
+ if (variantIdFromUrl && product.variants) {
116
+ const variantFromUrl = product.variants.find((v) => v.id === variantIdFromUrl)
117
+ if (variantFromUrl) {
118
+ const variantOptions = optionsAsKeymap(variantFromUrl.options)
119
+ setOptions(variantOptions ?? {})
120
+ return
121
+ }
122
+ }
123
+ }, [product.variants, searchParams])
124
+
125
+ const selectedVariant = useMemo(() => {
126
+ if (!product.variants || product.variants.length === 0) {
127
+ return
128
+ }
129
+
130
+ return product.variants.find((v) => {
131
+ const variantOptions = optionsAsKeymap(v.options)
132
+ return isEqual(variantOptions, options)
133
+ })
134
+ }, [product.variants, options])
135
+
136
+ useEffect(() => {
137
+ setSelectedOptions(options)
138
+ }, [options, setSelectedOptions])
139
+
140
+ // update the options when a variant is selected
141
+ const setOptionValue = (optionId: string, value: string) => {
142
+ setOptions((prev) => ({
143
+ ...prev,
144
+ [optionId]: value,
145
+ }))
146
+ // Clear error for this option
147
+ setValidationErrors((prev) => ({
148
+ ...prev,
149
+ [optionId]: false,
150
+ }))
151
+ }
152
+
153
+ //check if the selected options produce a valid variant
154
+ const isValidVariant = useMemo(() => {
155
+ return product.variants?.some((v) => {
156
+ const variantOptions = optionsAsKeymap(v.options)
157
+ return isEqual(variantOptions, options)
158
+ })
159
+ }, [product.variants, options])
160
+
161
+ // Reset notification message when options change
162
+ useEffect(() => {
163
+ setShowNotifyMessage(false)
164
+ }, [options])
165
+ useEffect(() => {
166
+ const params = new URLSearchParams(searchParams.toString())
167
+ const value = isValidVariant ? selectedVariant?.id : undefined
168
+
169
+ // Update Context
170
+ setSelectedVariantId(value)
171
+
172
+ if (params.get("v_id") === value) {
173
+ return
174
+ }
175
+
176
+ if (value) {
177
+ params.set("v_id", value)
178
+ } else {
179
+ params.delete("v_id")
180
+ }
181
+
182
+ // Update URL silently without triggering RSC navigation
183
+ // Use replaceState to avoid adding to history stack for variant selections
184
+ const newUrl = pathname + "?" + params.toString()
185
+ window.history.replaceState(null, "", newUrl)
186
+ }, [selectedVariant, isValidVariant, pathname, searchParams, setSelectedVariantId])
187
+
188
+ const variantInCart = useMemo(() => {
189
+ if (!cart || !selectedVariant) {
190
+ return null
191
+ }
192
+
193
+ const item = cart.items?.find((i) => i.variant_id === selectedVariant.id)
194
+ return item || null
195
+ }, [cart, selectedVariant])
196
+
197
+ const quantityInCart = useMemo(() => {
198
+ return variantInCart?.quantity || 0
199
+ }, [variantInCart])
200
+
201
+ // check if the selected variant is in stock
202
+ const inStock = useMemo(() => {
203
+ // If no variant selected, check if product has any variants
204
+ if (!selectedVariant) {
205
+ return product.variants && product.variants.length > 0
206
+ }
207
+
208
+ // If we don't manage inventory, we can always add to cart
209
+ if (!selectedVariant.manage_inventory) {
210
+ return true
211
+ }
212
+
213
+ // If we allow back orders on the variant, we can add to cart
214
+ if (selectedVariant.allow_backorder) {
215
+ return true
216
+ }
217
+
218
+ // If there is inventory available AND it is >= the selected quantity
219
+ // We treat the inventory as total - quantity already in cart
220
+ const totalInventory = selectedVariant.inventory_quantity || 0
221
+ const availableQuantity = totalInventory - quantityInCart
222
+
223
+ if (availableQuantity >= quantity) {
224
+ return true
225
+ }
226
+
227
+ // Otherwise, we can't add to cart
228
+ return false
229
+ }, [selectedVariant, product.variants, quantity, quantityInCart])
230
+
231
+ // Get current inventory limit for display
232
+ const inventoryLimit = useMemo(() => {
233
+ if (!selectedVariant || !selectedVariant.manage_inventory || selectedVariant.allow_backorder) {
234
+ return null
235
+ }
236
+ const totalInventory = selectedVariant.inventory_quantity || 0
237
+ return totalInventory - quantityInCart
238
+ }, [selectedVariant, quantityInCart])
239
+
240
+ const actionsRef = useRef<HTMLDivElement>(null)
241
+
242
+ const inView = useIntersection(actionsRef, "0px")
243
+
244
+ // add the selected variant to the cart
245
+ const handleAddToCart = async () => {
246
+ // Check for missing options
247
+ const missingOptions: Record<string, boolean> = {}
248
+ product.options?.forEach(option => {
249
+ if (!options[option.id]) {
250
+ missingOptions[option.id] = true
251
+ }
252
+ })
253
+
254
+ if (Object.keys(missingOptions).length > 0) {
255
+ setValidationErrors(missingOptions)
256
+ // Optional: scroll to the first error if many options
257
+ return null
258
+ }
259
+
260
+ if (!selectedVariant?.id) return null
261
+
262
+ setIsAdding(true)
263
+
264
+ await addToCart({
265
+ variantId: selectedVariant.id,
266
+ quantity: quantity,
267
+ countryCode,
268
+ })
269
+
270
+ const currency = region?.currency_code?.toUpperCase() ?? "USD"
271
+ const unitPrice = displayPrice?.calculated_price_number ?? 0
272
+ const value = (unitPrice * quantity) / 100
273
+ trackAddToCart({
274
+ currency,
275
+ value,
276
+ items: [
277
+ {
278
+ item_id: selectedVariant.id,
279
+ item_name: product.title ?? "",
280
+ price: unitPrice / 100,
281
+ quantity,
282
+ item_variant: selectedVariant.title ?? undefined,
283
+ },
284
+ ],
285
+ })
286
+
287
+ setIsAdding(false)
288
+ }
289
+
290
+ const handleBuyNow = async () => {
291
+ // Check for missing options
292
+ const missingOptions: Record<string, boolean> = {}
293
+ product.options?.forEach(option => {
294
+ if (!options[option.id]) {
295
+ missingOptions[option.id] = true
296
+ }
297
+ })
298
+
299
+ if (Object.keys(missingOptions).length > 0) {
300
+ setValidationErrors(missingOptions)
301
+ return null
302
+ }
303
+
304
+ if (!selectedVariant?.id) return null
305
+
306
+ setIsBuyingNow(true)
307
+
308
+ try {
309
+ await buyNow({
310
+ variantId: selectedVariant.id,
311
+ quantity: quantity,
312
+ countryCode,
313
+ })
314
+ } catch (e) {
315
+ console.error(e)
316
+ } finally {
317
+ setIsBuyingNow(false)
318
+ }
319
+ }
320
+
321
+ const handleIncreaseQuantity = async () => {
322
+ if (!variantInCart || isAdding) return
323
+ setIsAdding(true)
324
+ try {
325
+ await updateLineItem({
326
+ lineId: variantInCart.id,
327
+ quantity: variantInCart.quantity + 1,
328
+ })
329
+ } catch (e) {
330
+ console.error(e)
331
+ } finally {
332
+ setIsAdding(false)
333
+ }
334
+ }
335
+
336
+ const handleDecreaseQuantity = async () => {
337
+ if (!variantInCart || isAdding) return
338
+ setIsAdding(true)
339
+ try {
340
+ if (variantInCart.quantity > 1) {
341
+ await updateLineItem({
342
+ lineId: variantInCart.id,
343
+ quantity: variantInCart.quantity - 1,
344
+ })
345
+ } else {
346
+ await deleteLineItem(variantInCart.id)
347
+ }
348
+ } catch (e) {
349
+ console.error(e)
350
+ } finally {
351
+ setIsAdding(false)
352
+ }
353
+ }
354
+
355
+ // Get color option
356
+ const colorOption = useMemo(() => {
357
+ return product.options?.find(opt =>
358
+ opt.title?.toLowerCase().includes('color') ||
359
+ opt.title?.toLowerCase().includes('colour')
360
+ )
361
+ }, [product.options])
362
+
363
+ // Get color variants for MORE COLORS section
364
+ const colorVariants = useMemo(() => {
365
+ if (!product.variants || !colorOption) return []
366
+
367
+ const uniqueColors = new Map()
368
+ product.variants.forEach(variant => {
369
+ const colorValue = variant.options?.find(opt => opt.option_id === colorOption.id)?.value
370
+ if (colorValue && !uniqueColors.has(colorValue)) {
371
+ uniqueColors.set(colorValue, variant)
372
+ }
373
+ })
374
+ return Array.from(uniqueColors.values())
375
+ }, [product.variants, colorOption])
376
+
377
+ // Get currently selected color value
378
+ const selectedColorValue = useMemo(() => {
379
+ if (!colorOption) return null
380
+ return options[colorOption.id]
381
+ }, [options, colorOption])
382
+
383
+ // Get size option
384
+ const sizeOption = useMemo(() => {
385
+ return product.options?.find(opt =>
386
+ opt.title?.toLowerCase().includes('size')
387
+ )
388
+ }, [product.options])
389
+
390
+ // Get price for selected variant - updates when variant changes
391
+ const displayPrice = useMemo(() => {
392
+ // If a variant is selected, use its price
393
+ if (selectedVariant) {
394
+ const variantPrice = getPricesForVariant(selectedVariant)
395
+ if (variantPrice) {
396
+ return variantPrice
397
+ }
398
+ }
399
+
400
+ // Fallback to getProductPrice for cheapest price
401
+ const { cheapestPrice } = getProductPrice({ product })
402
+ return cheapestPrice
403
+ }, [selectedVariant, product])
404
+
405
+ // Calculate discount if original price exists
406
+ const originalPrice = displayPrice?.original_price_number
407
+ const currentPrice = displayPrice?.calculated_price_number
408
+ const discountPercentage = originalPrice && currentPrice && originalPrice > currentPrice
409
+ ? Math.round(((originalPrice - currentPrice) / originalPrice) * 100)
410
+ : null
411
+
412
+ return (
413
+ <>
414
+ <div className="flex flex-col gap-4 sm:gap-5 min-[768px]:gap-4 min-[1023px]:gap-4 min-[1024px]:gap-6 min-[1200px]:gap-6 min-[1360px]:gap-6 w-full" ref={actionsRef}>
415
+ {/* Price Display & In-Cart Status */}
416
+ {displayPrice && (
417
+ <div className="flex flex-col gap-2 pt-2 sm:pt-3 border-t border-gray-200">
418
+ <div className="flex items-end justify-between flex-wrap gap-2">
419
+ <div className="flex items-baseline gap-2 sm:gap-3 min-[768px]:gap-3 min-[1023px]:gap-3 min-[1360px]:gap-3 flex-wrap">
420
+ <span className="text-2xl sm:text-3xl min-[768px]:text-3xl min-[1023px]:text-3xl min-[1360px]:text-3xl font-bold text-gray-900">
421
+ {currentPrice !== undefined && currentPrice !== null
422
+ ? `₹${currentPrice.toLocaleString('en-IN')}`
423
+ : displayPrice.calculated_price}
424
+ </span>
425
+ {originalPrice && currentPrice && originalPrice > currentPrice && (
426
+ <>
427
+ <span className="text-base sm:text-lg min-[768px]:text-lg min-[1023px]:text-lg min-[1360px]:text-lg text-gray-700 line-through">
428
+ MRP ₹{originalPrice.toLocaleString('en-IN')}
429
+ </span>
430
+ {discountPercentage && (
431
+ <span className="text-base sm:text-lg min-[768px]:text-lg min-[1023px]:text-lg min-[1360px]:text-lg font-semibold" style={{ color: '#FE5FB7' }}>
432
+ ({discountPercentage}% OFF)
433
+ </span>
434
+ )}
435
+ </>
436
+ )}
437
+ </div>
438
+
439
+ {/* Already in Bag Indicator - Premium Badge */}
440
+ {quantityInCart > 0 && (
441
+ <div className="flex items-center gap-1.5 px-3 py-1.5 bg-green-50 text-green-700 text-[11px] font-black rounded-full border border-green-100 shadow-sm animate-in fade-in zoom-in-95 duration-500 uppercase tracking-tighter">
442
+ <div className="w-4 h-4 bg-green-500 rounded-full flex items-center justify-center shadow-sm">
443
+ <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="white" strokeWidth="4" strokeLinecap="round" strokeLinejoin="round">
444
+ <polyline points="20 6 9 17 4 12"></polyline>
445
+ </svg>
446
+ </div>
447
+ <span>{quantityInCart} <span className="hidden sm:inline">ITEM{quantityInCart > 1 ? 'S' : ''}</span> IN BAG</span>
448
+ </div>
449
+ )}
450
+ </div>
451
+ <p className="text-sm sm:text-md" style={{ color: '#156229', fontWeight: 500 }}>inclusive of all taxes</p>
452
+ </div>
453
+ )}
454
+ {/* COLOR Section */}
455
+ {colorOption && (
456
+ <div className="flex flex-col gap-3">
457
+ <div className="flex items-center gap-2">
458
+ <h3 className="text-xs sm:text-sm min-[768px]:text-sm font-semibold text-gray-900 uppercase">
459
+ {colorVariants.length > 0 ? "More Colors" : `Select ${colorOption.title}`}
460
+ </h3>
461
+ {validationErrors[colorOption.id] && (
462
+ <span className="text-[10px] text-red-600 font-black uppercase tracking-wider animate-pulse border-l-2 border-red-600 pl-2">
463
+ Please Select {String(colorOption.title || "")}
464
+ </span>
465
+ )}
466
+ </div>
467
+ {colorVariants.length > 0 ? (
468
+ <div className="flex gap-2 sm:gap-3 min-[768px]:gap-3 overflow-x-auto pb-2 transition-all duration-300">
469
+ {colorVariants.map((variant) => {
470
+ const variantImage =
471
+ (variant.metadata?.image as string) ||
472
+ (variant.metadata?.thumbnail as string) ||
473
+ (variant.metadata?.color_image as string) ||
474
+ (variant.metadata?.variant_image as string) ||
475
+ variant.thumbnail ||
476
+ variant.images?.[0]?.url ||
477
+ product.thumbnail
478
+ const variantColorValue = variant.options?.find((opt: any) => opt.option_id === colorOption.id)?.value
479
+ const isSelected = variantColorValue === selectedColorValue
480
+
481
+ // Get color for the bottom strip using metadata or library
482
+ let stripColor = (variant.metadata?.color_hex as string) || null
483
+ if (!stripColor && variantColorValue) {
484
+ try {
485
+ // Try parsing the original color name (handles things like "darkgreen")
486
+ stripColor = Color(variantColorValue.toLowerCase().replace(/\s+/g, '')).hex()
487
+ } catch (e) {
488
+ try {
489
+ // Try parsing without modification
490
+ stripColor = Color(variantColorValue).hex()
491
+ } catch (e2) {
492
+ stripColor = null
493
+ }
494
+ }
495
+ }
496
+
497
+ return (
498
+ <button
499
+ key={variant.id}
500
+ className={`w-12 h-14 sm:w-16 sm:h-[4.5rem] rounded-lg overflow-hidden border-2 transition-all flex-shrink-0 flex flex-col ${isSelected
501
+ ? 'border-purple-600 ring-2 ring-purple-200'
502
+ : 'border-gray-200 hover:border-purple-400'
503
+ }`}
504
+ onClick={() => {
505
+ if (colorOption && variantColorValue) {
506
+ setOptions((prev) => ({
507
+ ...prev,
508
+ [colorOption.id]: variantColorValue,
509
+ }))
510
+ setValidationErrors((prev) => ({
511
+ ...prev,
512
+ [colorOption.id]: false,
513
+ }))
514
+ }
515
+ }}
516
+ >
517
+ <div className="flex-1 w-full relative overflow-hidden">
518
+ {variantImage && (
519
+ <Image
520
+ src={variantImage}
521
+ alt={`Color variant: ${variantColorValue || 'Unknown'}`}
522
+ fill
523
+ sizes="(max-width: 768px) 48px, 64px"
524
+ className="object-cover"
525
+ />
526
+ )}
527
+ </div>
528
+ {stripColor && (
529
+ <div
530
+ className="h-2 w-full border-t border-gray-100"
531
+ style={{ backgroundColor: stripColor }}
532
+ title={variantColorValue}
533
+ />
534
+ )}
535
+ </button>
536
+ )
537
+ })}
538
+ </div>
539
+ ) : (
540
+ <OptionSelect
541
+ option={colorOption}
542
+ current={options[colorOption.id]}
543
+ updateOption={setOptionValue}
544
+ title={colorOption.title ?? ""}
545
+ data-testid="product-options"
546
+ disabled={!!disabled || isAdding}
547
+ error={validationErrors[colorOption.id]}
548
+ />
549
+ )}
550
+ </div>
551
+ )}
552
+
553
+ {/* SIZE Section */}
554
+ {sizeOption && (
555
+ <div className="flex flex-col gap-3">
556
+ <div className="flex items-center justify-between">
557
+ <div className="flex items-center gap-2">
558
+ <h3 className="text-xs sm:text-sm min-[768px]:text-sm font-semibold text-gray-900 uppercase">Select Size</h3>
559
+ {validationErrors[sizeOption.id] && (
560
+ <span className="text-[10px] text-red-600 font-black uppercase tracking-wider animate-pulse border-l-2 border-red-600 pl-2">
561
+ Please Select {String(sizeOption.title || "")}
562
+ </span>
563
+ )}
564
+ </div>
565
+ <button
566
+ onClick={() => setActiveFeature('size_chart')}
567
+ className="flex items-center gap-1.5 text-xs sm:text-sm min-[768px]:text-sm font-medium hover:opacity-80 transition-opacity"
568
+ style={{ color: '#8B5AB1' }}
569
+ >
570
+ <Image src="/Size Chart.svg" alt="Size Chart" width={16} height={16} />
571
+ Size Chart
572
+ </button>
573
+ </div>
574
+ <OptionSelect
575
+ option={sizeOption}
576
+ current={options[sizeOption.id]}
577
+ updateOption={setOptionValue}
578
+ title={sizeOption.title ?? ""}
579
+ data-testid="product-options"
580
+ disabled={!!disabled || isAdding}
581
+ error={validationErrors[sizeOption.id]}
582
+ />
583
+ </div>
584
+ )}
585
+
586
+ {/* Other Options (if any) */}
587
+ {(product.options || []).filter(opt => {
588
+ const isColor = opt.title?.toLowerCase().includes('color') || opt.title?.toLowerCase().includes('colour')
589
+ const isSize = opt.title?.toLowerCase().includes('size')
590
+ return !isColor && !isSize
591
+ })
592
+ .sort((a, b) => (a.title || "").localeCompare(b.title || ""))
593
+ .map((option) => {
594
+ return (
595
+ <div key={option.id} className="flex flex-col gap-3">
596
+ <div className="flex items-center gap-2">
597
+ <h3 className="text-xs sm:text-sm min-[768px]:text-sm font-semibold text-gray-900 uppercase">Select {String(option.title || "")}</h3>
598
+ {validationErrors[option.id] && (
599
+ <span className="text-[10px] text-red-600 font-black uppercase tracking-wider animate-pulse border-l-2 border-red-600 pl-2">
600
+ Please Select {String(option.title || "")}
601
+ </span>
602
+ )}
603
+ </div>
604
+ <OptionSelect
605
+ option={option}
606
+ current={options[option.id]}
607
+ updateOption={setOptionValue}
608
+ title={option.title ?? ""}
609
+ data-testid="product-options"
610
+ disabled={!!disabled || isAdding}
611
+ error={validationErrors[option.id]}
612
+ />
613
+ </div>
614
+ )
615
+ })}
616
+
617
+ {/* QUANTITY Section */}
618
+ <div className="flex flex-col gap-3">
619
+ <div className="flex items-center justify-between">
620
+ <h3 className="text-xs sm:text-sm min-[768px]:text-sm font-semibold text-gray-900 uppercase">Quantity</h3>
621
+ {inventoryLimit !== null && inventoryLimit > 0 && inventoryLimit <= 5 && (
622
+ <div className="flex items-center gap-1.5 animate-pulse">
623
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#EA580C" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
624
+ <path d="M10 2h4"></path>
625
+ <path d="M12 14v-4"></path>
626
+ <path d="M4 13a8 8 0 0 1 8-7 8 8 0 1 1-8 7z"></path>
627
+ <path d="M2 9h3"></path>
628
+ <path d="M2 12h2"></path>
629
+ <path d="M2 15h3"></path>
630
+ </svg>
631
+ <span className="text-[10px] sm:text-xs font-bold uppercase tracking-wider text-orange-600">
632
+ Hurry! Only {inventoryLimit} left in stock
633
+ </span>
634
+ </div>
635
+ )}
636
+ </div>
637
+ <div className="flex items-center border border-gray-300 rounded-[30px] overflow-hidden bg-white w-[141px]">
638
+ <button
639
+ onClick={() => setQuantity(Math.max(1, quantity - 1))}
640
+ disabled={quantity <= 1}
641
+ className="w-10 h-10 flex items-center justify-center hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
642
+ >
643
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
644
+ <line x1="5" y1="12" x2="19" y2="12"></line>
645
+ </svg>
646
+ </button>
647
+ <span className="flex-1 text-center font-semibold text-gray-900 py-2">{quantity}</span>
648
+ <button
649
+ onClick={() => {
650
+ if (inventoryLimit === null || quantity < inventoryLimit) {
651
+ setQuantity(quantity + 1)
652
+ }
653
+ }}
654
+ disabled={inventoryLimit !== null && quantity >= inventoryLimit}
655
+ className="w-10 h-10 flex items-center justify-center hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
656
+ >
657
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
658
+ <line x1="12" y1="5" x2="12" y2="19"></line>
659
+ <line x1="5" y1="12" x2="19" y2="12"></line>
660
+ </svg>
661
+ </button>
662
+ </div>
663
+ {inventoryLimit !== null && quantity >= inventoryLimit && inventoryLimit > 0 && (
664
+ <p className="text-[10px] text-red-600 font-medium">Maximum available stock reached</p>
665
+ )}
666
+ </div>
667
+
668
+
669
+ {/* Action Buttons */}
670
+ <div className="flex flex-col gap-3 w-full max-w-full">
671
+ {!inStock && selectedVariant ? (
672
+ <div className="flex flex-col gap-4 w-full animate-in fade-in slide-in-from-bottom-2 duration-500">
673
+ {/* Out of Stock Status Indicator */}
674
+ <div className="flex items-center gap-3 text-[#8B5AB1] bg-[#FAF5FF] px-4 py-3 rounded-2xl border border-purple-100 shadow-sm">
675
+ <div className="flex-shrink-0 w-8 h-8 rounded-full bg-purple-100 flex items-center justify-center">
676
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
677
+ <circle cx="12" cy="12" r="10"></circle>
678
+ <line x1="4.93" y1="4.93" x2="19.07" y2="19.07"></line>
679
+ </svg>
680
+ </div>
681
+ <div className="flex flex-col">
682
+ <span className="text-xs font-black uppercase tracking-widest leading-none mb-0.5">Out of Stock</span>
683
+ <span className="text-[11px] text-purple-400 font-medium leading-none">Restocking soon for your little one! 🧸</span>
684
+ </div>
685
+ </div>
686
+
687
+ {showNotifyMessage ? (
688
+ /* Premium Success Box */
689
+ <div className="bg-[#FAF5FF] p-6 rounded-2xl border-2 border-dashed border-purple-200 flex flex-col gap-4 animate-in zoom-in-95 duration-300">
690
+ <div className="flex items-center gap-3">
691
+ <div className="w-10 h-10 rounded-full bg-purple-600 flex items-center justify-center text-white shadow-lg shadow-purple-200">
692
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round">
693
+ <polyline points="20 6 9 17 4 12"></polyline>
694
+ </svg>
695
+ </div>
696
+ <div>
697
+ <h4 className="text-sm font-bold text-purple-900 leading-none mb-1">Request Received!</h4>
698
+ <p className="text-[11px] text-purple-400 font-medium uppercase tracking-wider">Back-in-stock alert active</p>
699
+ </div>
700
+ </div>
701
+ <p className="text-sm font-medium leading-relaxed" style={{ color: '#4B2A6B' }}>
702
+ Oops! This cute ChocoMelon favorite is currently out of stock. Don't worry — we'll notify you the moment it's back for your little one! 🧸✨
703
+ </p>
704
+ <button
705
+ onClick={() => {
706
+ const relatedSection = document.getElementById('related-products')
707
+ if (relatedSection) {
708
+ relatedSection.scrollIntoView({ behavior: 'smooth' })
709
+ } else {
710
+ router.push('/store')
711
+ }
712
+ }}
713
+ className="w-full sm:w-fit py-2.5 px-6 rounded-full bg-white border border-purple-200 text-sm font-bold text-purple-600 shadow-sm hover:shadow-md hover:bg-purple-50 transition-all flex items-center justify-center gap-2 group"
714
+ >
715
+ <span>Find Similar Favorites</span>
716
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" className="group-hover:translate-x-1 transition-transform">
717
+ <polyline points="9 18 15 12 9 6"></polyline>
718
+ </svg>
719
+ </button>
720
+ </div>
721
+ ) : (
722
+ <div className="flex flex-row gap-2.5 sm:gap-3 w-full">
723
+ <button
724
+ onClick={() => setShowNotifyMessage(true)}
725
+ className="h-12 rounded-[30px] font-bold text-white flex items-center justify-center gap-1.5 sm:gap-2 transition-all w-full shadow-md hover:shadow-lg hover:scale-[1.01] active:scale-[0.99] transform px-2 sm:px-4"
726
+ style={{ backgroundColor: '#8B5AB1' }}
727
+ >
728
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" className="flex-shrink-0 sm:w-5 sm:h-5">
729
+ <path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"></path>
730
+ <path d="M13.73 21a2 2 0 0 1-3.46 0"></path>
731
+ </svg>
732
+ <span className="text-[12px] sm:text-base leading-tight truncate">
733
+ <span className="hidden sm:inline">Notify Me When Available</span>
734
+ <span className="inline sm:hidden">Notify Me</span>
735
+ </span>
736
+ </button>
737
+ </div>
738
+ )}
739
+ </div>
740
+ ) : (
741
+ <>
742
+ <div className="flex flex-row gap-3 w-full items-end">
743
+ {variantInCart ? (
744
+ /* Flipkart Style Quantity Manager Button Container */
745
+ <div className="flex flex-col gap-1.5 flex-1 min-w-0">
746
+ <div className="text-[11px] font-black text-[#8B5AB1] uppercase tracking-[0.1em] px-1 flex items-center gap-1.5 animate-in fade-in slide-in-from-left-1">
747
+ <div className="w-1.5 h-1.5 rounded-full bg-[#8B5AB1]"></div>
748
+ Manage Quantity
749
+ </div>
750
+ <div
751
+ className="h-12 rounded-[30px] font-semibold text-white flex items-center justify-between transition-all w-full overflow-hidden shadow-md"
752
+ style={{ backgroundColor: '#8B5AB1' }}
753
+ >
754
+ <button
755
+ onClick={handleDecreaseQuantity}
756
+ disabled={isAdding}
757
+ className="w-12 h-full flex items-center justify-center hover:bg-black/10 active:bg-black/20 transition-colors disabled:opacity-50"
758
+ >
759
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="white" strokeWidth="3">
760
+ <line x1="5" y1="12" x2="19" y2="12"></line>
761
+ </svg>
762
+ </button>
763
+
764
+ <div className="flex-1 flex items-center justify-center gap-1 sm:gap-2 px-1 min-w-0">
765
+ <Image src="/cart.svg" alt="Cart" width={16} height={16} className="brightness-0 invert flex-shrink-0" />
766
+ <span className="text-[14px] sm:text-[16px] font-black tracking-tighter sm:tracking-tight truncate">
767
+ <span className="inline sm:hidden">{variantInCart.quantity} IN BAG</span>
768
+ <span className="hidden sm:inline">{variantInCart.quantity} {variantInCart.quantity > 1 ? 'ITEMS' : 'ITEM'} IN BAG</span>
769
+ </span>
770
+ </div>
771
+
772
+ <button
773
+ onClick={handleIncreaseQuantity}
774
+ disabled={isAdding || !inStock}
775
+ className="w-12 h-full flex items-center justify-center hover:bg-black/10 active:bg-black/20 transition-colors disabled:opacity-50"
776
+ >
777
+ <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="white" strokeWidth="3">
778
+ <line x1="12" y1="5" x2="12" y2="19"></line>
779
+ <line x1="5" y1="12" x2="19" y2="12"></line>
780
+ </svg>
781
+ </button>
782
+ </div>
783
+ </div>
784
+ ) : (
785
+ <button
786
+ onClick={handleAddToCart}
787
+ disabled={
788
+ !inStock ||
789
+ !!disabled ||
790
+ isAdding ||
791
+ isBuyingNow ||
792
+ (!isValidVariant && product.options?.every(opt => options[opt.id]))
793
+ }
794
+ className="h-12 rounded-[30px] font-semibold text-white flex items-center justify-center gap-2 transition-colors disabled:opacity-50 disabled:cursor-not-allowed flex-1 min-w-0"
795
+ style={{ backgroundColor: '#8B5AB1' }}
796
+ onMouseEnter={(e) => {
797
+ if (!disabled && !isAdding && !isBuyingNow && inStock && (isValidVariant || !product.options?.every(opt => options[opt.id]))) {
798
+ e.currentTarget.style.backgroundColor = '#7a4a9a'
799
+ }
800
+ }}
801
+ onMouseLeave={(e) => {
802
+ if (!disabled && !isAdding && !isBuyingNow && inStock) {
803
+ e.currentTarget.style.backgroundColor = '#8B5AB1'
804
+ }
805
+ }}
806
+ data-testid="add-product-button"
807
+ data-ga-event="add_to_bag_click"
808
+ data-ga-label={product.title || "Product"}
809
+ >
810
+ {isAdding ? (
811
+ <svg className="animate-spin h-5 w-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
812
+ <circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
813
+ <path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
814
+ </svg>
815
+ ) : (
816
+ <>
817
+ <Image src="/cart.svg" alt="Cart" width={20} height={20} className="brightness-0 invert" />
818
+ {!selectedVariant || !isValidVariant
819
+ ? product.options?.every(opt => options[opt.id]) ? "Not Available" : "Select variant"
820
+ : !inStock
821
+ ? (inventoryLimit !== null && (inventoryLimit + quantityInCart) > 0 && quantity > inventoryLimit)
822
+ ? (inventoryLimit <= 0 ? "Out of stock" : "No More Stock")
823
+ : "Out of stock"
824
+ : `Add to Bag${quantity > 1 ? ` (${quantity})` : ''}`}
825
+ </>
826
+ )}
827
+ </button>
828
+ )}
829
+
830
+ <button
831
+ onClick={handleBuyNow}
832
+ disabled={
833
+ !inStock ||
834
+ !!disabled ||
835
+ isAdding ||
836
+ isBuyingNow ||
837
+ (!isValidVariant && product.options?.every(opt => options[opt.id]))
838
+ }
839
+ className="h-12 flex-1 rounded-[30px] font-semibold text-gray-900 flex items-center justify-center gap-2 transition-all hover:scale-[1.01] active:scale-[0.99] shadow-[0_2px_8px_-2px_rgba(0,0,0,0.1)] text-base group min-w-0 border border-[#8B5AB1]"
840
+ style={{
841
+ backgroundColor: '#FAF5FF',
842
+ opacity: (!inStock || !!disabled || isAdding || isBuyingNow) ? 0.6 : 1
843
+ }}
844
+ >
845
+ {isBuyingNow ? (
846
+ <svg className="animate-spin h-5 w-5" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
847
+ <circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4"></circle>
848
+ <path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
849
+ </svg>
850
+ ) : (
851
+ <>
852
+ <div className="bg-purple-50 p-1.5 rounded-full group-hover:scale-110 transition-transform border border-purple-100">
853
+ <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#8B5AB1" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
854
+ <circle cx="9" cy="21" r="1.5"></circle>
855
+ <circle cx="20" cy="21" r="1.5"></circle>
856
+ <path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6"></path>
857
+ </svg>
858
+ </div>
859
+ <span>Buy Now</span>
860
+ </>
861
+ )}
862
+ </button>
863
+ </div>
864
+ </>
865
+ )}
866
+ </div>
867
+
868
+ {/* Payment & Trust Badges - Perfectly Balanced Gap */}
869
+ <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">
870
+ <div className="flex items-center gap-3 sm:gap-4">
871
+ <Image src="/Item.svg" alt="Mastercard" width={40} height={28} className="h-6 sm:h-7 w-auto grayscale-[0.2]" />
872
+ <Image src="/Item (1).svg" alt="Amex" width={40} height={28} className="h-6 sm:h-7 w-auto grayscale-[0.2]" />
873
+ <Image src="/Item (2).svg" alt="Diners" width={40} height={28} className="h-7 sm:h-7 w-auto grayscale-[0.2]" />
874
+ <Image src="/visa.svg" alt="Visa" width={40} height={28} className="h-5 sm:h-6 w-auto grayscale-[0.2]" />
875
+ </div>
876
+ <div className="flex items-center gap-1.5 sm:gap-2 ml-2 sm:ml-0">
877
+ <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="#156229" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round">
878
+ <rect x="3" y="11" width="18" height="11" rx="2" ry="2"></rect>
879
+ <path d="M7 11V7a5 5 0 0 1 10 0v4"></path>
880
+ </svg>
881
+ <span className="text-[9px] sm:text-[11px] font-bold text-gray-700 uppercase tracking-wider whitespace-nowrap">Secure Payments</span>
882
+ </div>
883
+ </div>
884
+
885
+ {/* Pincode Checker Section */}
886
+ <div className="mt-0.5 sm:-mt-1.5 mb-0">
887
+ <PincodeChecker variantId={selectedVariant?.id} />
888
+ </div>
889
+
890
+ {/* Service Features Section */}
891
+ <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">
892
+ {/* 7 Days Easy Return */}
893
+ <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">
894
+ <div className="w-8 h-8 flex items-center justify-center">
895
+ <Image src="/Return.svg" alt="Return" width={28} height={26} />
896
+ </div>
897
+ <div className="flex items-center gap-1.5 cursor-pointer hover:opacity-80 transition-opacity" onClick={() => setActiveFeature('return')}>
898
+ <span className="text-xs text-gray-800 font-medium text-center whitespace-nowrap leading-none">7 Days Easy Return</span>
899
+ <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]">
900
+ <polyline points="9 18 15 12 9 6"></polyline>
901
+ </svg>
902
+ </div>
903
+ </div>
904
+
905
+ {/* Separator */}
906
+ <div className="hidden min-[500px]:block w-px h-12 bg-gray-200 flex-shrink-0"></div>
907
+
908
+ {/* Fast Delivery */}
909
+ <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">
910
+ <div className="w-8 h-8 flex items-center justify-center">
911
+ <Image src="/delivery.svg" alt="Delivery" width={30} height={18} />
912
+ </div>
913
+ <div className="flex items-center gap-1.5 cursor-pointer hover:opacity-80 transition-opacity" onClick={() => setActiveFeature('delivery')}>
914
+ <span className="text-xs text-gray-800 font-medium text-center whitespace-nowrap leading-none">Fast Delivery</span>
915
+ <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]">
916
+ <polyline points="9 18 15 12 9 6"></polyline>
917
+ </svg>
918
+ </div>
919
+ </div>
920
+
921
+ {/* Separator */}
922
+ <div className="hidden min-[500px]:block w-px h-12 bg-gray-200 flex-shrink-0"></div>
923
+
924
+ {/* Cash On Delivery Available */}
925
+ <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">
926
+ <div className="w-8 h-8 flex items-center justify-center">
927
+ <Image src="/Cod.svg" alt="COD" width={30} height={18} />
928
+ </div>
929
+ <div className="flex items-center gap-1.5 cursor-pointer hover:opacity-80 transition-opacity" onClick={() => setActiveFeature('cod')}>
930
+ <span className="text-xs text-gray-800 font-medium text-center whitespace-nowrap leading-none">Cash On Delivery Available</span>
931
+ <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]">
932
+ <polyline points="9 18 15 12 9 6"></polyline>
933
+ </svg>
934
+ </div>
935
+ </div>
936
+ </div>
937
+
938
+ {/* Product Highlights Section - Refined Design */}
939
+ <div className="flex flex-col gap-6 py-10 border-t border-gray-100/80 animate-in fade-in slide-in-from-bottom-4 duration-700">
940
+ <div className="flex items-center gap-4">
941
+ <div className="w-1.5 h-8 rounded-full bg-gradient-to-b from-[#8B5AB1] to-[#A885C9]"></div>
942
+ <h3 className="text-sm sm:text-base font-black text-gray-800 uppercase tracking-[0.2em]">Product highlights</h3>
943
+ </div>
944
+
945
+ <div className="grid grid-cols-2 gap-3 sm:gap-4 mt-2">
946
+ {(() => {
947
+ // Extract unique colors from variants/options instead of metadata
948
+ const colorOpt = product.options?.find(opt =>
949
+ opt.title?.toLowerCase().includes('color') ||
950
+ opt.title?.toLowerCase().includes('colour')
951
+ )
952
+ const variantColors = colorOpt?.values?.map(v => v.value).filter(Boolean) || []
953
+ const uniqueColors = Array.from(new Set(variantColors))
954
+ const colorValue = uniqueColors.length > 0 ? uniqueColors.join(', ') : null
955
+
956
+ const highlights = [
957
+ { id: 'type', label: "Type", value: product.metadata?.product_type, icon: 'M20.59 13.41l-7.17 7.17a2 2 0 01-2.83 0L2 12V2h10l8.59 8.59a2 2 0 010 2.82z' },
958
+ { id: 'occasion', label: "Occasion", value: product.metadata?.occasion, icon: 'M19 21l-7-5-7 5V5a2 2 0 012-2h10a2 2 0 012 2z' },
959
+ { id: 'material', label: "Material", value: product.metadata?.material, icon: 'M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5' },
960
+ { id: 'gender', label: "Gender", value: product.metadata?.gender, icon: 'M20 21v-2a4 4 0 00-4-4H8a4 4 0 00-4 4v2M12 11a4 4 0 100-8 4 4 0 000 8z' },
961
+ { id: 'color', label: "Available Colors", value: colorValue, icon: 'M12 2.25l-7.5 12.13a6.75 6.75 0 1015 0L12 2.25z' },
962
+ ].filter(h => h.value)
963
+
964
+ return highlights.map((highlight) => (
965
+ <div
966
+ key={highlight.id}
967
+ className={clx(
968
+ "group flex items-center gap-3 sm:gap-4 p-3 sm:p-4 rounded-2xl bg-white border border-gray-100 shadow-[0_2px_10px_-4px_rgba(0,0,0,0.05)] hover:shadow-[0_10px_25px_-5px_rgba(139,90,177,0.15)] hover:border-purple-100 hover:-translate-y-1 transition-all duration-300",
969
+ highlight.id === 'color' && "col-span-2"
970
+ )}
971
+ >
972
+ <div className="flex-shrink-0 w-10 h-10 sm:w-12 sm:h-12 rounded-xl bg-[#FAF5FF] flex items-center justify-center group-hover:bg-purple-50 transition-all duration-300 shadow-inner overflow-hidden">
973
+ {highlight.id === 'color' ? (
974
+ <svg width="28" height="28" viewBox="0 0 100 100" fill="none" xmlns="http://www.w3.org/2000/svg" className="transform group-hover:scale-110 transition-transform duration-300">
975
+ {/* Palette Body */}
976
+ <path d="M75 55c0 15-12 27-27 27S21 70 21 55s12-27 27-27 27 12 27 27z" fill="#E8B088" stroke="#333" strokeWidth="3" />
977
+
978
+ {/* Color Dots */}
979
+ <circle cx="40" cy="45" r="5" fill="#FF4D4D" stroke="#333" strokeWidth="1.5" /> {/* Red */}
980
+ <circle cx="56" cy="46" r="5" fill="#2ECC71" stroke="#333" strokeWidth="1.5" /> {/* Green */}
981
+ <circle cx="42" cy="62" r="5" fill="#3498DB" stroke="#333" strokeWidth="1.5" /> {/* Blue */}
982
+ <circle cx="58" cy="64" r="5" fill="#F06292" stroke="#333" strokeWidth="1.5" /> {/* Pink */}
983
+
984
+ {/* Brush */}
985
+ <g transform="rotate(25 70 40)">
986
+ <rect x="68" y="15" width="6" height="35" rx="3" fill="#F39C12" stroke="#333" strokeWidth="2" /> {/* Handle */}
987
+ <path d="M68 12c0-4 3-8 3-8s3 4 3 8H68z" fill="#AED6F1" stroke="#333" strokeWidth="2" /> {/* Tip */}
988
+ <rect x="68" y="45" width="6" height="4" fill="#BDC3C7" stroke="#333" strokeWidth="1.5" /> {/* Ferrule */}
989
+ </g>
990
+ </svg>
991
+ ) : (
992
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round" className="w-[18px] h-[18px] sm:w-5 sm:h-5 text-[#8B5AB1]">
993
+ <path d={highlight.icon} />
994
+ </svg>
995
+ )}
996
+ </div>
997
+ <div className="flex flex-col min-w-0 flex-1">
998
+ <span className="text-[9px] sm:text-[10px] font-black text-gray-800 uppercase tracking-widest mb-0.5 sm:mb-1">{highlight.label}</span>
999
+ <div className="flex items-center gap-2">
1000
+ {highlight.id === 'color' ? (
1001
+ <div className="flex flex-wrap gap-x-4 gap-y-1.5">
1002
+ {String(highlight.value || '').split(/[,\s]+/).map((color, i) => {
1003
+ const trimmedColor = color.trim().toLowerCase()
1004
+ if (!trimmedColor) return null
1005
+ return (
1006
+ <div key={i} className="flex items-center gap-2">
1007
+ <div
1008
+ className="w-3.5 h-3.5 rounded-full border border-gray-100 shadow-sm"
1009
+ style={{ backgroundColor: trimmedColor }}
1010
+ />
1011
+ <span className="text-sm text-gray-800 font-semibold capitalize leading-none tracking-tight">{trimmedColor}</span>
1012
+ </div>
1013
+ )
1014
+ })}
1015
+ </div>
1016
+ ) : (
1017
+ <span className="text-xs sm:text-sm text-gray-800 font-semibold capitalize tracking-tight truncate">
1018
+ {String(highlight.value || '').replace(/_/g, ' ')}
1019
+ </span>
1020
+ )}
1021
+ </div>
1022
+ </div>
1023
+ </div>
1024
+ ))
1025
+ })()}
1026
+ </div>
1027
+ </div>
1028
+
1029
+ {/* Feature Modal / Drawer */}
1030
+ <SidePanel
1031
+ isOpen={!!activeFeature}
1032
+ onClose={() => setActiveFeature(null)}
1033
+ title={
1034
+ activeFeature === 'return' ? '7 Days Easy Return' :
1035
+ activeFeature === 'delivery' ? 'Fast Delivery' :
1036
+ activeFeature === 'cod' ? 'Cash On Delivery' :
1037
+ activeFeature === 'size_chart' ? 'Size Guide' : ''
1038
+ }
1039
+ >
1040
+ {activeFeature === 'return' && (
1041
+ <div className="space-y-6">
1042
+ <div className="bg-purple-50 p-4 rounded-xl border border-purple-100">
1043
+ <p className="text-sm text-purple-900 leading-relaxed font-medium">
1044
+ We offer a hassle-free 7-day return policy for all our customers. If you're not satisfied with your purchase, we're here to help!
1045
+ </p>
1046
+ </div>
1047
+ <div className="space-y-4">
1048
+ <div className="flex gap-4">
1049
+ <div className="w-10 h-10 rounded-full bg-gray-100 flex items-center justify-center flex-shrink-0">
1050
+ <span className="font-bold text-gray-700">1</span>
1051
+ </div>
1052
+ <div>
1053
+ <h4 className="font-bold text-gray-900 mb-1">Unused Conditions</h4>
1054
+ <p className="text-sm text-gray-600">Items must be returned in their original condition, unused, and with all tags intact.</p>
1055
+ </div>
1056
+ </div>
1057
+ <div className="flex gap-4">
1058
+ <div className="w-10 h-10 rounded-full bg-gray-100 flex items-center justify-center flex-shrink-0">
1059
+ <span className="font-bold text-gray-700">2</span>
1060
+ </div>
1061
+ <div>
1062
+ <h4 className="font-bold text-gray-900 mb-1">Easy Process</h4>
1063
+ <p className="text-sm text-gray-600">Raise a return request from your account or contact our support team directly.</p>
1064
+ </div>
1065
+ </div>
1066
+ <div className="flex gap-4">
1067
+ <div className="w-10 h-10 rounded-full bg-gray-100 flex items-center justify-center flex-shrink-0">
1068
+ <span className="font-bold text-gray-700">3</span>
1069
+ </div>
1070
+ <div>
1071
+ <h4 className="font-bold text-gray-900 mb-1">Quick Refund</h4>
1072
+ <p className="text-sm text-gray-600">Once the quality check is completed, your refund will be processed within 24–48 hours. The amount may take a few business days to reflect in your account depending on your bank.</p>
1073
+ </div>
1074
+ </div>
1075
+ </div>
1076
+ </div>
1077
+ )}
1078
+
1079
+ {activeFeature === 'delivery' && (
1080
+ <div className="space-y-6">
1081
+ <div className="bg-blue-50 p-4 rounded-xl border border-blue-100">
1082
+ <p className="text-sm text-blue-900 leading-relaxed font-medium">
1083
+ We prioritize your excitement! Our fast delivery network ensures your order reaches you as soon as possible.
1084
+ </p>
1085
+ </div>
1086
+ <div className="space-y-6">
1087
+ <div>
1088
+ <h4 className="font-bold text-gray-900 mb-2 flex items-center gap-2">
1089
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" className="text-blue-500">
1090
+ <path d="M12 2v20m10-10H2"></path>
1091
+ </svg>
1092
+ Order Processing
1093
+ </h4>
1094
+ <p className="text-sm text-gray-600 pl-7">Most orders are processed and dispatched within 24 hours of placement.</p>
1095
+ </div>
1096
+ <div>
1097
+ <h4 className="font-bold text-gray-900 mb-2 flex items-center gap-2">
1098
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" className="text-blue-500">
1099
+ <circle cx="12" cy="12" r="10"></circle>
1100
+ <polyline points="12 6 12 12 16 14"></polyline>
1101
+ </svg>
1102
+ Average Timelines
1103
+ </h4>
1104
+ <ul className="text-sm text-gray-600 pl-7 space-y-2 list-disc">
1105
+ <li>Metro Cities: 2-3 business days</li>
1106
+ <li>Tier 2 Cities: 4-5 business days</li>
1107
+ <li>Rest of India: 5-7 business days</li>
1108
+ </ul>
1109
+ </div>
1110
+ <div>
1111
+ <h4 className="font-bold text-gray-900 mb-2 flex items-center gap-2">
1112
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" className="text-blue-500">
1113
+ <path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"></path>
1114
+ <circle cx="12" cy="10" r="3"></circle>
1115
+ </svg>
1116
+ Live Tracking
1117
+ </h4>
1118
+ <p className="text-sm text-gray-600 pl-7">You will receive a real-time tracking link via Email & WhatsApp once your order is shipped.</p>
1119
+ </div>
1120
+ </div>
1121
+ </div>
1122
+ )}
1123
+
1124
+ {activeFeature === 'cod' && (
1125
+ <div className="space-y-6">
1126
+ <div className="bg-green-50 p-4 rounded-xl border border-green-100">
1127
+ <p className="text-sm text-green-900 leading-relaxed font-medium">
1128
+ Shop with confidence! Pay only when you receive your package at your doorstep.
1129
+ </p>
1130
+ </div>
1131
+ <div className="space-y-5">
1132
+ <div className="flex gap-4 p-4 bg-gray-50 rounded-lg">
1133
+ <div className="w-10 h-10 flex items-center justify-center bg-[#FFFAFE] rounded-lg shadow-sm flex-shrink-0">
1134
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#22c55e" strokeWidth="2">
1135
+ <rect x="2" y="5" width="20" height="14" rx="2"></rect>
1136
+ <line x1="2" y1="10" x2="22" y2="10"></line>
1137
+ </svg>
1138
+ </div>
1139
+ <div>
1140
+ <h4 className="font-bold text-gray-900">Zero Upfront Cost</h4>
1141
+ <p className="text-xs text-gray-500">No need for credit cards or online payments to place your order.</p>
1142
+ </div>
1143
+ </div>
1144
+ <div className="flex gap-4 p-4 bg-gray-50 rounded-lg">
1145
+ <div className="w-10 h-10 flex items-center justify-center bg-[#FFFAFE] rounded-lg shadow-sm flex-shrink-0">
1146
+ <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#22c55e" strokeWidth="2">
1147
+ <path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"></path>
1148
+ </svg>
1149
+ </div>
1150
+ <div>
1151
+ <h4 className="font-bold text-gray-900">Safe & Secure</h4>
1152
+ <p className="text-xs text-gray-500">Verify your package and then pay our delivery partner directly.</p>
1153
+ </div>
1154
+ </div>
1155
+ <ul className="text-xs text-gray-500 italic space-y-1 pl-4 list-disc">
1156
+ <li>Available for orders up to ₹10,000.</li>
1157
+ <li>Please keep the exact change ready for a smoother experience.</li>
1158
+ <li>Only cash payments are accepted for COD.</li>
1159
+ </ul>
1160
+ </div>
1161
+ </div>
1162
+ )}
1163
+
1164
+ {activeFeature === 'size_chart' && (
1165
+ <div className="flex flex-col h-full">
1166
+ {/* Tip Section */}
1167
+ <div className="mb-8 bg-gradient-to-r from-purple-50 to-[#FFFAFE] p-4 rounded-xl border border-purple-100 flex gap-3 shadow-sm">
1168
+ <div className="text-xl">💡</div>
1169
+ <div className="flex-1">
1170
+ <h4 className="text-purple-900 font-bold text-sm mb-1">Pro Tip</h4>
1171
+ <p className="text-xs text-purple-700 leading-relaxed font-medium">
1172
+ Kids grow fast! If your child is between sizes, we always recommend choosing the larger size for a longer-lasting fit.
1173
+ </p>
1174
+ </div>
1175
+ </div>
1176
+
1177
+ <div className="space-y-8">
1178
+ {/* Infants Section */}
1179
+ <div>
1180
+ <div className="flex items-center gap-3 mb-4">
1181
+ <div className="w-8 h-8 rounded-full bg-purple-100 flex items-center justify-center text-[#8B5AB1]">
1182
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
1183
+ <circle cx="12" cy="12" r="10" />
1184
+ <path d="M12 16v-4" />
1185
+ <path d="M12 8h.01" />
1186
+ </svg>
1187
+ </div>
1188
+ <h4 className="font-bold text-gray-900 text-base">Infants (0-24 Months)</h4>
1189
+ </div>
1190
+
1191
+ <div className="overflow-hidden rounded-xl border border-gray-100 shadow-sm">
1192
+ <table className="w-full text-sm text-left border-collapse">
1193
+ <thead>
1194
+ <tr className="bg-gray-50 border-b border-gray-100">
1195
+ <th className="p-4 font-bold text-gray-900 text-xs uppercase tracking-wider">Age</th>
1196
+ <th className="p-4 font-bold text-gray-900 text-xs uppercase tracking-wider text-center">Height</th>
1197
+ <th className="p-4 font-bold text-gray-900 text-xs uppercase tracking-wider text-center">Weight</th>
1198
+ </tr>
1199
+ </thead>
1200
+ <tbody className="divide-y divide-gray-100 bg-[#FFFAFE]">
1201
+ {[
1202
+ { age: 'Newborn', h: '45 - 52 cm', w: '2.5 - 3.5 kg' },
1203
+ { age: '0-3 Months', h: '52 - 60 cm', w: '3.5 - 5.5 kg' },
1204
+ { age: '3-6 Months', h: '60 - 68 cm', w: '5.5 - 7.5 kg' },
1205
+ { age: '6-12 Months', h: '68 - 76 cm', w: '7.5 - 10 kg' },
1206
+ { age: '12-18 Months', h: '76 - 83 cm', w: '10 - 12 kg' },
1207
+ { age: '18-24 Months', h: '83 - 90 cm', w: '12 - 14 kg' },
1208
+ ].map((row, i) => (
1209
+ <tr key={i} className="hover:bg-purple-50/30 transition-colors">
1210
+ <td className="p-4 font-semibold text-gray-900">{row.age}</td>
1211
+ <td className="p-4 text-center text-gray-600">{row.h}</td>
1212
+ <td className="p-4 text-center text-gray-600 font-medium">{row.w}</td>
1213
+ </tr>
1214
+ ))}
1215
+ </tbody>
1216
+ </table>
1217
+ </div>
1218
+ </div>
1219
+
1220
+ {/* Toddlers Section */}
1221
+ <div>
1222
+ <div className="flex items-center gap-3 mb-4">
1223
+ <div className="w-8 h-8 rounded-full bg-purple-100 flex items-center justify-center text-[#8B5AB1]">
1224
+ <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
1225
+ <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2" />
1226
+ <circle cx="12" cy="7" r="4" />
1227
+ </svg>
1228
+ </div>
1229
+ <h4 className="font-bold text-gray-900 text-base">Toddlers (2-6 Years)</h4>
1230
+ </div>
1231
+
1232
+ <div className="overflow-hidden rounded-xl border border-gray-100 shadow-sm">
1233
+ <table className="w-full text-sm text-left border-collapse">
1234
+ <thead>
1235
+ <tr className="bg-gray-50 border-b border-gray-100">
1236
+ <th className="p-4 font-bold text-gray-900 text-xs uppercase tracking-wider">Age</th>
1237
+ <th className="p-4 font-bold text-gray-900 text-xs uppercase tracking-wider text-center">Height</th>
1238
+ <th className="p-4 font-bold text-gray-900 text-xs uppercase tracking-wider text-center">Chest</th>
1239
+ </tr>
1240
+ </thead>
1241
+ <tbody className="divide-y divide-gray-100 bg-[#FFFAFE]">
1242
+ {[
1243
+ { age: '2-3 Years', h: '90 - 98 cm', c: '52 - 54 cm' },
1244
+ { age: '3-4 Years', h: '98 - 104 cm', c: '54 - 56 cm' },
1245
+ { age: '4-5 Years', h: '104 - 110 cm', c: '56 - 58 cm' },
1246
+ { age: '5-6 Years', h: '110 - 116 cm', c: '58 - 60 cm' },
1247
+ ].map((row, i) => (
1248
+ <tr key={i} className="hover:bg-purple-50/30 transition-colors">
1249
+ <td className="p-4 font-semibold text-gray-900">{row.age}</td>
1250
+ <td className="p-4 text-center text-gray-600">{row.h}</td>
1251
+ <td className="p-4 text-center text-gray-600 font-medium">{row.c}</td>
1252
+ </tr>
1253
+ ))}
1254
+ </tbody>
1255
+ </table>
1256
+ </div>
1257
+ </div>
1258
+ </div>
1259
+ </div>
1260
+ )}
1261
+ </SidePanel>
1262
+
1263
+
1264
+
1265
+ {/* Description Section */}
1266
+ {product.description && (
1267
+ <div className="flex flex-col gap-3 border-t border-gray-200 pt-4">
1268
+ <button
1269
+ onClick={() => setIsDescriptionOpen(!isDescriptionOpen)}
1270
+ className="flex items-center justify-between w-full"
1271
+ >
1272
+ <h3 className="text-sm sm:text-base font-bold text-gray-900 uppercase tracking-wider">Description</h3>
1273
+ <svg
1274
+ width="16"
1275
+ height="16"
1276
+ viewBox="0 0 24 24"
1277
+ fill="none"
1278
+ stroke="currentColor"
1279
+ strokeWidth="2"
1280
+ className={`transition-transform ${isDescriptionOpen ? 'rotate-45' : ''}`}
1281
+ >
1282
+ <line x1="12" y1="5" x2="12" y2="19"></line>
1283
+ <line x1="5" y1="12" x2="19" y2="12"></line>
1284
+ </svg>
1285
+ </button>
1286
+ {isDescriptionOpen && (
1287
+ <div className="text-sm text-gray-800 leading-relaxed">
1288
+ <div
1289
+ className="mb-3 whitespace-pre-line"
1290
+ dangerouslySetInnerHTML={{
1291
+ __html: product.description
1292
+ ? product.description.replace(/\n/g, '<br />')
1293
+ : ''
1294
+ }}
1295
+ />
1296
+ {(() => {
1297
+ const features = product.metadata?.features
1298
+ if (!features) return null
1299
+
1300
+ if (typeof features === 'string') {
1301
+ return (
1302
+ <ul className="list-disc list-inside space-y-1 text-gray-800">
1303
+ {features.split(',').map((feature: string, index: number) => (
1304
+ <li key={index}>{feature.trim()}</li>
1305
+ ))}
1306
+ </ul>
1307
+ )
1308
+ }
1309
+
1310
+ if (Array.isArray(features)) {
1311
+ return (
1312
+ <ul className="list-disc list-inside space-y-1 text-gray-800">
1313
+ {(features as string[]).map((feature: string, index: number) => (
1314
+ <li key={index}>{feature}</li>
1315
+ ))}
1316
+ </ul>
1317
+ )
1318
+ }
1319
+
1320
+ return null
1321
+ })()}
1322
+ </div>
1323
+ )}
1324
+ </div>
1325
+ )}
1326
+
1327
+ {/* Care Guide Section */}
1328
+
1329
+ {/* Care Guide Section */}
1330
+ {product.metadata?.care_guide && (
1331
+ <div className="flex flex-col gap-3 border-t border-gray-200 pt-4">
1332
+ <button
1333
+ onClick={() => setIsCareGuideOpen(!isCareGuideOpen)}
1334
+ className="flex items-center justify-between w-full"
1335
+ >
1336
+ <h3 className="text-sm sm:text-base font-bold text-gray-900 uppercase tracking-wider">Care Guide</h3>
1337
+ <svg
1338
+ width="16"
1339
+ height="16"
1340
+ viewBox="0 0 24 24"
1341
+ fill="none"
1342
+ stroke="currentColor"
1343
+ strokeWidth="2"
1344
+ className={`transition-transform ${isCareGuideOpen ? 'rotate-45' : ''}`}
1345
+ >
1346
+ <line x1="12" y1="5" x2="12" y2="19"></line>
1347
+ <line x1="5" y1="12" x2="19" y2="12"></line>
1348
+ </svg>
1349
+ </button>
1350
+ {isCareGuideOpen && (
1351
+ <div className="text-sm text-gray-800 leading-relaxed">
1352
+ {(() => {
1353
+ const careGuide = product.metadata.care_guide
1354
+ if (!careGuide) return null
1355
+
1356
+ const careGuideStr = typeof careGuide === 'string' ? careGuide : String(careGuide)
1357
+
1358
+ // Check if it's comma-separated or newline-separated
1359
+ const items = careGuideStr.includes(',')
1360
+ ? careGuideStr.split(',').map(item => item.trim()).filter(Boolean)
1361
+ : careGuideStr.includes('\n')
1362
+ ? careGuideStr.split('\n').map(item => item.trim()).filter(Boolean)
1363
+ : [careGuideStr]
1364
+
1365
+ if (items.length > 1) {
1366
+ return (
1367
+ <ul className="list-disc list-inside space-y-1 text-gray-800">
1368
+ {items.map((item: string, index: number) => (
1369
+ <li key={index}>{item}</li>
1370
+ ))}
1371
+ </ul>
1372
+ )
1373
+ }
1374
+
1375
+ return <p>{careGuideStr}</p>
1376
+ })()}
1377
+ </div>
1378
+ )}
1379
+ </div>
1380
+ )}
1381
+
1382
+ {/* Ratings Section */}
1383
+ <div className="flex flex-col gap-3 border-t border-gray-200 pt-4">
1384
+ <h3 className="text-sm sm:text-base font-bold text-gray-900 uppercase tracking-wider">Ratings</h3>
1385
+ <div className="text-sm text-gray-800 leading-relaxed">
1386
+ <RatingsTab product={product} />
1387
+ </div>
1388
+ </div>
1389
+
1390
+
1391
+ </div>
1392
+
1393
+ <LoginPopup
1394
+ isOpen={showLoginPopup}
1395
+ onClose={() => setShowLoginPopup(false)}
1396
+ countryCode={countryCode}
1397
+ />
1398
+ </>
1399
+ )
1400
+ }