@salla.sa/twilight-theme-engine 1.0.84 → 1.0.86

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 (377) hide show
  1. package/dist/{AddToCartForm-JUMFLSKN.js → AddToCartForm-5X2WZDWR.js} +3 -3
  2. package/dist/{AddToCartForm-JUMFLSKN.js.map → AddToCartForm-5X2WZDWR.js.map} +1 -1
  3. package/dist/{BlogAuthorPage-LFAUMKC7.js → BlogAuthorPage-LO7FSSJR.js} +12 -10
  4. package/dist/BlogAuthorPage-LO7FSSJR.js.map +1 -0
  5. package/dist/{BlogCategoryPage-NWYFFPCD.js → BlogCategoryPage-KKFQ3PGR.js} +12 -10
  6. package/dist/BlogCategoryPage-KKFQ3PGR.js.map +1 -0
  7. package/dist/{BlogPage-BOHXFV7E.js → BlogPage-VLF3NSGE.js} +11 -9
  8. package/dist/BlogPage-VLF3NSGE.js.map +1 -0
  9. package/dist/{BlogSinglePage-FYEXQPMB.js → BlogSinglePage-AVK535IW.js} +11 -9
  10. package/dist/BlogSinglePage-AVK535IW.js.map +1 -0
  11. package/dist/{BlogTagPage-DAM3RNVA.js → BlogTagPage-F3T4D7RH.js} +12 -10
  12. package/dist/BlogTagPage-F3T4D7RH.js.map +1 -0
  13. package/dist/{BrandsPage-GAKQCE6O.js → BrandsPage-LVSBZSVC.js} +11 -9
  14. package/dist/BrandsPage-LVSBZSVC.js.map +1 -0
  15. package/dist/{Breadcrumb-NAYSZNRV.js → Breadcrumb-VLFTXRFC.js} +11 -9
  16. package/dist/Breadcrumb-VLFTXRFC.js.map +1 -0
  17. package/dist/{CartItem-ZRVLIZT3.js → CartItem-G3H34GW6.js} +11 -9
  18. package/dist/CartItem-G3H34GW6.js.map +1 -0
  19. package/dist/{CartSummary-U2TVLG35.js → CartSummary-VH37RPUI.js} +11 -9
  20. package/dist/CartSummary-VH37RPUI.js.map +1 -0
  21. package/dist/{Copyright-2LSBADZD.js → Copyright-HE5ZWINZ.js} +3 -3
  22. package/dist/{Copyright-2LSBADZD.js.map → Copyright-HE5ZWINZ.js.map} +1 -1
  23. package/dist/{CustomerLayout-WSKXWI7D.js → CustomerLayout-LKBLOAVW.js} +11 -9
  24. package/dist/CustomerLayout-LKBLOAVW.js.map +1 -0
  25. package/dist/{Footer-ONNRJU4G.js → Footer-HHXAST72.js} +5 -5
  26. package/dist/{Footer-ONNRJU4G.js.map → Footer-HHXAST72.js.map} +1 -1
  27. package/dist/{Header-C6VXLLUC.js → Header-GEVZM2YG.js} +8 -6
  28. package/dist/{Header-C6VXLLUC.js.map → Header-GEVZM2YG.js.map} +1 -1
  29. package/dist/{LoyaltyPage-IG5KSYNO.js → LoyaltyPage-M5CT3CFD.js} +11 -9
  30. package/dist/LoyaltyPage-M5CT3CFD.js.map +1 -0
  31. package/dist/{LoyaltyPointsPage-RKO56ZER.js → LoyaltyPointsPage-SDJB52U6.js} +10 -3
  32. package/dist/LoyaltyPointsPage-SDJB52U6.js.map +1 -0
  33. package/dist/{MainMenu-USOYTKPU.js → MainMenu-I7EYS752.js} +3 -3
  34. package/dist/{MainMenu-USOYTKPU.js.map → MainMenu-I7EYS752.js.map} +1 -1
  35. package/dist/{MasterLayout-2W24ACEK.js → MasterLayout-QDK4T3NE.js} +11 -9
  36. package/dist/{MasterLayout-2W24ACEK.js.map → MasterLayout-QDK4T3NE.js.map} +1 -1
  37. package/dist/{MobileMenu-E4X2GOZ3.js → MobileMenu-7VTCWAVJ.js} +11 -9
  38. package/dist/MobileMenu-7VTCWAVJ.js.map +1 -0
  39. package/dist/{NotificationsPage-BJ5XVWEG.js → NotificationsPage-QCABE6QD.js} +11 -9
  40. package/dist/NotificationsPage-QCABE6QD.js.map +1 -0
  41. package/dist/{OrderSinglePage-544E36FJ.js → OrderSinglePage-2HVBKBG4.js} +11 -9
  42. package/dist/OrderSinglePage-2HVBKBG4.js.map +1 -0
  43. package/dist/{OrdersPage-AFBWVTZU.js → OrdersPage-5MNCPCM3.js} +11 -9
  44. package/dist/OrdersPage-5MNCPCM3.js.map +1 -0
  45. package/dist/{PageSingle-H2EJIL74.js → PageSingle-EN5XIM2B.js} +3 -3
  46. package/dist/{PageSingle-H2EJIL74.js.map → PageSingle-EN5XIM2B.js.map} +1 -1
  47. package/dist/{ProductDetails-SDZHALU6.js → ProductDetails-UEWQPPDZ.js} +13 -6
  48. package/dist/ProductDetails-UEWQPPDZ.js.map +1 -0
  49. package/dist/{ProfilePage-V5KV6GQK.js → ProfilePage-TZSU3GHZ.js} +11 -9
  50. package/dist/ProfilePage-TZSU3GHZ.js.map +1 -0
  51. package/dist/{ReviewsPage-CXKJABXP.js → ReviewsPage-WYZ6CHF2.js} +3 -3
  52. package/dist/{ReviewsPage-CXKJABXP.js.map → ReviewsPage-WYZ6CHF2.js.map} +1 -1
  53. package/dist/{SeoCartWidget-JAV6M3X5.js → SeoCartWidget-5JR5RXQM.js} +11 -9
  54. package/dist/SeoCartWidget-5JR5RXQM.js.map +1 -0
  55. package/dist/{SettingsPage-43SUCLQR.js → SettingsPage-DIDRQXMM.js} +11 -9
  56. package/dist/SettingsPage-DIDRQXMM.js.map +1 -0
  57. package/dist/{StoreClosedNotification-MINBJOGZ.js → StoreClosedNotification-C2G32E34.js} +12 -10
  58. package/dist/{StoreClosedNotification-MINBJOGZ.js.map → StoreClosedNotification-C2G32E34.js.map} +1 -1
  59. package/dist/{TestimonialsPage-RLLIDLOA.js → TestimonialsPage-XPHFRBZ7.js} +3 -3
  60. package/dist/{TestimonialsPage-RLLIDLOA.js.map → TestimonialsPage-XPHFRBZ7.js.map} +1 -1
  61. package/dist/{ThankYouPage-52SSZ7FV.js → ThankYouPage-UA6YYFB2.js} +11 -9
  62. package/dist/ThankYouPage-UA6YYFB2.js.map +1 -0
  63. package/dist/{WalletPage-4AN4UJ52.js → WalletPage-AJVV7LVL.js} +11 -9
  64. package/dist/WalletPage-AJVV7LVL.js.map +1 -0
  65. package/dist/{WishlistPage-L6IYJUJZ.js → WishlistPage-UW6LJLSW.js} +11 -9
  66. package/dist/WishlistPage-UW6LJLSW.js.map +1 -0
  67. package/dist/api/advertisement.d.ts +33 -0
  68. package/dist/api/advertisement.d.ts.map +1 -0
  69. package/dist/api/advertisement.js +8 -0
  70. package/dist/api/advertisement.js.map +1 -0
  71. package/dist/boot/critical-style.d.ts +3 -0
  72. package/dist/boot/critical-style.d.ts.map +1 -0
  73. package/dist/boot/index.d.ts +15 -0
  74. package/dist/boot/index.d.ts.map +1 -0
  75. package/dist/boot.d.ts +1 -0
  76. package/dist/boot.js +69 -0
  77. package/dist/boot.js.map +1 -0
  78. package/dist/{chunk-R5BQDCDK.js → chunk-2HCCIHU7.js} +4 -4
  79. package/dist/{chunk-R5BQDCDK.js.map → chunk-2HCCIHU7.js.map} +1 -1
  80. package/dist/chunk-4FJGZQD3.js +6 -0
  81. package/dist/chunk-4FJGZQD3.js.map +1 -0
  82. package/dist/{chunk-UNEZZMQA.js → chunk-4WVJJ7SN.js} +20 -4
  83. package/dist/chunk-4WVJJ7SN.js.map +1 -0
  84. package/dist/{chunk-FFWSQJLV.js → chunk-BAYGWTNM.js} +3 -3
  85. package/dist/{chunk-FFWSQJLV.js.map → chunk-BAYGWTNM.js.map} +1 -1
  86. package/dist/{chunk-WSRVPR7V.js → chunk-CLBFF2CY.js} +3 -3
  87. package/dist/{chunk-WSRVPR7V.js.map → chunk-CLBFF2CY.js.map} +1 -1
  88. package/dist/chunk-DBW7XXNE.js +103 -0
  89. package/dist/chunk-DBW7XXNE.js.map +1 -0
  90. package/dist/{chunk-CCUE2FMU.js → chunk-DBXH3EMJ.js} +3 -3
  91. package/dist/{chunk-CCUE2FMU.js.map → chunk-DBXH3EMJ.js.map} +1 -1
  92. package/dist/{chunk-MECSOKS7.js → chunk-E26YG3AO.js} +4 -4
  93. package/dist/{chunk-MECSOKS7.js.map → chunk-E26YG3AO.js.map} +1 -1
  94. package/dist/{chunk-22SH7OVF.js → chunk-E7WWO3V3.js} +3 -3
  95. package/dist/{chunk-22SH7OVF.js.map → chunk-E7WWO3V3.js.map} +1 -1
  96. package/dist/{chunk-IC6XW3PD.js → chunk-HQ7WA37L.js} +7 -7
  97. package/dist/{chunk-IC6XW3PD.js.map → chunk-HQ7WA37L.js.map} +1 -1
  98. package/dist/chunk-IVBOLREE.js +29 -0
  99. package/dist/chunk-IVBOLREE.js.map +1 -0
  100. package/dist/{chunk-RRGEASAL.js → chunk-KP3VYG4F.js} +6 -6
  101. package/dist/{chunk-RRGEASAL.js.map → chunk-KP3VYG4F.js.map} +1 -1
  102. package/dist/{chunk-EFF2WO6W.js → chunk-PWTXR4RR.js} +10 -10
  103. package/dist/{chunk-EFF2WO6W.js.map → chunk-PWTXR4RR.js.map} +1 -1
  104. package/dist/chunk-QJYUTRAQ.js +14 -0
  105. package/dist/{chunk-4EA56R6I.js.map → chunk-QJYUTRAQ.js.map} +1 -1
  106. package/dist/{chunk-YFQUSAET.js → chunk-U55ZJC2X.js} +4 -4
  107. package/dist/{chunk-YFQUSAET.js.map → chunk-U55ZJC2X.js.map} +1 -1
  108. package/dist/{chunk-R54GSKJS.js → chunk-WSDG4ZRI.js} +30 -16
  109. package/dist/chunk-WSDG4ZRI.js.map +1 -0
  110. package/dist/{chunk-3PWARKZX.js → chunk-X54QP332.js} +8 -8
  111. package/dist/chunk-X54QP332.js.map +1 -0
  112. package/dist/components/cart/index.js +1 -1
  113. package/dist/components/common/index.js +1 -1
  114. package/dist/components/drawer/index.js +9 -7
  115. package/dist/components/home/HomePageRenderer.d.ts +6 -0
  116. package/dist/components/home/HomePageRenderer.d.ts.map +1 -1
  117. package/dist/components/home/index.js +11 -9
  118. package/dist/components/layout/PageAdvertisements.d.ts +7 -0
  119. package/dist/components/layout/PageAdvertisements.d.ts.map +1 -0
  120. package/dist/components/layout/index.d.ts +1 -0
  121. package/dist/components/layout/index.d.ts.map +1 -1
  122. package/dist/components/layout/index.js +8 -1
  123. package/dist/components/product/index.js +10 -8
  124. package/dist/components/store/index.js +11 -9
  125. package/dist/components/store/index.js.map +1 -1
  126. package/dist/{document-class-SD6BUMZN.js → document-class-7AXAAWGZ.js} +6 -5
  127. package/dist/{document-class-SD6BUMZN.js.map → document-class-7AXAAWGZ.js.map} +1 -1
  128. package/dist/hooks/index.js +9 -7
  129. package/dist/hooks/useGtm.js +9 -7
  130. package/dist/hooks/useMoney.js +9 -7
  131. package/dist/hooks/useStore.js +9 -7
  132. package/dist/index.js +9 -7
  133. package/dist/index.js.map +1 -1
  134. package/dist/{not-found-TUU7WQLL.js → not-found-3EEMQZMU.js} +11 -9
  135. package/dist/not-found-3EEMQZMU.js.map +1 -0
  136. package/dist/providers/index.js +9 -7
  137. package/dist/routes/account/orders.js +1 -1
  138. package/dist/routes/account.js +6 -6
  139. package/dist/routes/blog.js +5 -5
  140. package/dist/routes/brands.js +1 -1
  141. package/dist/routes/cart.js +13 -11
  142. package/dist/routes/cart.js.map +1 -1
  143. package/dist/routes/home.js +12 -10
  144. package/dist/routes/loyalty.js +2 -2
  145. package/dist/routes/page.js +1 -1
  146. package/dist/routes/product-listing.js +11 -9
  147. package/dist/routes/product-listing.js.map +1 -1
  148. package/dist/routes/product.js +11 -9
  149. package/dist/routes/product.js.map +1 -1
  150. package/dist/routes/root/beforeLoad.d.ts +2 -2
  151. package/dist/routes/root/beforeLoad.d.ts.map +1 -1
  152. package/dist/routes/root/head.d.ts.map +1 -1
  153. package/dist/routes/testimonials.js +2 -2
  154. package/dist/routes/thank-you.js +1 -1
  155. package/dist/skeleton.js +12 -10
  156. package/dist/tanstack/document-class.d.ts.map +1 -1
  157. package/dist/tanstack/index.d.ts +1 -0
  158. package/dist/tanstack/index.d.ts.map +1 -1
  159. package/dist/tanstack/stylesheet.d.ts +15 -0
  160. package/dist/tanstack/stylesheet.d.ts.map +1 -0
  161. package/dist/tanstack.js +34 -5
  162. package/dist/tanstack.js.map +1 -1
  163. package/dist/types/index.d.ts +5 -0
  164. package/dist/types/index.d.ts.map +1 -1
  165. package/dist/utils/index.js +1 -1
  166. package/dist/vite/index.js +1 -1
  167. package/package.json +12 -3
  168. package/tailwind/components/Component.tsx.txt +21 -0
  169. package/tailwind/components/ComponentRegistry.ts.txt +66 -0
  170. package/tailwind/components/cart/CartItem.tsx.txt +211 -0
  171. package/tailwind/components/cart/CartSummary.tsx.txt +269 -0
  172. package/tailwind/components/cart/PreOrderNotification.tsx.txt +16 -0
  173. package/tailwind/components/cart/PriceQuoteButton.tsx.txt +21 -0
  174. package/tailwind/components/cart/SeoCartWidget.tsx.txt +99 -0
  175. package/tailwind/components/cart/index.ts.txt +11 -0
  176. package/tailwind/components/cart/lazy.ts.txt +15 -0
  177. package/tailwind/components/collapse/Collapse.tsx.txt +95 -0
  178. package/tailwind/components/collapse/index.ts.txt +41 -0
  179. package/tailwind/components/common/Breadcrumb.tsx.txt +27 -0
  180. package/tailwind/components/common/BreadcrumbJsonLd.tsx.txt +40 -0
  181. package/tailwind/components/common/BreadcrumbTrail.tsx.txt +97 -0
  182. package/tailwind/components/common/CurrencySymbol.tsx.txt +8 -0
  183. package/tailwind/components/common/DeferredData.tsx.txt +122 -0
  184. package/tailwind/components/common/ErrorPage.tsx.txt +69 -0
  185. package/tailwind/components/common/Image.tsx.txt +119 -0
  186. package/tailwind/components/common/Link.tsx.txt +36 -0
  187. package/tailwind/components/common/Link.types.ts.txt +85 -0
  188. package/tailwind/components/common/NoContent.tsx.txt +27 -0
  189. package/tailwind/components/common/RenderWhenVisible.tsx.txt +92 -0
  190. package/tailwind/components/common/index.ts.txt +42 -0
  191. package/tailwind/components/common/lazy.ts.txt +28 -0
  192. package/tailwind/components/defineComponent.ts.txt +32 -0
  193. package/tailwind/components/drawer/Drawer.tsx.txt +219 -0
  194. package/tailwind/components/drawer/index.ts.txt +38 -0
  195. package/tailwind/components/dropdown/Dropdown.tsx.txt +166 -0
  196. package/tailwind/components/dropdown/index.ts.txt +40 -0
  197. package/tailwind/components/home/BundleComponent.tsx.txt +35 -0
  198. package/tailwind/components/home/ComponentErrorBoundary.tsx.txt +98 -0
  199. package/tailwind/components/home/FeaturedProductsStyle1.tsx.txt +125 -0
  200. package/tailwind/components/home/FeaturedProductsStyle2.tsx.txt +127 -0
  201. package/tailwind/components/home/FeaturedProductsStyle3.tsx.txt +71 -0
  202. package/tailwind/components/home/FixedBanner.tsx.txt +68 -0
  203. package/tailwind/components/home/FixedProducts.tsx.txt +72 -0
  204. package/tailwind/components/home/HomePageRenderer.tsx.txt +284 -0
  205. package/tailwind/components/home/PageBlocks.tsx.txt +24 -0
  206. package/tailwind/components/home/ParallaxBackground.tsx.txt +50 -0
  207. package/tailwind/components/home/PhotosSlider.tsx.txt +61 -0
  208. package/tailwind/components/home/ProductsSlider.tsx.txt +87 -0
  209. package/tailwind/components/home/ReorderProducts.tsx.txt +38 -0
  210. package/tailwind/components/home/SquarePhotos.tsx.txt +68 -0
  211. package/tailwind/components/home/StoreFeatures.tsx.txt +34 -0
  212. package/tailwind/components/home/Testimonials.tsx.txt +36 -0
  213. package/tailwind/components/home/Youtube.tsx.txt +48 -0
  214. package/tailwind/components/home/index.ts.txt +50 -0
  215. package/tailwind/components/home/lazy.ts.txt +17 -0
  216. package/tailwind/components/home/register.ts.txt +44 -0
  217. package/tailwind/components/home/reorder-hooks.ts.txt +52 -0
  218. package/tailwind/components/home/types.ts.txt +4 -0
  219. package/tailwind/components/layout/Copyright.tsx.txt +23 -0
  220. package/tailwind/components/layout/CustomerLayout.tsx.txt +91 -0
  221. package/tailwind/components/layout/Footer.tsx.txt +343 -0
  222. package/tailwind/components/layout/Header.tsx.txt +154 -0
  223. package/tailwind/components/layout/MasterLayout.tsx.txt +63 -0
  224. package/tailwind/components/layout/PageAdvertisements.tsx.txt +131 -0
  225. package/tailwind/components/layout/WidgetHead.tsx.txt +53 -0
  226. package/tailwind/components/layout/index.ts.txt +27 -0
  227. package/tailwind/components/layout/lazy.ts.txt +25 -0
  228. package/tailwind/components/layout/types.ts.txt +11 -0
  229. package/tailwind/components/modal/ImageModal.tsx.txt +70 -0
  230. package/tailwind/components/modal/Modal.tsx.txt +245 -0
  231. package/tailwind/components/modal/index.ts.txt +39 -0
  232. package/tailwind/components/modal/lazy.ts.txt +10 -0
  233. package/tailwind/components/navigation/MainMenu.tsx.txt +94 -0
  234. package/tailwind/components/navigation/MobileMenu.tsx.txt +160 -0
  235. package/tailwind/components/navigation/index.ts.txt +4 -0
  236. package/tailwind/components/navigation/lazy.ts.txt +11 -0
  237. package/tailwind/components/product/AddToCartForm.tsx.txt +365 -0
  238. package/tailwind/components/product/ProductCard.tsx.txt +305 -0
  239. package/tailwind/components/product/ProductDetails.tsx.txt +267 -0
  240. package/tailwind/components/product/ProductGallery.tsx.txt +234 -0
  241. package/tailwind/components/product/index.ts.txt +15 -0
  242. package/tailwind/components/product/lazy.ts.txt +9 -0
  243. package/tailwind/components/snippets/AppsSnippets.tsx.txt +225 -0
  244. package/tailwind/components/snippets/SallaSdkScripts.tsx.txt +14 -0
  245. package/tailwind/components/snippets/index.ts.txt +4 -0
  246. package/tailwind/components/store/StoreClosedNotification.tsx.txt +73 -0
  247. package/tailwind/components/store/index.ts.txt +8 -0
  248. package/tailwind/components/store/lazy.ts.txt +8 -0
  249. package/tailwind/components/toast/Toaster.tsx.txt +177 -0
  250. package/tailwind/components/toast/index.ts.txt +10 -0
  251. package/tailwind/components/toast/types.ts.txt +38 -0
  252. package/tailwind/components/toast/useToast.ts.txt +61 -0
  253. package/tailwind/components/tracking/GoogleTag.tsx.txt +77 -0
  254. package/tailwind/components/tracking/SiftSnippet.tsx.txt +49 -0
  255. package/tailwind/components/tracking/TwilightBundles.tsx.txt +67 -0
  256. package/tailwind/components/tracking/index.ts.txt +11 -0
  257. package/tailwind/components/tracking/lazy.ts.txt +11 -0
  258. package/tailwind/components/useComponent.ts.txt +12 -0
  259. package/tailwind/routes/account/index.tsx.txt +16 -0
  260. package/tailwind/routes/account/notifications/NotificationsPage.tsx.txt +54 -0
  261. package/tailwind/routes/account/notifications/index.tsx.txt +39 -0
  262. package/tailwind/routes/account/notifications/loader.ts.txt +22 -0
  263. package/tailwind/routes/account/notifications/types.ts.txt +18 -0
  264. package/tailwind/routes/account/orders/OrderSinglePage.tsx.txt +181 -0
  265. package/tailwind/routes/account/orders/OrdersPage.tsx.txt +28 -0
  266. package/tailwind/routes/account/orders/index.tsx.txt +86 -0
  267. package/tailwind/routes/account/orders/loader.ts.txt +66 -0
  268. package/tailwind/routes/account/orders/single/OrderActions.tsx.txt +197 -0
  269. package/tailwind/routes/account/orders/single/OrderHeader.tsx.txt +143 -0
  270. package/tailwind/routes/account/orders/single/OrderItemRow.tsx.txt +209 -0
  271. package/tailwind/routes/account/orders/single/OrderSections.tsx.txt +215 -0
  272. package/tailwind/routes/account/orders/single/types.ts.txt +38 -0
  273. package/tailwind/routes/account/orders/types.ts.txt +29 -0
  274. package/tailwind/routes/account/profile/ProfilePage.tsx.txt +30 -0
  275. package/tailwind/routes/account/profile/index.tsx.txt +39 -0
  276. package/tailwind/routes/account/profile/loader.ts.txt +16 -0
  277. package/tailwind/routes/account/profile/types.ts.txt +5 -0
  278. package/tailwind/routes/account/settings/SettingsPage.tsx.txt +13 -0
  279. package/tailwind/routes/account/settings/index.tsx.txt +39 -0
  280. package/tailwind/routes/account/settings/loader.ts.txt +13 -0
  281. package/tailwind/routes/account/settings/types.ts.txt +5 -0
  282. package/tailwind/routes/account/types.ts.txt +320 -0
  283. package/tailwind/routes/account/wallet/WalletPage.tsx.txt +195 -0
  284. package/tailwind/routes/account/wallet/index.tsx.txt +38 -0
  285. package/tailwind/routes/account/wallet/loader.ts.txt +27 -0
  286. package/tailwind/routes/account/wallet/types.ts.txt +11 -0
  287. package/tailwind/routes/account/wishlist/WishlistPage.tsx.txt +314 -0
  288. package/tailwind/routes/account/wishlist/WishlistShare.tsx.txt +248 -0
  289. package/tailwind/routes/account/wishlist/index.tsx.txt +40 -0
  290. package/tailwind/routes/account/wishlist/loader.ts.txt +26 -0
  291. package/tailwind/routes/account/wishlist/types.ts.txt +9 -0
  292. package/tailwind/routes/blog/BlogAuthorPage.tsx.txt +82 -0
  293. package/tailwind/routes/blog/BlogCategoryPage.tsx.txt +84 -0
  294. package/tailwind/routes/blog/BlogPage.tsx.txt +191 -0
  295. package/tailwind/routes/blog/BlogSinglePage.tsx.txt +168 -0
  296. package/tailwind/routes/blog/BlogTagPage.tsx.txt +84 -0
  297. package/tailwind/routes/blog/BlogTags.tsx.txt +54 -0
  298. package/tailwind/routes/blog/components.tsx.txt +87 -0
  299. package/tailwind/routes/blog/index.tsx.txt +238 -0
  300. package/tailwind/routes/blog/loader.ts.txt +173 -0
  301. package/tailwind/routes/blog/types.ts.txt +106 -0
  302. package/tailwind/routes/brands/BrandsPage.tsx.txt +179 -0
  303. package/tailwind/routes/brands/index.tsx.txt +47 -0
  304. package/tailwind/routes/brands/loader.ts.txt +14 -0
  305. package/tailwind/routes/brands/types.ts.txt +53 -0
  306. package/tailwind/routes/cart/CartPage.tsx.txt +112 -0
  307. package/tailwind/routes/cart/index.tsx.txt +39 -0
  308. package/tailwind/routes/cart/loader.ts.txt +15 -0
  309. package/tailwind/routes/cart/types.ts.txt +6 -0
  310. package/tailwind/routes/home/HomePage.tsx.txt +53 -0
  311. package/tailwind/routes/home/index.tsx.txt +56 -0
  312. package/tailwind/routes/home/loader.ts.txt +24 -0
  313. package/tailwind/routes/home/types.ts.txt +11 -0
  314. package/tailwind/routes/index.ts.txt +94 -0
  315. package/tailwind/routes/lazy.ts.txt +105 -0
  316. package/tailwind/routes/loyalty/LoyaltyPage.tsx.txt +17 -0
  317. package/tailwind/routes/loyalty/LoyaltyPointsPage.tsx.txt +11 -0
  318. package/tailwind/routes/loyalty/index.tsx.txt +81 -0
  319. package/tailwind/routes/loyalty/loader.ts.txt +53 -0
  320. package/tailwind/routes/loyalty/types.ts.txt +109 -0
  321. package/tailwind/routes/not-found.tsx.txt +50 -0
  322. package/tailwind/routes/page/PageSingle.tsx.txt +34 -0
  323. package/tailwind/routes/page/index.tsx.txt +60 -0
  324. package/tailwind/routes/page/loader.ts.txt +19 -0
  325. package/tailwind/routes/page/types.ts.txt +34 -0
  326. package/tailwind/routes/product/ProductPage.tsx.txt +77 -0
  327. package/tailwind/routes/product/index.tsx.txt +157 -0
  328. package/tailwind/routes/product/loader.ts.txt +35 -0
  329. package/tailwind/routes/product/types.ts.txt +6 -0
  330. package/tailwind/routes/product-listing/ProductListPage.tsx.txt +321 -0
  331. package/tailwind/routes/product-listing/index.tsx.txt +95 -0
  332. package/tailwind/routes/product-listing/loader.ts.txt +323 -0
  333. package/tailwind/routes/product-listing/types.ts.txt +96 -0
  334. package/tailwind/routes/product-listing/useAppliedFilters.ts.txt +130 -0
  335. package/tailwind/routes/product-short-link.ts.txt +104 -0
  336. package/tailwind/routes/root/beforeLoad.ts.txt +354 -0
  337. package/tailwind/routes/root/head.ts.txt +424 -0
  338. package/tailwind/routes/root/index.ts.txt +2 -0
  339. package/tailwind/routes/seo-redirects.ts.txt +82 -0
  340. package/tailwind/routes/testimonials/ReviewsPage.tsx.txt +12 -0
  341. package/tailwind/routes/testimonials/TestimonialsPage.tsx.txt +80 -0
  342. package/tailwind/routes/testimonials/index.tsx.txt +80 -0
  343. package/tailwind/routes/testimonials/loader.ts.txt +29 -0
  344. package/tailwind/routes/testimonials/types.ts.txt +9 -0
  345. package/tailwind/routes/thank-you/ThankYouPage.tsx.txt +241 -0
  346. package/tailwind/routes/thank-you/index.tsx.txt +49 -0
  347. package/tailwind/routes/thank-you/loader.ts.txt +40 -0
  348. package/tailwind/routes/thank-you/types.ts.txt +16 -0
  349. package/tailwind/routes/types.ts.txt +41 -0
  350. package/dist/BlogAuthorPage-LFAUMKC7.js.map +0 -1
  351. package/dist/BlogCategoryPage-NWYFFPCD.js.map +0 -1
  352. package/dist/BlogPage-BOHXFV7E.js.map +0 -1
  353. package/dist/BlogSinglePage-FYEXQPMB.js.map +0 -1
  354. package/dist/BlogTagPage-DAM3RNVA.js.map +0 -1
  355. package/dist/BrandsPage-GAKQCE6O.js.map +0 -1
  356. package/dist/Breadcrumb-NAYSZNRV.js.map +0 -1
  357. package/dist/CartItem-ZRVLIZT3.js.map +0 -1
  358. package/dist/CartSummary-U2TVLG35.js.map +0 -1
  359. package/dist/CustomerLayout-WSKXWI7D.js.map +0 -1
  360. package/dist/LoyaltyPage-IG5KSYNO.js.map +0 -1
  361. package/dist/LoyaltyPointsPage-RKO56ZER.js.map +0 -1
  362. package/dist/MobileMenu-E4X2GOZ3.js.map +0 -1
  363. package/dist/NotificationsPage-BJ5XVWEG.js.map +0 -1
  364. package/dist/OrderSinglePage-544E36FJ.js.map +0 -1
  365. package/dist/OrdersPage-AFBWVTZU.js.map +0 -1
  366. package/dist/ProductDetails-SDZHALU6.js.map +0 -1
  367. package/dist/ProfilePage-V5KV6GQK.js.map +0 -1
  368. package/dist/SeoCartWidget-JAV6M3X5.js.map +0 -1
  369. package/dist/SettingsPage-43SUCLQR.js.map +0 -1
  370. package/dist/ThankYouPage-52SSZ7FV.js.map +0 -1
  371. package/dist/WalletPage-4AN4UJ52.js.map +0 -1
  372. package/dist/WishlistPage-L6IYJUJZ.js.map +0 -1
  373. package/dist/chunk-3PWARKZX.js.map +0 -1
  374. package/dist/chunk-4EA56R6I.js +0 -14
  375. package/dist/chunk-R54GSKJS.js.map +0 -1
  376. package/dist/chunk-UNEZZMQA.js.map +0 -1
  377. package/dist/not-found-TUU7WQLL.js.map +0 -1
@@ -0,0 +1,343 @@
1
+ import { useState } from 'react';
2
+ import { SallaAppsIcons } from '@salla.sa/twilight-components-react/apps-icons';
3
+ import { SallaSocial } from '@salla.sa/twilight-components-react/social';
4
+ import { SallaMenu } from '@salla.sa/twilight-components-react/menu';
5
+ import { SallaTrustBadges } from '@salla.sa/twilight-components-react/trust-badges';
6
+ import { SallaPayments } from '@salla.sa/twilight-components-react/payments';
7
+ import { useTwilight } from '../../providers/twilight-context';
8
+ import { useTranslation } from '../../providers/I18nProvider';
9
+ import { HookSlot } from '../../hooks/HookSlot';
10
+ import { Link, Image } from '../common';
11
+ import { ImageModal } from '../modal';
12
+
13
+ /** Salla's value-added-tax badge icon (70×70, CDN-resized) — a platform
14
+ * asset shared by every store, not specific to any one theme. */
15
+ const TAX_ICON_URL =
16
+ 'https://cdn.salla.network/cdn-cgi/image/fit=scale-down,width=70,height=70,onerror=redirect,format=auto/images/tax.png?v=2.0.5';
17
+
18
+ /**
19
+ * The store's value-added-tax registration: the tax number, plus the certificate as
20
+ * the tax icon that opens the certificate image full size in a modal. Nothing shows
21
+ * when the store has no tax number.
22
+ */
23
+ function FooterTax({ dark }: { dark: boolean }) {
24
+ const { store } = useTwilight();
25
+ const { t } = useTranslation();
26
+ const [open, setOpen] = useState(false);
27
+ const number = store.settings?.tax?.number;
28
+ const certificate = store.settings?.tax?.certificate;
29
+ if (!number) return null;
30
+
31
+ return (
32
+ <div
33
+ data-testid="store-footer-tax"
34
+ className="store-footer__tax flex items-end gap-2 rtl:space-x-reverse"
35
+ >
36
+ {certificate && (
37
+ <>
38
+ <button
39
+ type="button"
40
+ data-testid="store-footer-tax-certificate"
41
+ className="store-footer__tax-cert shrink-0"
42
+ aria-label={t('common.elements.tax_certificate', 'Tax Certificate')}
43
+ aria-haspopup="dialog"
44
+ onClick={() => setOpen(true)}
45
+ >
46
+ <img
47
+ src={TAX_ICON_URL}
48
+ alt={t('common.elements.tax_certificate', 'Tax Certificate')}
49
+ width={40}
50
+ height={50}
51
+ loading="lazy"
52
+ decoding="async"
53
+ className="w-10 rounded-sm transition-opacity hover:opacity-80"
54
+ />
55
+ </button>
56
+ <ImageModal
57
+ isOpen={open}
58
+ onClose={() => setOpen(false)}
59
+ src={certificate}
60
+ alt={t('common.elements.tax_certificate', 'Tax Certificate')}
61
+ title={t('common.elements.tax_certificate', 'Tax Certificate')}
62
+ />
63
+ </>
64
+ )}
65
+ <div>
66
+ <p className={`mb-1 text-sm opacity-70 ${dark ? 'text-white' : ''}`}>
67
+ {t('common.elements.tax_number', 'Tax Number')}
68
+ </p>
69
+ <b className="text-sm">{number}</b>
70
+ </div>
71
+ </div>
72
+ );
73
+ }
74
+
75
+ /** Saudi Business Center (SBC) logo, CDN-versioned like the tax icon -- also a
76
+ * platform asset, not theme-specific. */
77
+ const SBC_ICON_URL = 'https://cdn.salla.network/images/sbc.png?v=2.0.5';
78
+
79
+ /**
80
+ * The store's Saudi Business Center certificate: a "Trusted by Business Platform" badge that
81
+ * opens the certificate's page on the SBC site so visitors can verify the merchant. Nothing
82
+ * shows when the store has no certificate.
83
+ */
84
+ function FooterSbc({ dark }: { dark: boolean }) {
85
+ const { store } = useTwilight();
86
+ const { t } = useTranslation();
87
+ const id = store.settings?.certificate?.id;
88
+ if (!id) return null;
89
+
90
+ return (
91
+ <a
92
+ href={`https://eauthenticate.saudibusiness.gov.sa/certificate-details/${encodeURIComponent(id)}`}
93
+ target="_blank"
94
+ rel="noopener noreferrer"
95
+ data-testid="store-footer-sbc"
96
+ className="store-footer__sbc flex items-center gap-2.5 transition-opacity hover:opacity-75"
97
+ >
98
+ <img
99
+ src={SBC_ICON_URL}
100
+ alt={`SBC ${id}`}
101
+ width={56}
102
+ height={40}
103
+ loading="lazy"
104
+ decoding="async"
105
+ className="h-10 w-14 rounded-sm"
106
+ />
107
+ <div>
108
+ <p className={`text-xs opacity-70 ${dark ? 'text-white' : ''}`}>
109
+ {t('blocks.footer.sbc_trusted_by', 'Trusted by')}
110
+ </p>
111
+ <b className="text-sm">{t('blocks.footer.sbc_platform', 'Business Platform')}</b>
112
+ </div>
113
+ </a>
114
+ );
115
+ }
116
+
117
+ /** Trust badges, the tax registration and the SBC badge, side by side (wrapping on narrow columns). */
118
+ function FooterTrust({ dark, centered }: { dark: boolean; centered: boolean }) {
119
+ return (
120
+ <div
121
+ data-testid="store-footer-trust"
122
+ className={`store-footer__trust flex flex-wrap items-end gap-x-6 gap-y-3${centered ? ' justify-center' : ''}`}
123
+ >
124
+ <FooterTax dark={dark} />
125
+ <FooterSbc dark={dark} />
126
+ <SallaTrustBadges {...(dark ? { dark: true } : {})} />
127
+ </div>
128
+ );
129
+ }
130
+
131
+ /**
132
+ * The footer's own menu of ("important") links -- skipped entirely when
133
+ * `inSheet`, for a theme that opens it from somewhere else of its own (a
134
+ * mobile account sheet, a dedicated links screen, …) instead; showing it
135
+ * again down here would just be a redundant second copy. `heading` wraps it
136
+ * in the grid layout's own `.store-footer__links` column; the centered
137
+ * layout's inline row has none.
138
+ */
139
+ function FooterLinksMenu({
140
+ inSheet,
141
+ className,
142
+ heading,
143
+ dark,
144
+ }: {
145
+ inSheet: boolean;
146
+ className: string;
147
+ heading?: string;
148
+ dark: boolean;
149
+ }) {
150
+ if (inSheet) return null;
151
+
152
+ const menu = (
153
+ <SallaMenu source="footer" useReactLink className={className} suppressHydrationWarning />
154
+ );
155
+ if (!heading) return menu;
156
+
157
+ return (
158
+ <div className="store-footer__links">
159
+ <h3 className={`mb-3 text-lg font-bold lg:mb-5 ${dark ? 'text-white' : 'text-gray-900'}`}>
160
+ {heading}
161
+ </h3>
162
+ {menu}
163
+ </div>
164
+ );
165
+ }
166
+
167
+ /** The logo link, same shape in both layouts -- only its own spacing differs. */
168
+ function FooterLogo({ className }: { className: string }) {
169
+ const { store } = useTwilight();
170
+ return (
171
+ <Link
172
+ to="/"
173
+ className={`store-footer__logo inline-flex items-center m-0 transition-opacity hover:opacity-75 ${className}`}
174
+ >
175
+ <Image
176
+ src={store.logo}
177
+ alt={store.name || 'Store'}
178
+ width={150}
179
+ height={44}
180
+ objectFit="contain"
181
+ className="h-11 w-auto max-w-[180px]"
182
+ />
183
+ </Link>
184
+ );
185
+ }
186
+
187
+ /** Merchant-authored description HTML -- mirrors the Twig theme's
188
+ * `store.description|raw`. Renders nothing when the store has none set. */
189
+ function FooterDescription({ className }: { className: string }) {
190
+ const { store } = useTwilight();
191
+ if (!store.description) return null;
192
+ return (
193
+ <div
194
+ className={`store-footer__desc opacity-80 ${className}`}
195
+ dangerouslySetInnerHTML={{ __html: store.description }}
196
+ />
197
+ );
198
+ }
199
+
200
+ /** Centered layout: logo, description, social, footer menu, trust badges and
201
+ * app buttons stacked as one centered column. */
202
+ function FooterCenteredBody({ inSheet, isDark }: { inSheet: boolean; isDark: boolean }) {
203
+ const { store } = useTwilight();
204
+ const { t } = useTranslation();
205
+
206
+ return (
207
+ <div className="container mx-auto flex max-w-2xl flex-col items-center gap-6 text-center">
208
+ <FooterLogo className="" />
209
+ <FooterDescription className="" />
210
+ <SallaSocial />
211
+
212
+ <FooterLinksMenu
213
+ inSheet={inSheet}
214
+ className="store-footer__menu store-footer__menu--inline"
215
+ dark={isDark}
216
+ />
217
+
218
+ <FooterTrust dark={isDark} centered />
219
+
220
+ <div className="store-footer__apps flex flex-col items-center gap-3">
221
+ <SallaAppsIcons
222
+ apps={store.apps}
223
+ appsTitle={t('blocks.footer.download_apps', 'Get our apps')}
224
+ />
225
+ </div>
226
+ </div>
227
+ );
228
+ }
229
+
230
+ /** Columns layout: brand (logo / description / social / trust) and app
231
+ * buttons side by side, with the footer menu as its own labeled column. */
232
+ function FooterColumnsBody({ inSheet, isDark }: { inSheet: boolean; isDark: boolean }) {
233
+ const { store } = useTwilight();
234
+ const { t } = useTranslation();
235
+
236
+ return (
237
+ <div className="container grid grid-cols-1 gap-10 sm:grid-cols-2 lg:grid-cols-4 lg:gap-16">
238
+ <div className="store-footer__brand rtl:lg:pl-16 ltr:lg:pr-16 sm:col-span-2">
239
+ <FooterLogo className="mb-5" />
240
+ <FooterDescription className="max-w-sm leading-6 my-5" />
241
+ <SallaSocial className="block mb-6" />
242
+ <FooterTrust dark={isDark} centered={false} />
243
+ </div>
244
+
245
+ <FooterLinksMenu
246
+ inSheet={inSheet}
247
+ className="store-footer__menu store-footer__menu--stacked"
248
+ heading={t('blocks.footer.important_links', 'Important links')}
249
+ dark={isDark}
250
+ />
251
+
252
+ <div className="store-footer__apps">
253
+ <SallaAppsIcons
254
+ apps={store.apps}
255
+ appsTitle={t('blocks.footer.download_apps', 'Get our apps')}
256
+ />
257
+ </div>
258
+ </div>
259
+ );
260
+ }
261
+
262
+ export interface FooterProps {
263
+ /**
264
+ * Renders as a plain `<div>` (no second `<footer>` landmark) with the
265
+ * `footer:start` / `footer:end` hook slots skipped -- for a theme that
266
+ * repeats these widgets somewhere other than the page's own footer (a
267
+ * mobile sheet, for example) rather than rendering the real page footer a
268
+ * second time.
269
+ */
270
+ inSheet?: boolean;
271
+ }
272
+
273
+ /**
274
+ * Storefront footer.
275
+ *
276
+ * Two layouts, chosen by the `footer_layout` theme setting:
277
+ * - **columns** (default): logo / description / social / trust badges, the
278
+ * footer menu ("important links"), and app-store buttons, side by side.
279
+ * - **centered**: the same pieces stacked as one centered column, with the
280
+ * footer menu laid out as an inline, pipe-separated row.
281
+ *
282
+ * Styling for the handful of pieces rendered by web components this owns no
283
+ * markup for (`<salla-social>`'s `.social-link` icons, `<salla-menu>`'s
284
+ * `.s-menu-footer-item` rows, `<salla-payments>`'s `.s-payments-list-item`
285
+ * icons) is the consuming theme's own responsibility, the same way
286
+ * `inSheet`'s own presentation is -- this component owns structure and data,
287
+ * not every pixel.
288
+ */
289
+ export function Footer({ inSheet = false }: FooterProps = {}) {
290
+ const { store, theme } = useTwilight();
291
+ const { t } = useTranslation();
292
+ const year = new Date().getFullYear();
293
+ const isCentered = theme.settings?.footer_layout === 'centered';
294
+ const isDark = !!theme.settings?.footer_is_dark;
295
+
296
+ const Root = inSheet ? 'div' : 'footer';
297
+
298
+ return (
299
+ <Root
300
+ className={`store-footer store-footer--${isCentered ? 'centered' : 'columns'}${
301
+ inSheet ? ' store-footer--sheet' : ''
302
+ } relative mt-8 sm:mt-16 ${isDark ? 'bg-[#0E0F0F] text-white' : 'bg-[#F4F7F2] text-gray-600'}`}
303
+ suppressHydrationWarning
304
+ >
305
+ {!inSheet && <HookSlot name="footer:start" />}
306
+
307
+ <div
308
+ aria-label="footer"
309
+ className={`store-footer__inner border-b py-8 lg:py-16 ${
310
+ isDark ? 'bg-[#1D1F1F] border-dashed border-white/10' : 'bg-transparent border-gray-200'
311
+ }`}
312
+ >
313
+ {isCentered ? (
314
+ <FooterCenteredBody inSheet={inSheet} isDark={isDark} />
315
+ ) : (
316
+ <FooterColumnsBody inSheet={inSheet} isDark={isDark} />
317
+ )}
318
+ </div>
319
+
320
+ <div className="store-footer__bottom container md:flex items-center justify-between gap-4 py-4 lg:py-7 text-center">
321
+ <span className={`text-sm copyright-text ${isDark ? 'text-gray-400' : 'text-gray-700'}`}>
322
+ <HookSlot
323
+ name="copyright"
324
+ context={{ storeName: store.name }}
325
+ fallback={
326
+ <>
327
+ {t('blocks.footer.copyright', 'All rights reserved')}{' '}
328
+ <Link to="/" className="transition-opacity hover:opacity-75 hover:text-primary">
329
+ {store.name}
330
+ </Link>{' '}
331
+ © {year}
332
+ </>
333
+ }
334
+ />
335
+ </span>
336
+
337
+ <SallaPayments />
338
+ </div>
339
+
340
+ {!inSheet && <HookSlot name="footer:end" />}
341
+ </Root>
342
+ );
343
+ }
@@ -0,0 +1,154 @@
1
+ import { useState } from 'react';
2
+ import { useIsHome } from '../../providers/NavigationProvider';
3
+ import { SallaCartSummary } from '@salla.sa/twilight-components-react/cart-summary';
4
+ import { SallaContacts } from '@salla.sa/twilight-components-react/contacts';
5
+ import { SallaLocalizationModal } from '@salla.sa/twilight-components-react/localization-modal';
6
+ import { SallaMenu } from '@salla.sa/twilight-components-react/menu';
7
+ import { SallaSearch } from '@salla.sa/twilight-components-react/search';
8
+ import { SallaUserMenu } from '@salla.sa/twilight-components-react/user-menu';
9
+ import { useTwilight } from '../../providers/twilight-context';
10
+ import { useTranslation } from '../../providers/I18nProvider';
11
+ import { HookSlot } from '../../hooks/HookSlot';
12
+ import { MainMenu, MobileMenu } from '../navigation';
13
+ import { Image } from '../common';
14
+ import { PageAdvertisements } from './PageAdvertisements';
15
+ import type { Cart } from '../../types';
16
+
17
+ interface HeaderProps {
18
+ cart?: Cart;
19
+ }
20
+
21
+ export function Header(_props: HeaderProps) {
22
+ const { currency, theme, store } = useTwilight();
23
+ const { locale, languageName } = useTranslation();
24
+ const settings = theme?.settings || {};
25
+ const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
26
+
27
+ // Check if we're on homepage
28
+ const isHomepage = useIsHome();
29
+
30
+ const StoreTitle = isHomepage ? 'h1' : 'span';
31
+
32
+ return (
33
+ <header className="store-header" suppressHydrationWarning>
34
+ <HookSlot name="header:start" />
35
+ <div className="advertisement-slot">
36
+ <PageAdvertisements />
37
+ </div>
38
+ {/* Top Nav */}
39
+ <div className={`top-navbar${settings.topnav_is_dark ? ' top-navbar--dark' : ''}`}>
40
+ <div className="container flex justify-between">
41
+ <div className="flex-1 flex items-center gap-2">
42
+ {/* Footer Menu */}
43
+ {settings.important_links && <SallaMenu source="footer" topnav />}
44
+
45
+ {/* Language & Currency */}
46
+ <div className="header-buttons">
47
+ {(store?.settings?.is_multilingual || store?.settings?.currencies_enabled) && (
48
+ <>
49
+ <button
50
+ type="button"
51
+ onClick={() => window.salla?.event.dispatch('localization::open')}
52
+ className="btn--rounded-gray basis-0"
53
+ >
54
+ <span>{languageName}</span>
55
+ <span className="mx-1.5">|</span>
56
+ <span className={`flag iti__flag iti__${store?.country.toLowerCase()}`}></span>
57
+
58
+ <span>
59
+ {store?.settings?.use_sar_symbol && currency?.code === 'SAR' ? (
60
+ <i className="sicon-sar"></i>
61
+ ) : (
62
+ <>{currency?.symbol}</>
63
+ )}
64
+ </span>
65
+ </button>
66
+ {/* TODO:: support switch the lang without hit the web API, by set it in the sdk and local storage */}
67
+ {/* TODO:: Support switch the curency without hit the web API, by set it in the sdk and local storage */}
68
+ <SallaLocalizationModal
69
+ language={locale}
70
+ currency={currency?.code || 'SAR'}
71
+ suppressHydrationWarning
72
+ />
73
+ </>
74
+ )}
75
+ </div>
76
+
77
+ {/* Scopes / Branches */}
78
+ {store?.scope && (
79
+ <button
80
+ type="button"
81
+ className="btn--rounded-gray"
82
+ onClick={() =>
83
+ window.salla?.event.dispatch('scopes::open', {
84
+ mode: 'default',
85
+ })
86
+ }
87
+ >
88
+ <i className="sicon-location rtl:ml-1 ltr:mr-1"></i>
89
+ {store.scope.name}
90
+ </button>
91
+ )}
92
+
93
+ {/* Search bar */}
94
+ <div className="header-search flex-1">
95
+ {/* todo:: use a search icon insted with open the search popup */}
96
+ <SallaSearch inline oval height={36} />
97
+ </div>
98
+ </div>
99
+
100
+ <SallaContacts contacts={store?.contacts} contactsTitle="N/A" isHeader />
101
+ </div>
102
+ </div>
103
+
104
+ {/* Main Nav */}
105
+ <div
106
+ id="mainnav"
107
+ className={`main-nav-container shadow-default bg-white flex items-center relative z-50${settings.header_is_sticky ? ' sticky top-0' : ''}`}
108
+ >
109
+ <div className="inner bg-inherit w-full">
110
+ <div className="container">
111
+ <div className="flex items-stretch justify-between relative">
112
+ <div className="flex items-center">
113
+ <a
114
+ className="lg:hidden mburger mburger--collapse leading-none rtl:ml-4 ltr:mr-4"
115
+ href="#mobile-menu"
116
+ aria-label="Open-menu"
117
+ onClick={(e) => {
118
+ e.preventDefault();
119
+ setIsMobileMenuOpen(true);
120
+ }}
121
+ >
122
+ <i className="sicon-menu text-primary text-2xl"></i>
123
+ </a>
124
+ <a className="navbar-brand" href={store?.url || '/'}>
125
+ <div className="logo-wrapper">
126
+ <Image
127
+ src={store?.logo}
128
+ alt={`${store?.name || 'Store'} logo`}
129
+ priority
130
+ width={120}
131
+ height={48}
132
+ />
133
+ </div>
134
+ <StoreTitle className="sr-only">{store?.name || 'Store'}</StoreTitle>
135
+ </a>
136
+ <MainMenu />
137
+ </div>
138
+ <div className="flex items-center justify-end my-2.5">
139
+ <SallaUserMenu avatarOnly showHeader className="header-btn" />
140
+ <SallaCartSummary className="ml-4 rtl:ml-[unset] rtl:mr-4">
141
+ <i slot="icon" className="header-btn__icon icon sicon-shopping-bag"></i>
142
+ </SallaCartSummary>
143
+ </div>
144
+ </div>
145
+ </div>
146
+ </div>
147
+ </div>
148
+
149
+ {/* Mobile Menu */}
150
+ <MobileMenu isOpen={isMobileMenuOpen} onClose={() => setIsMobileMenuOpen(false)} />
151
+ <HookSlot name="header:end" />
152
+ </header>
153
+ );
154
+ }
@@ -0,0 +1,63 @@
1
+ import { lazy, Suspense } from 'react';
2
+ import { Header } from './Header';
3
+ import { Footer } from './Footer';
4
+ import type { LayoutProps } from '../../types';
5
+ import { useTwilight } from '../../providers/twilight-context';
6
+ import { useUser } from '../../hooks';
7
+
8
+ const SallaLoginModal = lazy(() =>
9
+ import('@salla.sa/twilight-components-react/login-modal').then((m) => ({
10
+ default: m.SallaLoginModal,
11
+ }))
12
+ );
13
+ const SallaScopes = lazy(() =>
14
+ import('@salla.sa/twilight-components-react/scopes').then((m) => ({
15
+ default: m.SallaScopes,
16
+ }))
17
+ );
18
+ const SallaOfferModal = lazy(() =>
19
+ import('@salla.sa/twilight-components-react/offer-modal').then((m) => ({
20
+ default: m.SallaOfferModal,
21
+ }))
22
+ );
23
+
24
+ export function MasterLayout({ children }: LayoutProps) {
25
+ const { store } = useTwilight();
26
+ const { isLoggedIn } = useUser();
27
+
28
+ return (
29
+ <>
30
+ <div className="app-inner flex flex-col min-h-full">
31
+ <Header />
32
+ <main id="main-content" role="main" className="flex-1">
33
+ {children}
34
+ </main>
35
+ <Footer />
36
+ </div>
37
+
38
+ <Suspense>
39
+ <SallaOfferModal />
40
+ </Suspense>
41
+
42
+ {!isLoggedIn && (
43
+ <Suspense>
44
+ <SallaLoginModal
45
+ isEmailAllowed={store?.settings?.auth?.email_allowed}
46
+ isMobileAllowed={store?.settings?.auth?.mobile_allowed}
47
+ isEmailRequired={store?.settings?.auth?.is_email_required}
48
+ suppressHydrationWarning
49
+ />
50
+ </Suspense>
51
+ )}
52
+
53
+ {store?.scope && (
54
+ <Suspense>
55
+ <SallaScopes
56
+ selection={store?.scope?.display_as === 'popup' ? 'mandatory' : 'optional'}
57
+ suppressHydrationWarning
58
+ />
59
+ </Suspense>
60
+ )}
61
+ </>
62
+ );
63
+ }
@@ -0,0 +1,131 @@
1
+ import { useEffect, useState, useSyncExternalStore } from 'react';
2
+ import { useQuery } from '@tanstack/react-query';
3
+ import { advertisement, advertisementsFor, type Advertisement } from '../../api/advertisement';
4
+ import { whenSallaReady } from '../../utils/sdk-helpers';
5
+ import { useTranslation } from '../../providers/I18nProvider';
6
+ import { getTwilightContext, subscribeTwilight } from '../../twilight/context';
7
+
8
+ const currentPageSlug = () => getTwilightContext().page?.slug;
9
+
10
+ /** The key `<salla-advertisement>` marks a closed ad with, through `salla.storage` (store.js, JSON). */
11
+ const dismissedKey = (id: number) => `statusAd-${id}`;
12
+
13
+ /** Stable while the query has no data, so the effect below doesn't rerun on every render. */
14
+ const NO_ADS: Advertisement[] = [];
15
+
16
+ /** Hides the ads this visitor closed before the first paint; the effect then drops them. */
17
+ const HIDE_DISMISSED_SCRIPT =
18
+ "(function(){try{if(window.self!==window.top)return;document.currentScript.parentNode.querySelectorAll('.s-advertisement[data-id]').forEach(function(e){if(localStorage.getItem('statusAd-'+e.getAttribute('data-id')))e.hidden=true})}catch(e){}})();";
19
+
20
+ function isDismissed(id: number): boolean {
21
+ try {
22
+ return Boolean(window.localStorage.getItem(dismissedKey(id)));
23
+ } catch {
24
+ return false;
25
+ }
26
+ }
27
+
28
+ function dismiss(id: number): void {
29
+ const storage = window.salla?.storage;
30
+ if (typeof storage?.set === 'function') storage.set(dismissedKey(id), 'dismissed');
31
+ else {
32
+ try {
33
+ window.localStorage.setItem(dismissedKey(id), JSON.stringify('dismissed'));
34
+ } catch {
35
+ // Storage disabled: the ad hides for this page view only.
36
+ }
37
+ }
38
+ }
39
+
40
+ interface ClientView {
41
+ /** The theme editor's iframe, or the SDK's debug flag: every ad shows, closed or not. */
42
+ preview: boolean;
43
+ /** Inside the Salla mobile app, which shows no ads. */
44
+ inApp: boolean;
45
+ dismissed: Set<number>;
46
+ }
47
+
48
+ /**
49
+ * The merchant's announcement bars, in place of `<salla-advertisement>`. Same data, rules and
50
+ * markup, but rendered on the server (the ads are prefetched in the root `beforeLoad`), so the
51
+ * bar is in the first paint instead of pushing the page down when the web component fetches it.
52
+ */
53
+ export function PageAdvertisements() {
54
+ const { t } = useTranslation();
55
+ // The deepest route loader's `page`, the page `salla.config` reports.
56
+ const pageSlug = useSyncExternalStore(subscribeTwilight, currentPageSlug, currentPageSlug);
57
+ // The query's `retry: 0` only bounds the server wait; the browser may retry.
58
+ const { data: ads = NO_ADS } = useQuery({ ...advertisement.queries.list(), retry: 2 });
59
+ const [view, setView] = useState<ClientView | null>(null);
60
+
61
+ useEffect(() => {
62
+ const dismissed = new Set(ads.filter((ad) => isDismissed(ad.id)).map((ad) => ad.id));
63
+ setView({ preview: window.self !== window.top, inApp: false, dismissed });
64
+ return whenSallaReady((sdk) => {
65
+ const config = sdk.config as { isMobileApp?: () => boolean; isDebug?: () => boolean };
66
+ const inApp = Boolean(config?.isMobileApp?.());
67
+ const debug = Boolean(config?.isDebug?.());
68
+ if (inApp || debug) {
69
+ setView((current) =>
70
+ current ? { ...current, inApp, preview: current.preview || debug } : current
71
+ );
72
+ }
73
+ });
74
+ }, [ads]);
75
+
76
+ if (view?.inApp) return null;
77
+ const shown = view?.preview
78
+ ? ads
79
+ : advertisementsFor(ads, pageSlug).filter((ad) => !view?.dismissed.has(ad.id));
80
+ if (!shown.length) return null;
81
+
82
+ const close = (id: number) => {
83
+ dismiss(id);
84
+ setView((current) =>
85
+ current ? { ...current, dismissed: new Set(current.dismissed).add(id) } : current
86
+ );
87
+ };
88
+
89
+ return (
90
+ <>
91
+ {shown.map((ad) => (
92
+ <div
93
+ key={ad.id}
94
+ className="s-advertisement"
95
+ data-id={ad.id}
96
+ style={{ backgroundColor: ad.colors?.bg, color: ad.colors?.text }}
97
+ >
98
+ <div className="s-advertisement-content container flex justify-between items-center py-2.5">
99
+ <span className="s-advertisement-content-main flex-1 font-bold text-center text-sm">
100
+ {/* Rendered without an icon too, as the SDK does: its margin is part of the layout. */}
101
+ <i
102
+ className={`s-advertisement-content-icon inline-block align-middle me-2 text-base ${ad.icon ?? ''}`}
103
+ />
104
+ {ad.url ? (
105
+ <a
106
+ href={ad.url}
107
+ target={ad.target ?? undefined}
108
+ rel={ad.target === '_blank' ? 'noopener' : undefined}
109
+ >
110
+ {ad.description}
111
+ </a>
112
+ ) : (
113
+ ad.description
114
+ )}
115
+ </span>
116
+ <button
117
+ type="button"
118
+ className="s-advertisement-action px-1"
119
+ aria-label={t('common.buttons.close', 'Close')}
120
+ onClick={() => close(ad.id)}
121
+ >
122
+ {/* `inline-block`: before the icon CSS loads, an empty inline `<i>` leaves the button 0px tall. */}
123
+ <i className="sicon-cancel inline-block" />
124
+ </button>
125
+ </div>
126
+ </div>
127
+ ))}
128
+ <script suppressHydrationWarning>{HIDE_DISMISSED_SCRIPT}</script>
129
+ </>
130
+ );
131
+ }