@fayz-ai/storefront 0.9.0 → 0.10.0

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 (368) hide show
  1. package/README.md +3 -3
  2. package/dist/app/createStorefrontApp.d.ts +65 -0
  3. package/dist/app/createStorefrontApp.d.ts.map +1 -0
  4. package/dist/{define.d.ts → app/define.d.ts} +3 -3
  5. package/dist/app/define.d.ts.map +1 -0
  6. package/dist/app/hydration.d.ts +37 -0
  7. package/dist/app/hydration.d.ts.map +1 -0
  8. package/dist/app/prerender.d.ts +57 -0
  9. package/dist/app/prerender.d.ts.map +1 -0
  10. package/dist/{router.d.ts → app/router.d.ts} +140 -113
  11. package/dist/app/router.d.ts.map +1 -0
  12. package/dist/{scaffold.d.ts → app/scaffold.d.ts} +2 -2
  13. package/dist/app/scaffold.d.ts.map +1 -0
  14. package/dist/app/serving.d.ts +70 -0
  15. package/dist/app/serving.d.ts.map +1 -0
  16. package/dist/app/static-render.d.ts +46 -0
  17. package/dist/app/static-render.d.ts.map +1 -0
  18. package/dist/blocks/block-at-path.d.ts +59 -0
  19. package/dist/blocks/block-at-path.d.ts.map +1 -0
  20. package/dist/blocks/page-blocks.d.ts +163 -0
  21. package/dist/blocks/page-blocks.d.ts.map +1 -0
  22. package/dist/blocks/page-data.d.ts +105 -0
  23. package/dist/blocks/page-data.d.ts.map +1 -0
  24. package/dist/{blocks.d.ts → blocks/registry.d.ts} +1 -1
  25. package/dist/blocks/registry.d.ts.map +1 -0
  26. package/dist/{sections.d.ts → blocks/sections.d.ts} +40 -2
  27. package/dist/blocks/sections.d.ts.map +1 -0
  28. package/dist/chunk-6B7H3IZ6.js +10232 -0
  29. package/dist/chunk-6B7H3IZ6.js.map +1 -0
  30. package/dist/chunk-6IXTENLB.js +162 -0
  31. package/dist/chunk-6IXTENLB.js.map +1 -0
  32. package/dist/components/PaymentTerms.d.ts +34 -0
  33. package/dist/components/PaymentTerms.d.ts.map +1 -0
  34. package/dist/components/SmoothImage.d.ts +1 -1
  35. package/dist/components/SmoothImage.d.ts.map +1 -1
  36. package/dist/components/account/CustomerAccountShell.d.ts.map +1 -0
  37. package/dist/components/account/SignInModal.d.ts.map +1 -0
  38. package/dist/components/cart/CartDrawer.d.ts.map +1 -0
  39. package/dist/components/cart/QuantityInput.d.ts.map +1 -0
  40. package/dist/components/checkout/DeliveryEstimator.d.ts.map +1 -0
  41. package/dist/components/collection/CategoryShowcase.d.ts +44 -0
  42. package/dist/components/collection/CategoryShowcase.d.ts.map +1 -0
  43. package/dist/components/collection/FiltersPanel.d.ts +19 -0
  44. package/dist/components/collection/FiltersPanel.d.ts.map +1 -0
  45. package/dist/components/collection/ProductGrid.d.ts.map +1 -0
  46. package/dist/components/layout/PageBodyBlocks.d.ts +40 -0
  47. package/dist/components/layout/PageBodyBlocks.d.ts.map +1 -0
  48. package/dist/components/layout/SearchOverlay.d.ts +13 -0
  49. package/dist/components/layout/SearchOverlay.d.ts.map +1 -0
  50. package/dist/components/layout/StorefrontErrorBoundary.d.ts.map +1 -0
  51. package/dist/components/layout/StorefrontFooter.d.ts.map +1 -0
  52. package/dist/components/layout/StorefrontHeader.d.ts.map +1 -0
  53. package/dist/components/layout/Toaster.d.ts.map +1 -0
  54. package/dist/components/marketing/CollectionSections.d.ts +463 -0
  55. package/dist/components/marketing/CollectionSections.d.ts.map +1 -0
  56. package/dist/components/marketing/ConversionSections.d.ts +221 -0
  57. package/dist/components/marketing/ConversionSections.d.ts.map +1 -0
  58. package/dist/components/marketing/EditorialSections.d.ts +248 -0
  59. package/dist/components/marketing/EditorialSections.d.ts.map +1 -0
  60. package/dist/components/marketing/EntityList.d.ts +97 -0
  61. package/dist/components/marketing/EntityList.d.ts.map +1 -0
  62. package/dist/components/marketing/FormBlock.d.ts +167 -0
  63. package/dist/components/marketing/FormBlock.d.ts.map +1 -0
  64. package/dist/components/marketing/HeroSection.d.ts +125 -0
  65. package/dist/components/marketing/HeroSection.d.ts.map +1 -0
  66. package/dist/components/marketing/MediaCarousel.d.ts +135 -0
  67. package/dist/components/marketing/MediaCarousel.d.ts.map +1 -0
  68. package/dist/components/marketing/MiscSections.d.ts +252 -0
  69. package/dist/components/marketing/MiscSections.d.ts.map +1 -0
  70. package/dist/components/order/OrderTrackingTimeline.d.ts.map +1 -0
  71. package/dist/components/product/ProductCard.d.ts +14 -0
  72. package/dist/components/product/ProductCard.d.ts.map +1 -0
  73. package/dist/components/product/ProductEnquiryForm.d.ts.map +1 -0
  74. package/dist/components/product/ProductGallery.d.ts +36 -0
  75. package/dist/components/product/ProductGallery.d.ts.map +1 -0
  76. package/dist/components/{ProductOptionSelector.d.ts → product/ProductOptionSelector.d.ts} +1 -1
  77. package/dist/components/product/ProductOptionSelector.d.ts.map +1 -0
  78. package/dist/components/product/ProductRail.d.ts +93 -0
  79. package/dist/components/product/ProductRail.d.ts.map +1 -0
  80. package/dist/components/product/ProductReviews.d.ts +39 -0
  81. package/dist/components/product/ProductReviews.d.ts.map +1 -0
  82. package/dist/components/product/ProductSlider.d.ts +169 -0
  83. package/dist/components/product/ProductSlider.d.ts.map +1 -0
  84. package/dist/components/product/ProductSpecs.d.ts.map +1 -0
  85. package/dist/components/product/RelatedProducts.d.ts.map +1 -0
  86. package/dist/{auth.d.ts → data/auth.d.ts} +9 -0
  87. package/dist/data/auth.d.ts.map +1 -0
  88. package/dist/data/facets.d.ts +39 -0
  89. package/dist/data/facets.d.ts.map +1 -0
  90. package/dist/data/loaders.d.ts +56 -0
  91. package/dist/data/loaders.d.ts.map +1 -0
  92. package/dist/data/order-status.d.ts.map +1 -0
  93. package/dist/data/product-options.d.ts +36 -0
  94. package/dist/data/product-options.d.ts.map +1 -0
  95. package/dist/diagnostics/seo-audit.d.ts +54 -0
  96. package/dist/diagnostics/seo-audit.d.ts.map +1 -0
  97. package/dist/diagnostics/seo-audit.js +3 -0
  98. package/dist/diagnostics/seo-audit.js.map +1 -0
  99. package/dist/document/change.d.ts +100 -0
  100. package/dist/document/change.d.ts.map +1 -0
  101. package/dist/{config.d.ts → document/config.d.ts} +126 -7
  102. package/dist/document/config.d.ts.map +1 -0
  103. package/dist/document/describe.d.ts +135 -0
  104. package/dist/document/describe.d.ts.map +1 -0
  105. package/dist/document/overlay.d.ts +58 -0
  106. package/dist/document/overlay.d.ts.map +1 -0
  107. package/dist/{presets.d.ts → document/presets.d.ts} +2 -2
  108. package/dist/document/presets.d.ts.map +1 -0
  109. package/dist/document/render.d.ts +171 -0
  110. package/dist/document/render.d.ts.map +1 -0
  111. package/dist/document/round-trip.d.ts +46 -0
  112. package/dist/document/round-trip.d.ts.map +1 -0
  113. package/dist/document/sections.d.ts +194 -0
  114. package/dist/document/sections.d.ts.map +1 -0
  115. package/dist/document/upgrade.d.ts +112 -0
  116. package/dist/document/upgrade.d.ts.map +1 -0
  117. package/dist/document/validate.d.ts +24 -0
  118. package/dist/document/validate.d.ts.map +1 -0
  119. package/dist/{component-contracts.d.ts → extend/component-contracts.d.ts} +74 -22
  120. package/dist/extend/component-contracts.d.ts.map +1 -0
  121. package/dist/extend/component-override.d.ts +38 -0
  122. package/dist/extend/component-override.d.ts.map +1 -0
  123. package/dist/extend/component-selectors.d.ts.map +1 -0
  124. package/dist/extend/editor-bridge.d.ts +59 -0
  125. package/dist/extend/editor-bridge.d.ts.map +1 -0
  126. package/dist/extend/extension-points.d.ts +103 -0
  127. package/dist/extend/extension-points.d.ts.map +1 -0
  128. package/dist/extend/slots.d.ts +216 -0
  129. package/dist/extend/slots.d.ts.map +1 -0
  130. package/dist/hooks/index.d.ts +25 -6
  131. package/dist/hooks/index.d.ts.map +1 -1
  132. package/dist/hooks/use-address.d.ts +26 -0
  133. package/dist/hooks/use-address.d.ts.map +1 -0
  134. package/dist/hooks/use-cart.d.ts +3 -0
  135. package/dist/hooks/use-cart.d.ts.map +1 -0
  136. package/dist/hooks/use-catalog.d.ts +24 -0
  137. package/dist/hooks/use-catalog.d.ts.map +1 -0
  138. package/dist/hooks/use-categories.d.ts +19 -0
  139. package/dist/hooks/use-categories.d.ts.map +1 -0
  140. package/dist/hooks/use-checkout.d.ts +41 -0
  141. package/dist/hooks/use-checkout.d.ts.map +1 -0
  142. package/dist/hooks/use-discount-validator.d.ts +20 -0
  143. package/dist/hooks/use-discount-validator.d.ts.map +1 -0
  144. package/dist/hooks/use-enquiry.d.ts +7 -0
  145. package/dist/hooks/use-enquiry.d.ts.map +1 -0
  146. package/dist/hooks/{useMyOrders.d.ts → use-my-orders.d.ts} +1 -1
  147. package/dist/hooks/use-my-orders.d.ts.map +1 -0
  148. package/dist/hooks/use-order.d.ts +17 -0
  149. package/dist/hooks/use-order.d.ts.map +1 -0
  150. package/dist/hooks/use-payment-methods.d.ts +19 -0
  151. package/dist/hooks/use-payment-methods.d.ts.map +1 -0
  152. package/dist/hooks/use-policies.d.ts +25 -0
  153. package/dist/hooks/use-policies.d.ts.map +1 -0
  154. package/dist/hooks/use-product.d.ts +15 -0
  155. package/dist/hooks/use-product.d.ts.map +1 -0
  156. package/dist/hooks/{useProducts.d.ts → use-products.d.ts} +1 -1
  157. package/dist/hooks/use-products.d.ts.map +1 -0
  158. package/dist/hooks/use-region.d.ts +15 -0
  159. package/dist/hooks/use-region.d.ts.map +1 -0
  160. package/dist/hooks/use-shipping.d.ts +29 -0
  161. package/dist/hooks/use-shipping.d.ts.map +1 -0
  162. package/dist/hooks/use-storefront-actions.d.ts +12 -0
  163. package/dist/hooks/use-storefront-actions.d.ts.map +1 -0
  164. package/dist/hooks/use-storefront-head.d.ts +19 -0
  165. package/dist/hooks/use-storefront-head.d.ts.map +1 -0
  166. package/dist/hooks/use-storefront.d.ts +3 -0
  167. package/dist/hooks/use-storefront.d.ts.map +1 -0
  168. package/dist/index.d.ts +123 -54
  169. package/dist/index.d.ts.map +1 -1
  170. package/dist/index.js +1311 -5012
  171. package/dist/index.js.map +1 -1
  172. package/dist/lib/format.d.ts.map +1 -0
  173. package/dist/lib/motion.d.ts.map +1 -0
  174. package/dist/lib/seo.d.ts +26 -0
  175. package/dist/lib/seo.d.ts.map +1 -0
  176. package/dist/{testids.d.ts → lib/testids.d.ts} +21 -0
  177. package/dist/lib/testids.d.ts.map +1 -0
  178. package/dist/{theme.d.ts → lib/theme.d.ts} +41 -3
  179. package/dist/lib/theme.d.ts.map +1 -0
  180. package/dist/render.d.ts +5 -0
  181. package/dist/render.d.ts.map +1 -0
  182. package/dist/render.js +359 -0
  183. package/dist/render.js.map +1 -0
  184. package/dist/stores/cart.store.d.ts +6 -2
  185. package/dist/stores/cart.store.d.ts.map +1 -1
  186. package/dist/stores/catalog.store.d.ts +11 -0
  187. package/dist/stores/catalog.store.d.ts.map +1 -1
  188. package/dist/views/CatalogPage.d.ts.map +1 -0
  189. package/dist/views/CheckoutPage.d.ts.map +1 -0
  190. package/dist/views/ContentPage.d.ts +11 -0
  191. package/dist/views/ContentPage.d.ts.map +1 -0
  192. package/dist/views/HomePage.d.ts +9 -0
  193. package/dist/views/HomePage.d.ts.map +1 -0
  194. package/dist/views/MyPurchasesPage.d.ts.map +1 -0
  195. package/dist/views/NotFoundPage.d.ts.map +1 -0
  196. package/dist/{pages → views}/OrderConfirmationPage.d.ts.map +1 -1
  197. package/dist/{pages → views}/PolicyPage.d.ts.map +1 -1
  198. package/dist/views/ProductDetailPage.d.ts.map +1 -0
  199. package/dist/views/StorefrontPage.d.ts +35 -0
  200. package/dist/views/StorefrontPage.d.ts.map +1 -0
  201. package/dist/workflows/checkout.d.ts +1 -1
  202. package/dist/workflows/checkout.d.ts.map +1 -1
  203. package/package.json +21 -14
  204. package/dist/auth.d.ts.map +0 -1
  205. package/dist/blocks.d.ts.map +0 -1
  206. package/dist/component-contracts.d.ts.map +0 -1
  207. package/dist/component-selectors.d.ts.map +0 -1
  208. package/dist/components/CartDrawer.d.ts.map +0 -1
  209. package/dist/components/CustomerAccountShell.d.ts.map +0 -1
  210. package/dist/components/DeliveryEstimator.d.ts.map +0 -1
  211. package/dist/components/FiltersPanel.d.ts +0 -3
  212. package/dist/components/FiltersPanel.d.ts.map +0 -1
  213. package/dist/components/OrderTrackingTimeline.d.ts.map +0 -1
  214. package/dist/components/ProductCard.d.ts +0 -4
  215. package/dist/components/ProductCard.d.ts.map +0 -1
  216. package/dist/components/ProductEnquiryForm.d.ts.map +0 -1
  217. package/dist/components/ProductGallery.d.ts +0 -22
  218. package/dist/components/ProductGallery.d.ts.map +0 -1
  219. package/dist/components/ProductGrid.d.ts.map +0 -1
  220. package/dist/components/ProductOptionSelector.d.ts.map +0 -1
  221. package/dist/components/ProductSpecs.d.ts.map +0 -1
  222. package/dist/components/QuantityInput.d.ts.map +0 -1
  223. package/dist/components/RelatedProducts.d.ts.map +0 -1
  224. package/dist/components/SignInModal.d.ts.map +0 -1
  225. package/dist/components/StorefrontErrorBoundary.d.ts.map +0 -1
  226. package/dist/components/StorefrontFooter.d.ts.map +0 -1
  227. package/dist/components/StorefrontHeader.d.ts.map +0 -1
  228. package/dist/components/Toaster.d.ts.map +0 -1
  229. package/dist/components/sections/CategoryShowcase.d.ts +0 -6
  230. package/dist/components/sections/CategoryShowcase.d.ts.map +0 -1
  231. package/dist/components/sections/HeroSection.d.ts +0 -9
  232. package/dist/components/sections/HeroSection.d.ts.map +0 -1
  233. package/dist/components/sections/MediaCarousel.d.ts +0 -11
  234. package/dist/components/sections/MediaCarousel.d.ts.map +0 -1
  235. package/dist/components/sections/MiscSections.d.ts +0 -33
  236. package/dist/components/sections/MiscSections.d.ts.map +0 -1
  237. package/dist/components/sections/ProductRail.d.ts +0 -9
  238. package/dist/components/sections/ProductRail.d.ts.map +0 -1
  239. package/dist/components/sections/ProductSlider.d.ts +0 -15
  240. package/dist/components/sections/ProductSlider.d.ts.map +0 -1
  241. package/dist/config.d.ts.map +0 -1
  242. package/dist/createStorefrontApp.d.ts +0 -28
  243. package/dist/createStorefrontApp.d.ts.map +0 -1
  244. package/dist/define.d.ts.map +0 -1
  245. package/dist/format.d.ts.map +0 -1
  246. package/dist/hooks/useCategories.d.ts +0 -6
  247. package/dist/hooks/useCategories.d.ts.map +0 -1
  248. package/dist/hooks/useDiscountValidator.d.ts +0 -14
  249. package/dist/hooks/useDiscountValidator.d.ts.map +0 -1
  250. package/dist/hooks/useMyOrders.d.ts.map +0 -1
  251. package/dist/hooks/useProduct.d.ts +0 -7
  252. package/dist/hooks/useProduct.d.ts.map +0 -1
  253. package/dist/hooks/useProducts.d.ts.map +0 -1
  254. package/dist/hooks/useStorefront.d.ts +0 -35
  255. package/dist/hooks/useStorefront.d.ts.map +0 -1
  256. package/dist/hooks/useStorefrontHead.d.ts +0 -11
  257. package/dist/hooks/useStorefrontHead.d.ts.map +0 -1
  258. package/dist/motion.d.ts.map +0 -1
  259. package/dist/order-status.d.ts.map +0 -1
  260. package/dist/pages/CatalogPage.d.ts.map +0 -1
  261. package/dist/pages/CheckoutPage.d.ts.map +0 -1
  262. package/dist/pages/HomePage.d.ts +0 -9
  263. package/dist/pages/HomePage.d.ts.map +0 -1
  264. package/dist/pages/MyPurchasesPage.d.ts.map +0 -1
  265. package/dist/pages/NotFoundPage.d.ts.map +0 -1
  266. package/dist/pages/ProductDetailPage.d.ts.map +0 -1
  267. package/dist/presets.d.ts.map +0 -1
  268. package/dist/product-options.d.ts +0 -12
  269. package/dist/product-options.d.ts.map +0 -1
  270. package/dist/router.d.ts.map +0 -1
  271. package/dist/scaffold.d.ts.map +0 -1
  272. package/dist/sections.d.ts.map +0 -1
  273. package/dist/testids.d.ts.map +0 -1
  274. package/dist/theme.d.ts.map +0 -1
  275. package/src/auth.ts +0 -227
  276. package/src/blocks.tsx +0 -51
  277. package/src/catalog.ts +0 -3
  278. package/src/component-contracts.ts +0 -154
  279. package/src/component-selectors.ts +0 -31
  280. package/src/components/CartDrawer.tsx +0 -279
  281. package/src/components/CustomerAccountShell.tsx +0 -102
  282. package/src/components/DeliveryEstimator.tsx +0 -157
  283. package/src/components/FiltersPanel.tsx +0 -109
  284. package/src/components/OrderTrackingTimeline.tsx +0 -135
  285. package/src/components/Price.tsx +0 -32
  286. package/src/components/ProductCard.tsx +0 -120
  287. package/src/components/ProductEnquiryForm.tsx +0 -175
  288. package/src/components/ProductGallery.tsx +0 -174
  289. package/src/components/ProductGrid.tsx +0 -43
  290. package/src/components/ProductOptionSelector.tsx +0 -50
  291. package/src/components/ProductSpecs.tsx +0 -75
  292. package/src/components/QuantityInput.tsx +0 -52
  293. package/src/components/RelatedProducts.tsx +0 -40
  294. package/src/components/SignInModal.tsx +0 -120
  295. package/src/components/SmoothImage.tsx +0 -59
  296. package/src/components/StorefrontErrorBoundary.tsx +0 -51
  297. package/src/components/StorefrontFooter.tsx +0 -110
  298. package/src/components/StorefrontHeader.tsx +0 -477
  299. package/src/components/Toaster.tsx +0 -60
  300. package/src/components/sections/CategoryShowcase.tsx +0 -80
  301. package/src/components/sections/HeroSection.tsx +0 -193
  302. package/src/components/sections/MediaCarousel.tsx +0 -141
  303. package/src/components/sections/MiscSections.tsx +0 -158
  304. package/src/components/sections/ProductRail.tsx +0 -81
  305. package/src/components/sections/ProductSlider.tsx +0 -119
  306. package/src/config.ts +0 -229
  307. package/src/createStorefrontApp.tsx +0 -247
  308. package/src/define.ts +0 -34
  309. package/src/format.ts +0 -7
  310. package/src/hooks/index.ts +0 -6
  311. package/src/hooks/useCategories.ts +0 -26
  312. package/src/hooks/useDiscountValidator.ts +0 -43
  313. package/src/hooks/useMyOrders.ts +0 -41
  314. package/src/hooks/useProduct.ts +0 -36
  315. package/src/hooks/useProducts.ts +0 -39
  316. package/src/hooks/useStorefront.ts +0 -58
  317. package/src/hooks/useStorefrontHead.ts +0 -25
  318. package/src/index.ts +0 -202
  319. package/src/motion.tsx +0 -94
  320. package/src/order-status.ts +0 -33
  321. package/src/pages/CatalogPage.tsx +0 -118
  322. package/src/pages/CheckoutPage.tsx +0 -689
  323. package/src/pages/HomePage.tsx +0 -16
  324. package/src/pages/MyPurchasesPage.tsx +0 -311
  325. package/src/pages/NotFoundPage.tsx +0 -25
  326. package/src/pages/OrderConfirmationPage.tsx +0 -190
  327. package/src/pages/PolicyPage.tsx +0 -47
  328. package/src/pages/ProductDetailPage.tsx +0 -264
  329. package/src/presets.ts +0 -324
  330. package/src/product-options.ts +0 -63
  331. package/src/router.ts +0 -49
  332. package/src/scaffold.tsx +0 -115
  333. package/src/sections.ts +0 -116
  334. package/src/shop.ts +0 -4
  335. package/src/stores/cart.store.ts +0 -185
  336. package/src/stores/catalog.store.ts +0 -39
  337. package/src/stores/delivery.store.ts +0 -152
  338. package/src/stores/session.store.ts +0 -28
  339. package/src/stores/toast.store.ts +0 -43
  340. package/src/testids.ts +0 -136
  341. package/src/theme.ts +0 -194
  342. package/src/ui.ts +0 -5
  343. package/src/workflows/checkout.ts +0 -119
  344. /package/dist/components/{CustomerAccountShell.d.ts → account/CustomerAccountShell.d.ts} +0 -0
  345. /package/dist/components/{SignInModal.d.ts → account/SignInModal.d.ts} +0 -0
  346. /package/dist/components/{CartDrawer.d.ts → cart/CartDrawer.d.ts} +0 -0
  347. /package/dist/components/{QuantityInput.d.ts → cart/QuantityInput.d.ts} +0 -0
  348. /package/dist/components/{DeliveryEstimator.d.ts → checkout/DeliveryEstimator.d.ts} +0 -0
  349. /package/dist/components/{ProductGrid.d.ts → collection/ProductGrid.d.ts} +0 -0
  350. /package/dist/components/{StorefrontErrorBoundary.d.ts → layout/StorefrontErrorBoundary.d.ts} +0 -0
  351. /package/dist/components/{StorefrontFooter.d.ts → layout/StorefrontFooter.d.ts} +0 -0
  352. /package/dist/components/{StorefrontHeader.d.ts → layout/StorefrontHeader.d.ts} +0 -0
  353. /package/dist/components/{Toaster.d.ts → layout/Toaster.d.ts} +0 -0
  354. /package/dist/components/{OrderTrackingTimeline.d.ts → order/OrderTrackingTimeline.d.ts} +0 -0
  355. /package/dist/components/{ProductEnquiryForm.d.ts → product/ProductEnquiryForm.d.ts} +0 -0
  356. /package/dist/components/{ProductSpecs.d.ts → product/ProductSpecs.d.ts} +0 -0
  357. /package/dist/components/{RelatedProducts.d.ts → product/RelatedProducts.d.ts} +0 -0
  358. /package/dist/{order-status.d.ts → data/order-status.d.ts} +0 -0
  359. /package/dist/{component-selectors.d.ts → extend/component-selectors.d.ts} +0 -0
  360. /package/dist/{format.d.ts → lib/format.d.ts} +0 -0
  361. /package/dist/{motion.d.ts → lib/motion.d.ts} +0 -0
  362. /package/dist/{pages → views}/CatalogPage.d.ts +0 -0
  363. /package/dist/{pages → views}/CheckoutPage.d.ts +0 -0
  364. /package/dist/{pages → views}/MyPurchasesPage.d.ts +0 -0
  365. /package/dist/{pages → views}/NotFoundPage.d.ts +0 -0
  366. /package/dist/{pages → views}/OrderConfirmationPage.d.ts +0 -0
  367. /package/dist/{pages → views}/PolicyPage.d.ts +0 -0
  368. /package/dist/{pages → views}/ProductDetailPage.d.ts +0 -0
@@ -1,175 +0,0 @@
1
- import React, { useState } from 'react'
2
- import { Mail, MessageCircle, Send } from 'lucide-react'
3
- import { getShopProvider } from '@fayz-ai/shop/runtime'
4
- import type { Product } from '@fayz-ai/shop/types'
5
- import { useStorefrontConfig } from '../config'
6
- import { storefrontComponentContracts } from '../component-selectors'
7
- import { TID } from '../testids'
8
-
9
- export interface ProductEnquiryFormProps {
10
- product: Product
11
- onSuccess?: () => void
12
- }
13
-
14
- function defaultSubject(prefix: string, product: Product): string {
15
- return `${prefix}: ${product.name}`
16
- }
17
-
18
- function buildWhatsAppHref(url: string, product: Product): string {
19
- const text = encodeURIComponent(`Hello, I would like more information about ${product.name}.`)
20
- const sep = url.includes('?') ? '&' : '?'
21
- return `${url}${sep}text=${text}`
22
- }
23
-
24
- export function ProductEnquiryForm({ product, onSuccess }: ProductEnquiryFormProps) {
25
- const config = useStorefrontConfig()
26
- const [customerName, setCustomerName] = useState('')
27
- const [customerEmail, setCustomerEmail] = useState('')
28
- const [customerPhone, setCustomerPhone] = useState('')
29
- const [subject, setSubject] = useState(defaultSubject(config.enquiry.subjectPrefix, product))
30
- const [message, setMessage] = useState(`I would like more information about ${product.name}.`)
31
- const [status, setStatus] = useState<'idle' | 'submitting' | 'success' | 'error'>('idle')
32
- const [error, setError] = useState<string | null>(null)
33
-
34
- const submit = async (event: React.FormEvent) => {
35
- event.preventDefault()
36
- setStatus('submitting')
37
- setError(null)
38
- try {
39
- const provider = getShopProvider()
40
- if (provider.createProductEnquiry) {
41
- await provider.createProductEnquiry({
42
- productId: product.id,
43
- productName: product.name,
44
- productSlug: product.slug,
45
- customerName,
46
- customerEmail,
47
- customerPhone: customerPhone || undefined,
48
- subject,
49
- message,
50
- sourceUrl: typeof window === 'undefined' ? undefined : window.location.href,
51
- metadata: { sku: product.sku, categoryName: product.categoryName },
52
- })
53
- } else if (config.enquiry.email) {
54
- window.location.href = `mailto:${config.enquiry.email}?subject=${encodeURIComponent(subject)}&body=${encodeURIComponent(message)}`
55
- }
56
- setStatus('success')
57
- onSuccess?.()
58
- } catch (e) {
59
- setStatus('error')
60
- setError(e instanceof Error ? e.message : String(e))
61
- }
62
- }
63
-
64
- return (
65
- <form
66
- {...storefrontComponentContracts.productDetail.enquiryForm}
67
- onSubmit={submit}
68
- className="space-y-4"
69
- >
70
- <div className="grid gap-4 sm:grid-cols-2">
71
- <label className="space-y-1.5 text-sm font-medium">
72
- <span>Your name</span>
73
- <input
74
- data-testid={TID.enquiryName}
75
- required
76
- value={customerName}
77
- onChange={(e) => setCustomerName(e.target.value)}
78
- className="w-full border bg-background px-3 py-2 outline-none focus:border-primary"
79
- style={{ borderRadius: 'var(--sf-radius-input)' }}
80
- />
81
- </label>
82
- <label className="space-y-1.5 text-sm font-medium">
83
- <span>Your email</span>
84
- <input
85
- data-testid={TID.enquiryEmail}
86
- required
87
- type="email"
88
- value={customerEmail}
89
- onChange={(e) => setCustomerEmail(e.target.value)}
90
- className="w-full border bg-background px-3 py-2 outline-none focus:border-primary"
91
- style={{ borderRadius: 'var(--sf-radius-input)' }}
92
- />
93
- </label>
94
- </div>
95
- <label className="block space-y-1.5 text-sm font-medium">
96
- <span>Phone{config.enquiry.requirePhone ? '' : ' (optional)'}</span>
97
- <input
98
- data-testid={TID.enquiryPhone}
99
- required={config.enquiry.requirePhone}
100
- value={customerPhone}
101
- onChange={(e) => setCustomerPhone(e.target.value)}
102
- className="w-full border bg-background px-3 py-2 outline-none focus:border-primary"
103
- style={{ borderRadius: 'var(--sf-radius-input)' }}
104
- />
105
- </label>
106
- <label className="block space-y-1.5 text-sm font-medium">
107
- <span>Subject</span>
108
- <input
109
- data-testid={TID.enquirySubject}
110
- required
111
- value={subject}
112
- onChange={(e) => setSubject(e.target.value)}
113
- className="w-full border bg-background px-3 py-2 outline-none focus:border-primary"
114
- style={{ borderRadius: 'var(--sf-radius-input)' }}
115
- />
116
- </label>
117
- <label className="block space-y-1.5 text-sm font-medium">
118
- <span>Message</span>
119
- <textarea
120
- data-testid={TID.enquiryMessage}
121
- required
122
- rows={5}
123
- value={message}
124
- onChange={(e) => setMessage(e.target.value)}
125
- className="w-full resize-none border bg-background px-3 py-2 outline-none focus:border-primary"
126
- style={{ borderRadius: 'var(--sf-radius-input)' }}
127
- />
128
- </label>
129
- <div className="flex flex-wrap items-center gap-3">
130
- <button
131
- data-testid={TID.enquirySubmit}
132
- type="submit"
133
- disabled={status === 'submitting'}
134
- className="sf-cta inline-flex items-center gap-2 bg-primary px-5 py-3 font-semibold text-primary-foreground disabled:cursor-wait disabled:opacity-60"
135
- style={{ borderRadius: 'var(--sf-radius-button)' }}
136
- >
137
- <Send className="h-4 w-4" />
138
- {status === 'submitting' ? 'Sending...' : 'Send enquiry'}
139
- </button>
140
- {config.enquiry.whatsappUrl && (
141
- <a
142
- href={buildWhatsAppHref(config.enquiry.whatsappUrl, product)}
143
- target="_blank"
144
- rel="noreferrer"
145
- className="inline-flex items-center gap-2 border px-5 py-3 text-sm font-semibold transition-colors hover:bg-muted"
146
- style={{ borderRadius: 'var(--sf-radius-button)' }}
147
- >
148
- <MessageCircle className="h-4 w-4" />
149
- WhatsApp
150
- </a>
151
- )}
152
- {config.enquiry.email && (
153
- <a
154
- href={`mailto:${config.enquiry.email}?subject=${encodeURIComponent(subject)}&body=${encodeURIComponent(message)}`}
155
- className="inline-flex items-center gap-2 border px-5 py-3 text-sm font-semibold transition-colors hover:bg-muted"
156
- style={{ borderRadius: 'var(--sf-radius-button)' }}
157
- >
158
- <Mail className="h-4 w-4" />
159
- Email
160
- </a>
161
- )}
162
- </div>
163
- {status === 'success' && (
164
- <p data-testid={TID.enquirySuccess} className="text-sm font-medium text-emerald-600">
165
- {config.enquiry.successMessage}
166
- </p>
167
- )}
168
- {error && (
169
- <p data-testid={TID.enquiryError} className="text-sm font-medium text-red-600">
170
- {error}
171
- </p>
172
- )}
173
- </form>
174
- )
175
- }
@@ -1,174 +0,0 @@
1
- import React, { useEffect, useRef, useState } from 'react'
2
- import type { ProductImage } from '@fayz-ai/shop/types'
3
- import type { ProductGalleryProps } from '../component-contracts'
4
- import { SmoothImage } from './SmoothImage'
5
- import { TID } from '../testids'
6
- import { storefrontComponentContracts } from '../component-selectors'
7
-
8
- /**
9
- * Product gallery: one framed image, hover-to-zoom, and a thumbnail strip when
10
- * the product has more than one photo.
11
- *
12
- * It replaces a bare <img> that rendered only the primary image. Three things
13
- * were wrong with that:
14
- *
15
- * · the frame was a grid child with the default `stretch`, so it grew to the
16
- * height of the (much taller) details column and left a slab of empty dark
17
- * box under a square photo;
18
- * · `plg_shop_product_images` has always been a collection with sort_order and
19
- * is_primary, and every extra photo a merchant uploaded was invisible;
20
- * · zooming is table stakes for anything sold by its looks — food especially.
21
- *
22
- * Zoom is pointer-driven: the transform origin follows the cursor, so the part
23
- * under the pointer is the part magnified. Disabled on coarse pointers, where
24
- * there is no hover and a stuck 2× would just be a broken image.
25
- */
26
- export function ProductGallery({ product, images, primaryImage }: ProductGalleryProps) {
27
- const ordered = orderImages(images, primaryImage)
28
- const [index, setIndex] = useState(0)
29
- const [zoom, setZoom] = useState<{ x: number; y: number } | null>(null)
30
- const frameRef = useRef<HTMLDivElement>(null)
31
- const canHover = useCanHover()
32
-
33
- // A different product (client-side navigation) must not keep the previous
34
- // product's selected slide.
35
- useEffect(() => { setIndex(0); setZoom(null) }, [product.id])
36
-
37
- const current = ordered[Math.min(index, ordered.length - 1)]
38
-
39
- function trackPointer(event: React.MouseEvent<HTMLDivElement>) {
40
- if (!canHover) return
41
- const frame = frameRef.current
42
- if (!frame) return
43
- const rect = frame.getBoundingClientRect()
44
- setZoom({
45
- x: ((event.clientX - rect.left) / rect.width) * 100,
46
- y: ((event.clientY - rect.top) / rect.height) * 100,
47
- })
48
- }
49
-
50
- function step(delta: number) {
51
- if (ordered.length < 2) return
52
- setIndex((current) => (current + delta + ordered.length) % ordered.length)
53
- setZoom(null)
54
- }
55
-
56
- return (
57
- <div className="flex flex-col gap-3 self-start">
58
- <div
59
- {...storefrontComponentContracts.productDetail.gallery}
60
- ref={frameRef}
61
- // `self-start` above and a fixed aspect here: the frame is sized by the
62
- // image, never by whatever the column beside it happens to contain.
63
- className="group relative aspect-square w-full overflow-hidden border bg-muted"
64
- style={{ borderRadius: 'var(--sf-radius-card)' }}
65
- onMouseMove={trackPointer}
66
- onMouseLeave={() => setZoom(null)}
67
- onKeyDown={(event) => {
68
- if (event.key === 'ArrowRight') step(1)
69
- if (event.key === 'ArrowLeft') step(-1)
70
- }}
71
- tabIndex={ordered.length > 1 ? 0 : -1}
72
- role={ordered.length > 1 ? 'group' : undefined}
73
- aria-label={ordered.length > 1 ? `Imagens de ${product.name}` : undefined}
74
- >
75
- {/*
76
- The transform lives on a wrapper, not on the image: SmoothImage writes
77
- its own inline `transition` for the load fade (SmoothImage.tsx:52,
78
- spread AFTER any style passed in), which would have stretched the zoom
79
- to 420ms and left it visibly lagging the cursor.
80
- */}
81
- {current && (
82
- <div
83
- className="h-full w-full transition-transform duration-150 ease-out"
84
- style={zoom
85
- ? { transform: 'scale(2)', transformOrigin: `${zoom.x}% ${zoom.y}%` }
86
- : { transform: 'scale(1)' }}
87
- >
88
- <SmoothImage
89
- key={current.id}
90
- src={current.url}
91
- alt={current.altText ?? product.name}
92
- data-testid={TID.pdpGalleryImage}
93
- className="h-full w-full object-cover"
94
- />
95
- </div>
96
- )}
97
-
98
- {ordered.length > 1 && (
99
- <>
100
- <GalleryArrow side="left" onClick={() => step(-1)} />
101
- <GalleryArrow side="right" onClick={() => step(1)} />
102
- <span className="pointer-events-none absolute bottom-3 right-3 rounded-full bg-black/60 px-2 py-0.5 text-[11px] font-medium text-white">
103
- {index + 1}/{ordered.length}
104
- </span>
105
- </>
106
- )}
107
- </div>
108
-
109
- {ordered.length > 1 && (
110
- <ul data-testid={TID.pdpGalleryThumbs} className="flex gap-2 overflow-x-auto pb-1">
111
- {ordered.map((image, position) => (
112
- <li key={image.id}>
113
- <button
114
- type="button"
115
- data-testid={TID.pdpGalleryThumb}
116
- data-index={position}
117
- aria-label={`Imagem ${position + 1} de ${ordered.length}`}
118
- aria-current={position === index}
119
- onClick={() => { setIndex(position); setZoom(null) }}
120
- className={`h-16 w-16 shrink-0 overflow-hidden rounded-lg border transition ${
121
- position === index ? 'border-primary ring-2 ring-primary/20' : 'border-border opacity-70 hover:opacity-100'
122
- }`}
123
- >
124
- <img src={image.url} alt="" className="h-full w-full object-cover" loading="lazy" />
125
- </button>
126
- </li>
127
- ))}
128
- </ul>
129
- )}
130
- </div>
131
- )
132
- }
133
-
134
- /** Primary first, then sort_order — the order the merchant set in the admin. */
135
- function orderImages(images: ProductImage[], primary?: ProductImage): ProductImage[] {
136
- const all = images.length > 0 ? images : primary ? [primary] : []
137
- return [...all].sort((a, b) => {
138
- if (a.isPrimary !== b.isPrimary) return a.isPrimary ? -1 : 1
139
- return (a.sortOrder ?? 0) - (b.sortOrder ?? 0)
140
- })
141
- }
142
-
143
- /**
144
- * Whether the device actually has a hover-capable pointer. On a phone every
145
- * element is permanently "hovered" by some emulations, which would leave the
146
- * photo stuck at 2× with no way back.
147
- */
148
- function useCanHover(): boolean {
149
- const [canHover, setCanHover] = useState(false)
150
- useEffect(() => {
151
- const query = window.matchMedia?.('(hover: hover) and (pointer: fine)')
152
- if (!query) return
153
- setCanHover(query.matches)
154
- const onChange = (event: MediaQueryListEvent) => setCanHover(event.matches)
155
- query.addEventListener?.('change', onChange)
156
- return () => query.removeEventListener?.('change', onChange)
157
- }, [])
158
- return canHover
159
- }
160
-
161
- function GalleryArrow({ side, onClick }: { side: 'left' | 'right'; onClick: () => void }) {
162
- return (
163
- <button
164
- type="button"
165
- onClick={onClick}
166
- aria-label={side === 'left' ? 'Imagem anterior' : 'Próxima imagem'}
167
- className={`absolute top-1/2 z-10 flex h-9 w-9 -translate-y-1/2 items-center justify-center rounded-full bg-background/80 opacity-0 shadow-md backdrop-blur transition hover:bg-background focus:opacity-100 group-hover:opacity-100 ${
168
- side === 'left' ? 'left-3' : 'right-3'
169
- }`}
170
- >
171
- <span aria-hidden className="text-lg leading-none">{side === 'left' ? '‹' : '›'}</span>
172
- </button>
173
- )
174
- }
@@ -1,43 +0,0 @@
1
- import React from 'react'
2
- import type { Product } from '@fayz-ai/shop/types'
3
- import { ProductCard } from './ProductCard'
4
- import { getStorefrontComponents, useStorefrontConfig } from '../config'
5
- import { useStorefrontActions } from '../hooks/useStorefront'
6
- import { TID } from '../testids'
7
-
8
- export function ProductGrid({ products, loading }: { products: Product[]; loading: boolean }) {
9
- const config = useStorefrontConfig()
10
- const actions = useStorefrontActions()
11
- const components = getStorefrontComponents(config)
12
- const ProductCardComponent = components.ProductCard ?? ProductCard
13
-
14
- if (loading) {
15
- return (
16
- <div data-testid={TID.productGrid} className="grid grid-cols-2 gap-5 md:grid-cols-3 xl:grid-cols-4">
17
- {Array.from({ length: 8 }, (_, i) => (
18
- <div key={i} className="aspect-[3/4] animate-pulse rounded-xl bg-muted" />
19
- ))}
20
- </div>
21
- )
22
- }
23
- if (products.length === 0) {
24
- return (
25
- <div data-testid={TID.productGrid} className="py-20 text-center text-muted-foreground">
26
- Nenhum produto encontrado.
27
- </div>
28
- )
29
- }
30
- return (
31
- <div data-testid={TID.productGrid} className="grid grid-cols-2 gap-5 md:grid-cols-3 xl:grid-cols-4">
32
- {products.map((p) => (
33
- <ProductCardComponent
34
- key={p.id}
35
- product={p}
36
- config={config}
37
- commerceMode={config.commerceMode}
38
- actions={actions}
39
- />
40
- ))}
41
- </div>
42
- )
43
- }
@@ -1,50 +0,0 @@
1
- import React from 'react'
2
- import type { ProductOptionGroup, ProductOptionSelection } from '../product-options'
3
- import { TID } from '../testids'
4
-
5
- export interface ProductOptionSelectorProps {
6
- groups: ProductOptionGroup[]
7
- value: ProductOptionSelection
8
- onChange: (value: ProductOptionSelection) => void
9
- }
10
-
11
- export function ProductOptionSelector({ groups, value, onChange }: ProductOptionSelectorProps) {
12
- if (groups.length === 0) return null
13
-
14
- return (
15
- <div className="mt-6 space-y-4">
16
- {groups.map((group) => (
17
- <div key={group.id} data-testid={TID.productOptionGroup(group.id)}>
18
- <div className="mb-2 flex items-center justify-between gap-3">
19
- <p className="text-sm font-medium">{group.label}</p>
20
- {value[group.label] && (
21
- <p className="truncate text-xs text-muted-foreground">{value[group.label]}</p>
22
- )}
23
- </div>
24
- <div className="flex flex-wrap gap-2">
25
- {group.values.map((option) => {
26
- const selected = value[group.label] === option
27
- return (
28
- <button
29
- key={option}
30
- type="button"
31
- data-testid={TID.productOptionValue(group.id, option)}
32
- aria-pressed={selected}
33
- onClick={() => onChange({ ...value, [group.label]: option })}
34
- className={[
35
- 'min-h-10 rounded-full border px-4 text-sm font-medium transition-colors',
36
- selected
37
- ? 'border-primary bg-primary text-primary-foreground'
38
- : 'border-border bg-background text-foreground hover:border-primary/60',
39
- ].join(' ')}
40
- >
41
- {option}
42
- </button>
43
- )
44
- })}
45
- </div>
46
- </div>
47
- ))}
48
- </div>
49
- )
50
- }
@@ -1,75 +0,0 @@
1
- import React from 'react'
2
- import type { Product } from '@fayz-ai/shop/types'
3
-
4
- // Metadata keys that drive the option selector (size/color) are excluded here so
5
- // specs don't duplicate the picker; the rest are internal SDK/seed/overlay keys
6
- // that aren't customer-facing specs.
7
- const EXCLUDED_KEYS = new Set([
8
- 'options', 'sizes', 'colors',
9
- 'fayzStoreCategory', 'source', 'storeKey', 'storeName', 'tenantId', 'slug',
10
- ])
11
-
12
- // Internal namespaced keys (fayz*, _*) are never shown as specs.
13
- function isInternalKey(key: string): boolean {
14
- return EXCLUDED_KEYS.has(key) || key.startsWith('_') || key.startsWith('fayz')
15
- }
16
-
17
- // Friendly PT-BR labels for common metadata keys; unknown keys are humanized.
18
- const LABELS: Record<string, string> = {
19
- colorway: 'Cor',
20
- color: 'Cor',
21
- fit: 'Caimento',
22
- material: 'Material',
23
- vintage: 'Safra',
24
- region: 'Região',
25
- grape: 'Uva',
26
- pairing: 'Harmonização',
27
- abv: 'Teor alcoólico',
28
- volume: 'Volume',
29
- brand: 'Marca',
30
- origin: 'Origem',
31
- warranty: 'Garantia',
32
- dimensions: 'Dimensões',
33
- }
34
-
35
- function humanize(key: string): string {
36
- if (LABELS[key]) return LABELS[key]
37
- const spaced = key.replace(/([a-z0-9])([A-Z])/g, '$1 $2').replace(/[_-]+/g, ' ').trim()
38
- return spaced.charAt(0).toUpperCase() + spaced.slice(1)
39
- }
40
-
41
- function scalarToString(value: unknown): string | null {
42
- if (typeof value === 'string') return value.trim() || null
43
- if (typeof value === 'number') return String(value)
44
- if (typeof value === 'boolean') return value ? 'Sim' : 'Não'
45
- return null
46
- }
47
-
48
- /** Real product specs from standard fields + scalar metadata. Renders nothing
49
- * when there's nothing to show. No invented data. */
50
- export function ProductSpecs({ product }: { product: Product }) {
51
- const rows: Array<[string, string]> = []
52
- if (product.sku) rows.push(['SKU', product.sku])
53
- if (product.categoryName) rows.push(['Categoria', product.categoryName])
54
- if (product.weight != null) rows.push(['Peso', `${product.weight} ${product.weightUnit}`])
55
- for (const [key, value] of Object.entries(product.metadata ?? {})) {
56
- if (isInternalKey(key)) continue
57
- const str = scalarToString(value)
58
- if (str) rows.push([humanize(key), str])
59
- }
60
- if (rows.length === 0) return null
61
-
62
- return (
63
- <section className="mt-14">
64
- <h2 className="sf-heading text-xl font-semibold tracking-tight">Especificações</h2>
65
- <dl className="mt-5 grid gap-x-12 sm:grid-cols-2">
66
- {rows.map(([label, value]) => (
67
- <div key={label} className="flex items-center justify-between gap-4 border-b py-3 text-sm">
68
- <dt className="text-muted-foreground">{label}</dt>
69
- <dd className="text-right font-medium">{value}</dd>
70
- </div>
71
- ))}
72
- </dl>
73
- </section>
74
- )
75
- }
@@ -1,52 +0,0 @@
1
- import React from 'react'
2
- import { Minus, Plus } from 'lucide-react'
3
-
4
- export interface QuantityInputProps {
5
- value: number
6
- onChange(next: number): void
7
- min?: number
8
- max?: number
9
- testId?: string
10
- incTestId?: string
11
- decTestId?: string
12
- }
13
-
14
- export function QuantityInput({ value, onChange, min = 1, max, testId, incTestId, decTestId }: QuantityInputProps) {
15
- const clamp = (n: number) => Math.max(min, max != null ? Math.min(n, max) : n)
16
- return (
17
- <div className="inline-flex items-center rounded-lg border">
18
- <button
19
- type="button"
20
- data-testid={decTestId}
21
- aria-label="Diminuir quantidade"
22
- onClick={() => onChange(clamp(value - 1))}
23
- className="px-2.5 py-1.5 text-muted-foreground hover:text-foreground disabled:opacity-40"
24
- disabled={value <= min}
25
- >
26
- <Minus className="h-3.5 w-3.5" />
27
- </button>
28
- <input
29
- data-testid={testId}
30
- type="number"
31
- value={value}
32
- min={min}
33
- max={max}
34
- onChange={(e) => {
35
- const n = parseInt(e.target.value, 10)
36
- if (!isNaN(n)) onChange(clamp(n))
37
- }}
38
- className="w-12 border-x bg-transparent py-1.5 text-center text-sm [appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none"
39
- />
40
- <button
41
- type="button"
42
- data-testid={incTestId}
43
- aria-label="Aumentar quantidade"
44
- onClick={() => onChange(clamp(value + 1))}
45
- className="px-2.5 py-1.5 text-muted-foreground hover:text-foreground disabled:opacity-40"
46
- disabled={max != null && value >= max}
47
- >
48
- <Plus className="h-3.5 w-3.5" />
49
- </button>
50
- </div>
51
- )
52
- }
@@ -1,40 +0,0 @@
1
- import React from 'react'
2
- import type { ListProductsOptions, Product } from '@fayz-ai/shop/types'
3
- import { useProducts } from '../hooks/useProducts'
4
- import { getStorefrontComponents, useStorefrontConfig } from '../config'
5
- import { useStorefrontActions } from '../hooks/useStorefront'
6
- import { ProductCard } from './ProductCard'
7
-
8
- /** "Você também pode gostar" — products from the same category, excluding the
9
- * current one. Renders through the store's configured card component for visual
10
- * consistency with the catalog. Hidden when there's nothing relevant. */
11
- export function RelatedProducts({ product }: { product: Product }) {
12
- const config = useStorefrontConfig()
13
- const actions = useStorefrontActions()
14
- const components = getStorefrontComponents(config)
15
- const Card = components.ProductCard ?? ProductCard
16
- const opts: ListProductsOptions = product.categoryId
17
- ? { categoryId: product.categoryId, status: 'active', limit: 5 }
18
- : { status: 'active', limit: 5 }
19
- const { products, loading } = useProducts(opts)
20
- const related = products.filter((p) => p.id !== product.id).slice(0, 4)
21
-
22
- if (loading || related.length === 0) return null
23
-
24
- return (
25
- <section className="mt-16">
26
- <h2 className="sf-heading text-xl font-semibold tracking-tight">Você também pode gostar</h2>
27
- <div className="mt-6 grid grid-cols-2 gap-5 md:grid-cols-4">
28
- {related.map((p) => (
29
- <Card
30
- key={p.id}
31
- product={p}
32
- config={config}
33
- commerceMode={config.commerceMode}
34
- actions={actions}
35
- />
36
- ))}
37
- </div>
38
- </section>
39
- )
40
- }