@fayz-ai/storefront 0.9.0 → 0.11.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 (391) 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-6IXTENLB.js +162 -0
  29. package/dist/chunk-6IXTENLB.js.map +1 -0
  30. package/dist/chunk-SX3J3QIT.js +11046 -0
  31. package/dist/chunk-SX3J3QIT.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/Skeleton.d.ts +17 -0
  35. package/dist/components/Skeleton.d.ts.map +1 -0
  36. package/dist/components/SmoothImage.d.ts +1 -1
  37. package/dist/components/SmoothImage.d.ts.map +1 -1
  38. package/dist/components/account/CustomerAccountShell.d.ts +17 -0
  39. package/dist/components/account/CustomerAccountShell.d.ts.map +1 -0
  40. package/dist/components/account/SignInModal.d.ts.map +1 -0
  41. package/dist/components/account/section.d.ts +15 -0
  42. package/dist/components/account/section.d.ts.map +1 -0
  43. package/dist/components/cart/CartDrawer.d.ts.map +1 -0
  44. package/dist/components/cart/QuantityInput.d.ts.map +1 -0
  45. package/dist/components/checkout/CheckoutPayment.d.ts +17 -0
  46. package/dist/components/checkout/CheckoutPayment.d.ts.map +1 -0
  47. package/dist/components/checkout/CheckoutSteps.d.ts +9 -0
  48. package/dist/components/checkout/CheckoutSteps.d.ts.map +1 -0
  49. package/dist/components/checkout/DeliveryEstimator.d.ts.map +1 -0
  50. package/dist/components/collection/CategoryShowcase.d.ts +44 -0
  51. package/dist/components/collection/CategoryShowcase.d.ts.map +1 -0
  52. package/dist/components/collection/FiltersPanel.d.ts +19 -0
  53. package/dist/components/collection/FiltersPanel.d.ts.map +1 -0
  54. package/dist/components/collection/ProductGrid.d.ts.map +1 -0
  55. package/dist/components/layout/PageBodyBlocks.d.ts +40 -0
  56. package/dist/components/layout/PageBodyBlocks.d.ts.map +1 -0
  57. package/dist/components/layout/SearchOverlay.d.ts +13 -0
  58. package/dist/components/layout/SearchOverlay.d.ts.map +1 -0
  59. package/dist/components/layout/StorefrontErrorBoundary.d.ts.map +1 -0
  60. package/dist/components/layout/StorefrontFooter.d.ts.map +1 -0
  61. package/dist/components/layout/StorefrontHeader.d.ts.map +1 -0
  62. package/dist/components/layout/Toaster.d.ts.map +1 -0
  63. package/dist/components/marketing/CollectionSections.d.ts +463 -0
  64. package/dist/components/marketing/CollectionSections.d.ts.map +1 -0
  65. package/dist/components/marketing/ConversionSections.d.ts +221 -0
  66. package/dist/components/marketing/ConversionSections.d.ts.map +1 -0
  67. package/dist/components/marketing/EditorialSections.d.ts +248 -0
  68. package/dist/components/marketing/EditorialSections.d.ts.map +1 -0
  69. package/dist/components/marketing/EntityList.d.ts +97 -0
  70. package/dist/components/marketing/EntityList.d.ts.map +1 -0
  71. package/dist/components/marketing/FormBlock.d.ts +167 -0
  72. package/dist/components/marketing/FormBlock.d.ts.map +1 -0
  73. package/dist/components/marketing/HeroSection.d.ts +125 -0
  74. package/dist/components/marketing/HeroSection.d.ts.map +1 -0
  75. package/dist/components/marketing/MediaCarousel.d.ts +135 -0
  76. package/dist/components/marketing/MediaCarousel.d.ts.map +1 -0
  77. package/dist/components/marketing/MiscSections.d.ts +252 -0
  78. package/dist/components/marketing/MiscSections.d.ts.map +1 -0
  79. package/dist/components/order/OrderTrackingTimeline.d.ts.map +1 -0
  80. package/dist/components/order/SuccessMark.d.ts +10 -0
  81. package/dist/components/order/SuccessMark.d.ts.map +1 -0
  82. package/dist/components/product/ProductCard.d.ts +14 -0
  83. package/dist/components/product/ProductCard.d.ts.map +1 -0
  84. package/dist/components/product/ProductEnquiryForm.d.ts.map +1 -0
  85. package/dist/components/product/ProductGallery.d.ts +36 -0
  86. package/dist/components/product/ProductGallery.d.ts.map +1 -0
  87. package/dist/components/{ProductOptionSelector.d.ts → product/ProductOptionSelector.d.ts} +1 -1
  88. package/dist/components/product/ProductOptionSelector.d.ts.map +1 -0
  89. package/dist/components/product/ProductRail.d.ts +93 -0
  90. package/dist/components/product/ProductRail.d.ts.map +1 -0
  91. package/dist/components/product/ProductReviews.d.ts +39 -0
  92. package/dist/components/product/ProductReviews.d.ts.map +1 -0
  93. package/dist/components/product/ProductSlider.d.ts +169 -0
  94. package/dist/components/product/ProductSlider.d.ts.map +1 -0
  95. package/dist/components/product/ProductSpecs.d.ts.map +1 -0
  96. package/dist/components/product/RelatedProducts.d.ts.map +1 -0
  97. package/dist/{auth.d.ts → data/auth.d.ts} +9 -0
  98. package/dist/data/auth.d.ts.map +1 -0
  99. package/dist/data/facets.d.ts +39 -0
  100. package/dist/data/facets.d.ts.map +1 -0
  101. package/dist/data/loaders.d.ts +52 -0
  102. package/dist/data/loaders.d.ts.map +1 -0
  103. package/dist/data/local-orders.d.ts +5 -0
  104. package/dist/data/local-orders.d.ts.map +1 -0
  105. package/dist/data/order-status.d.ts.map +1 -0
  106. package/dist/data/product-options.d.ts +36 -0
  107. package/dist/data/product-options.d.ts.map +1 -0
  108. package/dist/data/shipping-items.d.ts +20 -0
  109. package/dist/data/shipping-items.d.ts.map +1 -0
  110. package/dist/diagnostics/seo-audit.d.ts +54 -0
  111. package/dist/diagnostics/seo-audit.d.ts.map +1 -0
  112. package/dist/diagnostics/seo-audit.js +3 -0
  113. package/dist/diagnostics/seo-audit.js.map +1 -0
  114. package/dist/document/change.d.ts +100 -0
  115. package/dist/document/change.d.ts.map +1 -0
  116. package/dist/document/config.d.ts +311 -0
  117. package/dist/document/config.d.ts.map +1 -0
  118. package/dist/document/describe.d.ts +135 -0
  119. package/dist/document/describe.d.ts.map +1 -0
  120. package/dist/document/overlay.d.ts +58 -0
  121. package/dist/document/overlay.d.ts.map +1 -0
  122. package/dist/{presets.d.ts → document/presets.d.ts} +2 -2
  123. package/dist/document/presets.d.ts.map +1 -0
  124. package/dist/document/render.d.ts +171 -0
  125. package/dist/document/render.d.ts.map +1 -0
  126. package/dist/document/round-trip.d.ts +46 -0
  127. package/dist/document/round-trip.d.ts.map +1 -0
  128. package/dist/document/sections.d.ts +194 -0
  129. package/dist/document/sections.d.ts.map +1 -0
  130. package/dist/document/store-policy.d.ts +29 -0
  131. package/dist/document/store-policy.d.ts.map +1 -0
  132. package/dist/document/upgrade.d.ts +112 -0
  133. package/dist/document/upgrade.d.ts.map +1 -0
  134. package/dist/document/validate.d.ts +24 -0
  135. package/dist/document/validate.d.ts.map +1 -0
  136. package/dist/{component-contracts.d.ts → extend/component-contracts.d.ts} +74 -22
  137. package/dist/extend/component-contracts.d.ts.map +1 -0
  138. package/dist/extend/component-override.d.ts +38 -0
  139. package/dist/extend/component-override.d.ts.map +1 -0
  140. package/dist/extend/component-selectors.d.ts.map +1 -0
  141. package/dist/extend/editor-bridge.d.ts +59 -0
  142. package/dist/extend/editor-bridge.d.ts.map +1 -0
  143. package/dist/extend/extension-points.d.ts +103 -0
  144. package/dist/extend/extension-points.d.ts.map +1 -0
  145. package/dist/extend/slots.d.ts +216 -0
  146. package/dist/extend/slots.d.ts.map +1 -0
  147. package/dist/hooks/index.d.ts +25 -6
  148. package/dist/hooks/index.d.ts.map +1 -1
  149. package/dist/hooks/use-address.d.ts +26 -0
  150. package/dist/hooks/use-address.d.ts.map +1 -0
  151. package/dist/hooks/use-cart.d.ts +3 -0
  152. package/dist/hooks/use-cart.d.ts.map +1 -0
  153. package/dist/hooks/use-catalog.d.ts +24 -0
  154. package/dist/hooks/use-catalog.d.ts.map +1 -0
  155. package/dist/hooks/use-categories.d.ts +19 -0
  156. package/dist/hooks/use-categories.d.ts.map +1 -0
  157. package/dist/hooks/use-checkout.d.ts +41 -0
  158. package/dist/hooks/use-checkout.d.ts.map +1 -0
  159. package/dist/hooks/use-discount-validator.d.ts +20 -0
  160. package/dist/hooks/use-discount-validator.d.ts.map +1 -0
  161. package/dist/hooks/use-enquiry.d.ts +7 -0
  162. package/dist/hooks/use-enquiry.d.ts.map +1 -0
  163. package/dist/hooks/{useMyOrders.d.ts → use-my-orders.d.ts} +1 -1
  164. package/dist/hooks/use-my-orders.d.ts.map +1 -0
  165. package/dist/hooks/use-order.d.ts +17 -0
  166. package/dist/hooks/use-order.d.ts.map +1 -0
  167. package/dist/hooks/use-payment-methods.d.ts +19 -0
  168. package/dist/hooks/use-payment-methods.d.ts.map +1 -0
  169. package/dist/hooks/use-policies.d.ts +25 -0
  170. package/dist/hooks/use-policies.d.ts.map +1 -0
  171. package/dist/hooks/use-product.d.ts +15 -0
  172. package/dist/hooks/use-product.d.ts.map +1 -0
  173. package/dist/hooks/{useProducts.d.ts → use-products.d.ts} +1 -1
  174. package/dist/hooks/use-products.d.ts.map +1 -0
  175. package/dist/hooks/use-region.d.ts +15 -0
  176. package/dist/hooks/use-region.d.ts.map +1 -0
  177. package/dist/hooks/use-shipping.d.ts +32 -0
  178. package/dist/hooks/use-shipping.d.ts.map +1 -0
  179. package/dist/hooks/use-storefront-actions.d.ts +12 -0
  180. package/dist/hooks/use-storefront-actions.d.ts.map +1 -0
  181. package/dist/hooks/use-storefront-head.d.ts +19 -0
  182. package/dist/hooks/use-storefront-head.d.ts.map +1 -0
  183. package/dist/hooks/use-storefront.d.ts +3 -0
  184. package/dist/hooks/use-storefront.d.ts.map +1 -0
  185. package/dist/index.d.ts +124 -54
  186. package/dist/index.d.ts.map +1 -1
  187. package/dist/index.js +1310 -5010
  188. package/dist/index.js.map +1 -1
  189. package/dist/lib/errors.d.ts +9 -0
  190. package/dist/lib/errors.d.ts.map +1 -0
  191. package/dist/lib/format.d.ts.map +1 -0
  192. package/dist/lib/motion.d.ts.map +1 -0
  193. package/dist/lib/seo.d.ts +26 -0
  194. package/dist/lib/seo.d.ts.map +1 -0
  195. package/dist/{testids.d.ts → lib/testids.d.ts} +33 -0
  196. package/dist/lib/testids.d.ts.map +1 -0
  197. package/dist/{theme.d.ts → lib/theme.d.ts} +41 -3
  198. package/dist/lib/theme.d.ts.map +1 -0
  199. package/dist/render.d.ts +5 -0
  200. package/dist/render.d.ts.map +1 -0
  201. package/dist/render.js +359 -0
  202. package/dist/render.js.map +1 -0
  203. package/dist/stores/cart.store.d.ts +34 -2
  204. package/dist/stores/cart.store.d.ts.map +1 -1
  205. package/dist/stores/catalog.store.d.ts +11 -0
  206. package/dist/stores/catalog.store.d.ts.map +1 -1
  207. package/dist/stores/delivery.store.d.ts +9 -0
  208. package/dist/stores/delivery.store.d.ts.map +1 -1
  209. package/dist/views/CatalogPage.d.ts.map +1 -0
  210. package/dist/views/CheckoutPage.d.ts.map +1 -0
  211. package/dist/views/ContentPage.d.ts +11 -0
  212. package/dist/views/ContentPage.d.ts.map +1 -0
  213. package/dist/views/HomePage.d.ts +9 -0
  214. package/dist/views/HomePage.d.ts.map +1 -0
  215. package/dist/views/MyPurchasesPage.d.ts.map +1 -0
  216. package/dist/views/NotFoundPage.d.ts.map +1 -0
  217. package/dist/views/OrderConfirmationPage.d.ts.map +1 -0
  218. package/dist/{pages → views}/PolicyPage.d.ts.map +1 -1
  219. package/dist/views/ProductDetailPage.d.ts.map +1 -0
  220. package/dist/views/StorefrontPage.d.ts +35 -0
  221. package/dist/views/StorefrontPage.d.ts.map +1 -0
  222. package/dist/workflows/checkout.d.ts +1 -1
  223. package/dist/workflows/checkout.d.ts.map +1 -1
  224. package/package.json +21 -14
  225. package/dist/auth.d.ts.map +0 -1
  226. package/dist/blocks.d.ts.map +0 -1
  227. package/dist/component-contracts.d.ts.map +0 -1
  228. package/dist/component-selectors.d.ts.map +0 -1
  229. package/dist/components/CartDrawer.d.ts.map +0 -1
  230. package/dist/components/CustomerAccountShell.d.ts +0 -13
  231. package/dist/components/CustomerAccountShell.d.ts.map +0 -1
  232. package/dist/components/DeliveryEstimator.d.ts.map +0 -1
  233. package/dist/components/FiltersPanel.d.ts +0 -3
  234. package/dist/components/FiltersPanel.d.ts.map +0 -1
  235. package/dist/components/OrderTrackingTimeline.d.ts.map +0 -1
  236. package/dist/components/ProductCard.d.ts +0 -4
  237. package/dist/components/ProductCard.d.ts.map +0 -1
  238. package/dist/components/ProductEnquiryForm.d.ts.map +0 -1
  239. package/dist/components/ProductGallery.d.ts +0 -22
  240. package/dist/components/ProductGallery.d.ts.map +0 -1
  241. package/dist/components/ProductGrid.d.ts.map +0 -1
  242. package/dist/components/ProductOptionSelector.d.ts.map +0 -1
  243. package/dist/components/ProductSpecs.d.ts.map +0 -1
  244. package/dist/components/QuantityInput.d.ts.map +0 -1
  245. package/dist/components/RelatedProducts.d.ts.map +0 -1
  246. package/dist/components/SignInModal.d.ts.map +0 -1
  247. package/dist/components/StorefrontErrorBoundary.d.ts.map +0 -1
  248. package/dist/components/StorefrontFooter.d.ts.map +0 -1
  249. package/dist/components/StorefrontHeader.d.ts.map +0 -1
  250. package/dist/components/Toaster.d.ts.map +0 -1
  251. package/dist/components/sections/CategoryShowcase.d.ts +0 -6
  252. package/dist/components/sections/CategoryShowcase.d.ts.map +0 -1
  253. package/dist/components/sections/HeroSection.d.ts +0 -9
  254. package/dist/components/sections/HeroSection.d.ts.map +0 -1
  255. package/dist/components/sections/MediaCarousel.d.ts +0 -11
  256. package/dist/components/sections/MediaCarousel.d.ts.map +0 -1
  257. package/dist/components/sections/MiscSections.d.ts +0 -33
  258. package/dist/components/sections/MiscSections.d.ts.map +0 -1
  259. package/dist/components/sections/ProductRail.d.ts +0 -9
  260. package/dist/components/sections/ProductRail.d.ts.map +0 -1
  261. package/dist/components/sections/ProductSlider.d.ts +0 -15
  262. package/dist/components/sections/ProductSlider.d.ts.map +0 -1
  263. package/dist/config.d.ts +0 -174
  264. package/dist/config.d.ts.map +0 -1
  265. package/dist/createStorefrontApp.d.ts +0 -28
  266. package/dist/createStorefrontApp.d.ts.map +0 -1
  267. package/dist/define.d.ts.map +0 -1
  268. package/dist/format.d.ts.map +0 -1
  269. package/dist/hooks/useCategories.d.ts +0 -6
  270. package/dist/hooks/useCategories.d.ts.map +0 -1
  271. package/dist/hooks/useDiscountValidator.d.ts +0 -14
  272. package/dist/hooks/useDiscountValidator.d.ts.map +0 -1
  273. package/dist/hooks/useMyOrders.d.ts.map +0 -1
  274. package/dist/hooks/useProduct.d.ts +0 -7
  275. package/dist/hooks/useProduct.d.ts.map +0 -1
  276. package/dist/hooks/useProducts.d.ts.map +0 -1
  277. package/dist/hooks/useStorefront.d.ts +0 -35
  278. package/dist/hooks/useStorefront.d.ts.map +0 -1
  279. package/dist/hooks/useStorefrontHead.d.ts +0 -11
  280. package/dist/hooks/useStorefrontHead.d.ts.map +0 -1
  281. package/dist/motion.d.ts.map +0 -1
  282. package/dist/order-status.d.ts.map +0 -1
  283. package/dist/pages/CatalogPage.d.ts.map +0 -1
  284. package/dist/pages/CheckoutPage.d.ts.map +0 -1
  285. package/dist/pages/HomePage.d.ts +0 -9
  286. package/dist/pages/HomePage.d.ts.map +0 -1
  287. package/dist/pages/MyPurchasesPage.d.ts.map +0 -1
  288. package/dist/pages/NotFoundPage.d.ts.map +0 -1
  289. package/dist/pages/OrderConfirmationPage.d.ts.map +0 -1
  290. package/dist/pages/ProductDetailPage.d.ts.map +0 -1
  291. package/dist/presets.d.ts.map +0 -1
  292. package/dist/product-options.d.ts +0 -12
  293. package/dist/product-options.d.ts.map +0 -1
  294. package/dist/router.d.ts.map +0 -1
  295. package/dist/scaffold.d.ts.map +0 -1
  296. package/dist/sections.d.ts.map +0 -1
  297. package/dist/testids.d.ts.map +0 -1
  298. package/dist/theme.d.ts.map +0 -1
  299. package/src/auth.ts +0 -227
  300. package/src/blocks.tsx +0 -51
  301. package/src/catalog.ts +0 -3
  302. package/src/component-contracts.ts +0 -154
  303. package/src/component-selectors.ts +0 -31
  304. package/src/components/CartDrawer.tsx +0 -279
  305. package/src/components/CustomerAccountShell.tsx +0 -102
  306. package/src/components/DeliveryEstimator.tsx +0 -157
  307. package/src/components/FiltersPanel.tsx +0 -109
  308. package/src/components/OrderTrackingTimeline.tsx +0 -135
  309. package/src/components/Price.tsx +0 -32
  310. package/src/components/ProductCard.tsx +0 -120
  311. package/src/components/ProductEnquiryForm.tsx +0 -175
  312. package/src/components/ProductGallery.tsx +0 -174
  313. package/src/components/ProductGrid.tsx +0 -43
  314. package/src/components/ProductOptionSelector.tsx +0 -50
  315. package/src/components/ProductSpecs.tsx +0 -75
  316. package/src/components/QuantityInput.tsx +0 -52
  317. package/src/components/RelatedProducts.tsx +0 -40
  318. package/src/components/SignInModal.tsx +0 -120
  319. package/src/components/SmoothImage.tsx +0 -59
  320. package/src/components/StorefrontErrorBoundary.tsx +0 -51
  321. package/src/components/StorefrontFooter.tsx +0 -110
  322. package/src/components/StorefrontHeader.tsx +0 -477
  323. package/src/components/Toaster.tsx +0 -60
  324. package/src/components/sections/CategoryShowcase.tsx +0 -80
  325. package/src/components/sections/HeroSection.tsx +0 -193
  326. package/src/components/sections/MediaCarousel.tsx +0 -141
  327. package/src/components/sections/MiscSections.tsx +0 -158
  328. package/src/components/sections/ProductRail.tsx +0 -81
  329. package/src/components/sections/ProductSlider.tsx +0 -119
  330. package/src/config.ts +0 -229
  331. package/src/createStorefrontApp.tsx +0 -247
  332. package/src/define.ts +0 -34
  333. package/src/format.ts +0 -7
  334. package/src/hooks/index.ts +0 -6
  335. package/src/hooks/useCategories.ts +0 -26
  336. package/src/hooks/useDiscountValidator.ts +0 -43
  337. package/src/hooks/useMyOrders.ts +0 -41
  338. package/src/hooks/useProduct.ts +0 -36
  339. package/src/hooks/useProducts.ts +0 -39
  340. package/src/hooks/useStorefront.ts +0 -58
  341. package/src/hooks/useStorefrontHead.ts +0 -25
  342. package/src/index.ts +0 -202
  343. package/src/motion.tsx +0 -94
  344. package/src/order-status.ts +0 -33
  345. package/src/pages/CatalogPage.tsx +0 -118
  346. package/src/pages/CheckoutPage.tsx +0 -689
  347. package/src/pages/HomePage.tsx +0 -16
  348. package/src/pages/MyPurchasesPage.tsx +0 -311
  349. package/src/pages/NotFoundPage.tsx +0 -25
  350. package/src/pages/OrderConfirmationPage.tsx +0 -190
  351. package/src/pages/PolicyPage.tsx +0 -47
  352. package/src/pages/ProductDetailPage.tsx +0 -264
  353. package/src/presets.ts +0 -324
  354. package/src/product-options.ts +0 -63
  355. package/src/router.ts +0 -49
  356. package/src/scaffold.tsx +0 -115
  357. package/src/sections.ts +0 -116
  358. package/src/shop.ts +0 -4
  359. package/src/stores/cart.store.ts +0 -185
  360. package/src/stores/catalog.store.ts +0 -39
  361. package/src/stores/delivery.store.ts +0 -152
  362. package/src/stores/session.store.ts +0 -28
  363. package/src/stores/toast.store.ts +0 -43
  364. package/src/testids.ts +0 -136
  365. package/src/theme.ts +0 -194
  366. package/src/ui.ts +0 -5
  367. package/src/workflows/checkout.ts +0 -119
  368. /package/dist/components/{SignInModal.d.ts → account/SignInModal.d.ts} +0 -0
  369. /package/dist/components/{CartDrawer.d.ts → cart/CartDrawer.d.ts} +0 -0
  370. /package/dist/components/{QuantityInput.d.ts → cart/QuantityInput.d.ts} +0 -0
  371. /package/dist/components/{DeliveryEstimator.d.ts → checkout/DeliveryEstimator.d.ts} +0 -0
  372. /package/dist/components/{ProductGrid.d.ts → collection/ProductGrid.d.ts} +0 -0
  373. /package/dist/components/{StorefrontErrorBoundary.d.ts → layout/StorefrontErrorBoundary.d.ts} +0 -0
  374. /package/dist/components/{StorefrontFooter.d.ts → layout/StorefrontFooter.d.ts} +0 -0
  375. /package/dist/components/{StorefrontHeader.d.ts → layout/StorefrontHeader.d.ts} +0 -0
  376. /package/dist/components/{Toaster.d.ts → layout/Toaster.d.ts} +0 -0
  377. /package/dist/components/{OrderTrackingTimeline.d.ts → order/OrderTrackingTimeline.d.ts} +0 -0
  378. /package/dist/components/{ProductEnquiryForm.d.ts → product/ProductEnquiryForm.d.ts} +0 -0
  379. /package/dist/components/{ProductSpecs.d.ts → product/ProductSpecs.d.ts} +0 -0
  380. /package/dist/components/{RelatedProducts.d.ts → product/RelatedProducts.d.ts} +0 -0
  381. /package/dist/{order-status.d.ts → data/order-status.d.ts} +0 -0
  382. /package/dist/{component-selectors.d.ts → extend/component-selectors.d.ts} +0 -0
  383. /package/dist/{format.d.ts → lib/format.d.ts} +0 -0
  384. /package/dist/{motion.d.ts → lib/motion.d.ts} +0 -0
  385. /package/dist/{pages → views}/CatalogPage.d.ts +0 -0
  386. /package/dist/{pages → views}/CheckoutPage.d.ts +0 -0
  387. /package/dist/{pages → views}/MyPurchasesPage.d.ts +0 -0
  388. /package/dist/{pages → views}/NotFoundPage.d.ts +0 -0
  389. /package/dist/{pages → views}/OrderConfirmationPage.d.ts +0 -0
  390. /package/dist/{pages → views}/PolicyPage.d.ts +0 -0
  391. /package/dist/{pages → views}/ProductDetailPage.d.ts +0 -0
@@ -1,135 +0,0 @@
1
- import React from 'react'
2
- import { Check, Clock, PackageCheck, RotateCcw, Truck, XCircle } from 'lucide-react'
3
- import type { Order } from '@fayz-ai/shop/types'
4
- import { isPaid, isRefunded } from '../order-status'
5
- import { TID } from '../testids'
6
-
7
- type TrackingState = 'complete' | 'current' | 'pending' | 'cancelled'
8
-
9
- interface TrackingStep {
10
- id: string
11
- label: string
12
- description: string
13
- state: TrackingState
14
- icon: React.ComponentType<{ className?: string }>
15
- }
16
-
17
- function orderTrackingSteps(order: Order): TrackingStep[] {
18
- const received: TrackingStep = {
19
- id: 'received',
20
- label: 'Pedido recebido',
21
- description: `#${order.orderNumber}`,
22
- state: 'complete',
23
- icon: Check,
24
- }
25
-
26
- // Terminal states short-circuit the happy path.
27
- if (order.status === 'cancelled' || order.financialStatus === 'voided') {
28
- return [
29
- received,
30
- { id: 'payment', label: 'Pedido cancelado', description: 'Este pedido foi cancelado', state: 'cancelled', icon: XCircle },
31
- ]
32
- }
33
- if (isRefunded(order.financialStatus)) {
34
- return [
35
- received,
36
- { id: 'payment', label: 'Pagamento aprovado', description: 'Compra confirmada', state: 'complete', icon: Check },
37
- {
38
- id: 'refund',
39
- label: order.financialStatus === 'refunded' ? 'Reembolsado' : 'Reembolso parcial',
40
- description: 'Valor devolvido ao cliente',
41
- state: 'cancelled',
42
- icon: RotateCcw,
43
- },
44
- ]
45
- }
46
-
47
- const paid = isPaid(order.financialStatus)
48
- const partiallyFulfilled = order.fulfillmentStatus === 'partially_fulfilled'
49
- const fulfilled = order.fulfillmentStatus === 'fulfilled'
50
-
51
- return [
52
- received,
53
- {
54
- id: 'payment',
55
- label: paid ? 'Pagamento aprovado' : 'Aguardando pagamento',
56
- description: paid ? 'Compra confirmada' : 'Processando',
57
- state: paid ? 'complete' : 'current',
58
- icon: Check,
59
- },
60
- {
61
- id: 'preparing',
62
- label: 'Preparando envio',
63
- description: paid ? 'Separando produtos' : 'Começa após o pagamento',
64
- state: fulfilled || partiallyFulfilled ? 'complete' : paid ? 'current' : 'pending',
65
- icon: PackageCheck,
66
- },
67
- {
68
- id: 'delivery',
69
- label: fulfilled ? 'Pedido enviado' : partiallyFulfilled ? 'Envio parcial' : 'Entrega',
70
- description: fulfilled ? 'A caminho do cliente' : partiallyFulfilled ? 'Parte do pedido saiu' : 'Próxima etapa',
71
- state: fulfilled ? 'complete' : partiallyFulfilled ? 'current' : 'pending',
72
- icon: fulfilled || partiallyFulfilled ? Truck : Clock,
73
- },
74
- ]
75
- }
76
-
77
- const GRID_COLS: Record<number, string> = {
78
- 2: 'sm:grid-cols-2',
79
- 3: 'sm:grid-cols-3',
80
- 4: 'sm:grid-cols-4',
81
- }
82
-
83
- export function OrderTrackingTimeline({ order, compact = false }: { order: Order; compact?: boolean }) {
84
- const steps = orderTrackingSteps(order)
85
-
86
- return (
87
- <ol
88
- data-testid={TID.orderTracking}
89
- className={`grid ${compact ? 'gap-2' : 'gap-3'} ${GRID_COLS[steps.length] ?? 'sm:grid-cols-4'}`}
90
- aria-label="Acompanhamento do pedido"
91
- >
92
- {steps.map((step) => {
93
- const Icon = step.icon
94
- const cancelled = step.state === 'cancelled'
95
- const active = step.state !== 'pending'
96
- return (
97
- <li
98
- key={step.id}
99
- data-testid={TID.orderTrackingStep(step.id)}
100
- data-state={step.state}
101
- className={[
102
- 'relative min-w-0 rounded-xl border p-3',
103
- cancelled
104
- ? 'border-rose-200 bg-rose-50 text-rose-700'
105
- : active
106
- ? 'border-primary/35 bg-primary/5'
107
- : 'bg-muted/20 text-muted-foreground',
108
- compact ? 'text-xs' : 'text-sm',
109
- ].join(' ')}
110
- >
111
- <div className="flex items-start gap-2">
112
- <span
113
- className={[
114
- 'grid h-7 w-7 shrink-0 place-items-center rounded-full border',
115
- cancelled
116
- ? 'border-rose-300 bg-rose-500 text-white'
117
- : active
118
- ? 'border-primary bg-primary text-primary-foreground'
119
- : 'border-muted-foreground/30 bg-background',
120
- ].join(' ')}
121
- aria-hidden
122
- >
123
- <Icon className="h-3.5 w-3.5" />
124
- </span>
125
- <span className="min-w-0">
126
- <span className="block truncate font-semibold text-foreground">{step.label}</span>
127
- <span className="mt-0.5 block truncate text-muted-foreground">{step.description}</span>
128
- </span>
129
- </div>
130
- </li>
131
- )
132
- })}
133
- </ol>
134
- )
135
- }
@@ -1,32 +0,0 @@
1
- import React from 'react'
2
- import { formatMoney } from '../format'
3
- import { useStorefrontConfig } from '../config'
4
-
5
- export interface PriceProps {
6
- value: number
7
- compareAt?: number | null
8
- testId?: string
9
- compareTestId?: string
10
- className?: string
11
- }
12
-
13
- /** Formatted money. Always carries data-price so tests assert raw numbers, never locale strings. */
14
- export function Price({ value, compareAt, testId, compareTestId, className }: PriceProps) {
15
- const { currency, locale } = useStorefrontConfig()
16
- return (
17
- <span className={`inline-flex items-baseline gap-2 ${className ?? ''}`}>
18
- <span data-testid={testId} data-price={value.toFixed(2)} className="font-semibold">
19
- {formatMoney(value, currency, locale)}
20
- </span>
21
- {compareAt != null && compareAt > value && (
22
- <span
23
- data-testid={compareTestId}
24
- data-price={compareAt.toFixed(2)}
25
- className="text-sm text-muted-foreground line-through"
26
- >
27
- {formatMoney(compareAt, currency, locale)}
28
- </span>
29
- )}
30
- </span>
31
- )
32
- }
@@ -1,120 +0,0 @@
1
- import React from 'react'
2
- import { ShoppingBag } from 'lucide-react'
3
- import { useStorefrontConfig } from '../config'
4
- import { Link, navigateTo } from '../router'
5
- import { getProductOptionGroups } from '../product-options'
6
- import { Price } from './Price'
7
- import { TID } from '../testids'
8
- import { productCardComponentContract } from '../component-selectors'
9
- import { useStorefrontActions } from '../hooks/useStorefront'
10
- import type { ProductCardProps } from '../component-contracts'
11
- import { SmoothImage } from './SmoothImage'
12
-
13
- export function ProductCard({ product, config: providedConfig, actions: providedActions }: ProductCardProps) {
14
- const runtimeConfig = useStorefrontConfig()
15
- const runtimeActions = useStorefrontActions()
16
- const config = providedConfig ?? runtimeConfig
17
- const actions = providedActions ?? runtimeActions
18
- const hasOptions = getProductOptionGroups(product).length > 0
19
- const soldOut = product.inventoryCount <= 0
20
- const onSale = product.compareAtPrice != null && product.compareAtPrice > product.price
21
- const image = product.images.find((i) => i.isPrimary) ?? product.images[0]
22
- const cardStyle = config.theme?.productCard?.style ?? 'card'
23
- const aspect = config.theme?.productCard?.imageAspect === 'portrait' ? 'aspect-[3/4]' : 'aspect-square'
24
- const purchaseMode = config.commerceMode === 'checkout'
25
-
26
- return (
27
- <div
28
- {...productCardComponentContract.root}
29
- data-slug={product.slug}
30
- className={`group flex flex-col overflow-hidden transition-all duration-300 ${
31
- cardStyle === 'editorial'
32
- ? 'hover:-translate-y-1'
33
- : 'border bg-card hover:-translate-y-1 hover:shadow-lg'
34
- }`}
35
- style={{ borderRadius: 'var(--sf-radius-card)' }}
36
- >
37
- <Link to={`/product/${product.slug}`} className={`relative block ${aspect} overflow-hidden bg-muted`} style={{ borderRadius: cardStyle === 'editorial' ? 'var(--sf-radius-card)' : undefined }}>
38
- {image && (
39
- <SmoothImage
40
- src={image.url}
41
- alt={image.altText ?? product.name}
42
- className="h-full w-full object-cover transition-transform duration-300 group-hover:scale-105"
43
- loading="lazy"
44
- />
45
- )}
46
- <div className="absolute left-3 top-3 flex gap-2">
47
- {onSale && !soldOut && (
48
- <span
49
- data-testid={TID.badgeSale}
50
- className="rounded-full bg-red-600 px-2.5 py-0.5 text-xs font-semibold text-white"
51
- >
52
- Oferta
53
- </span>
54
- )}
55
- {soldOut && (
56
- <span
57
- data-testid={TID.badgeSoldout}
58
- className="rounded-full bg-gray-900/80 px-2.5 py-0.5 text-xs font-semibold text-white"
59
- >
60
- Esgotado
61
- </span>
62
- )}
63
- </div>
64
- </Link>
65
-
66
- <div className={`flex flex-1 flex-col gap-1 ${cardStyle === 'editorial' ? 'px-1 py-3' : 'p-4'}`}>
67
- <span className="text-xs text-muted-foreground">{product.categoryName}</span>
68
- <Link
69
- to={`/product/${product.slug}`}
70
- {...productCardComponentContract.name}
71
- className="font-medium leading-snug hover:underline"
72
- >
73
- {product.name}
74
- </Link>
75
- <div className="mt-auto flex items-center justify-between gap-3 pt-2">
76
- {purchaseMode ? (
77
- <Price
78
- value={product.price}
79
- compareAt={product.compareAtPrice}
80
- testId={productCardComponentContract.priceTestId}
81
- />
82
- ) : (
83
- <span data-testid={productCardComponentContract.priceTestId} className="text-sm text-muted-foreground">
84
- {config.commerceMode === 'enquiry' ? config.enquiry.label : 'Ver detalhes'}
85
- </span>
86
- )}
87
- <button
88
- type="button"
89
- {...productCardComponentContract.addButton}
90
- disabled={purchaseMode && soldOut}
91
- aria-label={
92
- purchaseMode
93
- ? hasOptions
94
- ? `Escolher opções de ${product.name}`
95
- : `Adicionar ${product.name} ao carrinho`
96
- : `${config.enquiry.label} ${product.name}`
97
- }
98
- onClick={() => {
99
- if (!purchaseMode) {
100
- navigateTo(`/product/${product.slug}`)
101
- return
102
- }
103
- // Products with variants (size/color) must pick an option on the PDP
104
- // — quick-adding would create an option-less line. Others add inline
105
- // (addItem auto-opens the cart drawer).
106
- if (hasOptions) {
107
- navigateTo(`/product/${product.slug}`)
108
- return
109
- }
110
- actions.addToCart(product)
111
- }}
112
- className="rounded-full border bg-background/90 p-2.5 text-foreground shadow-sm transition-all duration-200 hover:scale-110 hover:bg-primary hover:text-primary-foreground hover:shadow disabled:cursor-not-allowed disabled:opacity-40 lg:translate-y-1 lg:opacity-0 lg:group-hover:translate-y-0 lg:group-hover:opacity-100 lg:disabled:opacity-0 lg:group-hover:disabled:opacity-40"
113
- >
114
- <ShoppingBag className="h-4 w-4" />
115
- </button>
116
- </div>
117
- </div>
118
- </div>
119
- )
120
- }
@@ -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
- }