payaza-storefront-layouts 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +108 -0
- package/dist/components/learning/CertificateTemplates.d.ts +25 -0
- package/dist/components/learning/CertificateTemplates.d.ts.map +1 -0
- package/dist/components/learning/CertificateTemplates.js +31 -0
- package/dist/components/learning/CertificateViewer.d.ts +11 -0
- package/dist/components/learning/CertificateViewer.d.ts.map +1 -0
- package/dist/components/learning/CertificateViewer.js +114 -0
- package/dist/components/learning/MentorshipProgress.d.ts +18 -0
- package/dist/components/learning/MentorshipProgress.d.ts.map +1 -0
- package/dist/components/learning/MentorshipProgress.js +19 -0
- package/dist/components/ui/address-selector.d.ts +11 -0
- package/dist/components/ui/address-selector.d.ts.map +1 -0
- package/dist/components/ui/address-selector.js +21 -0
- package/dist/components/ui/avatar-image.d.ts +11 -0
- package/dist/components/ui/avatar-image.d.ts.map +1 -0
- package/dist/components/ui/avatar-image.js +39 -0
- package/dist/components/ui/background-patterns.d.ts +27 -0
- package/dist/components/ui/background-patterns.d.ts.map +1 -0
- package/dist/components/ui/background-patterns.js +5 -0
- package/dist/components/ui/badge.d.ts +10 -0
- package/dist/components/ui/badge.d.ts.map +1 -0
- package/dist/components/ui/badge.js +20 -0
- package/dist/components/ui/breadcrumbs.d.ts +11 -0
- package/dist/components/ui/breadcrumbs.d.ts.map +1 -0
- package/dist/components/ui/breadcrumbs.js +8 -0
- package/dist/components/ui/button.d.ts +11 -0
- package/dist/components/ui/button.d.ts.map +1 -0
- package/dist/components/ui/button.js +33 -0
- package/dist/components/ui/card.d.ts +10 -0
- package/dist/components/ui/card.d.ts.map +1 -0
- package/dist/components/ui/card.js +24 -0
- package/dist/components/ui/category-tree.d.ts +16 -0
- package/dist/components/ui/category-tree.d.ts.map +1 -0
- package/dist/components/ui/category-tree.js +74 -0
- package/dist/components/ui/checkout-button.d.ts +11 -0
- package/dist/components/ui/checkout-button.d.ts.map +1 -0
- package/dist/components/ui/checkout-button.js +174 -0
- package/dist/components/ui/guest-checkout-modal.d.ts +19 -0
- package/dist/components/ui/guest-checkout-modal.d.ts.map +1 -0
- package/dist/components/ui/guest-checkout-modal.js +92 -0
- package/dist/components/ui/image-skeleton.d.ts +7 -0
- package/dist/components/ui/image-skeleton.d.ts.map +1 -0
- package/dist/components/ui/image-skeleton.js +15 -0
- package/dist/components/ui/image-with-fallback.d.ts +9 -0
- package/dist/components/ui/image-with-fallback.d.ts.map +1 -0
- package/dist/components/ui/image-with-fallback.js +203 -0
- package/dist/components/ui/input.d.ts +6 -0
- package/dist/components/ui/input.d.ts.map +1 -0
- package/dist/components/ui/input.js +8 -0
- package/dist/components/ui/loading-skeleton-wrapper.d.ts +6 -0
- package/dist/components/ui/loading-skeleton-wrapper.d.ts.map +1 -0
- package/dist/components/ui/loading-skeleton-wrapper.js +10 -0
- package/dist/components/ui/modal.d.ts +11 -0
- package/dist/components/ui/modal.d.ts.map +1 -0
- package/dist/components/ui/modal.js +26 -0
- package/dist/components/ui/otp-input.d.ts +10 -0
- package/dist/components/ui/otp-input.d.ts.map +1 -0
- package/dist/components/ui/otp-input.js +57 -0
- package/dist/components/ui/out-of-stock-overlay.d.ts +8 -0
- package/dist/components/ui/out-of-stock-overlay.d.ts.map +1 -0
- package/dist/components/ui/out-of-stock-overlay.js +12 -0
- package/dist/components/ui/page-animate-wrapper.d.ts +7 -0
- package/dist/components/ui/page-animate-wrapper.d.ts.map +1 -0
- package/dist/components/ui/page-animate-wrapper.js +16 -0
- package/dist/components/ui/page-content-loader.d.ts +13 -0
- package/dist/components/ui/page-content-loader.d.ts.map +1 -0
- package/dist/components/ui/page-content-loader.js +12 -0
- package/dist/components/ui/page-loader.d.ts +20 -0
- package/dist/components/ui/page-loader.d.ts.map +1 -0
- package/dist/components/ui/page-loader.js +31 -0
- package/dist/components/ui/product-card.d.ts +11 -0
- package/dist/components/ui/product-card.d.ts.map +1 -0
- package/dist/components/ui/product-card.js +95 -0
- package/dist/components/ui/product-rating.d.ts +17 -0
- package/dist/components/ui/product-rating.d.ts.map +1 -0
- package/dist/components/ui/product-rating.js +54 -0
- package/dist/components/ui/review-form.d.ts +9 -0
- package/dist/components/ui/review-form.d.ts.map +1 -0
- package/dist/components/ui/review-form.js +28 -0
- package/dist/components/ui/route-skeleton-loader.d.ts +7 -0
- package/dist/components/ui/route-skeleton-loader.d.ts.map +1 -0
- package/dist/components/ui/route-skeleton-loader.js +75 -0
- package/dist/components/ui/sheet.d.ts +12 -0
- package/dist/components/ui/sheet.d.ts.map +1 -0
- package/dist/components/ui/sheet.js +29 -0
- package/dist/components/ui/skeleton.d.ts +3 -0
- package/dist/components/ui/skeleton.d.ts.map +1 -0
- package/dist/components/ui/skeleton.js +6 -0
- package/dist/components/ui/skeletons/category-grid-skeleton.d.ts +6 -0
- package/dist/components/ui/skeletons/category-grid-skeleton.d.ts.map +1 -0
- package/dist/components/ui/skeletons/category-grid-skeleton.js +16 -0
- package/dist/components/ui/skeletons/checkout-skeleton.d.ts +2 -0
- package/dist/components/ui/skeletons/checkout-skeleton.d.ts.map +1 -0
- package/dist/components/ui/skeletons/checkout-skeleton.js +4 -0
- package/dist/components/ui/skeletons/index.d.ts +6 -0
- package/dist/components/ui/skeletons/index.d.ts.map +1 -0
- package/dist/components/ui/skeletons/index.js +5 -0
- package/dist/components/ui/skeletons/page-skeleton.d.ts +2 -0
- package/dist/components/ui/skeletons/page-skeleton.d.ts.map +1 -0
- package/dist/components/ui/skeletons/page-skeleton.js +4 -0
- package/dist/components/ui/skeletons/product-card-skeleton.d.ts +2 -0
- package/dist/components/ui/skeletons/product-card-skeleton.d.ts.map +1 -0
- package/dist/components/ui/skeletons/product-card-skeleton.js +4 -0
- package/dist/components/ui/skeletons/product-detail-skeleton.d.ts +2 -0
- package/dist/components/ui/skeletons/product-detail-skeleton.d.ts.map +1 -0
- package/dist/components/ui/skeletons/product-detail-skeleton.js +4 -0
- package/dist/components/ui/skeletons/product-grid-skeleton.d.ts +7 -0
- package/dist/components/ui/skeletons/product-grid-skeleton.d.ts.map +1 -0
- package/dist/components/ui/skeletons/product-grid-skeleton.js +12 -0
- package/dist/components/ui/store-logos.d.ts +43 -0
- package/dist/components/ui/store-logos.d.ts.map +1 -0
- package/dist/components/ui/store-logos.js +83 -0
- package/dist/components/ui/toast.d.ts +19 -0
- package/dist/components/ui/toast.d.ts.map +1 -0
- package/dist/components/ui/toast.js +33 -0
- package/dist/components/ui/video-player.d.ts +48 -0
- package/dist/components/ui/video-player.d.ts.map +1 -0
- package/dist/components/ui/video-player.js +96 -0
- package/dist/hooks/use-analytics.d.ts +14 -0
- package/dist/hooks/use-analytics.d.ts.map +1 -0
- package/dist/hooks/use-analytics.js +66 -0
- package/dist/hooks/use-api.d.ts +17 -0
- package/dist/hooks/use-api.d.ts.map +1 -0
- package/dist/hooks/use-api.js +53 -0
- package/dist/hooks/use-cart-api.d.ts +17 -0
- package/dist/hooks/use-cart-api.d.ts.map +1 -0
- package/dist/hooks/use-cart-api.js +134 -0
- package/dist/hooks/use-content-ready.d.ts +17 -0
- package/dist/hooks/use-content-ready.d.ts.map +1 -0
- package/dist/hooks/use-content-ready.js +197 -0
- package/dist/hooks/use-navigation-loading.d.ts +4 -0
- package/dist/hooks/use-navigation-loading.d.ts.map +1 -0
- package/dist/hooks/use-navigation-loading.js +14 -0
- package/dist/hooks/use-payaza-checkout.d.ts +14 -0
- package/dist/hooks/use-payaza-checkout.d.ts.map +1 -0
- package/dist/hooks/use-payaza-checkout.js +56 -0
- package/dist/hooks/use-products-api.d.ts +19 -0
- package/dist/hooks/use-products-api.d.ts.map +1 -0
- package/dist/hooks/use-products-api.js +84 -0
- package/dist/hooks/use-store-api.d.ts +12 -0
- package/dist/hooks/use-store-api.d.ts.map +1 -0
- package/dist/hooks/use-store-api.js +73 -0
- package/dist/index.d.ts +116 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +126 -0
- package/dist/json/booking-agenda.json +18 -0
- package/dist/json/booking.json +71 -0
- package/dist/json/clothing-minimal.json +26 -0
- package/dist/json/clothing.json +169 -0
- package/dist/json/electronics-grid.json +18 -0
- package/dist/json/electronics.json +50 -0
- package/dist/json/food-modern.json +38 -0
- package/dist/json/food.json +85 -0
- package/dist/json/json-map.d.ts +7 -0
- package/dist/json/json-map.d.ts.map +1 -0
- package/dist/json/json-map.js +36 -0
- package/dist/json/motivational-speaker.json +96 -0
- package/dist/layouts/booking/components/BookingHomePage.d.ts +7 -0
- package/dist/layouts/booking/components/BookingHomePage.d.ts.map +1 -0
- package/dist/layouts/booking/components/BookingHomePage.js +49 -0
- package/dist/layouts/booking/index.d.ts +9 -0
- package/dist/layouts/booking/index.d.ts.map +1 -0
- package/dist/layouts/booking/index.js +9 -0
- package/dist/layouts/booking/pages/AboutPage.d.ts +7 -0
- package/dist/layouts/booking/pages/AboutPage.d.ts.map +1 -0
- package/dist/layouts/booking/pages/AboutPage.js +45 -0
- package/dist/layouts/booking/pages/BookPage.d.ts +8 -0
- package/dist/layouts/booking/pages/BookPage.d.ts.map +1 -0
- package/dist/layouts/booking/pages/BookPage.js +441 -0
- package/dist/layouts/booking/pages/CategoriesPage.d.ts +7 -0
- package/dist/layouts/booking/pages/CategoriesPage.d.ts.map +1 -0
- package/dist/layouts/booking/pages/CategoriesPage.js +162 -0
- package/dist/layouts/booking/pages/CategoryPage.d.ts +8 -0
- package/dist/layouts/booking/pages/CategoryPage.d.ts.map +1 -0
- package/dist/layouts/booking/pages/CategoryPage.js +67 -0
- package/dist/layouts/booking/pages/ContactPage.d.ts +7 -0
- package/dist/layouts/booking/pages/ContactPage.d.ts.map +1 -0
- package/dist/layouts/booking/pages/ContactPage.js +20 -0
- package/dist/layouts/booking/pages/ProductDetailPage.d.ts +8 -0
- package/dist/layouts/booking/pages/ProductDetailPage.d.ts.map +1 -0
- package/dist/layouts/booking/pages/ProductDetailPage.js +502 -0
- package/dist/layouts/booking/pages/ServicesPage.d.ts +9 -0
- package/dist/layouts/booking/pages/ServicesPage.d.ts.map +1 -0
- package/dist/layouts/booking/pages/ServicesPage.js +31 -0
- package/dist/layouts/booking-agenda/components/BookingHomePageAgenda.d.ts +7 -0
- package/dist/layouts/booking-agenda/components/BookingHomePageAgenda.d.ts.map +1 -0
- package/dist/layouts/booking-agenda/components/BookingHomePageAgenda.js +186 -0
- package/dist/layouts/booking-agenda/index.d.ts +9 -0
- package/dist/layouts/booking-agenda/index.d.ts.map +1 -0
- package/dist/layouts/booking-agenda/index.js +9 -0
- package/dist/layouts/booking-agenda/pages/AboutPage.d.ts +7 -0
- package/dist/layouts/booking-agenda/pages/AboutPage.d.ts.map +1 -0
- package/dist/layouts/booking-agenda/pages/AboutPage.js +45 -0
- package/dist/layouts/booking-agenda/pages/BookPage.d.ts +8 -0
- package/dist/layouts/booking-agenda/pages/BookPage.d.ts.map +1 -0
- package/dist/layouts/booking-agenda/pages/BookPage.js +441 -0
- package/dist/layouts/booking-agenda/pages/CategoriesPage.d.ts +7 -0
- package/dist/layouts/booking-agenda/pages/CategoriesPage.d.ts.map +1 -0
- package/dist/layouts/booking-agenda/pages/CategoriesPage.js +162 -0
- package/dist/layouts/booking-agenda/pages/CategoryPage.d.ts +8 -0
- package/dist/layouts/booking-agenda/pages/CategoryPage.d.ts.map +1 -0
- package/dist/layouts/booking-agenda/pages/CategoryPage.js +67 -0
- package/dist/layouts/booking-agenda/pages/ContactPage.d.ts +7 -0
- package/dist/layouts/booking-agenda/pages/ContactPage.d.ts.map +1 -0
- package/dist/layouts/booking-agenda/pages/ContactPage.js +20 -0
- package/dist/layouts/booking-agenda/pages/ProductDetailPage.d.ts +8 -0
- package/dist/layouts/booking-agenda/pages/ProductDetailPage.d.ts.map +1 -0
- package/dist/layouts/booking-agenda/pages/ProductDetailPage.js +502 -0
- package/dist/layouts/booking-agenda/pages/ServicesPage.d.ts +9 -0
- package/dist/layouts/booking-agenda/pages/ServicesPage.d.ts.map +1 -0
- package/dist/layouts/booking-agenda/pages/ServicesPage.js +31 -0
- package/dist/layouts/clothing/components/ClothingHomePage.d.ts +7 -0
- package/dist/layouts/clothing/components/ClothingHomePage.d.ts.map +1 -0
- package/dist/layouts/clothing/components/ClothingHomePage.js +295 -0
- package/dist/layouts/clothing/index.d.ts +8 -0
- package/dist/layouts/clothing/index.d.ts.map +1 -0
- package/dist/layouts/clothing/index.js +8 -0
- package/dist/layouts/clothing/pages/AboutPage.d.ts +7 -0
- package/dist/layouts/clothing/pages/AboutPage.d.ts.map +1 -0
- package/dist/layouts/clothing/pages/AboutPage.js +45 -0
- package/dist/layouts/clothing/pages/CategoriesPage.d.ts +7 -0
- package/dist/layouts/clothing/pages/CategoriesPage.d.ts.map +1 -0
- package/dist/layouts/clothing/pages/CategoriesPage.js +162 -0
- package/dist/layouts/clothing/pages/CategoryPage.d.ts +8 -0
- package/dist/layouts/clothing/pages/CategoryPage.d.ts.map +1 -0
- package/dist/layouts/clothing/pages/CategoryPage.js +67 -0
- package/dist/layouts/clothing/pages/ContactPage.d.ts +7 -0
- package/dist/layouts/clothing/pages/ContactPage.d.ts.map +1 -0
- package/dist/layouts/clothing/pages/ContactPage.js +20 -0
- package/dist/layouts/clothing/pages/ProductDetailPage.d.ts +8 -0
- package/dist/layouts/clothing/pages/ProductDetailPage.d.ts.map +1 -0
- package/dist/layouts/clothing/pages/ProductDetailPage.js +502 -0
- package/dist/layouts/clothing/pages/ProductsPage.d.ts +7 -0
- package/dist/layouts/clothing/pages/ProductsPage.d.ts.map +1 -0
- package/dist/layouts/clothing/pages/ProductsPage.js +297 -0
- package/dist/layouts/clothing-minimal/components/ClothingHomePageMinimal.d.ts +7 -0
- package/dist/layouts/clothing-minimal/components/ClothingHomePageMinimal.d.ts.map +1 -0
- package/dist/layouts/clothing-minimal/components/ClothingHomePageMinimal.js +48 -0
- package/dist/layouts/clothing-minimal/index.d.ts +8 -0
- package/dist/layouts/clothing-minimal/index.d.ts.map +1 -0
- package/dist/layouts/clothing-minimal/index.js +8 -0
- package/dist/layouts/clothing-minimal/pages/AboutPage.d.ts +7 -0
- package/dist/layouts/clothing-minimal/pages/AboutPage.d.ts.map +1 -0
- package/dist/layouts/clothing-minimal/pages/AboutPage.js +45 -0
- package/dist/layouts/clothing-minimal/pages/CategoriesPage.d.ts +7 -0
- package/dist/layouts/clothing-minimal/pages/CategoriesPage.d.ts.map +1 -0
- package/dist/layouts/clothing-minimal/pages/CategoriesPage.js +162 -0
- package/dist/layouts/clothing-minimal/pages/CategoryPage.d.ts +8 -0
- package/dist/layouts/clothing-minimal/pages/CategoryPage.d.ts.map +1 -0
- package/dist/layouts/clothing-minimal/pages/CategoryPage.js +67 -0
- package/dist/layouts/clothing-minimal/pages/ContactPage.d.ts +7 -0
- package/dist/layouts/clothing-minimal/pages/ContactPage.d.ts.map +1 -0
- package/dist/layouts/clothing-minimal/pages/ContactPage.js +20 -0
- package/dist/layouts/clothing-minimal/pages/ProductDetailPage.d.ts +8 -0
- package/dist/layouts/clothing-minimal/pages/ProductDetailPage.d.ts.map +1 -0
- package/dist/layouts/clothing-minimal/pages/ProductDetailPage.js +502 -0
- package/dist/layouts/clothing-minimal/pages/ProductsPage.d.ts +7 -0
- package/dist/layouts/clothing-minimal/pages/ProductsPage.d.ts.map +1 -0
- package/dist/layouts/clothing-minimal/pages/ProductsPage.js +297 -0
- package/dist/layouts/electronics/components/ElectronicsCategoriesPage.d.ts +7 -0
- package/dist/layouts/electronics/components/ElectronicsCategoriesPage.d.ts.map +1 -0
- package/dist/layouts/electronics/components/ElectronicsCategoriesPage.js +34 -0
- package/dist/layouts/electronics/components/ElectronicsGridProductCard.d.ts +10 -0
- package/dist/layouts/electronics/components/ElectronicsGridProductCard.d.ts.map +1 -0
- package/dist/layouts/electronics/components/ElectronicsGridProductCard.js +107 -0
- package/dist/layouts/electronics/components/ElectronicsHomePage.d.ts +7 -0
- package/dist/layouts/electronics/components/ElectronicsHomePage.d.ts.map +1 -0
- package/dist/layouts/electronics/components/ElectronicsHomePage.js +78 -0
- package/dist/layouts/electronics/components/ElectronicsPageWrapper.d.ts +8 -0
- package/dist/layouts/electronics/components/ElectronicsPageWrapper.d.ts.map +1 -0
- package/dist/layouts/electronics/components/ElectronicsPageWrapper.js +10 -0
- package/dist/layouts/electronics/components/ElectronicsProductsPage.d.ts +7 -0
- package/dist/layouts/electronics/components/ElectronicsProductsPage.d.ts.map +1 -0
- package/dist/layouts/electronics/components/ElectronicsProductsPage.js +100 -0
- package/dist/layouts/electronics/components/ElectronicsStoreFooter.d.ts +7 -0
- package/dist/layouts/electronics/components/ElectronicsStoreFooter.d.ts.map +1 -0
- package/dist/layouts/electronics/components/ElectronicsStoreFooter.js +10 -0
- package/dist/layouts/electronics/components/ElectronicsStoreHeader.d.ts +7 -0
- package/dist/layouts/electronics/components/ElectronicsStoreHeader.d.ts.map +1 -0
- package/dist/layouts/electronics/components/ElectronicsStoreHeader.js +27 -0
- package/dist/layouts/electronics/index.d.ts +19 -0
- package/dist/layouts/electronics/index.d.ts.map +1 -0
- package/dist/layouts/electronics/index.js +19 -0
- package/dist/layouts/electronics/pages/AboutPage.d.ts +7 -0
- package/dist/layouts/electronics/pages/AboutPage.d.ts.map +1 -0
- package/dist/layouts/electronics/pages/AboutPage.js +45 -0
- package/dist/layouts/electronics/pages/CategoriesPage.d.ts +7 -0
- package/dist/layouts/electronics/pages/CategoriesPage.d.ts.map +1 -0
- package/dist/layouts/electronics/pages/CategoriesPage.js +6 -0
- package/dist/layouts/electronics/pages/CategoryPage.d.ts +8 -0
- package/dist/layouts/electronics/pages/CategoryPage.d.ts.map +1 -0
- package/dist/layouts/electronics/pages/CategoryPage.js +67 -0
- package/dist/layouts/electronics/pages/ContactPage.d.ts +7 -0
- package/dist/layouts/electronics/pages/ContactPage.d.ts.map +1 -0
- package/dist/layouts/electronics/pages/ContactPage.js +20 -0
- package/dist/layouts/electronics/pages/HelpCenterPage.d.ts +7 -0
- package/dist/layouts/electronics/pages/HelpCenterPage.d.ts.map +1 -0
- package/dist/layouts/electronics/pages/HelpCenterPage.js +55 -0
- package/dist/layouts/electronics/pages/PrivacyPolicyPage.d.ts +7 -0
- package/dist/layouts/electronics/pages/PrivacyPolicyPage.d.ts.map +1 -0
- package/dist/layouts/electronics/pages/PrivacyPolicyPage.js +8 -0
- package/dist/layouts/electronics/pages/ProductDetailPage.d.ts +8 -0
- package/dist/layouts/electronics/pages/ProductDetailPage.d.ts.map +1 -0
- package/dist/layouts/electronics/pages/ProductDetailPage.js +502 -0
- package/dist/layouts/electronics/pages/ProductsPage.d.ts +7 -0
- package/dist/layouts/electronics/pages/ProductsPage.d.ts.map +1 -0
- package/dist/layouts/electronics/pages/ProductsPage.js +297 -0
- package/dist/layouts/electronics/pages/ShippingReturnsPage.d.ts +7 -0
- package/dist/layouts/electronics/pages/ShippingReturnsPage.d.ts.map +1 -0
- package/dist/layouts/electronics/pages/ShippingReturnsPage.js +7 -0
- package/dist/layouts/electronics/pages/TermsPage.d.ts +7 -0
- package/dist/layouts/electronics/pages/TermsPage.d.ts.map +1 -0
- package/dist/layouts/electronics/pages/TermsPage.js +8 -0
- package/dist/layouts/electronics/pages/TrackOrderPage.d.ts +7 -0
- package/dist/layouts/electronics/pages/TrackOrderPage.d.ts.map +1 -0
- package/dist/layouts/electronics/pages/TrackOrderPage.js +371 -0
- package/dist/layouts/electronics-grid/components/ElectronicsGridProductCard.d.ts +10 -0
- package/dist/layouts/electronics-grid/components/ElectronicsGridProductCard.d.ts.map +1 -0
- package/dist/layouts/electronics-grid/components/ElectronicsGridProductCard.js +107 -0
- package/dist/layouts/electronics-grid/components/ElectronicsHomePageGrid.d.ts +7 -0
- package/dist/layouts/electronics-grid/components/ElectronicsHomePageGrid.d.ts.map +1 -0
- package/dist/layouts/electronics-grid/components/ElectronicsHomePageGrid.js +89 -0
- package/dist/layouts/electronics-grid/index.d.ts +15 -0
- package/dist/layouts/electronics-grid/index.d.ts.map +1 -0
- package/dist/layouts/electronics-grid/index.js +15 -0
- package/dist/layouts/electronics-grid/pages/AboutPage.d.ts +7 -0
- package/dist/layouts/electronics-grid/pages/AboutPage.d.ts.map +1 -0
- package/dist/layouts/electronics-grid/pages/AboutPage.js +45 -0
- package/dist/layouts/electronics-grid/pages/CategoriesPage.d.ts +7 -0
- package/dist/layouts/electronics-grid/pages/CategoriesPage.d.ts.map +1 -0
- package/dist/layouts/electronics-grid/pages/CategoriesPage.js +6 -0
- package/dist/layouts/electronics-grid/pages/CategoryPage.d.ts +8 -0
- package/dist/layouts/electronics-grid/pages/CategoryPage.d.ts.map +1 -0
- package/dist/layouts/electronics-grid/pages/CategoryPage.js +67 -0
- package/dist/layouts/electronics-grid/pages/ContactPage.d.ts +7 -0
- package/dist/layouts/electronics-grid/pages/ContactPage.d.ts.map +1 -0
- package/dist/layouts/electronics-grid/pages/ContactPage.js +20 -0
- package/dist/layouts/electronics-grid/pages/ElectronicsGridCategoriesPage.d.ts +7 -0
- package/dist/layouts/electronics-grid/pages/ElectronicsGridCategoriesPage.d.ts.map +1 -0
- package/dist/layouts/electronics-grid/pages/ElectronicsGridCategoriesPage.js +15 -0
- package/dist/layouts/electronics-grid/pages/HelpCenterPage.d.ts +7 -0
- package/dist/layouts/electronics-grid/pages/HelpCenterPage.d.ts.map +1 -0
- package/dist/layouts/electronics-grid/pages/HelpCenterPage.js +55 -0
- package/dist/layouts/electronics-grid/pages/PrivacyPolicyPage.d.ts +7 -0
- package/dist/layouts/electronics-grid/pages/PrivacyPolicyPage.d.ts.map +1 -0
- package/dist/layouts/electronics-grid/pages/PrivacyPolicyPage.js +8 -0
- package/dist/layouts/electronics-grid/pages/ProductDetailPage.d.ts +8 -0
- package/dist/layouts/electronics-grid/pages/ProductDetailPage.d.ts.map +1 -0
- package/dist/layouts/electronics-grid/pages/ProductDetailPage.js +502 -0
- package/dist/layouts/electronics-grid/pages/ProductsPage.d.ts +7 -0
- package/dist/layouts/electronics-grid/pages/ProductsPage.d.ts.map +1 -0
- package/dist/layouts/electronics-grid/pages/ProductsPage.js +297 -0
- package/dist/layouts/electronics-grid/pages/ShippingReturnsPage.d.ts +7 -0
- package/dist/layouts/electronics-grid/pages/ShippingReturnsPage.d.ts.map +1 -0
- package/dist/layouts/electronics-grid/pages/ShippingReturnsPage.js +7 -0
- package/dist/layouts/electronics-grid/pages/TermsPage.d.ts +7 -0
- package/dist/layouts/electronics-grid/pages/TermsPage.d.ts.map +1 -0
- package/dist/layouts/electronics-grid/pages/TermsPage.js +8 -0
- package/dist/layouts/electronics-grid/pages/TrackOrderPage.d.ts +7 -0
- package/dist/layouts/electronics-grid/pages/TrackOrderPage.d.ts.map +1 -0
- package/dist/layouts/electronics-grid/pages/TrackOrderPage.js +371 -0
- package/dist/layouts/food/components/FoodHomePage.d.ts +7 -0
- package/dist/layouts/food/components/FoodHomePage.d.ts.map +1 -0
- package/dist/layouts/food/components/FoodHomePage.js +304 -0
- package/dist/layouts/food/index.d.ts +9 -0
- package/dist/layouts/food/index.d.ts.map +1 -0
- package/dist/layouts/food/index.js +9 -0
- package/dist/layouts/food/pages/AboutPage.d.ts +7 -0
- package/dist/layouts/food/pages/AboutPage.d.ts.map +1 -0
- package/dist/layouts/food/pages/AboutPage.js +45 -0
- package/dist/layouts/food/pages/CategoriesPage.d.ts +7 -0
- package/dist/layouts/food/pages/CategoriesPage.d.ts.map +1 -0
- package/dist/layouts/food/pages/CategoriesPage.js +162 -0
- package/dist/layouts/food/pages/CategoryPage.d.ts +8 -0
- package/dist/layouts/food/pages/CategoryPage.d.ts.map +1 -0
- package/dist/layouts/food/pages/CategoryPage.js +67 -0
- package/dist/layouts/food/pages/ContactPage.d.ts +7 -0
- package/dist/layouts/food/pages/ContactPage.d.ts.map +1 -0
- package/dist/layouts/food/pages/ContactPage.js +20 -0
- package/dist/layouts/food/pages/MenuPage.d.ts +8 -0
- package/dist/layouts/food/pages/MenuPage.d.ts.map +1 -0
- package/dist/layouts/food/pages/MenuPage.js +75 -0
- package/dist/layouts/food/pages/ProductDetailPage.d.ts +8 -0
- package/dist/layouts/food/pages/ProductDetailPage.d.ts.map +1 -0
- package/dist/layouts/food/pages/ProductDetailPage.js +502 -0
- package/dist/layouts/food/pages/ProductsPage.d.ts +7 -0
- package/dist/layouts/food/pages/ProductsPage.d.ts.map +1 -0
- package/dist/layouts/food/pages/ProductsPage.js +297 -0
- package/dist/layouts/food-modern/components/FoodHomePageModern.d.ts +7 -0
- package/dist/layouts/food-modern/components/FoodHomePageModern.d.ts.map +1 -0
- package/dist/layouts/food-modern/components/FoodHomePageModern.js +118 -0
- package/dist/layouts/food-modern/index.d.ts +9 -0
- package/dist/layouts/food-modern/index.d.ts.map +1 -0
- package/dist/layouts/food-modern/index.js +9 -0
- package/dist/layouts/food-modern/pages/AboutPage.d.ts +7 -0
- package/dist/layouts/food-modern/pages/AboutPage.d.ts.map +1 -0
- package/dist/layouts/food-modern/pages/AboutPage.js +45 -0
- package/dist/layouts/food-modern/pages/CategoriesPage.d.ts +7 -0
- package/dist/layouts/food-modern/pages/CategoriesPage.d.ts.map +1 -0
- package/dist/layouts/food-modern/pages/CategoriesPage.js +162 -0
- package/dist/layouts/food-modern/pages/CategoryPage.d.ts +8 -0
- package/dist/layouts/food-modern/pages/CategoryPage.d.ts.map +1 -0
- package/dist/layouts/food-modern/pages/CategoryPage.js +67 -0
- package/dist/layouts/food-modern/pages/ContactPage.d.ts +7 -0
- package/dist/layouts/food-modern/pages/ContactPage.d.ts.map +1 -0
- package/dist/layouts/food-modern/pages/ContactPage.js +20 -0
- package/dist/layouts/food-modern/pages/MenuPage.d.ts +8 -0
- package/dist/layouts/food-modern/pages/MenuPage.d.ts.map +1 -0
- package/dist/layouts/food-modern/pages/MenuPage.js +75 -0
- package/dist/layouts/food-modern/pages/ProductDetailPage.d.ts +8 -0
- package/dist/layouts/food-modern/pages/ProductDetailPage.d.ts.map +1 -0
- package/dist/layouts/food-modern/pages/ProductDetailPage.js +502 -0
- package/dist/layouts/food-modern/pages/ProductsPage.d.ts +7 -0
- package/dist/layouts/food-modern/pages/ProductsPage.d.ts.map +1 -0
- package/dist/layouts/food-modern/pages/ProductsPage.js +297 -0
- package/dist/layouts/motivational-speaker/components/MotivationalHomePage.d.ts +7 -0
- package/dist/layouts/motivational-speaker/components/MotivationalHomePage.d.ts.map +1 -0
- package/dist/layouts/motivational-speaker/components/MotivationalHomePage.js +115 -0
- package/dist/layouts/motivational-speaker/components/MotivationalPageWrapper.d.ts +8 -0
- package/dist/layouts/motivational-speaker/components/MotivationalPageWrapper.d.ts.map +1 -0
- package/dist/layouts/motivational-speaker/components/MotivationalPageWrapper.js +10 -0
- package/dist/layouts/motivational-speaker/index.d.ts +10 -0
- package/dist/layouts/motivational-speaker/index.d.ts.map +1 -0
- package/dist/layouts/motivational-speaker/index.js +10 -0
- package/dist/layouts/motivational-speaker/pages/AboutPage.d.ts +7 -0
- package/dist/layouts/motivational-speaker/pages/AboutPage.d.ts.map +1 -0
- package/dist/layouts/motivational-speaker/pages/AboutPage.js +45 -0
- package/dist/layouts/motivational-speaker/pages/CategoryPage.d.ts +8 -0
- package/dist/layouts/motivational-speaker/pages/CategoryPage.d.ts.map +1 -0
- package/dist/layouts/motivational-speaker/pages/CategoryPage.js +67 -0
- package/dist/layouts/motivational-speaker/pages/ContactPage.d.ts +7 -0
- package/dist/layouts/motivational-speaker/pages/ContactPage.d.ts.map +1 -0
- package/dist/layouts/motivational-speaker/pages/ContactPage.js +20 -0
- package/dist/layouts/motivational-speaker/pages/ProductsPage.d.ts +7 -0
- package/dist/layouts/motivational-speaker/pages/ProductsPage.d.ts.map +1 -0
- package/dist/layouts/motivational-speaker/pages/ProductsPage.js +297 -0
- package/dist/layouts/motivational-speaker/pages/ServiceDetailPage.d.ts +8 -0
- package/dist/layouts/motivational-speaker/pages/ServiceDetailPage.d.ts.map +1 -0
- package/dist/layouts/motivational-speaker/pages/ServiceDetailPage.js +144 -0
- package/dist/layouts/motivational-speaker/pages/ServicesPage.d.ts +9 -0
- package/dist/layouts/motivational-speaker/pages/ServicesPage.d.ts.map +1 -0
- package/dist/layouts/motivational-speaker/pages/ServicesPage.js +31 -0
- package/dist/layouts/motivational-speaker/pages/SubscriptionPage.d.ts +7 -0
- package/dist/layouts/motivational-speaker/pages/SubscriptionPage.d.ts.map +1 -0
- package/dist/layouts/motivational-speaker/pages/SubscriptionPage.js +136 -0
- package/dist/layouts/shared/components/BaseStoreLayout.d.ts +8 -0
- package/dist/layouts/shared/components/BaseStoreLayout.d.ts.map +1 -0
- package/dist/layouts/shared/components/BaseStoreLayout.js +59 -0
- package/dist/layouts/shared/components/GenericPageWrapper.d.ts +8 -0
- package/dist/layouts/shared/components/GenericPageWrapper.d.ts.map +1 -0
- package/dist/layouts/shared/components/GenericPageWrapper.js +19 -0
- package/dist/layouts/shared/components/PromoBanner.d.ts +9 -0
- package/dist/layouts/shared/components/PromoBanner.d.ts.map +1 -0
- package/dist/layouts/shared/components/PromoBanner.js +14 -0
- package/dist/layouts/shared/components/StoreFooter.d.ts +7 -0
- package/dist/layouts/shared/components/StoreFooter.d.ts.map +1 -0
- package/dist/layouts/shared/components/StoreFooter.js +147 -0
- package/dist/layouts/shared/components/StoreHeader.d.ts +8 -0
- package/dist/layouts/shared/components/StoreHeader.d.ts.map +1 -0
- package/dist/layouts/shared/components/StoreHeader.js +87 -0
- package/dist/layouts/shared/components/TestimonialCard.d.ts +8 -0
- package/dist/layouts/shared/components/TestimonialCard.d.ts.map +1 -0
- package/dist/layouts/shared/components/TestimonialCard.js +10 -0
- package/dist/layouts/shared/index.d.ts +23 -0
- package/dist/layouts/shared/index.d.ts.map +1 -0
- package/dist/layouts/shared/index.js +23 -0
- package/dist/layouts/shared/pages/AccountPage.d.ts +7 -0
- package/dist/layouts/shared/pages/AccountPage.d.ts.map +1 -0
- package/dist/layouts/shared/pages/AccountPage.js +605 -0
- package/dist/layouts/shared/pages/CartPage.d.ts +7 -0
- package/dist/layouts/shared/pages/CartPage.d.ts.map +1 -0
- package/dist/layouts/shared/pages/CartPage.js +83 -0
- package/dist/layouts/shared/pages/CheckoutPage.d.ts +7 -0
- package/dist/layouts/shared/pages/CheckoutPage.d.ts.map +1 -0
- package/dist/layouts/shared/pages/CheckoutPage.js +860 -0
- package/dist/layouts/shared/pages/CookiePolicyPage.d.ts +7 -0
- package/dist/layouts/shared/pages/CookiePolicyPage.d.ts.map +1 -0
- package/dist/layouts/shared/pages/CookiePolicyPage.js +8 -0
- package/dist/layouts/shared/pages/FeatureDisabledPage.d.ts +9 -0
- package/dist/layouts/shared/pages/FeatureDisabledPage.d.ts.map +1 -0
- package/dist/layouts/shared/pages/FeatureDisabledPage.js +8 -0
- package/dist/layouts/shared/pages/HelpCenterPage.d.ts +7 -0
- package/dist/layouts/shared/pages/HelpCenterPage.d.ts.map +1 -0
- package/dist/layouts/shared/pages/HelpCenterPage.js +55 -0
- package/dist/layouts/shared/pages/MaintenancePage.d.ts +7 -0
- package/dist/layouts/shared/pages/MaintenancePage.d.ts.map +1 -0
- package/dist/layouts/shared/pages/MaintenancePage.js +44 -0
- package/dist/layouts/shared/pages/PortfolioPage.d.ts +7 -0
- package/dist/layouts/shared/pages/PortfolioPage.d.ts.map +1 -0
- package/dist/layouts/shared/pages/PortfolioPage.js +130 -0
- package/dist/layouts/shared/pages/PrivacyPolicyPage.d.ts +7 -0
- package/dist/layouts/shared/pages/PrivacyPolicyPage.d.ts.map +1 -0
- package/dist/layouts/shared/pages/PrivacyPolicyPage.js +8 -0
- package/dist/layouts/shared/pages/ShippingReturnsPage.d.ts +7 -0
- package/dist/layouts/shared/pages/ShippingReturnsPage.d.ts.map +1 -0
- package/dist/layouts/shared/pages/ShippingReturnsPage.js +7 -0
- package/dist/layouts/shared/pages/SizeGuidePage.d.ts +7 -0
- package/dist/layouts/shared/pages/SizeGuidePage.d.ts.map +1 -0
- package/dist/layouts/shared/pages/SizeGuidePage.js +10 -0
- package/dist/layouts/shared/pages/StyleGuidePage.d.ts +7 -0
- package/dist/layouts/shared/pages/StyleGuidePage.d.ts.map +1 -0
- package/dist/layouts/shared/pages/StyleGuidePage.js +126 -0
- package/dist/layouts/shared/pages/TeamPage.d.ts +7 -0
- package/dist/layouts/shared/pages/TeamPage.d.ts.map +1 -0
- package/dist/layouts/shared/pages/TeamPage.js +39 -0
- package/dist/layouts/shared/pages/TermsPage.d.ts +7 -0
- package/dist/layouts/shared/pages/TermsPage.d.ts.map +1 -0
- package/dist/layouts/shared/pages/TermsPage.js +8 -0
- package/dist/layouts/shared/pages/TrackOrderPage.d.ts +7 -0
- package/dist/layouts/shared/pages/TrackOrderPage.d.ts.map +1 -0
- package/dist/layouts/shared/pages/TrackOrderPage.js +371 -0
- package/dist/layouts/shared/pages/WishlistPage.d.ts +7 -0
- package/dist/layouts/shared/pages/WishlistPage.d.ts.map +1 -0
- package/dist/layouts/shared/pages/WishlistPage.js +162 -0
- package/dist/lib/api.d.ts +8 -0
- package/dist/lib/api.d.ts.map +1 -0
- package/dist/lib/api.js +109 -0
- package/dist/lib/auth-context.d.ts +30 -0
- package/dist/lib/auth-context.d.ts.map +1 -0
- package/dist/lib/auth-context.js +207 -0
- package/dist/lib/content-selectors.d.ts +21 -0
- package/dist/lib/content-selectors.d.ts.map +1 -0
- package/dist/lib/content-selectors.js +75 -0
- package/dist/lib/countries.d.ts +11 -0
- package/dist/lib/countries.d.ts.map +1 -0
- package/dist/lib/countries.js +252 -0
- package/dist/lib/currency-symbols.d.ts +36 -0
- package/dist/lib/currency-symbols.d.ts.map +1 -0
- package/dist/lib/currency-symbols.js +153 -0
- package/dist/lib/guest-user.d.ts +27 -0
- package/dist/lib/guest-user.d.ts.map +1 -0
- package/dist/lib/guest-user.js +71 -0
- package/dist/lib/loading-context.d.ts +19 -0
- package/dist/lib/loading-context.d.ts.map +1 -0
- package/dist/lib/loading-context.js +197 -0
- package/dist/lib/mock-stores.d.ts +22 -0
- package/dist/lib/mock-stores.d.ts.map +1 -0
- package/dist/lib/mock-stores.js +2099 -0
- package/dist/lib/payaza-checkout.d.ts +167 -0
- package/dist/lib/payaza-checkout.d.ts.map +1 -0
- package/dist/lib/payaza-checkout.js +455 -0
- package/dist/lib/services/analytics.service.d.ts +58 -0
- package/dist/lib/services/analytics.service.d.ts.map +1 -0
- package/dist/lib/services/analytics.service.js +182 -0
- package/dist/lib/services/auth.service.d.ts +63 -0
- package/dist/lib/services/auth.service.d.ts.map +1 -0
- package/dist/lib/services/auth.service.js +101 -0
- package/dist/lib/services/booking.service.d.ts +53 -0
- package/dist/lib/services/booking.service.d.ts.map +1 -0
- package/dist/lib/services/booking.service.js +52 -0
- package/dist/lib/services/cart.service.d.ts +37 -0
- package/dist/lib/services/cart.service.d.ts.map +1 -0
- package/dist/lib/services/cart.service.js +55 -0
- package/dist/lib/services/category.service.d.ts +21 -0
- package/dist/lib/services/category.service.d.ts.map +1 -0
- package/dist/lib/services/category.service.js +41 -0
- package/dist/lib/services/checkout.service.d.ts +79 -0
- package/dist/lib/services/checkout.service.d.ts.map +1 -0
- package/dist/lib/services/checkout.service.js +37 -0
- package/dist/lib/services/customer.service.d.ts +52 -0
- package/dist/lib/services/customer.service.d.ts.map +1 -0
- package/dist/lib/services/customer.service.js +64 -0
- package/dist/lib/services/fees.service.d.ts +23 -0
- package/dist/lib/services/fees.service.d.ts.map +1 -0
- package/dist/lib/services/fees.service.js +13 -0
- package/dist/lib/services/index.d.ts +19 -0
- package/dist/lib/services/index.d.ts.map +1 -0
- package/dist/lib/services/index.js +18 -0
- package/dist/lib/services/order.service.d.ts +107 -0
- package/dist/lib/services/order.service.d.ts.map +1 -0
- package/dist/lib/services/order.service.js +82 -0
- package/dist/lib/services/payment.service.d.ts +51 -0
- package/dist/lib/services/payment.service.d.ts.map +1 -0
- package/dist/lib/services/payment.service.js +105 -0
- package/dist/lib/services/product.service.d.ts +90 -0
- package/dist/lib/services/product.service.d.ts.map +1 -0
- package/dist/lib/services/product.service.js +86 -0
- package/dist/lib/services/promo.service.d.ts +31 -0
- package/dist/lib/services/promo.service.d.ts.map +1 -0
- package/dist/lib/services/promo.service.js +29 -0
- package/dist/lib/services/review.service.d.ts +47 -0
- package/dist/lib/services/review.service.d.ts.map +1 -0
- package/dist/lib/services/review.service.js +54 -0
- package/dist/lib/services/service.service.d.ts +55 -0
- package/dist/lib/services/service.service.d.ts.map +1 -0
- package/dist/lib/services/service.service.js +37 -0
- package/dist/lib/services/shipping.service.d.ts +44 -0
- package/dist/lib/services/shipping.service.d.ts.map +1 -0
- package/dist/lib/services/shipping.service.js +23 -0
- package/dist/lib/services/store.service.d.ts +87 -0
- package/dist/lib/services/store.service.d.ts.map +1 -0
- package/dist/lib/services/store.service.js +41 -0
- package/dist/lib/services/wishlist.service.d.ts +30 -0
- package/dist/lib/services/wishlist.service.d.ts.map +1 -0
- package/dist/lib/services/wishlist.service.js +53 -0
- package/dist/lib/store-config-utils.d.ts +41 -0
- package/dist/lib/store-config-utils.d.ts.map +1 -0
- package/dist/lib/store-config-utils.js +1535 -0
- package/dist/lib/store-config.d.ts +3 -0
- package/dist/lib/store-config.d.ts.map +1 -0
- package/dist/lib/store-config.js +2 -0
- package/dist/lib/store-context.d.ts +39 -0
- package/dist/lib/store-context.d.ts.map +1 -0
- package/dist/lib/store-context.js +803 -0
- package/dist/lib/store-types.d.ts +663 -0
- package/dist/lib/store-types.d.ts.map +1 -0
- package/dist/lib/store-types.js +1 -0
- package/dist/lib/utils/asset-helpers.d.ts +102 -0
- package/dist/lib/utils/asset-helpers.d.ts.map +1 -0
- package/dist/lib/utils/asset-helpers.js +395 -0
- package/dist/lib/utils/category-tree.d.ts +25 -0
- package/dist/lib/utils/category-tree.d.ts.map +1 -0
- package/dist/lib/utils/category-tree.js +85 -0
- package/dist/lib/utils/demo-detection.d.ts +23 -0
- package/dist/lib/utils/demo-detection.d.ts.map +1 -0
- package/dist/lib/utils/demo-detection.js +36 -0
- package/dist/lib/utils/fee-calculations.d.ts +35 -0
- package/dist/lib/utils/fee-calculations.d.ts.map +1 -0
- package/dist/lib/utils/fee-calculations.js +64 -0
- package/dist/lib/utils/index.d.ts +37 -0
- package/dist/lib/utils/index.d.ts.map +1 -0
- package/dist/lib/utils/index.js +160 -0
- package/dist/lib/utils/url-helpers.d.ts +30 -0
- package/dist/lib/utils/url-helpers.d.ts.map +1 -0
- package/dist/lib/utils/url-helpers.js +56 -0
- package/dist/lib/utils/video-helpers.d.ts +32 -0
- package/dist/lib/utils/video-helpers.d.ts.map +1 -0
- package/dist/lib/utils/video-helpers.js +210 -0
- package/package.json +92 -0
|
@@ -0,0 +1,1535 @@
|
|
|
1
|
+
import { getStoreConfig } from './store-config';
|
|
2
|
+
import { shouldUseAPI, getBaseStoreSlug } from './utils/demo-detection';
|
|
3
|
+
import { storeService } from './services/store.service';
|
|
4
|
+
import { categoryService } from './services/category.service';
|
|
5
|
+
import { productService } from './services/product.service';
|
|
6
|
+
import { serviceService } from './services/service.service';
|
|
7
|
+
import { normalizePrice } from './utils';
|
|
8
|
+
// Simple ID generator fallback (doesn't require uuid package)
|
|
9
|
+
function generateId() {
|
|
10
|
+
if (typeof crypto !== 'undefined' && crypto.randomUUID) {
|
|
11
|
+
return crypto.randomUUID();
|
|
12
|
+
}
|
|
13
|
+
// Fallback for environments without crypto.randomUUID
|
|
14
|
+
return `${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Comprehensive function to extract and validate image URLs from any format
|
|
18
|
+
* Handles: strings, objects, arrays, JSON-encoded, comma-separated, fragmented JSON
|
|
19
|
+
* @param input - Can be string, object, array, or any combination
|
|
20
|
+
* @returns Array of valid absolute image URLs
|
|
21
|
+
*/
|
|
22
|
+
export function extractImageUrls(input) {
|
|
23
|
+
if (!input) {
|
|
24
|
+
return [];
|
|
25
|
+
}
|
|
26
|
+
// Handle arrays
|
|
27
|
+
if (Array.isArray(input)) {
|
|
28
|
+
if (input.length === 0) {
|
|
29
|
+
return [];
|
|
30
|
+
}
|
|
31
|
+
// Check if array contains objects with url property
|
|
32
|
+
const firstItem = input[0];
|
|
33
|
+
if (firstItem && typeof firstItem === 'object' && firstItem !== null && 'url' in firstItem) {
|
|
34
|
+
// Array of objects: [{url: "...", isPrimary: true}, ...]
|
|
35
|
+
return input
|
|
36
|
+
.map((item) => extractImageUrls(item)) // Recursively extract from each object
|
|
37
|
+
.flat()
|
|
38
|
+
.filter((url) => url.length > 0);
|
|
39
|
+
}
|
|
40
|
+
// Check if array contains fragmented JSON strings (TypeORM simple-array issue)
|
|
41
|
+
if (typeof firstItem === 'string' && firstItem.trim().startsWith('[') && firstItem.includes('"url"')) {
|
|
42
|
+
try {
|
|
43
|
+
// Reconstruct fragmented JSON
|
|
44
|
+
const reconstructedJson = input.join('');
|
|
45
|
+
const parsed = JSON.parse(reconstructedJson);
|
|
46
|
+
return extractImageUrls(parsed); // Recursively process parsed result
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
// If reconstruction fails, treat as string array
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// Array of strings: ["url1", "url2", ...]
|
|
53
|
+
return input
|
|
54
|
+
.map((item) => extractImageUrls(item)) // Recursively extract from each item
|
|
55
|
+
.flat()
|
|
56
|
+
.filter((url) => url.length > 0);
|
|
57
|
+
}
|
|
58
|
+
// Handle objects
|
|
59
|
+
if (typeof input === 'object' && input !== null) {
|
|
60
|
+
// Object with url property: {url: "...", isPrimary: true}
|
|
61
|
+
if ('url' in input && typeof input.url === 'string') {
|
|
62
|
+
const url = validateAndConvertUrl(input.url);
|
|
63
|
+
return url ? [url] : [];
|
|
64
|
+
}
|
|
65
|
+
// Stringified object: '{"url":"..."}'
|
|
66
|
+
if (typeof input === 'object' && Object.keys(input).length === 0) {
|
|
67
|
+
return [];
|
|
68
|
+
}
|
|
69
|
+
// Try to find url in nested structure
|
|
70
|
+
for (const key of ['url', 'image', 'src', 'imageUrl', 'image_url']) {
|
|
71
|
+
if (key in input && typeof input[key] === 'string') {
|
|
72
|
+
const url = validateAndConvertUrl(input[key]);
|
|
73
|
+
if (url)
|
|
74
|
+
return [url];
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return [];
|
|
78
|
+
}
|
|
79
|
+
// Handle strings
|
|
80
|
+
if (typeof input === 'string') {
|
|
81
|
+
const trimmed = input.trim();
|
|
82
|
+
if (trimmed.length === 0) {
|
|
83
|
+
return [];
|
|
84
|
+
}
|
|
85
|
+
// Check if it's JSON-encoded
|
|
86
|
+
if ((trimmed.startsWith('[') || trimmed.startsWith('{')) && (trimmed.includes('"url"') || trimmed.includes('"image"'))) {
|
|
87
|
+
try {
|
|
88
|
+
const parsed = JSON.parse(trimmed);
|
|
89
|
+
return extractImageUrls(parsed); // Recursively process parsed result
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
// Not valid JSON, continue as string
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// Check if it's comma-separated URLs
|
|
96
|
+
if (trimmed.includes(',') && (trimmed.includes('http://') || trimmed.includes('https://') || trimmed.includes('r2.dev'))) {
|
|
97
|
+
return trimmed
|
|
98
|
+
.split(',')
|
|
99
|
+
.map((s) => s.trim())
|
|
100
|
+
.map((url) => validateAndConvertUrl(url))
|
|
101
|
+
.filter((url) => url.length > 0);
|
|
102
|
+
}
|
|
103
|
+
// Single URL string
|
|
104
|
+
const url = validateAndConvertUrl(trimmed);
|
|
105
|
+
return url ? [url] : [];
|
|
106
|
+
}
|
|
107
|
+
return [];
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Validates a URL string (CDN URLs from Cloudflare Object Storage are used as-is)
|
|
111
|
+
* @param url - URL string to validate
|
|
112
|
+
* @returns Valid URL string or empty string
|
|
113
|
+
*/
|
|
114
|
+
function validateAndConvertUrl(url) {
|
|
115
|
+
if (!url || typeof url !== 'string') {
|
|
116
|
+
return '';
|
|
117
|
+
}
|
|
118
|
+
const trimmed = url.trim();
|
|
119
|
+
if (trimmed.length === 0) {
|
|
120
|
+
return '';
|
|
121
|
+
}
|
|
122
|
+
// URLs from Cloudflare Object Storage (CDN) are already absolute - use as-is
|
|
123
|
+
// Only validate that it's a non-empty string
|
|
124
|
+
return trimmed;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Validates a single image URL string
|
|
128
|
+
* @param url - URL to validate
|
|
129
|
+
* @returns Valid URL string or empty string
|
|
130
|
+
*/
|
|
131
|
+
function validateImageUrl(url) {
|
|
132
|
+
if (!url) {
|
|
133
|
+
return '';
|
|
134
|
+
}
|
|
135
|
+
// Handle string URLs
|
|
136
|
+
if (typeof url === 'string') {
|
|
137
|
+
const trimmed = url.trim();
|
|
138
|
+
return trimmed.length > 0 ? trimmed : '';
|
|
139
|
+
}
|
|
140
|
+
// Handle number (convert to string)
|
|
141
|
+
if (typeof url === 'number') {
|
|
142
|
+
return String(url).trim();
|
|
143
|
+
}
|
|
144
|
+
// Handle object with url property (nested structures)
|
|
145
|
+
if (typeof url === 'object' && url !== null) {
|
|
146
|
+
// Try common URL property names
|
|
147
|
+
for (const key of ['url', 'src', 'image', 'imageUrl', 'image_url', 'value']) {
|
|
148
|
+
if (key in url && url[key]) {
|
|
149
|
+
const extracted = validateImageUrl(url[key]);
|
|
150
|
+
if (extracted)
|
|
151
|
+
return extracted;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return '';
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Product-specific image extraction function
|
|
159
|
+
* Comprehensive function that handles all image storage formats
|
|
160
|
+
* URLs are used as-is from CDN (Cloudflare Object Storage)
|
|
161
|
+
* @param images - Can be:
|
|
162
|
+
* - Array of objects: [{url: string}, ...] or [{url: {url: string}}, ...]
|
|
163
|
+
* - Array of strings: ["url1", "url2"]
|
|
164
|
+
* - Single object: {url: string}
|
|
165
|
+
* - Single string: "url"
|
|
166
|
+
* - JSON string: '{"url":"..."}' or '[{"url":"..."}]'
|
|
167
|
+
* - null/undefined
|
|
168
|
+
* @returns Array of valid URL strings
|
|
169
|
+
*/
|
|
170
|
+
function extractProductImageUrls(images) {
|
|
171
|
+
// Handle null/undefined
|
|
172
|
+
if (!images) {
|
|
173
|
+
return [];
|
|
174
|
+
}
|
|
175
|
+
// Handle JSON strings (parsed or unparsed)
|
|
176
|
+
if (typeof images === 'string') {
|
|
177
|
+
const trimmed = images.trim();
|
|
178
|
+
if (trimmed.length === 0) {
|
|
179
|
+
return [];
|
|
180
|
+
}
|
|
181
|
+
// Try to parse as JSON (handles both single and double-stringified)
|
|
182
|
+
if ((trimmed.startsWith('[') || trimmed.startsWith('{') || trimmed.startsWith('"')) && (trimmed.includes('"url"') || trimmed.includes('"image"'))) {
|
|
183
|
+
try {
|
|
184
|
+
let parsed = JSON.parse(trimmed);
|
|
185
|
+
// Check if result is still a string (double-stringified)
|
|
186
|
+
if (typeof parsed === 'string') {
|
|
187
|
+
try {
|
|
188
|
+
parsed = JSON.parse(parsed);
|
|
189
|
+
}
|
|
190
|
+
catch (e) {
|
|
191
|
+
// Not double-stringified, continue as string URL
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
// Recursively process the parsed result
|
|
195
|
+
return extractProductImageUrls(parsed);
|
|
196
|
+
}
|
|
197
|
+
catch (e) {
|
|
198
|
+
// Not valid JSON, continue as string URL
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
// Check if it's comma-separated URLs
|
|
202
|
+
if (trimmed.includes(',') && (trimmed.includes('http://') || trimmed.includes('https://') || trimmed.includes('r2.dev') || trimmed.includes('pub-'))) {
|
|
203
|
+
return trimmed
|
|
204
|
+
.split(',')
|
|
205
|
+
.map((url) => validateImageUrl(url))
|
|
206
|
+
.filter((url) => url.length > 0);
|
|
207
|
+
}
|
|
208
|
+
// Single URL string
|
|
209
|
+
const url = validateImageUrl(trimmed);
|
|
210
|
+
return url ? [url] : [];
|
|
211
|
+
}
|
|
212
|
+
// Handle arrays - this is the most common case
|
|
213
|
+
if (Array.isArray(images)) {
|
|
214
|
+
if (images.length === 0) {
|
|
215
|
+
return [];
|
|
216
|
+
}
|
|
217
|
+
const firstItem = images[0];
|
|
218
|
+
// Case 1: Array of objects with url property: [{url: "...", isPrimary: true}, ...]
|
|
219
|
+
// This is what backend normalizeProductImages returns
|
|
220
|
+
// Also handles nested: [{url: {url: "..."}}]
|
|
221
|
+
if (firstItem && typeof firstItem === 'object' && firstItem !== null) {
|
|
222
|
+
const result = [];
|
|
223
|
+
for (const item of images) {
|
|
224
|
+
if (!item)
|
|
225
|
+
continue;
|
|
226
|
+
// Handle object items
|
|
227
|
+
if (typeof item === 'object' && item !== null) {
|
|
228
|
+
// Check for direct url property
|
|
229
|
+
if ('url' in item) {
|
|
230
|
+
const url = validateImageUrl(item.url);
|
|
231
|
+
if (url) {
|
|
232
|
+
result.push(url);
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
// Check for other common property names
|
|
237
|
+
for (const key of ['src', 'image', 'imageUrl', 'image_url', 'value']) {
|
|
238
|
+
if (key in item) {
|
|
239
|
+
const url = validateImageUrl(item[key]);
|
|
240
|
+
if (url) {
|
|
241
|
+
result.push(url);
|
|
242
|
+
break;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
// Handle string items in mixed arrays
|
|
248
|
+
else if (typeof item === 'string') {
|
|
249
|
+
const url = validateImageUrl(item);
|
|
250
|
+
if (url) {
|
|
251
|
+
result.push(url);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
if (result.length > 0) {
|
|
256
|
+
return result;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
// Case 2: Array of string URLs: ["url1", "url2", ...]
|
|
260
|
+
if (typeof firstItem === 'string') {
|
|
261
|
+
return images
|
|
262
|
+
.map((item) => validateImageUrl(item))
|
|
263
|
+
.filter((url) => url.length > 0);
|
|
264
|
+
}
|
|
265
|
+
// Case 3: Array of mixed types - try to extract from each
|
|
266
|
+
const result = [];
|
|
267
|
+
for (const item of images) {
|
|
268
|
+
const url = validateImageUrl(item);
|
|
269
|
+
if (url) {
|
|
270
|
+
result.push(url);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
return result;
|
|
274
|
+
}
|
|
275
|
+
// Handle single object with url property
|
|
276
|
+
if (typeof images === 'object' && images !== null && !Array.isArray(images)) {
|
|
277
|
+
// Check for direct url property
|
|
278
|
+
if ('url' in images) {
|
|
279
|
+
const url = validateImageUrl(images.url);
|
|
280
|
+
if (url) {
|
|
281
|
+
return [url];
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
// Check for other common property names
|
|
285
|
+
for (const key of ['src', 'image', 'imageUrl', 'image_url', 'value']) {
|
|
286
|
+
if (key in images) {
|
|
287
|
+
const url = validateImageUrl(images[key]);
|
|
288
|
+
if (url) {
|
|
289
|
+
return [url];
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
return [];
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Converts a relative URL to an absolute URL using the API base URL
|
|
298
|
+
* @param url - The URL to convert (can be relative like /uploads/... or already absolute)
|
|
299
|
+
* @returns Absolute URL string
|
|
300
|
+
* @deprecated Use validateAndConvertUrl instead for better validation
|
|
301
|
+
*/
|
|
302
|
+
function convertToAbsoluteUrl(url) {
|
|
303
|
+
return validateAndConvertUrl(url);
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Valid StoreLayoutType values
|
|
307
|
+
*/
|
|
308
|
+
const VALID_LAYOUTS = [
|
|
309
|
+
'food',
|
|
310
|
+
'food-modern',
|
|
311
|
+
'clothing',
|
|
312
|
+
'clothing-minimal',
|
|
313
|
+
'booking',
|
|
314
|
+
'booking-agenda',
|
|
315
|
+
'electronics',
|
|
316
|
+
'electronics-grid',
|
|
317
|
+
];
|
|
318
|
+
/**
|
|
319
|
+
* Maps API layout string to StoreLayoutType
|
|
320
|
+
* Falls back to 'food' if layout is invalid or missing
|
|
321
|
+
*/
|
|
322
|
+
function mapLayoutToStoreLayoutType(layout) {
|
|
323
|
+
if (!layout) {
|
|
324
|
+
return 'food'; // Default fallback
|
|
325
|
+
}
|
|
326
|
+
// Check if layout is a valid StoreLayoutType
|
|
327
|
+
if (VALID_LAYOUTS.includes(layout)) {
|
|
328
|
+
return layout;
|
|
329
|
+
}
|
|
330
|
+
// Try to match by prefix for flexibility
|
|
331
|
+
const normalizedLayout = layout.toLowerCase();
|
|
332
|
+
for (const validLayout of VALID_LAYOUTS) {
|
|
333
|
+
if (normalizedLayout.startsWith(validLayout) || validLayout.startsWith(normalizedLayout)) {
|
|
334
|
+
return validLayout;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
// Default fallback
|
|
338
|
+
return 'food';
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Category-specific image extraction function
|
|
342
|
+
* Handles all category image storage formats independently
|
|
343
|
+
* @param image - Can be:
|
|
344
|
+
* - String URL: "https://..."
|
|
345
|
+
* - Object: {url: "https://..."}
|
|
346
|
+
* - Array: ["https://..."] or [{url: "https://..."}]
|
|
347
|
+
* - JSON string: '{"url":"..."}' or '"https://..."'
|
|
348
|
+
* - null/undefined
|
|
349
|
+
* @returns Single valid URL string or undefined
|
|
350
|
+
*/
|
|
351
|
+
function extractCategoryImageUrl(image) {
|
|
352
|
+
if (!image) {
|
|
353
|
+
return undefined;
|
|
354
|
+
}
|
|
355
|
+
// Handle string URLs
|
|
356
|
+
if (typeof image === 'string') {
|
|
357
|
+
const trimmed = image.trim();
|
|
358
|
+
if (trimmed.length === 0) {
|
|
359
|
+
return undefined;
|
|
360
|
+
}
|
|
361
|
+
// Try to parse as JSON
|
|
362
|
+
if ((trimmed.startsWith('[') || trimmed.startsWith('{')) && (trimmed.includes('"url"') || trimmed.includes('"image"'))) {
|
|
363
|
+
try {
|
|
364
|
+
const parsed = JSON.parse(trimmed);
|
|
365
|
+
return extractCategoryImageUrl(parsed);
|
|
366
|
+
}
|
|
367
|
+
catch {
|
|
368
|
+
// Not valid JSON, continue as string URL
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
// Validate and return single URL string
|
|
372
|
+
const url = validateImageUrl(trimmed);
|
|
373
|
+
return url || undefined;
|
|
374
|
+
}
|
|
375
|
+
// Handle arrays - take first valid URL
|
|
376
|
+
if (Array.isArray(image)) {
|
|
377
|
+
if (image.length === 0) {
|
|
378
|
+
return undefined;
|
|
379
|
+
}
|
|
380
|
+
// Try to extract from first item
|
|
381
|
+
for (const item of image) {
|
|
382
|
+
const url = extractCategoryImageUrl(item);
|
|
383
|
+
if (url) {
|
|
384
|
+
return url;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
return undefined;
|
|
388
|
+
}
|
|
389
|
+
// Handle objects
|
|
390
|
+
if (typeof image === 'object' && image !== null) {
|
|
391
|
+
// Check for direct url property
|
|
392
|
+
if ('url' in image) {
|
|
393
|
+
const url = validateImageUrl(image.url);
|
|
394
|
+
if (url) {
|
|
395
|
+
return url;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
// Check for other common property names
|
|
399
|
+
for (const key of ['src', 'image', 'imageUrl', 'image_url', 'value']) {
|
|
400
|
+
if (key in image) {
|
|
401
|
+
const url = validateImageUrl(image[key]);
|
|
402
|
+
if (url) {
|
|
403
|
+
return url;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
return undefined;
|
|
409
|
+
}
|
|
410
|
+
/**
|
|
411
|
+
* Transforms API Category to StoreCategory format
|
|
412
|
+
* Uses dedicated category image extraction function for independent validation
|
|
413
|
+
*/
|
|
414
|
+
export function transformCategoryToStoreCategory(apiCategory) {
|
|
415
|
+
// Use category-specific image extraction function
|
|
416
|
+
// This ensures independent validation for category images
|
|
417
|
+
const imageUrl = extractCategoryImageUrl(apiCategory.image);
|
|
418
|
+
return {
|
|
419
|
+
id: apiCategory.id,
|
|
420
|
+
name: apiCategory.name,
|
|
421
|
+
slug: apiCategory.slug,
|
|
422
|
+
description: apiCategory.description,
|
|
423
|
+
image: imageUrl,
|
|
424
|
+
parentId: apiCategory.parentId,
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Transforms API Product to StoreProduct format
|
|
429
|
+
* Calculates inStock based on trackInventory and inventory data
|
|
430
|
+
*/
|
|
431
|
+
export function transformProductToStoreProduct(apiProduct) {
|
|
432
|
+
// Calculate inStock
|
|
433
|
+
let inStock = true; // Default to true
|
|
434
|
+
if (apiProduct.trackInventory === true) {
|
|
435
|
+
// If tracking inventory, check if any inventory item has quantity > 0
|
|
436
|
+
if (apiProduct.inventory && Array.isArray(apiProduct.inventory)) {
|
|
437
|
+
inStock = apiProduct.inventory.some((inv) => (inv.quantity || 0) > 0);
|
|
438
|
+
}
|
|
439
|
+
else {
|
|
440
|
+
// If inventory data is unavailable but tracking is enabled, default to false for safety
|
|
441
|
+
inStock = false;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
else {
|
|
445
|
+
// If not tracking inventory, always in stock
|
|
446
|
+
inStock = true;
|
|
447
|
+
}
|
|
448
|
+
// Convert price to number (API may return string from decimal type)
|
|
449
|
+
const price = normalizePrice(apiProduct.price);
|
|
450
|
+
// Convert compareAtPrice to number if it exists
|
|
451
|
+
const compareAtPrice = apiProduct.compareAtPrice
|
|
452
|
+
? normalizePrice(apiProduct.compareAtPrice)
|
|
453
|
+
: undefined;
|
|
454
|
+
// Use product-specific image extraction function
|
|
455
|
+
// Backend returns: [{url: string, isPrimary?: boolean}]
|
|
456
|
+
// Frontend needs: string[] (array of URL strings)
|
|
457
|
+
let images = extractProductImageUrls(apiProduct.images);
|
|
458
|
+
// Ensure all extracted URLs are valid (non-empty strings)
|
|
459
|
+
images = images.filter((url) => url && typeof url === 'string' && url.trim().length > 0);
|
|
460
|
+
// Enhanced logging for debugging image extraction issues
|
|
461
|
+
if (images.length === 0 && apiProduct.images) {
|
|
462
|
+
console.warn('[transformProductToStoreProduct] WARNING: No images extracted!', {
|
|
463
|
+
productId: apiProduct.id,
|
|
464
|
+
productName: apiProduct.name,
|
|
465
|
+
inputImages: apiProduct.images,
|
|
466
|
+
inputType: typeof apiProduct.images,
|
|
467
|
+
inputIsArray: Array.isArray(apiProduct.images),
|
|
468
|
+
inputLength: Array.isArray(apiProduct.images) ? apiProduct.images.length : 'N/A',
|
|
469
|
+
inputStringified: typeof apiProduct.images === 'string' ? apiProduct.images.substring(0, 200) : JSON.stringify(apiProduct.images || []).substring(0, 200)
|
|
470
|
+
});
|
|
471
|
+
// Try fallback extraction using the general extractImageUrls function
|
|
472
|
+
const fallbackImages = extractImageUrls(apiProduct.images);
|
|
473
|
+
if (fallbackImages.length > 0) {
|
|
474
|
+
console.log('[transformProductToStoreProduct] Fallback extraction succeeded:', {
|
|
475
|
+
productId: apiProduct.id,
|
|
476
|
+
extractedCount: fallbackImages.length
|
|
477
|
+
});
|
|
478
|
+
images = fallbackImages.filter((url) => url && typeof url === 'string' && url.trim().length > 0);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
else if (images.length > 0) {
|
|
482
|
+
// Log successful extraction in development
|
|
483
|
+
if (process.env.NODE_ENV === 'development') {
|
|
484
|
+
console.log('[transformProductToStoreProduct] Successfully extracted images:', {
|
|
485
|
+
productId: apiProduct.id,
|
|
486
|
+
productName: apiProduct.name,
|
|
487
|
+
imageCount: images.length,
|
|
488
|
+
firstImage: images[0]?.substring(0, 100)
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
// Convert rating to number, handling undefined, null, or string values
|
|
493
|
+
let rating;
|
|
494
|
+
if (apiProduct.rating !== undefined && apiProduct.rating !== null) {
|
|
495
|
+
if (typeof apiProduct.rating === 'number') {
|
|
496
|
+
rating = apiProduct.rating;
|
|
497
|
+
}
|
|
498
|
+
else if (typeof apiProduct.rating === 'string') {
|
|
499
|
+
const parsed = parseFloat(apiProduct.rating);
|
|
500
|
+
rating = isNaN(parsed) ? undefined : parsed;
|
|
501
|
+
}
|
|
502
|
+
else {
|
|
503
|
+
rating = undefined;
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
// Generate SKU from slug or name if not provided
|
|
507
|
+
const generateSKU = (product) => {
|
|
508
|
+
// Try to get SKU from metadata first
|
|
509
|
+
if (product.metadata?.sku) {
|
|
510
|
+
return String(product.metadata.sku);
|
|
511
|
+
}
|
|
512
|
+
// Check if any variation has a SKU (use the first one found)
|
|
513
|
+
if (product.variations && product.variations.length > 0) {
|
|
514
|
+
const variationWithSKU = product.variations.find((v) => v.sku);
|
|
515
|
+
if (variationWithSKU?.sku) {
|
|
516
|
+
return String(variationWithSKU.sku);
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
// Generate SKU from slug (uppercase, remove hyphens, take first 8-12 chars)
|
|
520
|
+
if (product.slug) {
|
|
521
|
+
const slugSKU = product.slug.toUpperCase().replace(/[^A-Z0-9]/g, '').substring(0, 12);
|
|
522
|
+
return slugSKU.length >= 6 ? slugSKU : slugSKU.padEnd(8, 'X');
|
|
523
|
+
}
|
|
524
|
+
// Fallback: generate from name (uppercase, alphanumeric only, first 8-12 chars)
|
|
525
|
+
const nameSKU = product.name
|
|
526
|
+
.toUpperCase()
|
|
527
|
+
.replace(/[^A-Z0-9]/g, '')
|
|
528
|
+
.substring(0, 12);
|
|
529
|
+
return nameSKU.length >= 6 ? nameSKU : nameSKU.padEnd(8, 'X');
|
|
530
|
+
};
|
|
531
|
+
// Extract reviewCount from API product (added by backend)
|
|
532
|
+
const reviewCount = apiProduct.reviewCount !== undefined
|
|
533
|
+
? Number(apiProduct.reviewCount)
|
|
534
|
+
: undefined;
|
|
535
|
+
return {
|
|
536
|
+
id: apiProduct.id,
|
|
537
|
+
name: apiProduct.name,
|
|
538
|
+
slug: apiProduct.slug,
|
|
539
|
+
description: apiProduct.description || '',
|
|
540
|
+
price,
|
|
541
|
+
compareAtPrice,
|
|
542
|
+
images,
|
|
543
|
+
categoryId: apiProduct.categoryId || '',
|
|
544
|
+
inStock,
|
|
545
|
+
rating,
|
|
546
|
+
reviewCount,
|
|
547
|
+
sku: generateSKU(apiProduct),
|
|
548
|
+
currency: apiProduct.currency || 'USD',
|
|
549
|
+
variants: apiProduct.variations?.map((variation) => ({
|
|
550
|
+
id: variation.id || variation.name || '',
|
|
551
|
+
name: variation.name || '',
|
|
552
|
+
value: variation.value || '',
|
|
553
|
+
price: variation.price
|
|
554
|
+
? (typeof variation.price === 'string'
|
|
555
|
+
? parseFloat(variation.price)
|
|
556
|
+
: Number(variation.price))
|
|
557
|
+
: undefined,
|
|
558
|
+
sku: variation.sku,
|
|
559
|
+
})),
|
|
560
|
+
specifications: apiProduct.brand ? { brand: apiProduct.brand } : undefined,
|
|
561
|
+
isActive: apiProduct.isActive,
|
|
562
|
+
status: apiProduct.status,
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
/**
|
|
566
|
+
* Transforms API Service to StoreService format
|
|
567
|
+
*/
|
|
568
|
+
export function transformServiceToStoreService(apiService) {
|
|
569
|
+
// Convert price to number (API may return string from decimal type)
|
|
570
|
+
const price = normalizePrice(apiService.price);
|
|
571
|
+
// Extract image from images array or use image field
|
|
572
|
+
let image;
|
|
573
|
+
if (apiService.image) {
|
|
574
|
+
image = apiService.image;
|
|
575
|
+
}
|
|
576
|
+
else if (apiService.images) {
|
|
577
|
+
const imageUrls = extractImageUrls(apiService.images);
|
|
578
|
+
image = imageUrls.length > 0 ? imageUrls[0] : undefined;
|
|
579
|
+
}
|
|
580
|
+
// Convert availability to StoreService format
|
|
581
|
+
let availability;
|
|
582
|
+
if (apiService.availability) {
|
|
583
|
+
if (Array.isArray(apiService.availability)) {
|
|
584
|
+
availability = apiService.availability.map((avail) => ({
|
|
585
|
+
day: avail.day || '',
|
|
586
|
+
slots: avail.slots || [],
|
|
587
|
+
}));
|
|
588
|
+
}
|
|
589
|
+
else if (typeof apiService.availability === 'object') {
|
|
590
|
+
// Handle object format - convert to array format
|
|
591
|
+
const availObj = apiService.availability;
|
|
592
|
+
if (availObj.days && Array.isArray(availObj.days)) {
|
|
593
|
+
availability = availObj.days.map((day) => ({
|
|
594
|
+
day,
|
|
595
|
+
slots: availObj.timeSlots || [],
|
|
596
|
+
}));
|
|
597
|
+
}
|
|
598
|
+
else if (availObj.day) {
|
|
599
|
+
availability = [{
|
|
600
|
+
day: availObj.day,
|
|
601
|
+
slots: availObj.slots || [],
|
|
602
|
+
}];
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
// Extract provider info if available (providerId exists but provider relation may not be loaded)
|
|
607
|
+
const provider = apiService.providerId ? {
|
|
608
|
+
id: apiService.providerId,
|
|
609
|
+
name: apiService.provider?.name || 'Service Provider',
|
|
610
|
+
avatar: apiService.provider?.avatar,
|
|
611
|
+
rating: apiService.provider?.rating,
|
|
612
|
+
} : undefined;
|
|
613
|
+
return {
|
|
614
|
+
id: apiService.id,
|
|
615
|
+
name: apiService.name,
|
|
616
|
+
slug: apiService.slug,
|
|
617
|
+
description: apiService.description || '',
|
|
618
|
+
price,
|
|
619
|
+
currency: apiService.currency,
|
|
620
|
+
duration: apiService.duration || 0,
|
|
621
|
+
categoryId: apiService.categoryId || '',
|
|
622
|
+
image,
|
|
623
|
+
provider,
|
|
624
|
+
availability,
|
|
625
|
+
isActive: apiService.isActive,
|
|
626
|
+
status: apiService.status,
|
|
627
|
+
};
|
|
628
|
+
}
|
|
629
|
+
/**
|
|
630
|
+
* Transforms API StoreResponse to StoreConfig format
|
|
631
|
+
*/
|
|
632
|
+
function transformStoreResponseToConfig(apiStore) {
|
|
633
|
+
const layout = mapLayoutToStoreLayoutType(apiStore.layout);
|
|
634
|
+
// Debug logging for layoutConfig
|
|
635
|
+
console.log('[transformStoreResponseToConfig] Received apiStore.layoutConfig:', {
|
|
636
|
+
exists: !!apiStore.layoutConfig,
|
|
637
|
+
type: typeof apiStore.layoutConfig,
|
|
638
|
+
hasSections: !!apiStore.layoutConfig?.sections,
|
|
639
|
+
hasHeroSliders: !!apiStore.layoutConfig?.sections?.hero?.sliders,
|
|
640
|
+
sliderCount: apiStore.layoutConfig?.sections?.hero?.sliders?.length || 0,
|
|
641
|
+
});
|
|
642
|
+
// Build contactInfo from various sources
|
|
643
|
+
let contactInfo = apiStore.contactInfo || {
|
|
644
|
+
email: apiStore.contactEmail,
|
|
645
|
+
phone: apiStore.contactPhone,
|
|
646
|
+
address: apiStore.address,
|
|
647
|
+
};
|
|
648
|
+
// If contactInfo exists but doesn't have an address, merge Store's address if available
|
|
649
|
+
if (contactInfo && !contactInfo.address && apiStore.address) {
|
|
650
|
+
contactInfo = {
|
|
651
|
+
...contactInfo,
|
|
652
|
+
address: apiStore.address,
|
|
653
|
+
};
|
|
654
|
+
}
|
|
655
|
+
const transformedLayoutConfig = transformLayoutConfig(apiStore.layoutConfig, apiStore.slug);
|
|
656
|
+
console.log('[transformStoreResponseToConfig] Transformed layoutConfig:', {
|
|
657
|
+
exists: !!transformedLayoutConfig,
|
|
658
|
+
hasSections: !!transformedLayoutConfig?.sections,
|
|
659
|
+
hasHeroSliders: !!transformedLayoutConfig?.sections?.hero?.sliders,
|
|
660
|
+
sliderCount: transformedLayoutConfig?.sections?.hero?.sliders?.length || 0,
|
|
661
|
+
});
|
|
662
|
+
return {
|
|
663
|
+
id: apiStore.id,
|
|
664
|
+
slug: apiStore.slug,
|
|
665
|
+
name: apiStore.name,
|
|
666
|
+
description: apiStore.description || '',
|
|
667
|
+
type: layout,
|
|
668
|
+
layout: layout,
|
|
669
|
+
branding: {
|
|
670
|
+
primaryColor: apiStore.branding?.primaryColor || '#000000',
|
|
671
|
+
secondaryColor: apiStore.branding?.secondaryColor,
|
|
672
|
+
accentColor: apiStore.branding?.secondaryColor || apiStore.branding?.primaryColor || '#000000',
|
|
673
|
+
logo: (apiStore.branding?.logo || apiStore.logo) ? convertToAbsoluteUrl(apiStore.branding?.logo || apiStore.logo || '') : undefined,
|
|
674
|
+
},
|
|
675
|
+
navigation: {
|
|
676
|
+
main: [],
|
|
677
|
+
footer: [],
|
|
678
|
+
},
|
|
679
|
+
features: {
|
|
680
|
+
cart: true,
|
|
681
|
+
wishlist: true,
|
|
682
|
+
reviews: true,
|
|
683
|
+
search: true,
|
|
684
|
+
filters: true,
|
|
685
|
+
booking: layout.startsWith('booking'),
|
|
686
|
+
delivery: true,
|
|
687
|
+
},
|
|
688
|
+
products: [],
|
|
689
|
+
categories: [],
|
|
690
|
+
settings: {
|
|
691
|
+
currency: apiStore.settings?.currency || 'USD',
|
|
692
|
+
taxRate: apiStore.settings?.taxRate, // Deprecated: kept for backward compatibility
|
|
693
|
+
vat: apiStore.settings?.vat,
|
|
694
|
+
serviceCharge: apiStore.settings?.serviceCharge,
|
|
695
|
+
},
|
|
696
|
+
contactInfo: (contactInfo.email || contactInfo.phone || contactInfo.address) ? contactInfo : undefined,
|
|
697
|
+
layoutConfig: transformedLayoutConfig, // Transform layoutConfig to match frontend structure
|
|
698
|
+
// Extract puckData from configuration if it exists
|
|
699
|
+
puckData: apiStore.configuration?.puck_configuration?.puckData || undefined,
|
|
700
|
+
};
|
|
701
|
+
}
|
|
702
|
+
/**
|
|
703
|
+
* Transforms backend layoutContent structure to frontend layoutConfig structure
|
|
704
|
+
* Backend uses assignedAssets/assignedText, frontend expects sections structure
|
|
705
|
+
*/
|
|
706
|
+
function transformLayoutConfig(layoutContent, storeSlug) {
|
|
707
|
+
if (!layoutContent || typeof layoutContent !== 'object') {
|
|
708
|
+
console.log('[transformLayoutConfig] No layoutContent provided or invalid');
|
|
709
|
+
return undefined;
|
|
710
|
+
}
|
|
711
|
+
// If it already has the sections structure, return as-is but ensure assignedAssets/assignedText are preserved
|
|
712
|
+
if (layoutContent.sections) {
|
|
713
|
+
console.log('[transformLayoutConfig] Sections structure found, returning as-is');
|
|
714
|
+
console.log('[transformLayoutConfig] Sections found:', {
|
|
715
|
+
hero: !!layoutContent.sections.hero,
|
|
716
|
+
heroSliders: layoutContent.sections?.hero?.sliders?.length || 0,
|
|
717
|
+
categories: !!layoutContent.sections.categories,
|
|
718
|
+
featuredProducts: !!layoutContent.sections.featuredProducts,
|
|
719
|
+
marketing: !!layoutContent.sections.marketing,
|
|
720
|
+
marketingEditorial: !!layoutContent.sections.marketing?.editorial,
|
|
721
|
+
marketingPromoBanner: !!layoutContent.sections.marketing?.promoBanner,
|
|
722
|
+
marketingNewsletter: !!layoutContent.sections.marketing?.newsletter,
|
|
723
|
+
hasAssignedAssets: !!layoutContent.assignedAssets,
|
|
724
|
+
hasAssignedText: !!layoutContent.assignedText,
|
|
725
|
+
});
|
|
726
|
+
// Return the entire structure as-is - all sections are already in the correct format
|
|
727
|
+
// Ensure assignedAssets and assignedText are preserved for backward compatibility
|
|
728
|
+
return {
|
|
729
|
+
...layoutContent,
|
|
730
|
+
assignedAssets: layoutContent.assignedAssets || {},
|
|
731
|
+
assignedText: layoutContent.assignedText || {},
|
|
732
|
+
};
|
|
733
|
+
}
|
|
734
|
+
// Transform from assignedAssets/assignedText structure to sections structure
|
|
735
|
+
const assignedAssets = layoutContent.assignedAssets || {};
|
|
736
|
+
const assignedText = layoutContent.assignedText || {};
|
|
737
|
+
// Build sections structure
|
|
738
|
+
const sections = {};
|
|
739
|
+
// Marketing section - migrate from old format to structured format
|
|
740
|
+
if (layoutContent.sections?.marketing) {
|
|
741
|
+
// New structure already exists - preserve it exactly as-is from database
|
|
742
|
+
console.log('[transformLayoutConfig] Found sections.marketing, preserving structure');
|
|
743
|
+
console.log('[transformLayoutConfig] Marketing sections:', {
|
|
744
|
+
hasEditorial: !!layoutContent.sections.marketing.editorial,
|
|
745
|
+
hasPromoBanner: !!layoutContent.sections.marketing.promoBanner,
|
|
746
|
+
hasNewsletter: !!layoutContent.sections.marketing.newsletter,
|
|
747
|
+
});
|
|
748
|
+
sections.marketing = {
|
|
749
|
+
show: layoutContent.sections.marketing.show !== false,
|
|
750
|
+
showNewsletter: layoutContent.sections.marketing.showNewsletter !== false,
|
|
751
|
+
showPromoBanner: layoutContent.sections.marketing.showPromoBanner !== false,
|
|
752
|
+
// Preserve all marketing subsections exactly as stored
|
|
753
|
+
editorial: layoutContent.sections.marketing.editorial,
|
|
754
|
+
promoBanner: layoutContent.sections.marketing.promoBanner,
|
|
755
|
+
newsletter: layoutContent.sections.marketing.newsletter,
|
|
756
|
+
shopTheLook: layoutContent.sections.marketing.shopTheLook,
|
|
757
|
+
chefRecommendations: layoutContent.sections.marketing.chefRecommendations,
|
|
758
|
+
};
|
|
759
|
+
}
|
|
760
|
+
else {
|
|
761
|
+
// Migrate from old format
|
|
762
|
+
const marketing = {
|
|
763
|
+
show: true,
|
|
764
|
+
showNewsletter: false,
|
|
765
|
+
showPromoBanner: false,
|
|
766
|
+
};
|
|
767
|
+
// Editorial section (lookbook)
|
|
768
|
+
if (assignedAssets.lookbook_main || assignedText['lookbook_main:section_header']) {
|
|
769
|
+
marketing.editorial = {
|
|
770
|
+
show: true,
|
|
771
|
+
label: assignedText['lookbook_main:section_header'] || 'EDITORIAL',
|
|
772
|
+
title: (assignedText['lookbook_main:main_caption'] && assignedText['lookbook_main:main_caption'].length < 50)
|
|
773
|
+
? assignedText['lookbook_main:main_caption']
|
|
774
|
+
: 'Redefining Modern\nElegance',
|
|
775
|
+
description: assignedText['lookbook_main:main_caption']?.length >= 50
|
|
776
|
+
? assignedText['lookbook_main:main_caption']
|
|
777
|
+
: assignedText['lookbook_detail:detail_caption'] || assignedText['lookbook_main:main_caption'] || 'Explore our latest editorial featuring timeless pieces crafted for the contemporary wardrobe. From essential basics to statement outwear, find your signature look.',
|
|
778
|
+
image: assignedAssets.lookbook_main,
|
|
779
|
+
detailImage: assignedAssets.lookbook_detail,
|
|
780
|
+
primaryButtonText: 'View Lookbook',
|
|
781
|
+
primaryButtonLink: '/style-guide',
|
|
782
|
+
secondaryButtonText: 'Read Our Story',
|
|
783
|
+
secondaryButtonLink: '/about',
|
|
784
|
+
};
|
|
785
|
+
}
|
|
786
|
+
// Promo banner
|
|
787
|
+
// Check for sale_banner (electronics-grid), promo_banner_image, seasonal_promo_banner, or promo_title text
|
|
788
|
+
const hasPromoBanner = assignedAssets.sale_banner ||
|
|
789
|
+
assignedAssets.promo_banner_image ||
|
|
790
|
+
assignedAssets.seasonal_promo_banner ||
|
|
791
|
+
assignedText['promo_banner_title'] ||
|
|
792
|
+
assignedText['promo_title'] ||
|
|
793
|
+
assignedText['sale_banner:banner_title'];
|
|
794
|
+
if (hasPromoBanner) {
|
|
795
|
+
// Get banner image - prioritize sale_banner for electronics-grid
|
|
796
|
+
const bannerImage = assignedAssets.sale_banner ||
|
|
797
|
+
assignedAssets.promo_banner_image ||
|
|
798
|
+
assignedAssets.seasonal_promo_banner;
|
|
799
|
+
// Get banner text - check sale_banner textElements first, then other sources
|
|
800
|
+
const bannerTitle = assignedText['sale_banner:banner_title'] ||
|
|
801
|
+
assignedText['promo_banner_title'] ||
|
|
802
|
+
assignedText['promo_title'] ||
|
|
803
|
+
assignedText['promo_headline'];
|
|
804
|
+
const bannerSubtitle = assignedText['sale_banner:banner_subtitle'] ||
|
|
805
|
+
assignedText['promo_banner_subtitle'] ||
|
|
806
|
+
assignedText['promo_subtext'] ||
|
|
807
|
+
assignedText['promo_subhead'];
|
|
808
|
+
const bannerButtonText = assignedText['sale_banner:cta_button'] ||
|
|
809
|
+
assignedText['promo_banner_button'] ||
|
|
810
|
+
assignedText['promo_cta'] ||
|
|
811
|
+
assignedText['cta_text'];
|
|
812
|
+
const bannerButtonLink = assignedText['promo_banner_link'] ||
|
|
813
|
+
assignedText['sale_banner:button_link'] ||
|
|
814
|
+
'/products';
|
|
815
|
+
marketing.showPromoBanner = true;
|
|
816
|
+
marketing.promoBanner = {
|
|
817
|
+
show: true,
|
|
818
|
+
image: bannerImage,
|
|
819
|
+
title: bannerTitle,
|
|
820
|
+
subtitle: bannerSubtitle,
|
|
821
|
+
buttonText: bannerButtonText,
|
|
822
|
+
buttonLink: bannerButtonLink,
|
|
823
|
+
};
|
|
824
|
+
}
|
|
825
|
+
// Newsletter
|
|
826
|
+
if (assignedText['newsletter_title'] || assignedText['footer_newsletter_title']) {
|
|
827
|
+
marketing.showNewsletter = true;
|
|
828
|
+
marketing.newsletter = {
|
|
829
|
+
show: true,
|
|
830
|
+
title: assignedText['newsletter_title'] || assignedText['footer_newsletter_title'],
|
|
831
|
+
subtitle: assignedText['newsletter_subtitle'] || assignedText['footer_newsletter_subtitle'],
|
|
832
|
+
button: assignedText['newsletter_button'] || assignedText['footer_newsletter_button'],
|
|
833
|
+
placeholder: assignedText['newsletter_placeholder'],
|
|
834
|
+
disclaimer: assignedText['newsletter_disclaimer'],
|
|
835
|
+
};
|
|
836
|
+
}
|
|
837
|
+
// Shop the look (clothing-minimal)
|
|
838
|
+
if (assignedAssets.shop_the_look_image || assignedText['shop_the_look:section_title']) {
|
|
839
|
+
marketing.shopTheLook = {
|
|
840
|
+
show: true,
|
|
841
|
+
image: assignedAssets.shop_the_look_image,
|
|
842
|
+
title: assignedText['shop_the_look:section_title'] || assignedText['section_title'],
|
|
843
|
+
description: assignedText['shop_the_look:look_desc'] || assignedText['look_desc'],
|
|
844
|
+
products: [], // Will be populated from product data
|
|
845
|
+
};
|
|
846
|
+
}
|
|
847
|
+
// Chef recommendations (food layout)
|
|
848
|
+
if (assignedText['chef_recommendations_title'] || assignedAssets.chef_image) {
|
|
849
|
+
marketing.chefRecommendations = {
|
|
850
|
+
show: true,
|
|
851
|
+
title: assignedText['chef_recommendations_title'] || 'Chef\'s Recommendations',
|
|
852
|
+
description: assignedText['chef_recommendations_description'],
|
|
853
|
+
items: [], // Will be populated from menu item data
|
|
854
|
+
};
|
|
855
|
+
}
|
|
856
|
+
if (Object.keys(marketing).length > 3) { // More than just show, showNewsletter, showPromoBanner
|
|
857
|
+
sections.marketing = marketing;
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
// Hero section - migrate old format to new slider structure
|
|
861
|
+
if (layoutContent.sections?.hero?.sliders && Array.isArray(layoutContent.sections.hero.sliders)) {
|
|
862
|
+
// New structure already exists - preserve it exactly as-is from database
|
|
863
|
+
// This ensures the slider structure with images, text, and buttons is not modified
|
|
864
|
+
console.log('[transformLayoutConfig] Found sections.hero.sliders, preserving structure');
|
|
865
|
+
console.log('[transformLayoutConfig] Sliders count:', layoutContent.sections.hero.sliders.length);
|
|
866
|
+
// Log each slider for debugging
|
|
867
|
+
layoutContent.sections.hero.sliders.forEach((slider, index) => {
|
|
868
|
+
console.log(`[transformLayoutConfig] Slider ${index + 1}:`, {
|
|
869
|
+
id: slider.id,
|
|
870
|
+
hasImage: !!slider.image,
|
|
871
|
+
imageUrl: slider.image ? (slider.image.substring(0, 50) + '...') : 'missing',
|
|
872
|
+
hasTitle: !!slider.title,
|
|
873
|
+
title: slider.title || 'missing',
|
|
874
|
+
primaryButtonText: slider.primaryButton?.text || 'empty',
|
|
875
|
+
secondaryButtonText: slider.secondaryButton?.text || 'empty',
|
|
876
|
+
});
|
|
877
|
+
});
|
|
878
|
+
sections.hero = {
|
|
879
|
+
...layoutContent.sections.hero,
|
|
880
|
+
show: layoutContent.sections.hero.show !== false,
|
|
881
|
+
showCTA: layoutContent.sections.hero.showCTA !== false,
|
|
882
|
+
showSecondaryCTA: layoutContent.sections.hero.showSecondaryCTA !== false,
|
|
883
|
+
autoPlay: layoutContent.sections.hero.autoPlay !== false,
|
|
884
|
+
showBadges: layoutContent.sections.hero.showBadges !== false,
|
|
885
|
+
// Preserve sliders array exactly as stored - don't transform or modify
|
|
886
|
+
sliders: layoutContent.sections.hero.sliders,
|
|
887
|
+
};
|
|
888
|
+
}
|
|
889
|
+
else if (assignedAssets.hero_slide_1 || assignedAssets.hero_slide_2 || assignedAssets.hero_slide_3) {
|
|
890
|
+
// Old format detected - migrate to new structure
|
|
891
|
+
const sliders = [];
|
|
892
|
+
// Build sliders array from old format (support up to 10 slides for future growth)
|
|
893
|
+
for (let i = 1; i <= 10; i++) {
|
|
894
|
+
const slideKey = `hero_slide_${i}`;
|
|
895
|
+
const imageUrl = assignedAssets[slideKey];
|
|
896
|
+
if (imageUrl) {
|
|
897
|
+
// Determine store slug for default links (use passed parameter or fallback)
|
|
898
|
+
const defaultProductsLink = storeSlug ? `/${storeSlug}/products` : '/products';
|
|
899
|
+
const defaultCategoriesLink = storeSlug ? `/${storeSlug}/categories` : '/categories';
|
|
900
|
+
const slider = {
|
|
901
|
+
id: slideKey,
|
|
902
|
+
image: imageUrl,
|
|
903
|
+
order: i,
|
|
904
|
+
};
|
|
905
|
+
// Map old text keys to new structure
|
|
906
|
+
// Support multiple key variations for backward compatibility
|
|
907
|
+
const badgeKey = `${slideKey}:badge`;
|
|
908
|
+
const headingKey = `${slideKey}:heading`;
|
|
909
|
+
const titleKey = `${slideKey}:title`;
|
|
910
|
+
const subheadingKey = `${slideKey}:subheading`;
|
|
911
|
+
const descriptionKey = `${slideKey}:description`;
|
|
912
|
+
const btnLabelKey = `${slideKey}:btn_label`;
|
|
913
|
+
const primaryButtonKey = `${slideKey}:primaryButton`;
|
|
914
|
+
const secondaryBtnKey = `${slideKey}:secondary_btn`;
|
|
915
|
+
const secondaryButtonKey = `${slideKey}:secondaryButton`;
|
|
916
|
+
const highlightKey = `${slideKey}:highlight`; // Food layout specific
|
|
917
|
+
if (assignedText[badgeKey]) {
|
|
918
|
+
slider.badge = assignedText[badgeKey];
|
|
919
|
+
}
|
|
920
|
+
slider.title = assignedText[headingKey] || assignedText[titleKey] || '';
|
|
921
|
+
if (assignedText[subheadingKey] || assignedText[descriptionKey]) {
|
|
922
|
+
slider.description = assignedText[subheadingKey] || assignedText[descriptionKey];
|
|
923
|
+
}
|
|
924
|
+
// Food layout specific: highlight field
|
|
925
|
+
if (assignedText[highlightKey]) {
|
|
926
|
+
slider.highlight = assignedText[highlightKey];
|
|
927
|
+
}
|
|
928
|
+
// Primary button - handle both string and object formats
|
|
929
|
+
const primaryButtonText = assignedText[btnLabelKey] || assignedText[primaryButtonKey];
|
|
930
|
+
if (primaryButtonText) {
|
|
931
|
+
// Check if it's already an object (from new format migration)
|
|
932
|
+
if (typeof primaryButtonText === 'object' && primaryButtonText !== null) {
|
|
933
|
+
slider.primaryButton = primaryButtonText;
|
|
934
|
+
}
|
|
935
|
+
else {
|
|
936
|
+
slider.primaryButton = {
|
|
937
|
+
text: String(primaryButtonText),
|
|
938
|
+
link: defaultProductsLink,
|
|
939
|
+
};
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
// Secondary button - handle both string and object formats
|
|
943
|
+
const secondaryButtonText = assignedText[secondaryBtnKey] || assignedText[secondaryButtonKey];
|
|
944
|
+
if (secondaryButtonText) {
|
|
945
|
+
// Check if it's already an object (from new format migration)
|
|
946
|
+
if (typeof secondaryButtonText === 'object' && secondaryButtonText !== null) {
|
|
947
|
+
slider.secondaryButton = secondaryButtonText;
|
|
948
|
+
}
|
|
949
|
+
else {
|
|
950
|
+
slider.secondaryButton = {
|
|
951
|
+
text: String(secondaryButtonText),
|
|
952
|
+
link: defaultCategoriesLink,
|
|
953
|
+
};
|
|
954
|
+
}
|
|
955
|
+
}
|
|
956
|
+
sliders.push(slider);
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
// Only create hero section if we have at least one slider
|
|
960
|
+
if (sliders.length > 0) {
|
|
961
|
+
sections.hero = {
|
|
962
|
+
show: true,
|
|
963
|
+
showCTA: true,
|
|
964
|
+
showSecondaryCTA: true,
|
|
965
|
+
autoPlay: true,
|
|
966
|
+
showBadges: true,
|
|
967
|
+
sliders: sliders,
|
|
968
|
+
};
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
else if (assignedAssets.hero_bg || assignedAssets.hero_image || assignedAssets.hero_split_image || assignedAssets.hero_video) {
|
|
972
|
+
// Single image/video hero (non-slider layouts)
|
|
973
|
+
sections.hero = {
|
|
974
|
+
show: true,
|
|
975
|
+
showCTA: true,
|
|
976
|
+
showSecondaryCTA: true,
|
|
977
|
+
autoPlay: false,
|
|
978
|
+
showBadges: true,
|
|
979
|
+
};
|
|
980
|
+
}
|
|
981
|
+
// Categories section - migrate from old format to structured format
|
|
982
|
+
if (layoutContent.sections?.categories) {
|
|
983
|
+
// New structure already exists - use it
|
|
984
|
+
sections.categories = {
|
|
985
|
+
show: layoutContent.sections.categories.show !== false,
|
|
986
|
+
showViewAll: layoutContent.sections.categories.showViewAll !== false,
|
|
987
|
+
limit: layoutContent.sections.categories.limit || 4,
|
|
988
|
+
title: layoutContent.sections.categories.title,
|
|
989
|
+
subtitle: layoutContent.sections.categories.subtitle,
|
|
990
|
+
viewAll: layoutContent.sections.categories.viewAll,
|
|
991
|
+
};
|
|
992
|
+
}
|
|
993
|
+
else {
|
|
994
|
+
// Migrate from old format
|
|
995
|
+
sections.categories = {
|
|
996
|
+
show: true,
|
|
997
|
+
showViewAll: true,
|
|
998
|
+
limit: 4,
|
|
999
|
+
title: assignedText['categories_title'] || assignedText['sections.categories.title'],
|
|
1000
|
+
subtitle: assignedText['categories_subtitle'] || assignedText['sections.categories.subtitle'],
|
|
1001
|
+
viewAll: assignedText['categories_view_all'] || assignedText['sections.categories.viewAll'] || 'View All Categories',
|
|
1002
|
+
};
|
|
1003
|
+
}
|
|
1004
|
+
// Featured products section - migrate from old format to structured format
|
|
1005
|
+
if (layoutContent.sections?.featuredProducts) {
|
|
1006
|
+
// New structure already exists - use it
|
|
1007
|
+
sections.featuredProducts = {
|
|
1008
|
+
show: layoutContent.sections.featuredProducts.show !== false,
|
|
1009
|
+
title: layoutContent.sections.featuredProducts.title,
|
|
1010
|
+
subtitle: layoutContent.sections.featuredProducts.subtitle,
|
|
1011
|
+
showViewAll: layoutContent.sections.featuredProducts.showViewAll !== false,
|
|
1012
|
+
viewAll: layoutContent.sections.featuredProducts.viewAll,
|
|
1013
|
+
showAddToCart: layoutContent.sections.featuredProducts.showAddToCart !== false,
|
|
1014
|
+
showWishlist: layoutContent.sections.featuredProducts.showWishlist !== false,
|
|
1015
|
+
showRatings: layoutContent.sections.featuredProducts.showRatings !== false,
|
|
1016
|
+
emptyState: layoutContent.sections.featuredProducts.emptyState,
|
|
1017
|
+
};
|
|
1018
|
+
}
|
|
1019
|
+
else {
|
|
1020
|
+
// Migrate from old format
|
|
1021
|
+
sections.featuredProducts = {
|
|
1022
|
+
show: true,
|
|
1023
|
+
title: assignedText['featured_products_title'] || assignedText['trending_title'] || assignedText['sections.featuredProducts.title'] || 'Trending Now',
|
|
1024
|
+
subtitle: assignedText['featured_products_subtitle'] || assignedText['sections.featuredProducts.subtitle'],
|
|
1025
|
+
showViewAll: true,
|
|
1026
|
+
viewAll: assignedText['featured_products_view_all'] || assignedText['sections.featuredProducts.viewAll'] || 'View All Products',
|
|
1027
|
+
showAddToCart: true,
|
|
1028
|
+
showWishlist: true,
|
|
1029
|
+
showRatings: true,
|
|
1030
|
+
emptyState: assignedText['featured_products_empty'] || assignedText['sections.featuredProducts.emptyState'],
|
|
1031
|
+
};
|
|
1032
|
+
}
|
|
1033
|
+
// Testimonials section - migrate from old format to structured format
|
|
1034
|
+
if (layoutContent.sections?.testimonials) {
|
|
1035
|
+
const testimonialsData = layoutContent.sections.testimonials;
|
|
1036
|
+
sections.testimonials = {
|
|
1037
|
+
show: testimonialsData.show !== false,
|
|
1038
|
+
title: testimonialsData.title,
|
|
1039
|
+
subtitle: testimonialsData.subtitle,
|
|
1040
|
+
// Support both new items array and old single quote format
|
|
1041
|
+
items: testimonialsData.items && Array.isArray(testimonialsData.items)
|
|
1042
|
+
? testimonialsData.items.map((item) => ({
|
|
1043
|
+
id: item.id || generateId(),
|
|
1044
|
+
name: item.name || 'Customer',
|
|
1045
|
+
role: item.role || 'Customer',
|
|
1046
|
+
image: item.image || '',
|
|
1047
|
+
quote: item.quote || '',
|
|
1048
|
+
rating: typeof item.rating === 'number' ? item.rating : 5,
|
|
1049
|
+
order: typeof item.order === 'number' ? item.order : 0,
|
|
1050
|
+
})).sort((a, b) => a.order - b.order)
|
|
1051
|
+
: testimonialsData.quote
|
|
1052
|
+
? [{
|
|
1053
|
+
id: generateId(),
|
|
1054
|
+
name: testimonialsData.author || 'Customer',
|
|
1055
|
+
role: testimonialsData.role || 'Customer',
|
|
1056
|
+
image: '',
|
|
1057
|
+
quote: testimonialsData.quote,
|
|
1058
|
+
rating: 5,
|
|
1059
|
+
order: 0,
|
|
1060
|
+
}]
|
|
1061
|
+
: [],
|
|
1062
|
+
// Keep backward compatibility with old single quote format
|
|
1063
|
+
quote: testimonialsData.quote,
|
|
1064
|
+
author: testimonialsData.author,
|
|
1065
|
+
role: testimonialsData.role,
|
|
1066
|
+
};
|
|
1067
|
+
}
|
|
1068
|
+
else if (assignedText['testimonial_quote'] || assignedText['testimonials_title']) {
|
|
1069
|
+
sections.testimonials = {
|
|
1070
|
+
show: true,
|
|
1071
|
+
title: assignedText['testimonials_title'] || assignedText['sections.testimonials.title'],
|
|
1072
|
+
subtitle: assignedText['testimonials_subtitle'] || assignedText['sections.testimonials.subtitle'],
|
|
1073
|
+
items: assignedText['testimonial_quote'] ? [{
|
|
1074
|
+
id: generateId(),
|
|
1075
|
+
name: assignedText['testimonial_author'] || 'Customer',
|
|
1076
|
+
role: assignedText['testimonial_role'] || 'Customer',
|
|
1077
|
+
image: '',
|
|
1078
|
+
quote: assignedText['testimonial_quote'],
|
|
1079
|
+
rating: 5,
|
|
1080
|
+
order: 0,
|
|
1081
|
+
}] : [],
|
|
1082
|
+
// Keep backward compatibility
|
|
1083
|
+
quote: assignedText['testimonial_quote'],
|
|
1084
|
+
author: assignedText['testimonial_author'],
|
|
1085
|
+
role: assignedText['testimonial_role'],
|
|
1086
|
+
};
|
|
1087
|
+
}
|
|
1088
|
+
// Story section - new structured format
|
|
1089
|
+
if (layoutContent.sections?.story) {
|
|
1090
|
+
sections.story = {
|
|
1091
|
+
show: layoutContent.sections.story.show !== false,
|
|
1092
|
+
label: layoutContent.sections.story.label,
|
|
1093
|
+
title: layoutContent.sections.story.title || '',
|
|
1094
|
+
description: layoutContent.sections.story.description || '',
|
|
1095
|
+
image: layoutContent.sections.story.image,
|
|
1096
|
+
signature: layoutContent.sections.story.signature,
|
|
1097
|
+
};
|
|
1098
|
+
}
|
|
1099
|
+
// PromoBanner section - new structured format (directly in sections, not just in marketing)
|
|
1100
|
+
if (layoutContent.sections?.promoBanner) {
|
|
1101
|
+
sections.promoBanner = {
|
|
1102
|
+
show: layoutContent.sections.promoBanner.show !== false,
|
|
1103
|
+
image: layoutContent.sections.promoBanner.image,
|
|
1104
|
+
title: layoutContent.sections.promoBanner.title || '',
|
|
1105
|
+
subtitle: layoutContent.sections.promoBanner.subtitle,
|
|
1106
|
+
buttonText: layoutContent.sections.promoBanner.buttonText,
|
|
1107
|
+
buttonLink: layoutContent.sections.promoBanner.buttonLink,
|
|
1108
|
+
};
|
|
1109
|
+
}
|
|
1110
|
+
// Team section - migrate from old format to structured format
|
|
1111
|
+
if (layoutContent.sections?.team) {
|
|
1112
|
+
sections.team = {
|
|
1113
|
+
show: layoutContent.sections.team.show !== false,
|
|
1114
|
+
title: layoutContent.sections.team.title,
|
|
1115
|
+
description: layoutContent.sections.team.description,
|
|
1116
|
+
showViewAll: layoutContent.sections.team.showViewAll !== false,
|
|
1117
|
+
viewAll: layoutContent.sections.team.viewAll,
|
|
1118
|
+
items: layoutContent.sections.team.items && Array.isArray(layoutContent.sections.team.items)
|
|
1119
|
+
? layoutContent.sections.team.items.map((item) => ({
|
|
1120
|
+
id: item.id || generateId(),
|
|
1121
|
+
name: item.name || 'Team Member',
|
|
1122
|
+
role: item.role || 'Specialist',
|
|
1123
|
+
image: item.image || item.photo || '',
|
|
1124
|
+
rating: item.rating,
|
|
1125
|
+
reviews: item.reviews,
|
|
1126
|
+
}))
|
|
1127
|
+
: [],
|
|
1128
|
+
};
|
|
1129
|
+
}
|
|
1130
|
+
else if (assignedText['team_section_title'] || assignedText['team_title']) {
|
|
1131
|
+
sections.team = {
|
|
1132
|
+
show: true,
|
|
1133
|
+
title: assignedText['team_section_title'] || assignedText['team_title'] || assignedText['sections.team.title'],
|
|
1134
|
+
description: assignedText['team_section_subtitle'] || assignedText['team_description'] || assignedText['team_desc'] || assignedText['sections.team.description'],
|
|
1135
|
+
showViewAll: true,
|
|
1136
|
+
viewAll: assignedText['team_view_all'] || assignedText['sections.team.viewAll'] || 'View All Team',
|
|
1137
|
+
items: [],
|
|
1138
|
+
};
|
|
1139
|
+
}
|
|
1140
|
+
// Services section - migrate from old format to structured format (for booking layouts)
|
|
1141
|
+
if (layoutContent.sections?.services) {
|
|
1142
|
+
sections.services = {
|
|
1143
|
+
show: layoutContent.sections.services.show !== false,
|
|
1144
|
+
title: layoutContent.sections.services.title,
|
|
1145
|
+
subtitle: layoutContent.sections.services.subtitle,
|
|
1146
|
+
showViewAll: layoutContent.sections.services.showViewAll !== false,
|
|
1147
|
+
viewAll: layoutContent.sections.services.viewAll,
|
|
1148
|
+
limit: layoutContent.sections.services.limit,
|
|
1149
|
+
};
|
|
1150
|
+
}
|
|
1151
|
+
else if (assignedText['services_section_title'] || assignedText['services_title']) {
|
|
1152
|
+
sections.services = {
|
|
1153
|
+
show: true,
|
|
1154
|
+
title: assignedText['services_section_title'] || assignedText['services_title'] || assignedText['sections.services.title'],
|
|
1155
|
+
subtitle: assignedText['services_section_subtitle'] || assignedText['services_subtitle'] || assignedText['sections.services.subtitle'],
|
|
1156
|
+
showViewAll: true,
|
|
1157
|
+
viewAll: assignedText['services_view_all'] || assignedText['sections.services.viewAll'] || 'View All Services',
|
|
1158
|
+
limit: 5,
|
|
1159
|
+
};
|
|
1160
|
+
}
|
|
1161
|
+
// Process section - migrate from old format to structured format (for booking layouts)
|
|
1162
|
+
if (layoutContent.sections?.process) {
|
|
1163
|
+
sections.process = {
|
|
1164
|
+
show: layoutContent.sections.process.show !== false,
|
|
1165
|
+
title: layoutContent.sections.process.title,
|
|
1166
|
+
subtitle: layoutContent.sections.process.subtitle,
|
|
1167
|
+
steps: layoutContent.sections.process.steps,
|
|
1168
|
+
};
|
|
1169
|
+
}
|
|
1170
|
+
else if (assignedText['process_title'] || assignedText['how_it_works_title']) {
|
|
1171
|
+
sections.process = {
|
|
1172
|
+
show: true,
|
|
1173
|
+
title: assignedText['process_title'] || assignedText['how_it_works_title'] || assignedText['sections.process.title'] || 'How it Works',
|
|
1174
|
+
subtitle: assignedText['process_subtitle'] || assignedText['how_it_works_subtitle'] || assignedText['sections.process.subtitle'],
|
|
1175
|
+
steps: [], // Will be populated from layout-specific process steps if available
|
|
1176
|
+
};
|
|
1177
|
+
}
|
|
1178
|
+
// Transform assignedText to structured text config
|
|
1179
|
+
const textConfig = {
|
|
1180
|
+
hero: {
|
|
1181
|
+
badge: assignedText['hero_badge'],
|
|
1182
|
+
title: assignedText['hero_title'],
|
|
1183
|
+
subtitle: assignedText['hero_subtitle'],
|
|
1184
|
+
primaryButton: assignedText['hero_primary_button'] || assignedText['hero_btn_label'] || assignedText['book_btn'],
|
|
1185
|
+
secondaryButton: assignedText['hero_secondary_button'] || assignedText['view_services_btn'],
|
|
1186
|
+
// Support multi-slide heroes
|
|
1187
|
+
slides: assignedText['hero_slide_1:heading'] ? [
|
|
1188
|
+
{
|
|
1189
|
+
badge: assignedText['hero_slide_1:badge'] || assignedText['hero_badge'],
|
|
1190
|
+
title: assignedText['hero_slide_1:heading'] || assignedText['hero_title'],
|
|
1191
|
+
description: assignedText['hero_slide_1:subheading'] || assignedText['hero_subtitle'],
|
|
1192
|
+
primaryButton: assignedText['hero_slide_1:btn_label'] || assignedText['hero_primary_button'],
|
|
1193
|
+
secondaryButton: assignedText['hero_slide_1:secondary_btn'] || assignedText['hero_secondary_button'],
|
|
1194
|
+
},
|
|
1195
|
+
assignedText['hero_slide_2:heading'] ? {
|
|
1196
|
+
badge: assignedText['hero_slide_2:badge'],
|
|
1197
|
+
title: assignedText['hero_slide_2:heading'],
|
|
1198
|
+
description: assignedText['hero_slide_2:subheading'],
|
|
1199
|
+
primaryButton: assignedText['hero_slide_2:btn_label'],
|
|
1200
|
+
secondaryButton: assignedText['hero_slide_2:secondary_btn'],
|
|
1201
|
+
} : undefined,
|
|
1202
|
+
assignedText['hero_slide_3:heading'] ? {
|
|
1203
|
+
badge: assignedText['hero_slide_3:badge'],
|
|
1204
|
+
title: assignedText['hero_slide_3:heading'],
|
|
1205
|
+
description: assignedText['hero_slide_3:subheading'],
|
|
1206
|
+
primaryButton: assignedText['hero_slide_3:btn_label'],
|
|
1207
|
+
secondaryButton: assignedText['hero_slide_3:secondary_btn'],
|
|
1208
|
+
} : undefined,
|
|
1209
|
+
].filter(Boolean) : undefined,
|
|
1210
|
+
},
|
|
1211
|
+
sections: {
|
|
1212
|
+
categories: {
|
|
1213
|
+
title: assignedText['categories_title'],
|
|
1214
|
+
subtitle: assignedText['categories_subtitle'],
|
|
1215
|
+
viewAll: assignedText['categories_view_all'],
|
|
1216
|
+
},
|
|
1217
|
+
services: {
|
|
1218
|
+
title: assignedText['services_section_title'],
|
|
1219
|
+
subtitle: assignedText['services_section_subtitle'],
|
|
1220
|
+
listTitle: assignedText['services_list_title'],
|
|
1221
|
+
listCount: assignedText['services_list_count'],
|
|
1222
|
+
},
|
|
1223
|
+
testimonials: {
|
|
1224
|
+
quote: assignedText['testimonial_quote'],
|
|
1225
|
+
author: assignedText['testimonial_author'],
|
|
1226
|
+
role: assignedText['testimonial_role'],
|
|
1227
|
+
title: assignedText['testimonials_title'],
|
|
1228
|
+
subtitle: assignedText['testimonials_subtitle'],
|
|
1229
|
+
},
|
|
1230
|
+
cta: {
|
|
1231
|
+
title: assignedText['cta_title'],
|
|
1232
|
+
description: assignedText['cta_description'],
|
|
1233
|
+
},
|
|
1234
|
+
promo: {
|
|
1235
|
+
badge: assignedText['promo_badge'],
|
|
1236
|
+
title: assignedText['promo_title'],
|
|
1237
|
+
description: assignedText['promo_desc'],
|
|
1238
|
+
},
|
|
1239
|
+
experts: {
|
|
1240
|
+
title: assignedText['experts_section_title'],
|
|
1241
|
+
viewAll: assignedText['experts_view_all'],
|
|
1242
|
+
},
|
|
1243
|
+
featuredProducts: {
|
|
1244
|
+
title: assignedText['featured_products_title'] || assignedText['trending_title'] || assignedText['hero_slide_2:heading'],
|
|
1245
|
+
subtitle: assignedText['featured_products_subtitle'],
|
|
1246
|
+
viewAll: assignedText['featured_products_view_all'],
|
|
1247
|
+
emptyState: assignedText['featured_products_empty'],
|
|
1248
|
+
},
|
|
1249
|
+
marketing: {
|
|
1250
|
+
newsletter: {
|
|
1251
|
+
title: assignedText['newsletter_title'] || assignedText['footer_newsletter_title'],
|
|
1252
|
+
subtitle: assignedText['newsletter_subtitle'] || assignedText['footer_newsletter_subtitle'],
|
|
1253
|
+
button: assignedText['newsletter_button'] || assignedText['footer_newsletter_button'],
|
|
1254
|
+
placeholder: assignedText['newsletter_placeholder'],
|
|
1255
|
+
disclaimer: assignedText['newsletter_disclaimer'],
|
|
1256
|
+
},
|
|
1257
|
+
promoBanner: {
|
|
1258
|
+
title: assignedText['promo_banner_title'] || assignedText['promo_title'],
|
|
1259
|
+
subtitle: assignedText['promo_banner_subtitle'] || assignedText['promo_subtext'],
|
|
1260
|
+
button: assignedText['promo_banner_button'] || assignedText['promo_cta'],
|
|
1261
|
+
},
|
|
1262
|
+
},
|
|
1263
|
+
team: {
|
|
1264
|
+
title: assignedText['team_section_title'] || assignedText['team_title'],
|
|
1265
|
+
subtitle: assignedText['team_section_subtitle'] || assignedText['team_description'] || assignedText['team_desc'],
|
|
1266
|
+
viewAll: assignedText['team_view_all'],
|
|
1267
|
+
},
|
|
1268
|
+
process: {
|
|
1269
|
+
title: assignedText['process_title'],
|
|
1270
|
+
subtitle: assignedText['process_subtitle'],
|
|
1271
|
+
},
|
|
1272
|
+
},
|
|
1273
|
+
common: {
|
|
1274
|
+
shopNow: assignedText['common_shop_now'],
|
|
1275
|
+
viewAll: assignedText['common_view_all'],
|
|
1276
|
+
addToCart: assignedText['common_add_to_cart'],
|
|
1277
|
+
buyNow: assignedText['common_buy_now'],
|
|
1278
|
+
learnMore: assignedText['common_learn_more'],
|
|
1279
|
+
readMore: assignedText['common_read_more'],
|
|
1280
|
+
seeMore: assignedText['common_see_more'],
|
|
1281
|
+
discover: assignedText['common_discover'],
|
|
1282
|
+
explore: assignedText['common_explore'],
|
|
1283
|
+
new: assignedText['common_new'],
|
|
1284
|
+
trending: assignedText['common_trending'],
|
|
1285
|
+
limited: assignedText['common_limited'],
|
|
1286
|
+
sale: assignedText['common_sale'],
|
|
1287
|
+
outOfStock: assignedText['common_out_of_stock'],
|
|
1288
|
+
inStock: assignedText['common_in_stock'],
|
|
1289
|
+
selectSize: assignedText['common_select_size'],
|
|
1290
|
+
selectColor: assignedText['common_select_color'],
|
|
1291
|
+
quantity: assignedText['common_quantity'],
|
|
1292
|
+
reviews: assignedText['common_reviews'],
|
|
1293
|
+
writeReview: assignedText['common_write_review'],
|
|
1294
|
+
customerReviews: assignedText['common_customer_reviews'],
|
|
1295
|
+
noReviews: assignedText['common_no_reviews'],
|
|
1296
|
+
search: assignedText['common_search'],
|
|
1297
|
+
searchPlaceholder: assignedText['common_search_placeholder'],
|
|
1298
|
+
cart: assignedText['common_cart'],
|
|
1299
|
+
wishlist: assignedText['common_wishlist'],
|
|
1300
|
+
account: assignedText['common_account'],
|
|
1301
|
+
login: assignedText['common_login'],
|
|
1302
|
+
logout: assignedText['common_logout'],
|
|
1303
|
+
signup: assignedText['common_signup'],
|
|
1304
|
+
checkout: assignedText['common_checkout'],
|
|
1305
|
+
continueShopping: assignedText['common_continue_shopping'],
|
|
1306
|
+
proceedToCheckout: assignedText['common_proceed_to_checkout'],
|
|
1307
|
+
},
|
|
1308
|
+
footer: {
|
|
1309
|
+
newsletter: {
|
|
1310
|
+
title: assignedText['footer_newsletter_title'],
|
|
1311
|
+
subtitle: assignedText['footer_newsletter_subtitle'],
|
|
1312
|
+
button: assignedText['footer_newsletter_button'],
|
|
1313
|
+
placeholder: assignedText['footer_newsletter_placeholder'],
|
|
1314
|
+
disclaimer: assignedText['footer_newsletter_disclaimer'],
|
|
1315
|
+
},
|
|
1316
|
+
copyright: assignedText['footer_copyright'],
|
|
1317
|
+
links: {
|
|
1318
|
+
about: assignedText['footer_link_about'],
|
|
1319
|
+
contact: assignedText['footer_link_contact'],
|
|
1320
|
+
privacy: assignedText['footer_link_privacy'],
|
|
1321
|
+
terms: assignedText['footer_link_terms'],
|
|
1322
|
+
shipping: assignedText['footer_link_shipping'],
|
|
1323
|
+
faq: assignedText['footer_link_faq'],
|
|
1324
|
+
},
|
|
1325
|
+
},
|
|
1326
|
+
header: {
|
|
1327
|
+
searchPlaceholder: assignedText['header_search_placeholder'],
|
|
1328
|
+
cartEmpty: assignedText['header_cart_empty'],
|
|
1329
|
+
wishlistEmpty: assignedText['header_wishlist_empty'],
|
|
1330
|
+
accountMenu: {
|
|
1331
|
+
profile: assignedText['header_account_profile'],
|
|
1332
|
+
orders: assignedText['header_account_orders'],
|
|
1333
|
+
wishlist: assignedText['header_account_wishlist'],
|
|
1334
|
+
logout: assignedText['header_account_logout'],
|
|
1335
|
+
},
|
|
1336
|
+
},
|
|
1337
|
+
booking: {
|
|
1338
|
+
pageTitle: assignedText['booking_page_title'],
|
|
1339
|
+
pageSubtitle: assignedText['booking_page_subtitle'],
|
|
1340
|
+
bookAppointment: assignedText['booking_book_appointment'] || assignedText['book_btn'],
|
|
1341
|
+
viewServices: assignedText['booking_view_services'] || assignedText['view_services_btn'],
|
|
1342
|
+
selectDate: assignedText['booking_select_date'],
|
|
1343
|
+
selectService: assignedText['booking_select_service'],
|
|
1344
|
+
selectTime: assignedText['booking_select_time'],
|
|
1345
|
+
confirmBooking: assignedText['booking_confirm'],
|
|
1346
|
+
completePayment: assignedText['booking_complete_payment'],
|
|
1347
|
+
bookingConfirmed: assignedText['booking_confirmed'],
|
|
1348
|
+
limitedAvailability: assignedText['booking_limited_availability'],
|
|
1349
|
+
availableServices: assignedText['booking_available_services'] || assignedText['services_list_title'],
|
|
1350
|
+
showingResults: assignedText['booking_showing_results'] || assignedText['services_list_count'],
|
|
1351
|
+
filters: assignedText['booking_filters'],
|
|
1352
|
+
providers: assignedText['booking_providers'],
|
|
1353
|
+
},
|
|
1354
|
+
food: {
|
|
1355
|
+
reserveTable: assignedText['food_reserveTable'] || assignedText['food_reserve_table'],
|
|
1356
|
+
viewMenu: assignedText['food_viewMenu'] || assignedText['food_view_menu'],
|
|
1357
|
+
chefRecommendations: assignedText['food_chefRecommendations'] || assignedText['food_chef_recommendations'],
|
|
1358
|
+
ourPhilosophy: assignedText['food_ourPhilosophy'] || assignedText['food_our_philosophy'],
|
|
1359
|
+
meetChef: assignedText['food_meetChef'] || assignedText['food_meet_chef'],
|
|
1360
|
+
menuCategories: assignedText['food_menuCategories'] || assignedText['food_menu_categories'],
|
|
1361
|
+
},
|
|
1362
|
+
electronics: {
|
|
1363
|
+
newArrivals: assignedText['electronics_new_arrivals'],
|
|
1364
|
+
techSpecs: assignedText['electronics_tech_specs'],
|
|
1365
|
+
compare: assignedText['electronics_compare'],
|
|
1366
|
+
addToCompare: assignedText['electronics_add_to_compare'],
|
|
1367
|
+
removeFromCompare: assignedText['electronics_remove_from_compare'],
|
|
1368
|
+
brands: assignedText['electronics_brands'],
|
|
1369
|
+
},
|
|
1370
|
+
};
|
|
1371
|
+
// Remove undefined values to keep the config clean
|
|
1372
|
+
const cleanTextConfig = JSON.parse(JSON.stringify(textConfig, (key, value) => {
|
|
1373
|
+
if (value === undefined || (typeof value === 'object' && value !== null && Object.keys(value).length === 0)) {
|
|
1374
|
+
return undefined;
|
|
1375
|
+
}
|
|
1376
|
+
return value;
|
|
1377
|
+
}));
|
|
1378
|
+
// Transform theme colors if provided
|
|
1379
|
+
let themeColors;
|
|
1380
|
+
if (layoutContent.themeColors && typeof layoutContent.themeColors === 'object') {
|
|
1381
|
+
themeColors = {
|
|
1382
|
+
background: layoutContent.themeColors.background || undefined,
|
|
1383
|
+
text: layoutContent.themeColors.text || undefined,
|
|
1384
|
+
border: layoutContent.themeColors.border || undefined,
|
|
1385
|
+
gradient: layoutContent.themeColors.gradient || undefined,
|
|
1386
|
+
accent: layoutContent.themeColors.accent || undefined,
|
|
1387
|
+
layoutSpecific: layoutContent.themeColors.layoutSpecific || undefined,
|
|
1388
|
+
};
|
|
1389
|
+
// Remove undefined categories
|
|
1390
|
+
if (Object.values(themeColors).every(v => v === undefined)) {
|
|
1391
|
+
themeColors = undefined;
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
// Also set hero at top level for backward compatibility (if sections.hero exists)
|
|
1395
|
+
const hero = sections?.hero ? {
|
|
1396
|
+
show: sections.hero.show !== false,
|
|
1397
|
+
showCTA: sections.hero.showCTA !== false,
|
|
1398
|
+
showSecondaryCTA: sections.hero.showSecondaryCTA !== false,
|
|
1399
|
+
autoPlay: sections.hero.autoPlay !== false,
|
|
1400
|
+
showBadges: sections.hero.showBadges !== false,
|
|
1401
|
+
sliders: sections.hero.sliders,
|
|
1402
|
+
} : undefined;
|
|
1403
|
+
return {
|
|
1404
|
+
hero, // Top-level hero for backward compatibility
|
|
1405
|
+
sections,
|
|
1406
|
+
assignedAssets,
|
|
1407
|
+
assignedText, // Preserve assignedText for direct access via getTextContent()
|
|
1408
|
+
assetRequirements: layoutContent.assetRequirements,
|
|
1409
|
+
text: Object.keys(cleanTextConfig).length > 0 ? cleanTextConfig : undefined,
|
|
1410
|
+
themeColors,
|
|
1411
|
+
version: layoutContent.version, // Preserve version for layout version checking
|
|
1412
|
+
};
|
|
1413
|
+
}
|
|
1414
|
+
/**
|
|
1415
|
+
* Async function to get store configuration
|
|
1416
|
+
* - For demo stores: uses mock data from mock-stores
|
|
1417
|
+
* - For real stores: fetches from API and transforms to StoreConfig
|
|
1418
|
+
* - Returns null storeConfig if store not found (for 404 handling)
|
|
1419
|
+
* - Returns maintenanceMode flag when store exists but is not published/active
|
|
1420
|
+
*/
|
|
1421
|
+
export async function getStoreConfigAsync(storeSlug) {
|
|
1422
|
+
try {
|
|
1423
|
+
console.log(`[StoreConfig] Loading store config for slug: ${storeSlug}`);
|
|
1424
|
+
if (shouldUseAPI(storeSlug)) {
|
|
1425
|
+
// Use API for non-demo stores
|
|
1426
|
+
const baseSlug = getBaseStoreSlug(storeSlug);
|
|
1427
|
+
console.log(`[StoreConfig] Using API for store. Base slug: ${baseSlug}`);
|
|
1428
|
+
const { store: apiStore, maintenanceMode } = await storeService.getStoreBySlug(baseSlug);
|
|
1429
|
+
console.log(`[StoreConfig] Store loaded from API: ${apiStore.name} (ID: ${apiStore.id})`);
|
|
1430
|
+
const storeConfig = transformStoreResponseToConfig(apiStore);
|
|
1431
|
+
// Fetch categories, products, and services in parallel
|
|
1432
|
+
const [categoriesResult, productsResult, servicesResult] = await Promise.allSettled([
|
|
1433
|
+
// Fetch categories (backend Category entity doesn't have isActive field, so include all)
|
|
1434
|
+
categoryService.getCategories(apiStore.id).then((apiCategories) => apiCategories.map(transformCategoryToStoreCategory)),
|
|
1435
|
+
// Fetch products (backend already filters for isActive: true)
|
|
1436
|
+
productService
|
|
1437
|
+
.getProducts({
|
|
1438
|
+
storeId: apiStore.id,
|
|
1439
|
+
limit: 1000, // Fetch a large number to get all products for the store config
|
|
1440
|
+
})
|
|
1441
|
+
.then((response) => response.data.map(transformProductToStoreProduct)),
|
|
1442
|
+
// Fetch services (backend already filters for isActive: true)
|
|
1443
|
+
serviceService
|
|
1444
|
+
.getServices(apiStore.id)
|
|
1445
|
+
.then((apiServices) => apiServices.map(transformServiceToStoreService)),
|
|
1446
|
+
]);
|
|
1447
|
+
// Extract results with graceful degradation
|
|
1448
|
+
const categories = categoriesResult.status === 'fulfilled' ? categoriesResult.value : [];
|
|
1449
|
+
const products = productsResult.status === 'fulfilled' ? productsResult.value : [];
|
|
1450
|
+
const services = servicesResult.status === 'fulfilled' ? servicesResult.value : [];
|
|
1451
|
+
// Log errors if any occurred
|
|
1452
|
+
if (categoriesResult.status === 'rejected') {
|
|
1453
|
+
console.error('[StoreConfig] Failed to fetch categories:', categoriesResult.reason);
|
|
1454
|
+
console.error('[StoreConfig] Category error details:', {
|
|
1455
|
+
message: categoriesResult.reason?.message,
|
|
1456
|
+
status: categoriesResult.reason?.status,
|
|
1457
|
+
response: categoriesResult.reason?.response?.data,
|
|
1458
|
+
});
|
|
1459
|
+
}
|
|
1460
|
+
else {
|
|
1461
|
+
console.log(`[StoreConfig] Successfully fetched ${categories.length} categories for store ${apiStore.id} (${apiStore.slug})`);
|
|
1462
|
+
if (categories.length === 0) {
|
|
1463
|
+
console.warn(`[StoreConfig] WARNING: No categories found for store ${apiStore.id}. This may indicate missing data in the database.`);
|
|
1464
|
+
}
|
|
1465
|
+
}
|
|
1466
|
+
if (productsResult.status === 'rejected') {
|
|
1467
|
+
console.error('[StoreConfig] Failed to fetch products:', productsResult.reason);
|
|
1468
|
+
console.error('[StoreConfig] Product error details:', {
|
|
1469
|
+
message: productsResult.reason?.message,
|
|
1470
|
+
status: productsResult.reason?.status,
|
|
1471
|
+
response: productsResult.reason?.response?.data,
|
|
1472
|
+
});
|
|
1473
|
+
}
|
|
1474
|
+
else {
|
|
1475
|
+
console.log(`[StoreConfig] Successfully fetched ${products.length} products for store ${apiStore.id} (${apiStore.slug})`);
|
|
1476
|
+
if (products.length === 0) {
|
|
1477
|
+
console.warn(`[StoreConfig] WARNING: No products found for store ${apiStore.id}. This may indicate missing data in the database.`);
|
|
1478
|
+
}
|
|
1479
|
+
}
|
|
1480
|
+
if (servicesResult.status === 'rejected') {
|
|
1481
|
+
console.error('[StoreConfig] Failed to fetch services:', servicesResult.reason);
|
|
1482
|
+
console.error('[StoreConfig] Service error details:', {
|
|
1483
|
+
message: servicesResult.reason?.message,
|
|
1484
|
+
status: servicesResult.reason?.status,
|
|
1485
|
+
response: servicesResult.reason?.response?.data,
|
|
1486
|
+
});
|
|
1487
|
+
}
|
|
1488
|
+
else {
|
|
1489
|
+
console.log(`[StoreConfig] Successfully fetched ${services.length} services for store ${apiStore.id} (${apiStore.slug})`);
|
|
1490
|
+
if (services.length === 0) {
|
|
1491
|
+
console.warn(`[StoreConfig] WARNING: No services found for store ${apiStore.id}. This may indicate missing data in the database.`);
|
|
1492
|
+
}
|
|
1493
|
+
}
|
|
1494
|
+
// Populate categories, products, and services in storeConfig
|
|
1495
|
+
storeConfig.categories = categories;
|
|
1496
|
+
storeConfig.products = products;
|
|
1497
|
+
storeConfig.services = services;
|
|
1498
|
+
console.log(`[StoreConfig] Final storeConfig populated with ${categories.length} categories, ${products.length} products, and ${services.length} services`);
|
|
1499
|
+
return {
|
|
1500
|
+
storeConfig,
|
|
1501
|
+
maintenanceMode: maintenanceMode || false,
|
|
1502
|
+
};
|
|
1503
|
+
}
|
|
1504
|
+
else {
|
|
1505
|
+
// Use mock data for demo stores (always active and published)
|
|
1506
|
+
const baseSlug = getBaseStoreSlug(storeSlug);
|
|
1507
|
+
console.log(`[StoreConfig] Using mock data for demo store. Base slug: ${baseSlug}`);
|
|
1508
|
+
const storeConfig = getStoreConfig(baseSlug);
|
|
1509
|
+
if (storeConfig) {
|
|
1510
|
+
console.log(`[StoreConfig] Mock store loaded: ${storeConfig.name} with ${storeConfig.categories?.length || 0} categories and ${storeConfig.products?.length || 0} products`);
|
|
1511
|
+
}
|
|
1512
|
+
return {
|
|
1513
|
+
storeConfig,
|
|
1514
|
+
maintenanceMode: false, // Demo stores are always available
|
|
1515
|
+
};
|
|
1516
|
+
}
|
|
1517
|
+
}
|
|
1518
|
+
catch (error) {
|
|
1519
|
+
// Check if it's a 404 (store not found) vs other error
|
|
1520
|
+
const isNotFound = error?.status === 404 || error?.response?.status === 404;
|
|
1521
|
+
if (isNotFound) {
|
|
1522
|
+
// Store truly doesn't exist
|
|
1523
|
+
return {
|
|
1524
|
+
storeConfig: null,
|
|
1525
|
+
maintenanceMode: false,
|
|
1526
|
+
};
|
|
1527
|
+
}
|
|
1528
|
+
// Other API errors - log and return null
|
|
1529
|
+
console.error('Failed to fetch store config:', error);
|
|
1530
|
+
return {
|
|
1531
|
+
storeConfig: null,
|
|
1532
|
+
maintenanceMode: false,
|
|
1533
|
+
};
|
|
1534
|
+
}
|
|
1535
|
+
}
|