@salla.sa/twilight-theme-engine 1.0.85 → 1.0.87

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 (390) hide show
  1. package/README.md +4 -3
  2. package/bin/twilight-check-manifest.mjs +16 -0
  3. package/dist/{AddToCartForm-B5C2M4OZ.js → AddToCartForm-P644O2QL.js} +4 -4
  4. package/dist/{AddToCartForm-B5C2M4OZ.js.map → AddToCartForm-P644O2QL.js.map} +1 -1
  5. package/dist/{BlogAuthorPage-GTF4UGSS.js → BlogAuthorPage-VWSO6MPY.js} +13 -11
  6. package/dist/BlogAuthorPage-VWSO6MPY.js.map +1 -0
  7. package/dist/{BlogCategoryPage-TN7Y4NBP.js → BlogCategoryPage-K2KSQ6T6.js} +13 -11
  8. package/dist/BlogCategoryPage-K2KSQ6T6.js.map +1 -0
  9. package/dist/{BlogPage-TDB3M3PI.js → BlogPage-XRM7VWOZ.js} +12 -10
  10. package/dist/BlogPage-XRM7VWOZ.js.map +1 -0
  11. package/dist/{BlogSinglePage-4A52Q5QW.js → BlogSinglePage-XIAXT54H.js} +12 -10
  12. package/dist/BlogSinglePage-XIAXT54H.js.map +1 -0
  13. package/dist/{BlogTagPage-TOVPBETA.js → BlogTagPage-IHOA267V.js} +13 -11
  14. package/dist/BlogTagPage-IHOA267V.js.map +1 -0
  15. package/dist/{BrandsPage-VUDGYSQI.js → BrandsPage-ZFEILRT3.js} +12 -10
  16. package/dist/BrandsPage-ZFEILRT3.js.map +1 -0
  17. package/dist/{Breadcrumb-CI3SN5YR.js → Breadcrumb-CVSISL2K.js} +12 -10
  18. package/dist/Breadcrumb-CVSISL2K.js.map +1 -0
  19. package/dist/{CartItem-BHLEURAB.js → CartItem-EOI7P3JA.js} +12 -10
  20. package/dist/CartItem-EOI7P3JA.js.map +1 -0
  21. package/dist/{CartSummary-QERVESYN.js → CartSummary-RZGUBDKJ.js} +12 -10
  22. package/dist/CartSummary-RZGUBDKJ.js.map +1 -0
  23. package/dist/{Copyright-RL6ALKRC.js → Copyright-5BLYSP24.js} +4 -4
  24. package/dist/{Copyright-RL6ALKRC.js.map → Copyright-5BLYSP24.js.map} +1 -1
  25. package/dist/{CustomerLayout-VRZUWGQK.js → CustomerLayout-6F4BJAMU.js} +12 -10
  26. package/dist/CustomerLayout-6F4BJAMU.js.map +1 -0
  27. package/dist/{Footer-ZWBZCMDL.js → Footer-NF2NLDLR.js} +6 -6
  28. package/dist/{Footer-ZWBZCMDL.js.map → Footer-NF2NLDLR.js.map} +1 -1
  29. package/dist/{Header-WNK4O66B.js → Header-6DQJ5X7J.js} +9 -7
  30. package/dist/{Header-WNK4O66B.js.map → Header-6DQJ5X7J.js.map} +1 -1
  31. package/dist/{LoyaltyPage-CGTHWJ76.js → LoyaltyPage-YHWHIWCS.js} +12 -10
  32. package/dist/LoyaltyPage-YHWHIWCS.js.map +1 -0
  33. package/dist/{LoyaltyPointsPage-QSU6K3PW.js → LoyaltyPointsPage-T6K3H5A5.js} +10 -3
  34. package/dist/LoyaltyPointsPage-T6K3H5A5.js.map +1 -0
  35. package/dist/{MainMenu-V3T7F32R.js → MainMenu-Y2TAM7DK.js} +4 -4
  36. package/dist/{MainMenu-V3T7F32R.js.map → MainMenu-Y2TAM7DK.js.map} +1 -1
  37. package/dist/{MasterLayout-XQF644LD.js → MasterLayout-GBEEFFPG.js} +12 -10
  38. package/dist/{MasterLayout-XQF644LD.js.map → MasterLayout-GBEEFFPG.js.map} +1 -1
  39. package/dist/{MobileMenu-U4BVQYKH.js → MobileMenu-RE5XVGZL.js} +12 -10
  40. package/dist/MobileMenu-RE5XVGZL.js.map +1 -0
  41. package/dist/{NotificationsPage-ZRRQY7TF.js → NotificationsPage-QAFWZAS3.js} +12 -10
  42. package/dist/NotificationsPage-QAFWZAS3.js.map +1 -0
  43. package/dist/{OrderSinglePage-O3XYVMG5.js → OrderSinglePage-T4PRXTKG.js} +12 -10
  44. package/dist/OrderSinglePage-T4PRXTKG.js.map +1 -0
  45. package/dist/{OrdersPage-CMXZF5MI.js → OrdersPage-3S6ZS56E.js} +12 -10
  46. package/dist/OrdersPage-3S6ZS56E.js.map +1 -0
  47. package/dist/{PageSingle-IIG2US43.js → PageSingle-W3UTXDOW.js} +4 -4
  48. package/dist/{PageSingle-IIG2US43.js.map → PageSingle-W3UTXDOW.js.map} +1 -1
  49. package/dist/{ProductDetails-65GDABI7.js → ProductDetails-X5A7TG3F.js} +14 -7
  50. package/dist/ProductDetails-X5A7TG3F.js.map +1 -0
  51. package/dist/{ProfilePage-SNJ4WXU4.js → ProfilePage-DUH3AILJ.js} +12 -10
  52. package/dist/ProfilePage-DUH3AILJ.js.map +1 -0
  53. package/dist/{ReviewsPage-UT7UT55J.js → ReviewsPage-2C2P7VS3.js} +4 -4
  54. package/dist/{ReviewsPage-UT7UT55J.js.map → ReviewsPage-2C2P7VS3.js.map} +1 -1
  55. package/dist/{SeoCartWidget-DBJAXJKT.js → SeoCartWidget-VYLGEW2I.js} +12 -10
  56. package/dist/SeoCartWidget-VYLGEW2I.js.map +1 -0
  57. package/dist/{SettingsPage-FLTIOPU5.js → SettingsPage-OZJEW6CW.js} +12 -10
  58. package/dist/SettingsPage-OZJEW6CW.js.map +1 -0
  59. package/dist/{StoreClosedNotification-ZT3QKDKS.js → StoreClosedNotification-QHIZBNXO.js} +13 -11
  60. package/dist/{StoreClosedNotification-ZT3QKDKS.js.map → StoreClosedNotification-QHIZBNXO.js.map} +1 -1
  61. package/dist/{TestimonialsPage-CU7HKU4O.js → TestimonialsPage-K6WJKKME.js} +4 -4
  62. package/dist/{TestimonialsPage-CU7HKU4O.js.map → TestimonialsPage-K6WJKKME.js.map} +1 -1
  63. package/dist/{ThankYouPage-5X6B4X3T.js → ThankYouPage-H6QQEI5R.js} +12 -10
  64. package/dist/ThankYouPage-H6QQEI5R.js.map +1 -0
  65. package/dist/{WalletPage-H2BKIDPA.js → WalletPage-G5IBGN6B.js} +12 -10
  66. package/dist/WalletPage-G5IBGN6B.js.map +1 -0
  67. package/dist/{WishlistPage-RS5UCIQ3.js → WishlistPage-AIOTAB6L.js} +12 -10
  68. package/dist/WishlistPage-AIOTAB6L.js.map +1 -0
  69. package/dist/api/advertisement.d.ts +33 -0
  70. package/dist/api/advertisement.d.ts.map +1 -0
  71. package/dist/api/advertisement.js +8 -0
  72. package/dist/api/advertisement.js.map +1 -0
  73. package/dist/boot/critical-style.d.ts +3 -0
  74. package/dist/boot/critical-style.d.ts.map +1 -0
  75. package/dist/boot/index.d.ts +15 -0
  76. package/dist/boot/index.d.ts.map +1 -0
  77. package/dist/boot.d.ts +1 -0
  78. package/dist/boot.js +69 -0
  79. package/dist/boot.js.map +1 -0
  80. package/dist/chunk-4FJGZQD3.js +6 -0
  81. package/dist/chunk-4FJGZQD3.js.map +1 -0
  82. package/dist/{chunk-SDFDS4Q3.js → chunk-BCJTF3V7.js} +20 -4
  83. package/dist/chunk-BCJTF3V7.js.map +1 -0
  84. package/dist/chunk-DBW7XXNE.js +103 -0
  85. package/dist/chunk-DBW7XXNE.js.map +1 -0
  86. package/dist/{chunk-22SH7OVF.js → chunk-E7WWO3V3.js} +3 -3
  87. package/dist/{chunk-22SH7OVF.js.map → chunk-E7WWO3V3.js.map} +1 -1
  88. package/dist/{chunk-SE5JYWOK.js → chunk-EZHXVHXY.js} +4 -4
  89. package/dist/{chunk-SE5JYWOK.js.map → chunk-EZHXVHXY.js.map} +1 -1
  90. package/dist/{chunk-JO7JWV34.js → chunk-FWYKOQV5.js} +5 -5
  91. package/dist/chunk-FWYKOQV5.js.map +1 -0
  92. package/dist/{chunk-JRNH3FUF.js → chunk-FYHF62KI.js} +4 -4
  93. package/dist/{chunk-JRNH3FUF.js.map → chunk-FYHF62KI.js.map} +1 -1
  94. package/dist/{chunk-UJKFBZFJ.js → chunk-GJ5IMPNY.js} +7 -7
  95. package/dist/{chunk-UJKFBZFJ.js.map → chunk-GJ5IMPNY.js.map} +1 -1
  96. package/dist/chunk-IVBOLREE.js +29 -0
  97. package/dist/chunk-IVBOLREE.js.map +1 -0
  98. package/dist/chunk-LWPKBVIE.js +14 -0
  99. package/dist/{chunk-7WAXN2PP.js.map → chunk-LWPKBVIE.js.map} +1 -1
  100. package/dist/{chunk-SX5MF2YA.js → chunk-N3BCZIGS.js} +4 -4
  101. package/dist/{chunk-SX5MF2YA.js.map → chunk-N3BCZIGS.js.map} +1 -1
  102. package/dist/{chunk-NZVR4OF7.js → chunk-NXQZ4RLM.js} +8 -8
  103. package/dist/chunk-NXQZ4RLM.js.map +1 -0
  104. package/dist/{chunk-NXQJA7D3.js → chunk-PCURRKIH.js} +3 -3
  105. package/dist/{chunk-NXQJA7D3.js.map → chunk-PCURRKIH.js.map} +1 -1
  106. package/dist/{chunk-4CZRUAZB.js → chunk-PU2LHFDQ.js} +10 -11
  107. package/dist/chunk-PU2LHFDQ.js.map +1 -0
  108. package/dist/{chunk-OWX2CPNQ.js → chunk-UQIVX4YA.js} +3 -3
  109. package/dist/{chunk-OWX2CPNQ.js.map → chunk-UQIVX4YA.js.map} +1 -1
  110. package/dist/chunk-X5NK4TZH.js +228 -0
  111. package/dist/chunk-X5NK4TZH.js.map +1 -0
  112. package/dist/{chunk-6SWDSRIL.js → chunk-XKNXYQ2K.js} +6 -6
  113. package/dist/{chunk-6SWDSRIL.js.map → chunk-XKNXYQ2K.js.map} +1 -1
  114. package/dist/{chunk-KHBSGD2D.js → chunk-XO7B3VWS.js} +3 -3
  115. package/dist/{chunk-KHBSGD2D.js.map → chunk-XO7B3VWS.js.map} +1 -1
  116. package/dist/cli/check-manifest.d.ts +10 -0
  117. package/dist/cli/check-manifest.d.ts.map +1 -0
  118. package/dist/cli/check-manifest.js +44 -0
  119. package/dist/cli/check-manifest.js.map +1 -0
  120. package/dist/components/cart/index.js +1 -1
  121. package/dist/components/common/index.js +2 -2
  122. package/dist/components/drawer/index.js +10 -8
  123. package/dist/components/home/index.js +12 -10
  124. package/dist/components/layout/Footer.d.ts.map +1 -1
  125. package/dist/components/layout/MasterLayout.d.ts.map +1 -1
  126. package/dist/components/layout/PageAdvertisements.d.ts +7 -0
  127. package/dist/components/layout/PageAdvertisements.d.ts.map +1 -0
  128. package/dist/components/layout/index.d.ts +1 -0
  129. package/dist/components/layout/index.d.ts.map +1 -1
  130. package/dist/components/layout/index.js +8 -1
  131. package/dist/components/product/index.js +11 -9
  132. package/dist/components/store/index.js +12 -10
  133. package/dist/components/store/index.js.map +1 -1
  134. package/dist/{document-class-67HTP45G.js → document-class-FRL7JHWD.js} +6 -5
  135. package/dist/{document-class-67HTP45G.js.map → document-class-FRL7JHWD.js.map} +1 -1
  136. package/dist/hooks/index.js +10 -8
  137. package/dist/hooks/useGtm.js +10 -8
  138. package/dist/hooks/useMoney.js +10 -8
  139. package/dist/hooks/useStore.js +10 -8
  140. package/dist/index.js +10 -8
  141. package/dist/index.js.map +1 -1
  142. package/dist/{not-found-QTHBFPNT.js → not-found-E6AR4CZ4.js} +12 -10
  143. package/dist/not-found-E6AR4CZ4.js.map +1 -0
  144. package/dist/providers/index.js +10 -8
  145. package/dist/routes/account/orders.js +1 -1
  146. package/dist/routes/account.js +6 -6
  147. package/dist/routes/blog.js +5 -5
  148. package/dist/routes/brands.js +1 -1
  149. package/dist/routes/cart.js +14 -12
  150. package/dist/routes/cart.js.map +1 -1
  151. package/dist/routes/home.js +13 -11
  152. package/dist/routes/loyalty.js +2 -2
  153. package/dist/routes/page.js +1 -1
  154. package/dist/routes/product-listing.js +12 -10
  155. package/dist/routes/product-listing.js.map +1 -1
  156. package/dist/routes/product.js +12 -10
  157. package/dist/routes/product.js.map +1 -1
  158. package/dist/routes/root/beforeLoad.d.ts +2 -2
  159. package/dist/routes/root/beforeLoad.d.ts.map +1 -1
  160. package/dist/routes/root/head.d.ts.map +1 -1
  161. package/dist/routes/testimonials.js +2 -2
  162. package/dist/routes/thank-you.js +1 -1
  163. package/dist/skeleton.js +13 -11
  164. package/dist/tanstack/document-class.d.ts.map +1 -1
  165. package/dist/tanstack/index.d.ts +1 -0
  166. package/dist/tanstack/index.d.ts.map +1 -1
  167. package/dist/tanstack/stylesheet.d.ts +15 -0
  168. package/dist/tanstack/stylesheet.d.ts.map +1 -0
  169. package/dist/tanstack.js +33 -4
  170. package/dist/tanstack.js.map +1 -1
  171. package/dist/utils/index.js +2 -2
  172. package/dist/vite/index.d.ts +1 -1
  173. package/dist/vite/index.d.ts.map +1 -1
  174. package/dist/vite/index.js +14 -25
  175. package/dist/vite/index.js.map +1 -1
  176. package/dist/vite/plugins/twilight-manifest.d.ts +23 -0
  177. package/dist/vite/plugins/twilight-manifest.d.ts.map +1 -1
  178. package/dist/vite/plugins/twilight-schema.plugin.d.ts.map +1 -1
  179. package/package.json +16 -3
  180. package/tailwind/components/Component.tsx.txt +21 -0
  181. package/tailwind/components/ComponentRegistry.ts.txt +66 -0
  182. package/tailwind/components/cart/CartItem.tsx.txt +211 -0
  183. package/tailwind/components/cart/CartSummary.tsx.txt +269 -0
  184. package/tailwind/components/cart/PreOrderNotification.tsx.txt +16 -0
  185. package/tailwind/components/cart/PriceQuoteButton.tsx.txt +21 -0
  186. package/tailwind/components/cart/SeoCartWidget.tsx.txt +99 -0
  187. package/tailwind/components/cart/index.ts.txt +11 -0
  188. package/tailwind/components/cart/lazy.ts.txt +15 -0
  189. package/tailwind/components/collapse/Collapse.tsx.txt +95 -0
  190. package/tailwind/components/collapse/index.ts.txt +41 -0
  191. package/tailwind/components/common/Breadcrumb.tsx.txt +27 -0
  192. package/tailwind/components/common/BreadcrumbJsonLd.tsx.txt +40 -0
  193. package/tailwind/components/common/BreadcrumbTrail.tsx.txt +97 -0
  194. package/tailwind/components/common/CurrencySymbol.tsx.txt +8 -0
  195. package/tailwind/components/common/DeferredData.tsx.txt +122 -0
  196. package/tailwind/components/common/ErrorPage.tsx.txt +69 -0
  197. package/tailwind/components/common/Image.tsx.txt +119 -0
  198. package/tailwind/components/common/Link.tsx.txt +36 -0
  199. package/tailwind/components/common/Link.types.ts.txt +85 -0
  200. package/tailwind/components/common/NoContent.tsx.txt +27 -0
  201. package/tailwind/components/common/RenderWhenVisible.tsx.txt +92 -0
  202. package/tailwind/components/common/index.ts.txt +42 -0
  203. package/tailwind/components/common/lazy.ts.txt +28 -0
  204. package/tailwind/components/defineComponent.ts.txt +32 -0
  205. package/tailwind/components/drawer/Drawer.tsx.txt +219 -0
  206. package/tailwind/components/drawer/index.ts.txt +38 -0
  207. package/tailwind/components/dropdown/Dropdown.tsx.txt +166 -0
  208. package/tailwind/components/dropdown/index.ts.txt +40 -0
  209. package/tailwind/components/home/BundleComponent.tsx.txt +35 -0
  210. package/tailwind/components/home/ComponentErrorBoundary.tsx.txt +98 -0
  211. package/tailwind/components/home/FeaturedProductsStyle1.tsx.txt +125 -0
  212. package/tailwind/components/home/FeaturedProductsStyle2.tsx.txt +127 -0
  213. package/tailwind/components/home/FeaturedProductsStyle3.tsx.txt +71 -0
  214. package/tailwind/components/home/FixedBanner.tsx.txt +68 -0
  215. package/tailwind/components/home/FixedProducts.tsx.txt +72 -0
  216. package/tailwind/components/home/HomePageRenderer.tsx.txt +284 -0
  217. package/tailwind/components/home/PageBlocks.tsx.txt +24 -0
  218. package/tailwind/components/home/ParallaxBackground.tsx.txt +50 -0
  219. package/tailwind/components/home/PhotosSlider.tsx.txt +61 -0
  220. package/tailwind/components/home/ProductsSlider.tsx.txt +87 -0
  221. package/tailwind/components/home/ReorderProducts.tsx.txt +38 -0
  222. package/tailwind/components/home/SquarePhotos.tsx.txt +68 -0
  223. package/tailwind/components/home/StoreFeatures.tsx.txt +34 -0
  224. package/tailwind/components/home/Testimonials.tsx.txt +36 -0
  225. package/tailwind/components/home/Youtube.tsx.txt +48 -0
  226. package/tailwind/components/home/index.ts.txt +50 -0
  227. package/tailwind/components/home/lazy.ts.txt +17 -0
  228. package/tailwind/components/home/register.ts.txt +44 -0
  229. package/tailwind/components/home/reorder-hooks.ts.txt +52 -0
  230. package/tailwind/components/home/types.ts.txt +4 -0
  231. package/tailwind/components/layout/Copyright.tsx.txt +23 -0
  232. package/tailwind/components/layout/CustomerLayout.tsx.txt +91 -0
  233. package/tailwind/components/layout/Footer.tsx.txt +341 -0
  234. package/tailwind/components/layout/Header.tsx.txt +154 -0
  235. package/tailwind/components/layout/MasterLayout.tsx.txt +62 -0
  236. package/tailwind/components/layout/PageAdvertisements.tsx.txt +131 -0
  237. package/tailwind/components/layout/WidgetHead.tsx.txt +53 -0
  238. package/tailwind/components/layout/index.ts.txt +27 -0
  239. package/tailwind/components/layout/lazy.ts.txt +25 -0
  240. package/tailwind/components/layout/types.ts.txt +11 -0
  241. package/tailwind/components/modal/ImageModal.tsx.txt +70 -0
  242. package/tailwind/components/modal/Modal.tsx.txt +245 -0
  243. package/tailwind/components/modal/index.ts.txt +39 -0
  244. package/tailwind/components/modal/lazy.ts.txt +10 -0
  245. package/tailwind/components/navigation/MainMenu.tsx.txt +94 -0
  246. package/tailwind/components/navigation/MobileMenu.tsx.txt +160 -0
  247. package/tailwind/components/navigation/index.ts.txt +4 -0
  248. package/tailwind/components/navigation/lazy.ts.txt +11 -0
  249. package/tailwind/components/product/AddToCartForm.tsx.txt +365 -0
  250. package/tailwind/components/product/ProductCard.tsx.txt +305 -0
  251. package/tailwind/components/product/ProductDetails.tsx.txt +267 -0
  252. package/tailwind/components/product/ProductGallery.tsx.txt +234 -0
  253. package/tailwind/components/product/index.ts.txt +15 -0
  254. package/tailwind/components/product/lazy.ts.txt +9 -0
  255. package/tailwind/components/snippets/AppsSnippets.tsx.txt +225 -0
  256. package/tailwind/components/snippets/SallaSdkScripts.tsx.txt +14 -0
  257. package/tailwind/components/snippets/index.ts.txt +4 -0
  258. package/tailwind/components/store/StoreClosedNotification.tsx.txt +73 -0
  259. package/tailwind/components/store/index.ts.txt +8 -0
  260. package/tailwind/components/store/lazy.ts.txt +8 -0
  261. package/tailwind/components/toast/Toaster.tsx.txt +177 -0
  262. package/tailwind/components/toast/index.ts.txt +10 -0
  263. package/tailwind/components/toast/types.ts.txt +38 -0
  264. package/tailwind/components/toast/useToast.ts.txt +61 -0
  265. package/tailwind/components/tracking/GoogleTag.tsx.txt +77 -0
  266. package/tailwind/components/tracking/SiftSnippet.tsx.txt +49 -0
  267. package/tailwind/components/tracking/TwilightBundles.tsx.txt +67 -0
  268. package/tailwind/components/tracking/index.ts.txt +11 -0
  269. package/tailwind/components/tracking/lazy.ts.txt +11 -0
  270. package/tailwind/components/useComponent.ts.txt +12 -0
  271. package/tailwind/routes/account/index.tsx.txt +16 -0
  272. package/tailwind/routes/account/notifications/NotificationsPage.tsx.txt +54 -0
  273. package/tailwind/routes/account/notifications/index.tsx.txt +39 -0
  274. package/tailwind/routes/account/notifications/loader.ts.txt +22 -0
  275. package/tailwind/routes/account/notifications/types.ts.txt +18 -0
  276. package/tailwind/routes/account/orders/OrderSinglePage.tsx.txt +181 -0
  277. package/tailwind/routes/account/orders/OrdersPage.tsx.txt +28 -0
  278. package/tailwind/routes/account/orders/index.tsx.txt +86 -0
  279. package/tailwind/routes/account/orders/loader.ts.txt +66 -0
  280. package/tailwind/routes/account/orders/single/OrderActions.tsx.txt +197 -0
  281. package/tailwind/routes/account/orders/single/OrderHeader.tsx.txt +143 -0
  282. package/tailwind/routes/account/orders/single/OrderItemRow.tsx.txt +209 -0
  283. package/tailwind/routes/account/orders/single/OrderSections.tsx.txt +215 -0
  284. package/tailwind/routes/account/orders/single/types.ts.txt +38 -0
  285. package/tailwind/routes/account/orders/types.ts.txt +29 -0
  286. package/tailwind/routes/account/profile/ProfilePage.tsx.txt +30 -0
  287. package/tailwind/routes/account/profile/index.tsx.txt +39 -0
  288. package/tailwind/routes/account/profile/loader.ts.txt +16 -0
  289. package/tailwind/routes/account/profile/types.ts.txt +5 -0
  290. package/tailwind/routes/account/settings/SettingsPage.tsx.txt +13 -0
  291. package/tailwind/routes/account/settings/index.tsx.txt +39 -0
  292. package/tailwind/routes/account/settings/loader.ts.txt +13 -0
  293. package/tailwind/routes/account/settings/types.ts.txt +5 -0
  294. package/tailwind/routes/account/types.ts.txt +320 -0
  295. package/tailwind/routes/account/wallet/WalletPage.tsx.txt +195 -0
  296. package/tailwind/routes/account/wallet/index.tsx.txt +38 -0
  297. package/tailwind/routes/account/wallet/loader.ts.txt +27 -0
  298. package/tailwind/routes/account/wallet/types.ts.txt +11 -0
  299. package/tailwind/routes/account/wishlist/WishlistPage.tsx.txt +314 -0
  300. package/tailwind/routes/account/wishlist/WishlistShare.tsx.txt +248 -0
  301. package/tailwind/routes/account/wishlist/index.tsx.txt +40 -0
  302. package/tailwind/routes/account/wishlist/loader.ts.txt +26 -0
  303. package/tailwind/routes/account/wishlist/types.ts.txt +9 -0
  304. package/tailwind/routes/blog/BlogAuthorPage.tsx.txt +82 -0
  305. package/tailwind/routes/blog/BlogCategoryPage.tsx.txt +84 -0
  306. package/tailwind/routes/blog/BlogPage.tsx.txt +191 -0
  307. package/tailwind/routes/blog/BlogSinglePage.tsx.txt +168 -0
  308. package/tailwind/routes/blog/BlogTagPage.tsx.txt +84 -0
  309. package/tailwind/routes/blog/BlogTags.tsx.txt +54 -0
  310. package/tailwind/routes/blog/components.tsx.txt +87 -0
  311. package/tailwind/routes/blog/index.tsx.txt +238 -0
  312. package/tailwind/routes/blog/loader.ts.txt +173 -0
  313. package/tailwind/routes/blog/types.ts.txt +106 -0
  314. package/tailwind/routes/brands/BrandsPage.tsx.txt +179 -0
  315. package/tailwind/routes/brands/index.tsx.txt +47 -0
  316. package/tailwind/routes/brands/loader.ts.txt +14 -0
  317. package/tailwind/routes/brands/types.ts.txt +53 -0
  318. package/tailwind/routes/cart/CartPage.tsx.txt +112 -0
  319. package/tailwind/routes/cart/index.tsx.txt +39 -0
  320. package/tailwind/routes/cart/loader.ts.txt +15 -0
  321. package/tailwind/routes/cart/types.ts.txt +6 -0
  322. package/tailwind/routes/home/HomePage.tsx.txt +53 -0
  323. package/tailwind/routes/home/index.tsx.txt +56 -0
  324. package/tailwind/routes/home/loader.ts.txt +24 -0
  325. package/tailwind/routes/home/types.ts.txt +11 -0
  326. package/tailwind/routes/index.ts.txt +94 -0
  327. package/tailwind/routes/lazy.ts.txt +105 -0
  328. package/tailwind/routes/loyalty/LoyaltyPage.tsx.txt +17 -0
  329. package/tailwind/routes/loyalty/LoyaltyPointsPage.tsx.txt +11 -0
  330. package/tailwind/routes/loyalty/index.tsx.txt +81 -0
  331. package/tailwind/routes/loyalty/loader.ts.txt +53 -0
  332. package/tailwind/routes/loyalty/types.ts.txt +109 -0
  333. package/tailwind/routes/not-found.tsx.txt +50 -0
  334. package/tailwind/routes/page/PageSingle.tsx.txt +34 -0
  335. package/tailwind/routes/page/index.tsx.txt +60 -0
  336. package/tailwind/routes/page/loader.ts.txt +19 -0
  337. package/tailwind/routes/page/types.ts.txt +34 -0
  338. package/tailwind/routes/product/ProductPage.tsx.txt +77 -0
  339. package/tailwind/routes/product/index.tsx.txt +157 -0
  340. package/tailwind/routes/product/loader.ts.txt +35 -0
  341. package/tailwind/routes/product/types.ts.txt +6 -0
  342. package/tailwind/routes/product-listing/ProductListPage.tsx.txt +321 -0
  343. package/tailwind/routes/product-listing/index.tsx.txt +95 -0
  344. package/tailwind/routes/product-listing/loader.ts.txt +323 -0
  345. package/tailwind/routes/product-listing/types.ts.txt +96 -0
  346. package/tailwind/routes/product-listing/useAppliedFilters.ts.txt +130 -0
  347. package/tailwind/routes/product-short-link.ts.txt +104 -0
  348. package/tailwind/routes/root/beforeLoad.ts.txt +354 -0
  349. package/tailwind/routes/root/head.ts.txt +424 -0
  350. package/tailwind/routes/root/index.ts.txt +2 -0
  351. package/tailwind/routes/seo-redirects.ts.txt +82 -0
  352. package/tailwind/routes/testimonials/ReviewsPage.tsx.txt +12 -0
  353. package/tailwind/routes/testimonials/TestimonialsPage.tsx.txt +80 -0
  354. package/tailwind/routes/testimonials/index.tsx.txt +80 -0
  355. package/tailwind/routes/testimonials/loader.ts.txt +29 -0
  356. package/tailwind/routes/testimonials/types.ts.txt +9 -0
  357. package/tailwind/routes/thank-you/ThankYouPage.tsx.txt +241 -0
  358. package/tailwind/routes/thank-you/index.tsx.txt +49 -0
  359. package/tailwind/routes/thank-you/loader.ts.txt +40 -0
  360. package/tailwind/routes/thank-you/types.ts.txt +16 -0
  361. package/tailwind/routes/types.ts.txt +41 -0
  362. package/dist/BlogAuthorPage-GTF4UGSS.js.map +0 -1
  363. package/dist/BlogCategoryPage-TN7Y4NBP.js.map +0 -1
  364. package/dist/BlogPage-TDB3M3PI.js.map +0 -1
  365. package/dist/BlogSinglePage-4A52Q5QW.js.map +0 -1
  366. package/dist/BlogTagPage-TOVPBETA.js.map +0 -1
  367. package/dist/BrandsPage-VUDGYSQI.js.map +0 -1
  368. package/dist/Breadcrumb-CI3SN5YR.js.map +0 -1
  369. package/dist/CartItem-BHLEURAB.js.map +0 -1
  370. package/dist/CartSummary-QERVESYN.js.map +0 -1
  371. package/dist/CustomerLayout-VRZUWGQK.js.map +0 -1
  372. package/dist/LoyaltyPage-CGTHWJ76.js.map +0 -1
  373. package/dist/LoyaltyPointsPage-QSU6K3PW.js.map +0 -1
  374. package/dist/MobileMenu-U4BVQYKH.js.map +0 -1
  375. package/dist/NotificationsPage-ZRRQY7TF.js.map +0 -1
  376. package/dist/OrderSinglePage-O3XYVMG5.js.map +0 -1
  377. package/dist/OrdersPage-CMXZF5MI.js.map +0 -1
  378. package/dist/ProductDetails-65GDABI7.js.map +0 -1
  379. package/dist/ProfilePage-SNJ4WXU4.js.map +0 -1
  380. package/dist/SeoCartWidget-DBJAXJKT.js.map +0 -1
  381. package/dist/SettingsPage-FLTIOPU5.js.map +0 -1
  382. package/dist/ThankYouPage-5X6B4X3T.js.map +0 -1
  383. package/dist/WalletPage-H2BKIDPA.js.map +0 -1
  384. package/dist/WishlistPage-RS5UCIQ3.js.map +0 -1
  385. package/dist/chunk-4CZRUAZB.js.map +0 -1
  386. package/dist/chunk-7WAXN2PP.js +0 -14
  387. package/dist/chunk-JO7JWV34.js.map +0 -1
  388. package/dist/chunk-NZVR4OF7.js.map +0 -1
  389. package/dist/chunk-SDFDS4Q3.js.map +0 -1
  390. package/dist/not-found-QTHBFPNT.js.map +0 -1
@@ -47,4 +47,27 @@ export declare function isReactThemeManifest(raw: unknown): boolean;
47
47
  * plugin owns the console.
48
48
  */
49
49
  export declare function describeThemeTypeIssue(raw: unknown, fileLabel?: string): string | null;
50
+ /** One problem found in a `twilight.json`. */
51
+ export interface ManifestIssue {
52
+ /** Where it is, as a path into the file: `settings[3].id`, `components[0].fields[2]`. */
53
+ path: string;
54
+ /** What is wrong and what to do about it. */
55
+ message: string;
56
+ }
57
+ export interface ManifestValidation {
58
+ /** Problems that break the theme or the dashboard. Fix these. */
59
+ errors: ManifestIssue[];
60
+ /** Things that are unusual or not recognised. Check them, they may be intended. */
61
+ warnings: ManifestIssue[];
62
+ }
63
+ /**
64
+ * Check a parsed `twilight.json` for mistakes that are easy to make by hand and hard to see:
65
+ * a missing `type`, a duplicate setting `id`, a dropdown with no options, a boolean whose `value`
66
+ * is not a boolean, a component with no `path`.
67
+ *
68
+ * Pure and dependency-free, so it runs in Node, in a test or in a script. It never throws.
69
+ * It checks the structure the engine and the dev settings widget read. It cannot know everything
70
+ * the Salla dashboard accepts, so anything merely unfamiliar is a warning.
71
+ */
72
+ export declare function validateTwilightManifest(raw: unknown, fileLabel?: string): ManifestValidation;
50
73
  //# sourceMappingURL=twilight-manifest.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"twilight-manifest.d.ts","sourceRoot":"","sources":["../../../src/vite/plugins/twilight-manifest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,+DAA+D;AAC/D,eAAO,MAAM,gBAAgB,UAAU,CAAC;AAExC,0EAA0E;AAC1E,MAAM,WAAW,gBAAgB;IAC/B;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,EAAE,CAAC;CACxB;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAI7D;AAED,mEAAmE;AACnE,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAE1D;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,OAAO,EAAE,SAAS,SAAkB,GAAG,MAAM,GAAG,IAAI,CAyB/F"}
1
+ {"version":3,"file":"twilight-manifest.d.ts","sourceRoot":"","sources":["../../../src/vite/plugins/twilight-manifest.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,+DAA+D;AAC/D,eAAO,MAAM,gBAAgB,UAAU,CAAC;AAExC,0EAA0E;AAC1E,MAAM,WAAW,gBAAgB;IAC/B;;;OAGG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC;IACrB,UAAU,CAAC,EAAE,OAAO,EAAE,CAAC;CACxB;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAI7D;AAED,mEAAmE;AACnE,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAE1D;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,OAAO,EAAE,SAAS,SAAkB,GAAG,MAAM,GAAG,IAAI,CAyB/F;AAID,8CAA8C;AAC9C,MAAM,WAAW,aAAa;IAC5B,yFAAyF;IACzF,IAAI,EAAE,MAAM,CAAC;IACb,6CAA6C;IAC7C,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,kBAAkB;IACjC,iEAAiE;IACjE,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB,mFAAmF;IACnF,QAAQ,EAAE,aAAa,EAAE,CAAC;CAC3B;AAqGD;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,OAAO,EACZ,SAAS,SAAkB,GAC1B,kBAAkB,CA4JpB"}
@@ -1 +1 @@
1
- {"version":3,"file":"twilight-schema.plugin.d.ts","sourceRoot":"","sources":["../../../src/vite/plugins/twilight-schema.plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAYnC,wBAAgB,oBAAoB,CAAC,gBAAgB,SAAkB,GAAG,MAAM,CA+E/E"}
1
+ {"version":3,"file":"twilight-schema.plugin.d.ts","sourceRoot":"","sources":["../../../src/vite/plugins/twilight-schema.plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAcnC,wBAAgB,oBAAoB,CAAC,gBAAgB,SAAkB,GAAG,MAAM,CAwF/E"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salla.sa/twilight-theme-engine",
3
- "version": "1.0.85",
3
+ "version": "1.0.87",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -202,6 +202,10 @@
202
202
  "types": "./dist/dom.d.ts",
203
203
  "import": "./dist/dom.js"
204
204
  },
205
+ "./boot": {
206
+ "types": "./dist/boot.d.ts",
207
+ "import": "./dist/boot.js"
208
+ },
205
209
  "./sentry/client": {
206
210
  "types": "./dist/sentry/client.d.ts",
207
211
  "import": "./dist/sentry/client.js"
@@ -362,6 +366,10 @@
362
366
  "types": "./dist/api/profile.d.ts",
363
367
  "import": "./dist/api/profile.js"
364
368
  },
369
+ "./api/advertisement": {
370
+ "types": "./dist/api/advertisement.d.ts",
371
+ "import": "./dist/api/advertisement.js"
372
+ },
365
373
  "./api/apps": {
366
374
  "types": "./dist/api/apps.d.ts",
367
375
  "import": "./dist/api/apps.js"
@@ -389,13 +397,18 @@
389
397
  },
390
398
  "files": [
391
399
  "dist",
400
+ "bin",
401
+ "tailwind",
392
402
  "README.md"
393
403
  ],
404
+ "bin": {
405
+ "twilight-check-manifest": "./bin/twilight-check-manifest.mjs"
406
+ },
394
407
  "scripts": {
395
- "build": "tsup && node scripts/add-js-extensions.mjs && rm -f tsconfig.tsbuildinfo && tsc --build && node scripts/generate-dts-stubs.mjs && node scripts/copy-ambient-dts.mjs",
408
+ "build": "tsup && node scripts/add-js-extensions.mjs && rm -f tsconfig.tsbuildinfo && tsc --build && node scripts/generate-dts-stubs.mjs && node scripts/copy-ambient-dts.mjs && node scripts/copy-tailwind-sources.mjs",
396
409
  "dev": "tsup --watch",
397
410
  "typecheck": "tsc --noEmit",
398
- "clean": "rm -rf dist",
411
+ "clean": "rm -rf dist tailwind",
399
412
  "test": "vitest --reporter=agent run",
400
413
  "test:watch": "vitest",
401
414
  "test:coverage": "vitest run --coverage",
@@ -0,0 +1,21 @@
1
+ import React from 'react';
2
+ import { registry } from './ComponentRegistry';
3
+
4
+ export interface ComponentProps {
5
+ name: string;
6
+ fallback?: React.ReactNode;
7
+ [key: string]: unknown;
8
+ }
9
+
10
+ export function Component({ name, fallback = null, ...props }: ComponentProps): React.ReactNode {
11
+ const ResolvedComponent = registry.resolve(name);
12
+
13
+ if (!ResolvedComponent) {
14
+ if (process.env.NODE_ENV === 'development') {
15
+ window.salla?.logger?.warn(`Component::"${name}" not found in registry`);
16
+ }
17
+ return fallback;
18
+ }
19
+
20
+ return <ResolvedComponent {...props} />;
21
+ }
@@ -0,0 +1,66 @@
1
+ import type { ComponentType, ComponentDefinition } from '../types';
2
+
3
+ class ComponentRegistry {
4
+ private components = new Map<string, ComponentDefinition>();
5
+
6
+ register<P = Record<string, unknown>>(name: string, component: ComponentType<P>): void {
7
+ this.components.set(name, {
8
+ component: component as ComponentType,
9
+ displayName: name,
10
+ });
11
+ }
12
+
13
+ override<P = Record<string, unknown>>(name: string, component: ComponentType<P>): void {
14
+ const existing = this.components.get(name);
15
+ if (existing) {
16
+ this.components.set(name, {
17
+ component: component as ComponentType,
18
+ displayName: name,
19
+ original: existing,
20
+ });
21
+ } else {
22
+ this.register(name, component);
23
+ }
24
+ }
25
+
26
+ resolve<P = Record<string, unknown>>(name: string): ComponentType<P> | null {
27
+ const definition = this.components.get(name);
28
+ return (definition?.component as ComponentType<P>) ?? null;
29
+ }
30
+
31
+ getOriginal<P = Record<string, unknown>>(name: string): ComponentType<P> | null {
32
+ const definition = this.components.get(name);
33
+ return (definition?.original?.component as ComponentType<P>) ?? null;
34
+ }
35
+
36
+ has(name: string): boolean {
37
+ return this.components.has(name);
38
+ }
39
+
40
+ list(): string[] {
41
+ return Array.from(this.components.keys());
42
+ }
43
+
44
+ listByPrefix(prefix: string): string[] {
45
+ return this.list().filter((name) => name.startsWith(prefix));
46
+ }
47
+
48
+ clear(): void {
49
+ this.components.clear();
50
+ }
51
+
52
+ remove(name: string): boolean {
53
+ return this.components.delete(name);
54
+ }
55
+ }
56
+
57
+ export const registry = new ComponentRegistry();
58
+
59
+ export { ComponentRegistry };
60
+
61
+ export const COMPONENT_KEYS = {
62
+ PRODUCT_CARD: 'product:card',
63
+ PRODUCT_GALLERY: 'product:gallery',
64
+ PRODUCT_LIST_PAGE: 'product:list-page',
65
+ PRODUCT_LIST_SKELETON: 'product:list-skeleton',
66
+ } as const;
@@ -0,0 +1,211 @@
1
+ import type { Money, CartItem as CartItemType } from '../../types';
2
+ import { useTranslation } from '../../providers/I18nProvider';
3
+ import { useMoney } from '../../hooks/useMoney';
4
+ import { Link, Image } from '../common';
5
+ import { SallaCartItemOffers } from '@salla.sa/twilight-components-react/cart-item-offers';
6
+ import { SallaQuantityInput } from '@salla.sa/twilight-components-react/quantity-input';
7
+ import { SallaButton } from '@salla.sa/twilight-components-react/button';
8
+ import { SallaProductOptions } from '@salla.sa/twilight-components-react/product-options';
9
+ import { useHostAttributes } from '@salla.sa/twilight-theme-engine/hooks/useHostAttributes';
10
+
11
+ interface CartItemProps {
12
+ item: CartItemType;
13
+ isFirst?: boolean;
14
+ }
15
+
16
+ function toNumber(value: number | Money | undefined): number | undefined {
17
+ if (value === undefined) return undefined;
18
+ if (typeof value === 'number') return value;
19
+ return value.amount;
20
+ }
21
+
22
+ function moneyCurrency(value: number | Money | undefined): string | undefined {
23
+ if (typeof value !== 'object') return undefined;
24
+ return value.currency;
25
+ }
26
+
27
+ function CartItemPrices({ item }: { item: CartItemType }) {
28
+ const { format } = useMoney();
29
+ return (
30
+ <div className="flex items-center gap-2 flex-wrap">
31
+ {item.offer && (
32
+ <span className="text-sm text-gray-500 line-through item-regular-price">
33
+ {format(toNumber(item.product_price), {
34
+ currency: moneyCurrency(item.product_price),
35
+ })}
36
+ </span>
37
+ )}
38
+
39
+ {item.is_on_sale && (
40
+ <span className="inline-flex gap-1 leading-4 text-sm text-gray-500 line-through item-original-price">
41
+ {format(toNumber(item.original_price), {
42
+ currency: moneyCurrency(item.original_price),
43
+ })}
44
+ </span>
45
+ )}
46
+
47
+ <span
48
+ className={`inline-flex gap-1 leading-4 item-price ${item.has_discount ? 'text-red-800' : 'text-sm text-gray-500'}`}
49
+ >
50
+ {format(toNumber(item.price), { currency: moneyCurrency(item.price) })}
51
+ </span>
52
+ </div>
53
+ );
54
+ }
55
+
56
+ function CartItemQuantity({ item }: { item: CartItemType }) {
57
+ const { t } = useTranslation();
58
+ const quantityRef = useHostAttributes({
59
+ max: item.max_quantity,
60
+ value: item.quantity,
61
+ name: 'quantity',
62
+ });
63
+
64
+ if (item.type === 'donating') return <span></span>;
65
+
66
+ if (item.is_hidden_quantity) {
67
+ return (
68
+ <>
69
+ <input
70
+ type="hidden"
71
+ value={item.quantity}
72
+ name="quantity"
73
+ aria-label={t('pages.products.quantity')}
74
+ />
75
+ <span className="w-10 text-center">{item.quantity}</span>
76
+ </>
77
+ );
78
+ }
79
+
80
+ return (
81
+ <SallaQuantityInput
82
+ // The loaded component reads `value` only once, so remount when the quantity changes.
83
+ key={`${item.id}-${item.quantity}`}
84
+ ref={quantityRef}
85
+ cartItemId={item.id}
86
+ max={item.max_quantity ?? undefined}
87
+ className="transition transition-color duration-300"
88
+ aria-label={t('pages.products.quantity')}
89
+ value={item.quantity}
90
+ name="quantity"
91
+ />
92
+ );
93
+ }
94
+
95
+ function CartItemTotal({ item }: { item: CartItemType }) {
96
+ const { t } = useTranslation();
97
+ const { format } = useMoney();
98
+ const total = item.detailed_offers?.length ? item.total_special_price : item.total;
99
+ return (
100
+ <p className="text-primary flex-none font-bold text-sm rtl:md:pl-12 ltr:md:pr-12">
101
+ <span>{t('pages.cart.total')}:</span>
102
+ <span className="inline-block item-total">
103
+ {item.is_available
104
+ ? format(toNumber(total), { currency: moneyCurrency(total) })
105
+ : t('pages.products.out_of_stock')}
106
+ </span>
107
+ </p>
108
+ );
109
+ }
110
+
111
+ const handleChange = (e: React.ChangeEvent<HTMLFormElement>) => {
112
+ window.salla?.form.onChange('cart.updateItem', e);
113
+ };
114
+
115
+ export function CartItem({ item, isFirst = false }: CartItemProps) {
116
+ const { t } = useTranslation();
117
+
118
+ const handleDelete = async () => {
119
+ await window.salla?.cart.deleteItem(item.id);
120
+ document.querySelector(`#item-${item.id}`)?.remove();
121
+ };
122
+
123
+ const isFreeProduct = toNumber(item.price) === 0 && item.has_discount;
124
+
125
+ return (
126
+ <form onChange={handleChange} id={`item-${item.id}`}>
127
+ <section className="cart-item bg-white p-5 xs:p-7 rounded-md mb-5 relative">
128
+ <input type="hidden" name="id" value={item.id} />
129
+
130
+ <div className="md:flex rtl:space-x-reverse md:space-x-12 items-start justify-between mb-8 last:mb-0">
131
+ <div className="flex flex-1 rtl:space-x-reverse space-x-4">
132
+ <Link to={item.url} className="relative overflow-hidden shrink-0">
133
+ <Image
134
+ src={item.product_image}
135
+ alt={item.product_name}
136
+ className="flex-none w-24 h-20 border border-gray-200 bg-gray-100 rounded-md object-center object-cover"
137
+ aspectRatio="6/5"
138
+ priority={isFirst}
139
+ />
140
+
141
+ {isFreeProduct && (
142
+ <div className="free-ribbon absolute top-[11px] right-[-38px] w-32 bg-red-600 text-white text-xs font-bold text-center py-1 rotate-45 shadow-md">
143
+ {/* Twig: trans('pages.loyalty_program.free_product') */}
144
+ {t('pages.loyalty_program.free_product')}
145
+ </div>
146
+ )}
147
+ </Link>
148
+
149
+ <div className="space-y-1">
150
+ <div className="text-gray-900 leading-6 text-lg font-bold">
151
+ <Link to={item.url} className="text-base">
152
+ {item.product_name}
153
+ </Link>
154
+ </div>
155
+
156
+ <CartItemPrices item={item} />
157
+
158
+ {item.weight_label && (
159
+ <p className="text-sm text-gray-500">
160
+ {t('pages.products.weight')}: <span>{item.weight_label}</span>
161
+ </p>
162
+ )}
163
+
164
+ {/* Twig: class="{{ item.detailed_offers ? 'hidden' : '' }}": hide old offer when detailed_offers present */}
165
+ {item.offer && !item.detailed_offers?.length && (
166
+ <span className="old-offers flex items-center gap-1 mx-1.5">
167
+ <i className="sicon-discount-calculator text-gray-500 offer-icon"></i>
168
+ <span className="text-sm text-gray-500 offer-name">{item.offer.names}</span>
169
+ </span>
170
+ )}
171
+
172
+ {item.detailed_offers && item.detailed_offers.length > 0 && (
173
+ <SallaCartItemOffers
174
+ quantity={item.quantity}
175
+ itemId={String(item.id) as any}
176
+ productPrice={toNumber(item.product_price) ?? 0}
177
+ offers={JSON.stringify(item.detailed_offers)}
178
+ />
179
+ )}
180
+ </div>
181
+ </div>
182
+
183
+ <div className="border-t border-b border-gray-200 py-3 md:p-0 md:border-none mt-5 md:mt-0 flex gap-8 justify-between items-center md:items-start">
184
+ <CartItemQuantity item={item} />
185
+
186
+ <CartItemTotal item={item} />
187
+ </div>
188
+ </div>
189
+
190
+ {/* Twig: {% include 'pages.partials.product.options' with {product:item} %} */}
191
+ {item.options && item.options.length > 0 && (
192
+ <SallaProductOptions options={JSON.stringify(item.options)} productId={item.product_id} />
193
+ )}
194
+
195
+ <span className="absolute top-1.5 rtl:left-1.5 ltr:right-1.5 rtl:xs:left-5 ltr:xs:right-5 xs:top-5">
196
+ <SallaButton
197
+ type="button"
198
+ shape="icon"
199
+ size="small"
200
+ color="danger"
201
+ className="btn--delete"
202
+ ariaLabel={t('common.elements.remove')}
203
+ onClick={handleDelete}
204
+ >
205
+ <i className="sicon-cancel"></i>
206
+ </SallaButton>
207
+ </span>
208
+ </section>
209
+ </form>
210
+ );
211
+ }
@@ -0,0 +1,269 @@
1
+ import { useCallback } from 'react';
2
+ import { HookSlot } from '../../hooks/HookSlot';
3
+ import { useTranslation } from '../../providers/I18nProvider';
4
+ import { useCoupon } from '../../hooks/useCoupon';
5
+ import { useMoney } from '../../hooks/useMoney';
6
+ import { stripUnsafeHtml } from '../../utils/sanitize';
7
+ import { SallaLoyalty } from '@salla.sa/twilight-components-react/loyalty';
8
+ import { SallaGifting } from '@salla.sa/twilight-components-react/gifting';
9
+ import { SallaButton } from '@salla.sa/twilight-components-react/button';
10
+ import { SallaTieredOffer } from '@salla.sa/twilight-components-react/tiered-offer';
11
+ import type { Cart } from '../../types';
12
+
13
+ interface CartSummaryProps {
14
+ cart: Cart;
15
+ applyCouponEnabled?: boolean;
16
+ taxAmount?: number;
17
+ loyalty?: Cart['loyalty'];
18
+ gift?: Cart['gift'];
19
+ loyaltyPoints?: number;
20
+ }
21
+
22
+ export function CartSummary({
23
+ cart,
24
+ applyCouponEnabled = true,
25
+ taxAmount,
26
+ loyalty,
27
+ gift,
28
+ loyaltyPoints,
29
+ }: CartSummaryProps) {
30
+ const { t } = useTranslation();
31
+ const { format } = useMoney();
32
+
33
+ const handleCartSubmit = useCallback(async () => {
34
+ // Twig's cart.js: a submit with an empty required option fails and the SDK resets the cart.
35
+ const forms = document.querySelectorAll<HTMLFormElement>('form[id^="item-"]');
36
+ if (![...forms].every((form) => form.reportValidity())) {
37
+ window.salla?.notify.error(window.salla.lang.get('common.messages.required_fields'));
38
+ return;
39
+ }
40
+ try {
41
+ await window.salla?.cart.submit();
42
+ } catch {
43
+ // Error handling
44
+ }
45
+ }, []);
46
+
47
+ return (
48
+ <div className="sticky top-24 w-full lg:w-96 rtl:lg:mr-8 ltr:lg:ml-8">
49
+ <HookSlot name="cart:summary.start" />
50
+ {cart.free_shipping_bar && <FreeShippingBar freeShippingBar={cart.free_shipping_bar} />}
51
+
52
+ {/* Twig: {% if loyalty and user.loyalty_points %} — top-level page vars rendered inside sidebar */}
53
+ {loyalty && loyaltyPoints && (
54
+ <div className="shadow-default mb-5 rounded-md p-3 xs:p-7 bg-white">
55
+ <SallaLoyalty
56
+ customerPoints={loyaltyPoints}
57
+ prizePoints={loyalty.prize.points}
58
+ prizeTitle={loyalty.prize.title}
59
+ />
60
+ </div>
61
+ )}
62
+
63
+ {/* Twig: {{gift.enabled ? '' : 'hidden'}} — top-level page var rendered inside sidebar */}
64
+ {gift?.enabled && (
65
+ <div id="cart-gifting" className="shadow-default bg-white xs:p-2 rounded-md mb-5 relative">
66
+ <SallaGifting
67
+ id="salla-gifting"
68
+ className="mt-5"
69
+ widgetTitle={t('pages.cart.gift_widget_title', 'Send as a gift')}
70
+ widgetSubtitle={gift.text}
71
+ {...(gift.type ? { [gift.type + 'Products']: true } : {})}
72
+ vertical
73
+ />
74
+ </div>
75
+ )}
76
+
77
+ <div className="shadow-default bg-white p-5 xs:p-7 rounded-md mb-5 relative transition-height duration-1000">
78
+ <h2 className="font-bold text-sm mb-5">{t('pages.cart.summary', 'Order Summary')}</h2>
79
+
80
+ <div className="flex justify-between text-sm mb-5">
81
+ <span className="text-gray-500">
82
+ {taxAmount
83
+ ? t('pages.cart.items_total_without_tax', 'Items total (excl. tax)')
84
+ : t('pages.cart.items_total', 'Items total')}
85
+ </span>
86
+ <b id="sub-total">{format(cart.sub_total)}</b>
87
+ </div>
88
+
89
+ {cart.options?.length > 0 && (
90
+ <div className="flex justify-between text-sm mb-5">
91
+ <span className="text-gray-500">
92
+ {t('pages.cart.order_options_total', 'Options total')}
93
+ </span>
94
+ <b id="cart-options-total">{format(cart.options_total ?? undefined)}</b>
95
+ </div>
96
+ )}
97
+
98
+ {cart.has_shipping && (
99
+ <div id="shipping-cost" className="flex justify-between text-sm mb-5">
100
+ <span className="text-gray-500">{t('pages.cart.shipping_cost', 'Shipping cost')}</span>
101
+ <b>{format(cart.real_shipping_cost)}</b>
102
+ </div>
103
+ )}
104
+
105
+ {applyCouponEnabled && <CouponInput initialCoupon={cart.coupon ?? undefined} />}
106
+
107
+ {!!cart.total_discount && (
108
+ <div id="total-discount" className="flex justify-between text-sm">
109
+ <span className="text-gray-500 block h-10">{t('pages.cart.discount', 'Discount')}</span>
110
+ <b>- {format(cart.total_discount)}</b>
111
+ </div>
112
+ )}
113
+
114
+ {!!taxAmount && (
115
+ <div className="flex justify-between text-sm mb-5">
116
+ <span className="text-gray-500">
117
+ {t('pages.cart.VAT_tax_amount', 'VAT Tax Amount')}
118
+ </span>
119
+ <b id="tax-amount">{format(taxAmount)}</b>
120
+ </div>
121
+ )}
122
+
123
+ <div className="flex justify-between text-lg mb-5">
124
+ <span className="text-gray-500">{t('pages.cart.final_total', 'Total')}</span>
125
+ <b data-cart-total>{format(cart.total)}</b>
126
+ </div>
127
+
128
+ <div className="cart-submit-wrap">
129
+ <HookSlot name="cart:submit.start" />
130
+ <SallaButton
131
+ id="cart-submit"
132
+ loaderPosition="center"
133
+ width="wide"
134
+ onClick={handleCartSubmit}
135
+ >
136
+ {t('pages.cart.complete_order', 'Complete Order')}
137
+ </SallaButton>
138
+ <HookSlot name="cart:submit.end" />
139
+ </div>
140
+
141
+ <HookSlot name="cart:summary.end" />
142
+ </div>
143
+
144
+ <SallaTieredOffer />
145
+ </div>
146
+ );
147
+ }
148
+
149
+ function FreeShippingBar({
150
+ freeShippingBar,
151
+ }: {
152
+ freeShippingBar: NonNullable<Cart['free_shipping_bar']>;
153
+ }) {
154
+ const isFree = freeShippingBar.has_free_shipping;
155
+ const { t } = useTranslation();
156
+
157
+ return (
158
+ <div className="shadow-default bg-white p-5 xs:p-7 rounded-md mb-5 relative" id="free-shipping">
159
+ <div className="flex rtl:space-x-reverse space-x-3 items-center">
160
+ <i className="bg-primary text-white rounded-icon sicon-shipping-fast"></i>
161
+ <div className="flex-1">
162
+ <h4 className="shipping-item font-bold text-sm mb-1.5">
163
+ {t('pages.cart.free_shipping', 'Free Shipping')}
164
+ </h4>
165
+ <p className="shipping-item text-sm text-gray-500">
166
+ {isFree ? (
167
+ <>
168
+ <span
169
+ id="free-shipping-msg"
170
+ dangerouslySetInnerHTML={{
171
+ __html: stripUnsafeHtml(
172
+ t('pages.cart.has_free_shipping', 'You have free shipping!')
173
+ ),
174
+ }}
175
+ />
176
+ <span className="emoji" id="free-shipping-applied">
177
+ 🎉
178
+ </span>
179
+ </>
180
+ ) : (
181
+ <span
182
+ id="free-shipping-msg"
183
+ dangerouslySetInnerHTML={{
184
+ __html: stripUnsafeHtml(
185
+ t(
186
+ 'pages.cart.free_shipping_alert',
187
+ 'Get free shipping when you add :amount more'
188
+ ).replace(':amount', String(freeShippingBar.remaining))
189
+ ),
190
+ }}
191
+ />
192
+ )}
193
+ </p>
194
+ </div>
195
+ </div>
196
+ {!isFree && (
197
+ <div className="mt-8 bg-border-color rounded-full" id="free-shipping-bar">
198
+ <div
199
+ className="progress-bg transition-all duration-500 h-2.5 bg-primary relative rounded-full flex justify-end"
200
+ style={{ width: `${freeShippingBar.percent}%` }}
201
+ >
202
+ <i className="inline-block sicon-shipping-fast absolute -top-5 rtl:left-0 ltr:right-0"></i>
203
+ </div>
204
+ </div>
205
+ )}
206
+ </div>
207
+ );
208
+ }
209
+
210
+ function CouponInput({ initialCoupon }: { initialCoupon?: string }) {
211
+ const { t } = useTranslation();
212
+ const { value, error, apply, remove } = useCoupon();
213
+
214
+ // Use initial coupon from cart if available
215
+ const appliedCoupon = value ?? initialCoupon;
216
+
217
+ const handleApplyCoupon = useCallback(
218
+ async (event: React.FormEvent<HTMLFormElement>) => {
219
+ event.preventDefault();
220
+ const formData = new FormData(event.currentTarget);
221
+ const code = formData.get('coupon') as string;
222
+ if (code?.trim()) {
223
+ await apply(code.trim());
224
+ }
225
+ },
226
+ [apply]
227
+ );
228
+
229
+ const handleRemoveCoupon = useCallback(async () => {
230
+ await remove();
231
+ }, [remove]);
232
+
233
+ return (
234
+ <form className="border-t border-gray-200 border-b py-5 mb-5" onSubmit={handleApplyCoupon}>
235
+ <label htmlFor="coupon-input" className="block text-sm">
236
+ {t('pages.cart.have_coupon', 'Have a coupon?')}
237
+ </label>
238
+ <div className="mt-2.5 relative">
239
+ <input
240
+ placeholder={t('pages.cart.coupon_placeholder', 'Enter coupon code')}
241
+ disabled={!!appliedCoupon}
242
+ className="rtl:pl-24 ltr:pr-24 form-input"
243
+ defaultValue={appliedCoupon}
244
+ id="coupon-input"
245
+ name="coupon"
246
+ aria-label={t('pages.cart.apply_coupon', 'Apply coupon')}
247
+ type="text"
248
+ />
249
+
250
+ <SallaButton
251
+ type="submit"
252
+ className={`btn--coupon ${appliedCoupon ? 'has-coupon btn--danger' : 'has-not-coupon btn--default'}`}
253
+ loaderPosition="center"
254
+ onClick={appliedCoupon ? handleRemoveCoupon : undefined}
255
+ >
256
+ <span className="coupon-text">
257
+ {appliedCoupon ? '' : t('pages.cart.apply_coupon_btn', 'Apply')}
258
+ </span>
259
+ {appliedCoupon && <i className="sicon-cancel icon text-xl w-8"></i>}
260
+ </SallaButton>
261
+ </div>
262
+ {error && (
263
+ <span className="text-xs text-red-700" id="coupon-error">
264
+ {error}
265
+ </span>
266
+ )}
267
+ </form>
268
+ );
269
+ }
@@ -0,0 +1,16 @@
1
+ import { useTranslation } from '../../providers/I18nProvider';
2
+
3
+ export function PreOrderNotification() {
4
+ const { t } = useTranslation();
5
+
6
+ return (
7
+ <salla-alert
8
+ className="mb-5"
9
+ message={t(
10
+ 'pages.cart.pre_order_shipping',
11
+ 'Your order will be shipped when the pre-order product becomes available in the store.'
12
+ )}
13
+ variant="info"
14
+ />
15
+ );
16
+ }