@salla.sa/twilight-theme-engine 1.0.85 → 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.
- package/dist/{AddToCartForm-B5C2M4OZ.js → AddToCartForm-5X2WZDWR.js} +3 -3
- package/dist/{AddToCartForm-B5C2M4OZ.js.map → AddToCartForm-5X2WZDWR.js.map} +1 -1
- package/dist/{BlogAuthorPage-GTF4UGSS.js → BlogAuthorPage-LO7FSSJR.js} +12 -10
- package/dist/BlogAuthorPage-LO7FSSJR.js.map +1 -0
- package/dist/{BlogCategoryPage-TN7Y4NBP.js → BlogCategoryPage-KKFQ3PGR.js} +12 -10
- package/dist/BlogCategoryPage-KKFQ3PGR.js.map +1 -0
- package/dist/{BlogPage-TDB3M3PI.js → BlogPage-VLF3NSGE.js} +11 -9
- package/dist/BlogPage-VLF3NSGE.js.map +1 -0
- package/dist/{BlogSinglePage-4A52Q5QW.js → BlogSinglePage-AVK535IW.js} +11 -9
- package/dist/BlogSinglePage-AVK535IW.js.map +1 -0
- package/dist/{BlogTagPage-TOVPBETA.js → BlogTagPage-F3T4D7RH.js} +12 -10
- package/dist/BlogTagPage-F3T4D7RH.js.map +1 -0
- package/dist/{BrandsPage-VUDGYSQI.js → BrandsPage-LVSBZSVC.js} +11 -9
- package/dist/BrandsPage-LVSBZSVC.js.map +1 -0
- package/dist/{Breadcrumb-CI3SN5YR.js → Breadcrumb-VLFTXRFC.js} +11 -9
- package/dist/Breadcrumb-VLFTXRFC.js.map +1 -0
- package/dist/{CartItem-BHLEURAB.js → CartItem-G3H34GW6.js} +11 -9
- package/dist/CartItem-G3H34GW6.js.map +1 -0
- package/dist/{CartSummary-QERVESYN.js → CartSummary-VH37RPUI.js} +11 -9
- package/dist/CartSummary-VH37RPUI.js.map +1 -0
- package/dist/{Copyright-RL6ALKRC.js → Copyright-HE5ZWINZ.js} +3 -3
- package/dist/{Copyright-RL6ALKRC.js.map → Copyright-HE5ZWINZ.js.map} +1 -1
- package/dist/{CustomerLayout-VRZUWGQK.js → CustomerLayout-LKBLOAVW.js} +11 -9
- package/dist/CustomerLayout-LKBLOAVW.js.map +1 -0
- package/dist/{Footer-ZWBZCMDL.js → Footer-HHXAST72.js} +5 -5
- package/dist/{Footer-ZWBZCMDL.js.map → Footer-HHXAST72.js.map} +1 -1
- package/dist/{Header-WNK4O66B.js → Header-GEVZM2YG.js} +8 -6
- package/dist/{Header-WNK4O66B.js.map → Header-GEVZM2YG.js.map} +1 -1
- package/dist/{LoyaltyPage-CGTHWJ76.js → LoyaltyPage-M5CT3CFD.js} +11 -9
- package/dist/LoyaltyPage-M5CT3CFD.js.map +1 -0
- package/dist/{LoyaltyPointsPage-QSU6K3PW.js → LoyaltyPointsPage-SDJB52U6.js} +10 -3
- package/dist/LoyaltyPointsPage-SDJB52U6.js.map +1 -0
- package/dist/{MainMenu-V3T7F32R.js → MainMenu-I7EYS752.js} +3 -3
- package/dist/{MainMenu-V3T7F32R.js.map → MainMenu-I7EYS752.js.map} +1 -1
- package/dist/{MasterLayout-XQF644LD.js → MasterLayout-QDK4T3NE.js} +11 -9
- package/dist/{MasterLayout-XQF644LD.js.map → MasterLayout-QDK4T3NE.js.map} +1 -1
- package/dist/{MobileMenu-U4BVQYKH.js → MobileMenu-7VTCWAVJ.js} +11 -9
- package/dist/MobileMenu-7VTCWAVJ.js.map +1 -0
- package/dist/{NotificationsPage-ZRRQY7TF.js → NotificationsPage-QCABE6QD.js} +11 -9
- package/dist/NotificationsPage-QCABE6QD.js.map +1 -0
- package/dist/{OrderSinglePage-O3XYVMG5.js → OrderSinglePage-2HVBKBG4.js} +11 -9
- package/dist/OrderSinglePage-2HVBKBG4.js.map +1 -0
- package/dist/{OrdersPage-CMXZF5MI.js → OrdersPage-5MNCPCM3.js} +11 -9
- package/dist/OrdersPage-5MNCPCM3.js.map +1 -0
- package/dist/{PageSingle-IIG2US43.js → PageSingle-EN5XIM2B.js} +3 -3
- package/dist/{PageSingle-IIG2US43.js.map → PageSingle-EN5XIM2B.js.map} +1 -1
- package/dist/{ProductDetails-65GDABI7.js → ProductDetails-UEWQPPDZ.js} +13 -6
- package/dist/ProductDetails-UEWQPPDZ.js.map +1 -0
- package/dist/{ProfilePage-SNJ4WXU4.js → ProfilePage-TZSU3GHZ.js} +11 -9
- package/dist/ProfilePage-TZSU3GHZ.js.map +1 -0
- package/dist/{ReviewsPage-UT7UT55J.js → ReviewsPage-WYZ6CHF2.js} +3 -3
- package/dist/{ReviewsPage-UT7UT55J.js.map → ReviewsPage-WYZ6CHF2.js.map} +1 -1
- package/dist/{SeoCartWidget-DBJAXJKT.js → SeoCartWidget-5JR5RXQM.js} +11 -9
- package/dist/SeoCartWidget-5JR5RXQM.js.map +1 -0
- package/dist/{SettingsPage-FLTIOPU5.js → SettingsPage-DIDRQXMM.js} +11 -9
- package/dist/SettingsPage-DIDRQXMM.js.map +1 -0
- package/dist/{StoreClosedNotification-ZT3QKDKS.js → StoreClosedNotification-C2G32E34.js} +12 -10
- package/dist/{StoreClosedNotification-ZT3QKDKS.js.map → StoreClosedNotification-C2G32E34.js.map} +1 -1
- package/dist/{TestimonialsPage-CU7HKU4O.js → TestimonialsPage-XPHFRBZ7.js} +3 -3
- package/dist/{TestimonialsPage-CU7HKU4O.js.map → TestimonialsPage-XPHFRBZ7.js.map} +1 -1
- package/dist/{ThankYouPage-5X6B4X3T.js → ThankYouPage-UA6YYFB2.js} +11 -9
- package/dist/ThankYouPage-UA6YYFB2.js.map +1 -0
- package/dist/{WalletPage-H2BKIDPA.js → WalletPage-AJVV7LVL.js} +11 -9
- package/dist/WalletPage-AJVV7LVL.js.map +1 -0
- package/dist/{WishlistPage-RS5UCIQ3.js → WishlistPage-UW6LJLSW.js} +11 -9
- package/dist/WishlistPage-UW6LJLSW.js.map +1 -0
- package/dist/api/advertisement.d.ts +33 -0
- package/dist/api/advertisement.d.ts.map +1 -0
- package/dist/api/advertisement.js +8 -0
- package/dist/api/advertisement.js.map +1 -0
- package/dist/boot/critical-style.d.ts +3 -0
- package/dist/boot/critical-style.d.ts.map +1 -0
- package/dist/boot/index.d.ts +15 -0
- package/dist/boot/index.d.ts.map +1 -0
- package/dist/boot.d.ts +1 -0
- package/dist/boot.js +69 -0
- package/dist/boot.js.map +1 -0
- package/dist/{chunk-SE5JYWOK.js → chunk-2HCCIHU7.js} +4 -4
- package/dist/{chunk-SE5JYWOK.js.map → chunk-2HCCIHU7.js.map} +1 -1
- package/dist/chunk-4FJGZQD3.js +6 -0
- package/dist/chunk-4FJGZQD3.js.map +1 -0
- package/dist/{chunk-SDFDS4Q3.js → chunk-4WVJJ7SN.js} +20 -4
- package/dist/chunk-4WVJJ7SN.js.map +1 -0
- package/dist/{chunk-NXQJA7D3.js → chunk-BAYGWTNM.js} +3 -3
- package/dist/{chunk-NXQJA7D3.js.map → chunk-BAYGWTNM.js.map} +1 -1
- package/dist/{chunk-OWX2CPNQ.js → chunk-CLBFF2CY.js} +3 -3
- package/dist/{chunk-OWX2CPNQ.js.map → chunk-CLBFF2CY.js.map} +1 -1
- package/dist/chunk-DBW7XXNE.js +103 -0
- package/dist/chunk-DBW7XXNE.js.map +1 -0
- package/dist/{chunk-KHBSGD2D.js → chunk-DBXH3EMJ.js} +3 -3
- package/dist/{chunk-KHBSGD2D.js.map → chunk-DBXH3EMJ.js.map} +1 -1
- package/dist/{chunk-JRNH3FUF.js → chunk-E26YG3AO.js} +4 -4
- package/dist/{chunk-JRNH3FUF.js.map → chunk-E26YG3AO.js.map} +1 -1
- package/dist/{chunk-22SH7OVF.js → chunk-E7WWO3V3.js} +3 -3
- package/dist/{chunk-22SH7OVF.js.map → chunk-E7WWO3V3.js.map} +1 -1
- package/dist/{chunk-UJKFBZFJ.js → chunk-HQ7WA37L.js} +7 -7
- package/dist/{chunk-UJKFBZFJ.js.map → chunk-HQ7WA37L.js.map} +1 -1
- package/dist/chunk-IVBOLREE.js +29 -0
- package/dist/chunk-IVBOLREE.js.map +1 -0
- package/dist/{chunk-6SWDSRIL.js → chunk-KP3VYG4F.js} +6 -6
- package/dist/{chunk-6SWDSRIL.js.map → chunk-KP3VYG4F.js.map} +1 -1
- package/dist/{chunk-4CZRUAZB.js → chunk-PWTXR4RR.js} +10 -10
- package/dist/{chunk-4CZRUAZB.js.map → chunk-PWTXR4RR.js.map} +1 -1
- package/dist/chunk-QJYUTRAQ.js +14 -0
- package/dist/{chunk-7WAXN2PP.js.map → chunk-QJYUTRAQ.js.map} +1 -1
- package/dist/{chunk-JO7JWV34.js → chunk-U55ZJC2X.js} +4 -4
- package/dist/{chunk-JO7JWV34.js.map → chunk-U55ZJC2X.js.map} +1 -1
- package/dist/{chunk-SX5MF2YA.js → chunk-WSDG4ZRI.js} +4 -4
- package/dist/{chunk-SX5MF2YA.js.map → chunk-WSDG4ZRI.js.map} +1 -1
- package/dist/{chunk-NZVR4OF7.js → chunk-X54QP332.js} +8 -8
- package/dist/chunk-X54QP332.js.map +1 -0
- package/dist/components/cart/index.js +1 -1
- package/dist/components/common/index.js +1 -1
- package/dist/components/drawer/index.js +9 -7
- package/dist/components/home/index.js +11 -9
- package/dist/components/layout/PageAdvertisements.d.ts +7 -0
- package/dist/components/layout/PageAdvertisements.d.ts.map +1 -0
- package/dist/components/layout/index.d.ts +1 -0
- package/dist/components/layout/index.d.ts.map +1 -1
- package/dist/components/layout/index.js +8 -1
- package/dist/components/product/index.js +10 -8
- package/dist/components/store/index.js +11 -9
- package/dist/components/store/index.js.map +1 -1
- package/dist/{document-class-67HTP45G.js → document-class-7AXAAWGZ.js} +6 -5
- package/dist/{document-class-67HTP45G.js.map → document-class-7AXAAWGZ.js.map} +1 -1
- package/dist/hooks/index.js +9 -7
- package/dist/hooks/useGtm.js +9 -7
- package/dist/hooks/useMoney.js +9 -7
- package/dist/hooks/useStore.js +9 -7
- package/dist/index.js +9 -7
- package/dist/index.js.map +1 -1
- package/dist/{not-found-QTHBFPNT.js → not-found-3EEMQZMU.js} +11 -9
- package/dist/not-found-3EEMQZMU.js.map +1 -0
- package/dist/providers/index.js +9 -7
- package/dist/routes/account/orders.js +1 -1
- package/dist/routes/account.js +6 -6
- package/dist/routes/blog.js +5 -5
- package/dist/routes/brands.js +1 -1
- package/dist/routes/cart.js +13 -11
- package/dist/routes/cart.js.map +1 -1
- package/dist/routes/home.js +12 -10
- package/dist/routes/loyalty.js +2 -2
- package/dist/routes/page.js +1 -1
- package/dist/routes/product-listing.js +11 -9
- package/dist/routes/product-listing.js.map +1 -1
- package/dist/routes/product.js +11 -9
- package/dist/routes/product.js.map +1 -1
- package/dist/routes/root/beforeLoad.d.ts +2 -2
- package/dist/routes/root/beforeLoad.d.ts.map +1 -1
- package/dist/routes/root/head.d.ts.map +1 -1
- package/dist/routes/testimonials.js +2 -2
- package/dist/routes/thank-you.js +1 -1
- package/dist/skeleton.js +12 -10
- package/dist/tanstack/document-class.d.ts.map +1 -1
- package/dist/tanstack/index.d.ts +1 -0
- package/dist/tanstack/index.d.ts.map +1 -1
- package/dist/tanstack/stylesheet.d.ts +15 -0
- package/dist/tanstack/stylesheet.d.ts.map +1 -0
- package/dist/tanstack.js +34 -5
- package/dist/tanstack.js.map +1 -1
- package/dist/utils/index.js +1 -1
- package/dist/vite/index.js +1 -1
- package/package.json +12 -3
- package/tailwind/components/Component.tsx.txt +21 -0
- package/tailwind/components/ComponentRegistry.ts.txt +66 -0
- package/tailwind/components/cart/CartItem.tsx.txt +211 -0
- package/tailwind/components/cart/CartSummary.tsx.txt +269 -0
- package/tailwind/components/cart/PreOrderNotification.tsx.txt +16 -0
- package/tailwind/components/cart/PriceQuoteButton.tsx.txt +21 -0
- package/tailwind/components/cart/SeoCartWidget.tsx.txt +99 -0
- package/tailwind/components/cart/index.ts.txt +11 -0
- package/tailwind/components/cart/lazy.ts.txt +15 -0
- package/tailwind/components/collapse/Collapse.tsx.txt +95 -0
- package/tailwind/components/collapse/index.ts.txt +41 -0
- package/tailwind/components/common/Breadcrumb.tsx.txt +27 -0
- package/tailwind/components/common/BreadcrumbJsonLd.tsx.txt +40 -0
- package/tailwind/components/common/BreadcrumbTrail.tsx.txt +97 -0
- package/tailwind/components/common/CurrencySymbol.tsx.txt +8 -0
- package/tailwind/components/common/DeferredData.tsx.txt +122 -0
- package/tailwind/components/common/ErrorPage.tsx.txt +69 -0
- package/tailwind/components/common/Image.tsx.txt +119 -0
- package/tailwind/components/common/Link.tsx.txt +36 -0
- package/tailwind/components/common/Link.types.ts.txt +85 -0
- package/tailwind/components/common/NoContent.tsx.txt +27 -0
- package/tailwind/components/common/RenderWhenVisible.tsx.txt +92 -0
- package/tailwind/components/common/index.ts.txt +42 -0
- package/tailwind/components/common/lazy.ts.txt +28 -0
- package/tailwind/components/defineComponent.ts.txt +32 -0
- package/tailwind/components/drawer/Drawer.tsx.txt +219 -0
- package/tailwind/components/drawer/index.ts.txt +38 -0
- package/tailwind/components/dropdown/Dropdown.tsx.txt +166 -0
- package/tailwind/components/dropdown/index.ts.txt +40 -0
- package/tailwind/components/home/BundleComponent.tsx.txt +35 -0
- package/tailwind/components/home/ComponentErrorBoundary.tsx.txt +98 -0
- package/tailwind/components/home/FeaturedProductsStyle1.tsx.txt +125 -0
- package/tailwind/components/home/FeaturedProductsStyle2.tsx.txt +127 -0
- package/tailwind/components/home/FeaturedProductsStyle3.tsx.txt +71 -0
- package/tailwind/components/home/FixedBanner.tsx.txt +68 -0
- package/tailwind/components/home/FixedProducts.tsx.txt +72 -0
- package/tailwind/components/home/HomePageRenderer.tsx.txt +284 -0
- package/tailwind/components/home/PageBlocks.tsx.txt +24 -0
- package/tailwind/components/home/ParallaxBackground.tsx.txt +50 -0
- package/tailwind/components/home/PhotosSlider.tsx.txt +61 -0
- package/tailwind/components/home/ProductsSlider.tsx.txt +87 -0
- package/tailwind/components/home/ReorderProducts.tsx.txt +38 -0
- package/tailwind/components/home/SquarePhotos.tsx.txt +68 -0
- package/tailwind/components/home/StoreFeatures.tsx.txt +34 -0
- package/tailwind/components/home/Testimonials.tsx.txt +36 -0
- package/tailwind/components/home/Youtube.tsx.txt +48 -0
- package/tailwind/components/home/index.ts.txt +50 -0
- package/tailwind/components/home/lazy.ts.txt +17 -0
- package/tailwind/components/home/register.ts.txt +44 -0
- package/tailwind/components/home/reorder-hooks.ts.txt +52 -0
- package/tailwind/components/home/types.ts.txt +4 -0
- package/tailwind/components/layout/Copyright.tsx.txt +23 -0
- package/tailwind/components/layout/CustomerLayout.tsx.txt +91 -0
- package/tailwind/components/layout/Footer.tsx.txt +343 -0
- package/tailwind/components/layout/Header.tsx.txt +154 -0
- package/tailwind/components/layout/MasterLayout.tsx.txt +63 -0
- package/tailwind/components/layout/PageAdvertisements.tsx.txt +131 -0
- package/tailwind/components/layout/WidgetHead.tsx.txt +53 -0
- package/tailwind/components/layout/index.ts.txt +27 -0
- package/tailwind/components/layout/lazy.ts.txt +25 -0
- package/tailwind/components/layout/types.ts.txt +11 -0
- package/tailwind/components/modal/ImageModal.tsx.txt +70 -0
- package/tailwind/components/modal/Modal.tsx.txt +245 -0
- package/tailwind/components/modal/index.ts.txt +39 -0
- package/tailwind/components/modal/lazy.ts.txt +10 -0
- package/tailwind/components/navigation/MainMenu.tsx.txt +94 -0
- package/tailwind/components/navigation/MobileMenu.tsx.txt +160 -0
- package/tailwind/components/navigation/index.ts.txt +4 -0
- package/tailwind/components/navigation/lazy.ts.txt +11 -0
- package/tailwind/components/product/AddToCartForm.tsx.txt +365 -0
- package/tailwind/components/product/ProductCard.tsx.txt +305 -0
- package/tailwind/components/product/ProductDetails.tsx.txt +267 -0
- package/tailwind/components/product/ProductGallery.tsx.txt +234 -0
- package/tailwind/components/product/index.ts.txt +15 -0
- package/tailwind/components/product/lazy.ts.txt +9 -0
- package/tailwind/components/snippets/AppsSnippets.tsx.txt +225 -0
- package/tailwind/components/snippets/SallaSdkScripts.tsx.txt +14 -0
- package/tailwind/components/snippets/index.ts.txt +4 -0
- package/tailwind/components/store/StoreClosedNotification.tsx.txt +73 -0
- package/tailwind/components/store/index.ts.txt +8 -0
- package/tailwind/components/store/lazy.ts.txt +8 -0
- package/tailwind/components/toast/Toaster.tsx.txt +177 -0
- package/tailwind/components/toast/index.ts.txt +10 -0
- package/tailwind/components/toast/types.ts.txt +38 -0
- package/tailwind/components/toast/useToast.ts.txt +61 -0
- package/tailwind/components/tracking/GoogleTag.tsx.txt +77 -0
- package/tailwind/components/tracking/SiftSnippet.tsx.txt +49 -0
- package/tailwind/components/tracking/TwilightBundles.tsx.txt +67 -0
- package/tailwind/components/tracking/index.ts.txt +11 -0
- package/tailwind/components/tracking/lazy.ts.txt +11 -0
- package/tailwind/components/useComponent.ts.txt +12 -0
- package/tailwind/routes/account/index.tsx.txt +16 -0
- package/tailwind/routes/account/notifications/NotificationsPage.tsx.txt +54 -0
- package/tailwind/routes/account/notifications/index.tsx.txt +39 -0
- package/tailwind/routes/account/notifications/loader.ts.txt +22 -0
- package/tailwind/routes/account/notifications/types.ts.txt +18 -0
- package/tailwind/routes/account/orders/OrderSinglePage.tsx.txt +181 -0
- package/tailwind/routes/account/orders/OrdersPage.tsx.txt +28 -0
- package/tailwind/routes/account/orders/index.tsx.txt +86 -0
- package/tailwind/routes/account/orders/loader.ts.txt +66 -0
- package/tailwind/routes/account/orders/single/OrderActions.tsx.txt +197 -0
- package/tailwind/routes/account/orders/single/OrderHeader.tsx.txt +143 -0
- package/tailwind/routes/account/orders/single/OrderItemRow.tsx.txt +209 -0
- package/tailwind/routes/account/orders/single/OrderSections.tsx.txt +215 -0
- package/tailwind/routes/account/orders/single/types.ts.txt +38 -0
- package/tailwind/routes/account/orders/types.ts.txt +29 -0
- package/tailwind/routes/account/profile/ProfilePage.tsx.txt +30 -0
- package/tailwind/routes/account/profile/index.tsx.txt +39 -0
- package/tailwind/routes/account/profile/loader.ts.txt +16 -0
- package/tailwind/routes/account/profile/types.ts.txt +5 -0
- package/tailwind/routes/account/settings/SettingsPage.tsx.txt +13 -0
- package/tailwind/routes/account/settings/index.tsx.txt +39 -0
- package/tailwind/routes/account/settings/loader.ts.txt +13 -0
- package/tailwind/routes/account/settings/types.ts.txt +5 -0
- package/tailwind/routes/account/types.ts.txt +320 -0
- package/tailwind/routes/account/wallet/WalletPage.tsx.txt +195 -0
- package/tailwind/routes/account/wallet/index.tsx.txt +38 -0
- package/tailwind/routes/account/wallet/loader.ts.txt +27 -0
- package/tailwind/routes/account/wallet/types.ts.txt +11 -0
- package/tailwind/routes/account/wishlist/WishlistPage.tsx.txt +314 -0
- package/tailwind/routes/account/wishlist/WishlistShare.tsx.txt +248 -0
- package/tailwind/routes/account/wishlist/index.tsx.txt +40 -0
- package/tailwind/routes/account/wishlist/loader.ts.txt +26 -0
- package/tailwind/routes/account/wishlist/types.ts.txt +9 -0
- package/tailwind/routes/blog/BlogAuthorPage.tsx.txt +82 -0
- package/tailwind/routes/blog/BlogCategoryPage.tsx.txt +84 -0
- package/tailwind/routes/blog/BlogPage.tsx.txt +191 -0
- package/tailwind/routes/blog/BlogSinglePage.tsx.txt +168 -0
- package/tailwind/routes/blog/BlogTagPage.tsx.txt +84 -0
- package/tailwind/routes/blog/BlogTags.tsx.txt +54 -0
- package/tailwind/routes/blog/components.tsx.txt +87 -0
- package/tailwind/routes/blog/index.tsx.txt +238 -0
- package/tailwind/routes/blog/loader.ts.txt +173 -0
- package/tailwind/routes/blog/types.ts.txt +106 -0
- package/tailwind/routes/brands/BrandsPage.tsx.txt +179 -0
- package/tailwind/routes/brands/index.tsx.txt +47 -0
- package/tailwind/routes/brands/loader.ts.txt +14 -0
- package/tailwind/routes/brands/types.ts.txt +53 -0
- package/tailwind/routes/cart/CartPage.tsx.txt +112 -0
- package/tailwind/routes/cart/index.tsx.txt +39 -0
- package/tailwind/routes/cart/loader.ts.txt +15 -0
- package/tailwind/routes/cart/types.ts.txt +6 -0
- package/tailwind/routes/home/HomePage.tsx.txt +53 -0
- package/tailwind/routes/home/index.tsx.txt +56 -0
- package/tailwind/routes/home/loader.ts.txt +24 -0
- package/tailwind/routes/home/types.ts.txt +11 -0
- package/tailwind/routes/index.ts.txt +94 -0
- package/tailwind/routes/lazy.ts.txt +105 -0
- package/tailwind/routes/loyalty/LoyaltyPage.tsx.txt +17 -0
- package/tailwind/routes/loyalty/LoyaltyPointsPage.tsx.txt +11 -0
- package/tailwind/routes/loyalty/index.tsx.txt +81 -0
- package/tailwind/routes/loyalty/loader.ts.txt +53 -0
- package/tailwind/routes/loyalty/types.ts.txt +109 -0
- package/tailwind/routes/not-found.tsx.txt +50 -0
- package/tailwind/routes/page/PageSingle.tsx.txt +34 -0
- package/tailwind/routes/page/index.tsx.txt +60 -0
- package/tailwind/routes/page/loader.ts.txt +19 -0
- package/tailwind/routes/page/types.ts.txt +34 -0
- package/tailwind/routes/product/ProductPage.tsx.txt +77 -0
- package/tailwind/routes/product/index.tsx.txt +157 -0
- package/tailwind/routes/product/loader.ts.txt +35 -0
- package/tailwind/routes/product/types.ts.txt +6 -0
- package/tailwind/routes/product-listing/ProductListPage.tsx.txt +321 -0
- package/tailwind/routes/product-listing/index.tsx.txt +95 -0
- package/tailwind/routes/product-listing/loader.ts.txt +323 -0
- package/tailwind/routes/product-listing/types.ts.txt +96 -0
- package/tailwind/routes/product-listing/useAppliedFilters.ts.txt +130 -0
- package/tailwind/routes/product-short-link.ts.txt +104 -0
- package/tailwind/routes/root/beforeLoad.ts.txt +354 -0
- package/tailwind/routes/root/head.ts.txt +424 -0
- package/tailwind/routes/root/index.ts.txt +2 -0
- package/tailwind/routes/seo-redirects.ts.txt +82 -0
- package/tailwind/routes/testimonials/ReviewsPage.tsx.txt +12 -0
- package/tailwind/routes/testimonials/TestimonialsPage.tsx.txt +80 -0
- package/tailwind/routes/testimonials/index.tsx.txt +80 -0
- package/tailwind/routes/testimonials/loader.ts.txt +29 -0
- package/tailwind/routes/testimonials/types.ts.txt +9 -0
- package/tailwind/routes/thank-you/ThankYouPage.tsx.txt +241 -0
- package/tailwind/routes/thank-you/index.tsx.txt +49 -0
- package/tailwind/routes/thank-you/loader.ts.txt +40 -0
- package/tailwind/routes/thank-you/types.ts.txt +16 -0
- package/tailwind/routes/types.ts.txt +41 -0
- package/dist/BlogAuthorPage-GTF4UGSS.js.map +0 -1
- package/dist/BlogCategoryPage-TN7Y4NBP.js.map +0 -1
- package/dist/BlogPage-TDB3M3PI.js.map +0 -1
- package/dist/BlogSinglePage-4A52Q5QW.js.map +0 -1
- package/dist/BlogTagPage-TOVPBETA.js.map +0 -1
- package/dist/BrandsPage-VUDGYSQI.js.map +0 -1
- package/dist/Breadcrumb-CI3SN5YR.js.map +0 -1
- package/dist/CartItem-BHLEURAB.js.map +0 -1
- package/dist/CartSummary-QERVESYN.js.map +0 -1
- package/dist/CustomerLayout-VRZUWGQK.js.map +0 -1
- package/dist/LoyaltyPage-CGTHWJ76.js.map +0 -1
- package/dist/LoyaltyPointsPage-QSU6K3PW.js.map +0 -1
- package/dist/MobileMenu-U4BVQYKH.js.map +0 -1
- package/dist/NotificationsPage-ZRRQY7TF.js.map +0 -1
- package/dist/OrderSinglePage-O3XYVMG5.js.map +0 -1
- package/dist/OrdersPage-CMXZF5MI.js.map +0 -1
- package/dist/ProductDetails-65GDABI7.js.map +0 -1
- package/dist/ProfilePage-SNJ4WXU4.js.map +0 -1
- package/dist/SeoCartWidget-DBJAXJKT.js.map +0 -1
- package/dist/SettingsPage-FLTIOPU5.js.map +0 -1
- package/dist/ThankYouPage-5X6B4X3T.js.map +0 -1
- package/dist/WalletPage-H2BKIDPA.js.map +0 -1
- package/dist/WishlistPage-RS5UCIQ3.js.map +0 -1
- package/dist/chunk-7WAXN2PP.js +0 -14
- package/dist/chunk-NZVR4OF7.js.map +0 -1
- package/dist/chunk-SDFDS4Q3.js.map +0 -1
- package/dist/not-found-QTHBFPNT.js.map +0 -1
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { SallaButton } from '@salla.sa/twilight-components-react/button';
|
|
2
|
+
import { useTranslation } from '../../providers/I18nProvider';
|
|
3
|
+
|
|
4
|
+
const handlePriceQuote = async () => {
|
|
5
|
+
await window.salla?.cart.api.priceQuote?.();
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export function PriceQuoteButton() {
|
|
9
|
+
const { t } = useTranslation();
|
|
10
|
+
|
|
11
|
+
return (
|
|
12
|
+
<SallaButton
|
|
13
|
+
id="request-price-quote-btn"
|
|
14
|
+
className="btn--oval btn--md"
|
|
15
|
+
fill="outline"
|
|
16
|
+
onClick={handlePriceQuote}
|
|
17
|
+
>
|
|
18
|
+
{t('pages.cart.request_price_quote', 'Request for Quotation')}
|
|
19
|
+
</SallaButton>
|
|
20
|
+
);
|
|
21
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
import { useTwilight } from '../../providers/TwilightProvider';
|
|
3
|
+
import { useTranslation } from '../../providers/I18nProvider';
|
|
4
|
+
import type { Cart } from '../../types';
|
|
5
|
+
import { pushCheckoutEvent } from '../../lib/analytics/gtm';
|
|
6
|
+
import { onSallaEvent } from '../../utils/sdk-helpers';
|
|
7
|
+
|
|
8
|
+
// ─── Types ────────────────────────────────────────────────────────────────────
|
|
9
|
+
|
|
10
|
+
export interface SeoCartWidgetProps {
|
|
11
|
+
/**
|
|
12
|
+
* Cart data from the TanStack Router loader — no extra fetch needed.
|
|
13
|
+
*/
|
|
14
|
+
cart: Cart;
|
|
15
|
+
/**
|
|
16
|
+
* Current checkout funnel step.
|
|
17
|
+
* Defaults to `1` (cart view) — CartPage always passes this default.
|
|
18
|
+
* The widget internally listens to Salla SDK events to promote to 2 / 3.
|
|
19
|
+
*/
|
|
20
|
+
step?: 1 | 2 | 3;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// ─── Page title translation keys (mirrors PHP SeoCartWidget::$pageTitle) ─────
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Matches the PHP backend exactly:
|
|
27
|
+
* ```php
|
|
28
|
+
* protected $pageTitle = [
|
|
29
|
+
* self::STEP_CART => 'common.titles.cart',
|
|
30
|
+
* self::STEP_SHIPPING => 'common.titles.shipping',
|
|
31
|
+
* self::STEP_PAYMENT => 'common.titles.checkout',
|
|
32
|
+
* ];
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
const PAGE_TITLE_KEYS: Record<1 | 2 | 3, string> = {
|
|
36
|
+
1: 'common.titles.cart',
|
|
37
|
+
2: 'common.titles.shipping',
|
|
38
|
+
3: 'common.titles.checkout',
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// ─── Component ────────────────────────────────────────────────────────────────
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* SeoCartWidget — Null-rendering analytics side-effect component.
|
|
45
|
+
*
|
|
46
|
+
* Responsibilities:
|
|
47
|
+
* 1. Fires a GTM `checkout` event (step 1) on cart page load.
|
|
48
|
+
* 2. Listens to Salla SDK checkout overlay events to fire steps 2 & 3.
|
|
49
|
+
* 3. Updates `document.title` to reflect the cart context.
|
|
50
|
+
*
|
|
51
|
+
* Architecture:
|
|
52
|
+
* - Lives in `theme-engine` so all themes using the engine get GTM tracking.
|
|
53
|
+
* - Mounted directly inside `CartPage`, which already has loader data.
|
|
54
|
+
* - `window.dataLayer` must be initialised by the store's GTM embed snippet;
|
|
55
|
+
* the widget initialises it as a safe-guard but does NOT inject GTM itself.
|
|
56
|
+
* - Must be mounted inside `<TwilightProvider>` (reads `currency` from context).
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* // In CartPage:
|
|
60
|
+
* <SeoCartWidget cart={cart} />
|
|
61
|
+
*/
|
|
62
|
+
export function SeoCartWidget({ cart, step = 1 }: SeoCartWidgetProps) {
|
|
63
|
+
const { store, currency } = useTwilight();
|
|
64
|
+
const { t } = useTranslation();
|
|
65
|
+
const currencyCode = currency?.code ?? 'SAR';
|
|
66
|
+
|
|
67
|
+
// ── Fire GTM checkout event for step 1 (or whichever step is passed) ────────
|
|
68
|
+
useEffect(() => {
|
|
69
|
+
pushCheckoutEvent(step, cart, currencyCode);
|
|
70
|
+
// Re-fire if the cart id, contents, or currency changes
|
|
71
|
+
}, [step, cart, currencyCode]);
|
|
72
|
+
|
|
73
|
+
// ── Subscribe to Salla SDK checkout step events (steps 2 & 3) ──────────────
|
|
74
|
+
// Fires GTM events when the user advances through the checkout overlay.
|
|
75
|
+
useEffect(() => {
|
|
76
|
+
const onShipping = () => pushCheckoutEvent(2, cart, currencyCode);
|
|
77
|
+
const onPayment = () => pushCheckoutEvent(3, cart, currencyCode);
|
|
78
|
+
|
|
79
|
+
const offShipping = onSallaEvent('checkout::step.shipping', onShipping);
|
|
80
|
+
const offPayment = onSallaEvent('checkout::step.payment', onPayment);
|
|
81
|
+
|
|
82
|
+
return () => {
|
|
83
|
+
offShipping();
|
|
84
|
+
offPayment();
|
|
85
|
+
};
|
|
86
|
+
}, [cart, currencyCode]);
|
|
87
|
+
|
|
88
|
+
// ── Update document title — mirrors PHP SeoCartWidget::$pageTitle ───────────
|
|
89
|
+
// Step 1 → 'common.titles.cart' (e.g. "Cart")
|
|
90
|
+
// Step 2 → 'common.titles.shipping' (e.g. "Shipping")
|
|
91
|
+
// Step 3 → 'common.titles.checkout' (e.g. "Checkout")
|
|
92
|
+
useEffect(() => {
|
|
93
|
+
const storeTitle = store.meta?.title || store.name;
|
|
94
|
+
const stepTitle = t(PAGE_TITLE_KEYS[step], PAGE_TITLE_KEYS[step]);
|
|
95
|
+
document.title = storeTitle ? `${stepTitle} - ${storeTitle}` : stepTitle;
|
|
96
|
+
}, [step, store.meta?.title, store.name, t]);
|
|
97
|
+
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { LazyCartItem, LazyCartSummary, LazySeoCartWidget } from './lazy';
|
|
2
|
+
|
|
3
|
+
// Lazy-loaded cart components (code-split)
|
|
4
|
+
export {
|
|
5
|
+
LazyCartItem as CartItem,
|
|
6
|
+
LazyCartSummary as CartSummary,
|
|
7
|
+
LazySeoCartWidget as SeoCartWidget,
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
// Types are co-located with components (not in types.ts)
|
|
11
|
+
export type { SeoCartWidgetProps } from './SeoCartWidget';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { lazy } from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Lazy-loaded cart components
|
|
5
|
+
* Use these for code-splitting cart sections
|
|
6
|
+
*/
|
|
7
|
+
export const LazyCartItem = lazy(() => import('./CartItem').then((m) => ({ default: m.CartItem })));
|
|
8
|
+
|
|
9
|
+
export const LazyCartSummary = lazy(() =>
|
|
10
|
+
import('./CartSummary').then((m) => ({ default: m.CartSummary }))
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
export const LazySeoCartWidget = lazy(() =>
|
|
14
|
+
import('./SeoCartWidget').then((m) => ({ default: m.SeoCartWidget }))
|
|
15
|
+
);
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Collapse Component
|
|
3
|
+
*
|
|
4
|
+
* A controlled collapsible content area with smooth animation.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```tsx
|
|
8
|
+
* function FAQItem({ question, answer }) {
|
|
9
|
+
* const [isOpen, setIsOpen] = useState(false);
|
|
10
|
+
*
|
|
11
|
+
* return (
|
|
12
|
+
* <Collapse isOpen={isOpen} onToggle={() => setIsOpen(!isOpen)}>
|
|
13
|
+
* <Collapse.Trigger>
|
|
14
|
+
* <div className="flex justify-between">
|
|
15
|
+
* <span>{question}</span>
|
|
16
|
+
* <span>{isOpen ? '−' : '+'}</span>
|
|
17
|
+
* </div>
|
|
18
|
+
* </Collapse.Trigger>
|
|
19
|
+
* <Collapse.Content>
|
|
20
|
+
* <p>{answer}</p>
|
|
21
|
+
* </Collapse.Content>
|
|
22
|
+
* </Collapse>
|
|
23
|
+
* );
|
|
24
|
+
* }
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
import { type ReactNode, type HTMLAttributes, type ButtonHTMLAttributes } from 'react';
|
|
29
|
+
|
|
30
|
+
export interface CollapseProps {
|
|
31
|
+
isOpen: boolean;
|
|
32
|
+
onToggle: () => void;
|
|
33
|
+
children: ReactNode;
|
|
34
|
+
/**
|
|
35
|
+
* Additional className for the collapse container
|
|
36
|
+
*/
|
|
37
|
+
className?: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function Collapse({ children, className }: CollapseProps) {
|
|
41
|
+
return <div className={`overflow-hidden ${className || ''}`}>{children}</div>;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Trigger Component
|
|
45
|
+
export interface CollapseTriggerProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
46
|
+
children: ReactNode;
|
|
47
|
+
isOpen?: boolean;
|
|
48
|
+
onToggle?: () => void;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
Collapse.Trigger = function CollapseTrigger({
|
|
52
|
+
children,
|
|
53
|
+
isOpen = false,
|
|
54
|
+
onToggle,
|
|
55
|
+
className,
|
|
56
|
+
...props
|
|
57
|
+
}: CollapseTriggerProps) {
|
|
58
|
+
return (
|
|
59
|
+
<button
|
|
60
|
+
type="button"
|
|
61
|
+
onClick={onToggle}
|
|
62
|
+
className={`block cursor-pointer border-0 bg-transparent p-0 text-start ${className || ''}`}
|
|
63
|
+
aria-expanded={isOpen}
|
|
64
|
+
{...props}
|
|
65
|
+
>
|
|
66
|
+
{children}
|
|
67
|
+
</button>
|
|
68
|
+
);
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
// Content Component
|
|
72
|
+
export interface CollapseContentProps extends HTMLAttributes<HTMLDivElement> {
|
|
73
|
+
children: ReactNode;
|
|
74
|
+
isOpen?: boolean;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
Collapse.Content = function CollapseContent({
|
|
78
|
+
children,
|
|
79
|
+
isOpen = false,
|
|
80
|
+
className,
|
|
81
|
+
...props
|
|
82
|
+
}: CollapseContentProps) {
|
|
83
|
+
return (
|
|
84
|
+
<div
|
|
85
|
+
className={`transition-all duration-200 ease-out ${
|
|
86
|
+
isOpen ? 'grid grid-rows-[1fr] opacity-100' : 'grid grid-rows-[0fr] opacity-0'
|
|
87
|
+
} ${className || ''}`}
|
|
88
|
+
{...props}
|
|
89
|
+
>
|
|
90
|
+
<div className="overflow-hidden">
|
|
91
|
+
<div className="pb-2">{children}</div>
|
|
92
|
+
</div>
|
|
93
|
+
</div>
|
|
94
|
+
);
|
|
95
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Collapse Component System
|
|
3
|
+
*
|
|
4
|
+
* A controlled collapsible panel for FAQs, accordions, and expandable sections.
|
|
5
|
+
* Eager-loaded because it uses compound component pattern (Collapse.Trigger, Collapse.Content).
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```tsx
|
|
9
|
+
* import { Collapse } from '@salla.sa/twilight-theme-engine/components/collapse';
|
|
10
|
+
*
|
|
11
|
+
* function Accordion() {
|
|
12
|
+
* const [openId, setOpenId] = useState(null);
|
|
13
|
+
*
|
|
14
|
+
* return (
|
|
15
|
+
* <div className="space-y-2">
|
|
16
|
+
* {items.map((item) => (
|
|
17
|
+
* <Collapse
|
|
18
|
+
* key={item.id}
|
|
19
|
+
* isOpen={openId === item.id}
|
|
20
|
+
* onToggle={() => setOpenId(openId === item.id ? null : item.id)}
|
|
21
|
+
* >
|
|
22
|
+
* <Collapse.Trigger isOpen={openId === item.id} onToggle={() => setOpenId(item.id)}>
|
|
23
|
+
* <h3>{item.title}</h3>
|
|
24
|
+
* </Collapse.Trigger>
|
|
25
|
+
* <Collapse.Content isOpen={openId === item.id}>
|
|
26
|
+
* <p>{item.content}</p>
|
|
27
|
+
* </Collapse.Content>
|
|
28
|
+
* </Collapse>
|
|
29
|
+
* ))}
|
|
30
|
+
* </div>
|
|
31
|
+
* );
|
|
32
|
+
* }
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
export {
|
|
37
|
+
Collapse,
|
|
38
|
+
type CollapseProps,
|
|
39
|
+
type CollapseTriggerProps,
|
|
40
|
+
type CollapseContentProps,
|
|
41
|
+
} from './Collapse';
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Page } from '../../types';
|
|
2
|
+
import { useBreadcrumbs } from '../../hooks/useBreadcrumbs';
|
|
3
|
+
import { useTwilight } from '../../providers/TwilightProvider';
|
|
4
|
+
import { BreadcrumbTrail } from './BreadcrumbTrail';
|
|
5
|
+
import { BreadcrumbJsonLd } from './BreadcrumbJsonLd';
|
|
6
|
+
|
|
7
|
+
export interface BreadcrumbProps {
|
|
8
|
+
page?: Page | null;
|
|
9
|
+
className?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function Breadcrumb({ page, className }: BreadcrumbProps) {
|
|
13
|
+
const { theme, store } = useTwilight();
|
|
14
|
+
const breadcrumbsEnabled = theme.settings.is_breadcrumbs_enabled ?? true;
|
|
15
|
+
const origin = store.url;
|
|
16
|
+
|
|
17
|
+
const items = useBreadcrumbs(page);
|
|
18
|
+
|
|
19
|
+
if (!breadcrumbsEnabled) return null;
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<>
|
|
23
|
+
<BreadcrumbTrail items={items} className={className} />
|
|
24
|
+
<BreadcrumbJsonLd items={items} origin={origin} />
|
|
25
|
+
</>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { Breadcrumb } from '../../types';
|
|
2
|
+
|
|
3
|
+
interface BreadcrumbJsonLdProps {
|
|
4
|
+
items: Breadcrumb[];
|
|
5
|
+
origin: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function toAbsoluteUrl(origin: string, path: string): string {
|
|
9
|
+
if (path.startsWith('http')) return path;
|
|
10
|
+
return origin + path;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function BreadcrumbJsonLd({ items, origin }: BreadcrumbJsonLdProps) {
|
|
14
|
+
if (items.length === 0) return null;
|
|
15
|
+
|
|
16
|
+
const schema = {
|
|
17
|
+
'@context': 'https://schema.org',
|
|
18
|
+
'@type': 'BreadcrumbList',
|
|
19
|
+
itemListElement: items.map((item, index) => {
|
|
20
|
+
const isLast = index === items.length - 1;
|
|
21
|
+
const url = toAbsoluteUrl(origin, item.url);
|
|
22
|
+
return {
|
|
23
|
+
'@type': 'ListItem',
|
|
24
|
+
position: index + 1,
|
|
25
|
+
name: item.name,
|
|
26
|
+
...(isLast ? { '@id': url } : { item: url }),
|
|
27
|
+
};
|
|
28
|
+
}),
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const html = JSON.stringify(schema);
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<script
|
|
35
|
+
type="application/ld+json"
|
|
36
|
+
suppressHydrationWarning
|
|
37
|
+
dangerouslySetInnerHTML={{ __html: html }}
|
|
38
|
+
/>
|
|
39
|
+
);
|
|
40
|
+
}
|