@alphasquad/saleor-template-advance 0.1.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/.env.example +57 -0
- package/APPLE_PAY_QUICK_START.md +165 -0
- package/APPLE_PAY_SETUP.md +331 -0
- package/README.md +46 -0
- package/SEO_AUDIT_CHECKLIST_STATUS.md +244 -0
- package/SEO_AUDIT_REPORT.md +66 -0
- package/eslint.config.mjs +16 -0
- package/next-env.d.ts +5 -0
- package/next.config.ts +109 -0
- package/package.json +47 -0
- package/postcss.config.mjs +5 -0
- package/public/.well-known/apple-developer-merchantid-domain-association +1 -0
- package/public/Logo.png +0 -0
- package/public/brand-video.mp4 +0 -0
- package/public/favicon.ico +0 -0
- package/public/file.svg +1 -0
- package/public/footer/facebook.tsx +34 -0
- package/public/footer/instagram.tsx +27 -0
- package/public/footer/mail.tsx +5 -0
- package/public/footer/x.tsx +35 -0
- package/public/globe.svg +1 -0
- package/public/icons/Authorize.net.webp +0 -0
- package/public/icons/amex.gif +0 -0
- package/public/icons/appIcon.png +0 -0
- package/public/icons/discover.gif +0 -0
- package/public/icons/master.gif +0 -0
- package/public/icons/paypal.png +0 -0
- package/public/icons/stripe.png +0 -0
- package/public/icons/visa.gif +0 -0
- package/public/images/BackgroundNoise.png +0 -0
- package/public/images/footer-background.png +0 -0
- package/public/next.svg +1 -0
- package/public/no-image-avail-large.png +0 -0
- package/public/random-car-1.jpeg +0 -0
- package/public/random-car-2.png +0 -0
- package/public/random-car-3.jpg +0 -0
- package/public/random-car-4.jpg +0 -0
- package/public/random-car-5.jpg +0 -0
- package/public/star.svg +3 -0
- package/public/vercel.svg +1 -0
- package/public/window.svg +1 -0
- package/scripts/seo-audit/generate-checklist.mjs +156 -0
- package/src/app/(auth)/account/forgot-password/layout.tsx +16 -0
- package/src/app/(auth)/account/forgot-password/page.tsx +135 -0
- package/src/app/(auth)/account/login/layout.tsx +16 -0
- package/src/app/(auth)/account/login/page.tsx +288 -0
- package/src/app/(auth)/account/otp/layout.tsx +16 -0
- package/src/app/(auth)/account/otp/page.tsx +108 -0
- package/src/app/(auth)/account/register/layout.tsx +16 -0
- package/src/app/(auth)/account/register/page.tsx +431 -0
- package/src/app/(auth)/account/reset-password/layout.tsx +16 -0
- package/src/app/(auth)/account/reset-password/page.tsx +222 -0
- package/src/app/[slug]/page.tsx +43 -0
- package/src/app/about/loading.tsx +17 -0
- package/src/app/about/page.tsx +61 -0
- package/src/app/account/address/layout.tsx +15 -0
- package/src/app/account/address/page.tsx +166 -0
- package/src/app/account/head.tsx +4 -0
- package/src/app/account/layout.tsx +62 -0
- package/src/app/account/orders/[id]/layout.tsx +17 -0
- package/src/app/account/orders/[id]/page.tsx +115 -0
- package/src/app/account/orders/components/orderDetailsModal.tsx +410 -0
- package/src/app/account/orders/layout.tsx +15 -0
- package/src/app/account/orders/page.tsx +146 -0
- package/src/app/account/page.tsx +39 -0
- package/src/app/account/settings/components/editProfileSuccessModal.tsx +28 -0
- package/src/app/account/settings/layout.tsx +15 -0
- package/src/app/account/settings/page.tsx +260 -0
- package/src/app/api/affirm/check-status/route.ts +94 -0
- package/src/app/api/affirm/create-checkout/route.ts +109 -0
- package/src/app/api/affirm/get-config/route.ts +108 -0
- package/src/app/api/affirm/process-payment/route.ts +244 -0
- package/src/app/api/affirm/test-connection/route.ts +45 -0
- package/src/app/api/auth/clear/route.ts +16 -0
- package/src/app/api/auth/clear-cookies/route.ts +42 -0
- package/src/app/api/auth/set/route.ts +47 -0
- package/src/app/api/configuration/route.ts +18 -0
- package/src/app/api/dynamic-page/[slug]/route.ts +24 -0
- package/src/app/api/form-submission/route.ts +237 -0
- package/src/app/api/paypal/capture-order/route.ts +303 -0
- package/src/app/api/paypal/create-order/route.ts +211 -0
- package/src/app/api/paypal/get-config/route.ts +240 -0
- package/src/app/api/search-proxy/route.ts +52 -0
- package/src/app/authorize-net-success/layout.tsx +19 -0
- package/src/app/authorize-net-success/page.tsx +12 -0
- package/src/app/authorize-net-success/summary.tsx +486 -0
- package/src/app/blog/[slug]/blogContentRenderer.tsx +369 -0
- package/src/app/blog/[slug]/layout.tsx +17 -0
- package/src/app/blog/[slug]/page.tsx +151 -0
- package/src/app/blog/constant.tsx +147 -0
- package/src/app/blog/layout.tsx +31 -0
- package/src/app/blog/page.tsx +81 -0
- package/src/app/brand/[id]/BrandPageClient.tsx +188 -0
- package/src/app/brand/[id]/layout.tsx +17 -0
- package/src/app/brand/[id]/page.tsx +176 -0
- package/src/app/brands/components/brandsListingClient.tsx +97 -0
- package/src/app/brands/layout.tsx +31 -0
- package/src/app/brands/page.tsx +40 -0
- package/src/app/cancellation-policy/page.tsx +53 -0
- package/src/app/cart/layout.tsx +19 -0
- package/src/app/cart/page.tsx +752 -0
- package/src/app/category/[slug]/CategoryPageClient.tsx +377 -0
- package/src/app/category/[slug]/layout.tsx +17 -0
- package/src/app/category/[slug]/page.tsx +224 -0
- package/src/app/category/page.tsx +114 -0
- package/src/app/checkout/components/addNewAddressModal.tsx +474 -0
- package/src/app/checkout/layout.tsx +19 -0
- package/src/app/checkout/page.tsx +3312 -0
- package/src/app/components/account/AccountTabs.tsx +40 -0
- package/src/app/components/ads/GoogleAdSense.tsx +74 -0
- package/src/app/components/analytics/AnalyticsScripts.tsx +78 -0
- package/src/app/components/analytics/ConditionalGTMNoscript.tsx +24 -0
- package/src/app/components/analytics/ConditionalGoogleAnalytics.tsx +16 -0
- package/src/app/components/ancillary/AncillaryContent.tsx +7 -0
- package/src/app/components/auth/TokenExpirationHandler.tsx +8 -0
- package/src/app/components/blog/BlogList.tsx +112 -0
- package/src/app/components/checkout/AddressInformationSection.tsx +34 -0
- package/src/app/components/checkout/AddressManagement.tsx +571 -0
- package/src/app/components/checkout/CheckoutHeader.tsx +51 -0
- package/src/app/components/checkout/CheckoutQuestions.tsx +454 -0
- package/src/app/components/checkout/CheckoutTermsModal.tsx +81 -0
- package/src/app/components/checkout/ContactDetailsSection.tsx +52 -0
- package/src/app/components/checkout/DealerShippingSection.tsx +359 -0
- package/src/app/components/checkout/DeliveryMethodSection.tsx +249 -0
- package/src/app/components/checkout/OrderSummary.tsx +386 -0
- package/src/app/components/checkout/TermsContentRenderer.tsx +147 -0
- package/src/app/components/checkout/WillCallSection.tsx +133 -0
- package/src/app/components/checkout/affirmPayment.tsx +383 -0
- package/src/app/components/checkout/checkoutProcessingModal.tsx +96 -0
- package/src/app/components/checkout/googlePayButton.tsx +334 -0
- package/src/app/components/checkout/paymentStep.tsx +180 -0
- package/src/app/components/checkout/paypalPayment.tsx +1083 -0
- package/src/app/components/checkout/saleorNativePayment.tsx +1758 -0
- package/src/app/components/dynamicPage/DynamicPageRenderer.tsx +13 -0
- package/src/app/components/dynamicPage/HtmlWidgetRenderer.tsx +144 -0
- package/src/app/components/filtersCollapsible/index.tsx +365 -0
- package/src/app/components/globalSearch/index.tsx +423 -0
- package/src/app/components/layout/cartDropDown.tsx +628 -0
- package/src/app/components/layout/components/FooterNewsletter.tsx +21 -0
- package/src/app/components/layout/footer.tsx +283 -0
- package/src/app/components/layout/header/accountMenuDropdown.tsx +53 -0
- package/src/app/components/layout/header/components/CartBadge.tsx +18 -0
- package/src/app/components/layout/header/components/LoadingState.tsx +17 -0
- package/src/app/components/layout/header/components/MenuItemDropdown.tsx +124 -0
- package/src/app/components/layout/header/components/MobileNavbar.tsx +123 -0
- package/src/app/components/layout/header/components/NavbarActions.tsx +125 -0
- package/src/app/components/layout/header/components/NavbarBrand.tsx +29 -0
- package/src/app/components/layout/header/components/NavigationLinks.tsx +131 -0
- package/src/app/components/layout/header/hamMenuSlide.tsx +318 -0
- package/src/app/components/layout/header/header.tsx +44 -0
- package/src/app/components/layout/header/hooks/useDropdown.ts +45 -0
- package/src/app/components/layout/header/hooks/useNavbarData.ts +138 -0
- package/src/app/components/layout/header/hooks/useNavbarState.ts +66 -0
- package/src/app/components/layout/header/megaMenuDropdown.tsx +116 -0
- package/src/app/components/layout/header/navBar.tsx +121 -0
- package/src/app/components/layout/header/search.tsx +418 -0
- package/src/app/components/layout/header/styles/navbarStyles.ts +27 -0
- package/src/app/components/layout/header/topBar.tsx +214 -0
- package/src/app/components/layout/joinNewsletterForm/index.tsx +72 -0
- package/src/app/components/layout/mobileAccordian/index.tsx +92 -0
- package/src/app/components/layout/paymentMethods.tsx +75 -0
- package/src/app/components/layout/rootLayout.tsx +23 -0
- package/src/app/components/layout/siteInfo.tsx +103 -0
- package/src/app/components/layout/socialLinks.tsx +65 -0
- package/src/app/components/newsletterSection/emailListSection.tsx +224 -0
- package/src/app/components/newsletterSection/emailSectionServer.tsx +8 -0
- package/src/app/components/providers/ApolloWrapper.tsx +12 -0
- package/src/app/components/providers/AppConfigurationProvider.tsx +108 -0
- package/src/app/components/providers/GoogleAnalyticsProvider.tsx +149 -0
- package/src/app/components/providers/GoogleTagManagerProvider.tsx +31 -0
- package/src/app/components/providers/RecaptchaProvider.tsx +18 -0
- package/src/app/components/providers/ServerAppConfigurationProvider.tsx +133 -0
- package/src/app/components/providers/YMMStatusProvider.tsx +15 -0
- package/src/app/components/reuseableUI/AboutUs.tsx +115 -0
- package/src/app/components/reuseableUI/AddToCartClient.tsx +125 -0
- package/src/app/components/reuseableUI/EditorJsRenderer.tsx +219 -0
- package/src/app/components/reuseableUI/HeroSectionsearchByVehicle.tsx +188 -0
- package/src/app/components/reuseableUI/ImageWithFallback.tsx +41 -0
- package/src/app/components/reuseableUI/Toast.tsx +101 -0
- package/src/app/components/reuseableUI/blogCard.tsx +52 -0
- package/src/app/components/reuseableUI/brandCard.tsx +68 -0
- package/src/app/components/reuseableUI/breadcrumb.tsx +38 -0
- package/src/app/components/reuseableUI/categoryCard.tsx +37 -0
- package/src/app/components/reuseableUI/categorySkeleton.tsx +31 -0
- package/src/app/components/reuseableUI/commonButton.tsx +48 -0
- package/src/app/components/reuseableUI/defaultInputField/index.tsx +84 -0
- package/src/app/components/reuseableUI/emptyState.tsx +29 -0
- package/src/app/components/reuseableUI/errorTag.tsx +15 -0
- package/src/app/components/reuseableUI/heading/index.tsx +20 -0
- package/src/app/components/reuseableUI/input.tsx +117 -0
- package/src/app/components/reuseableUI/listCard.tsx +137 -0
- package/src/app/components/reuseableUI/loadingUI.tsx +12 -0
- package/src/app/components/reuseableUI/modalLayout.tsx +76 -0
- package/src/app/components/reuseableUI/newsletter/newsletterClient.tsx +622 -0
- package/src/app/components/reuseableUI/newsletter/newslettersHomeModal.tsx +68 -0
- package/src/app/components/reuseableUI/offerCard.tsx +42 -0
- package/src/app/components/reuseableUI/passwordRules/passwordRules.tsx +56 -0
- package/src/app/components/reuseableUI/primaryButton/index.tsx +34 -0
- package/src/app/components/reuseableUI/productCard.tsx +118 -0
- package/src/app/components/reuseableUI/productSkeleton.tsx +34 -0
- package/src/app/components/reuseableUI/searchByVehicle.tsx +187 -0
- package/src/app/components/reuseableUI/secondaryButton/index.tsx +34 -0
- package/src/app/components/reuseableUI/section.tsx +20 -0
- package/src/app/components/reuseableUI/select/index.tsx +98 -0
- package/src/app/components/reuseableUI/skeletonLoader.tsx +117 -0
- package/src/app/components/reuseableUI/statusTag.tsx +24 -0
- package/src/app/components/reuseableUI/tags/saleTag.tsx +19 -0
- package/src/app/components/reuseableUI/testimonialCard.tsx +93 -0
- package/src/app/components/richText/EditorRenderer.tsx +318 -0
- package/src/app/components/search/HierarchicalCategoryFilter.tsx +155 -0
- package/src/app/components/search/SearchFilters.tsx +155 -0
- package/src/app/components/search/YMMSearchSidebar.tsx +187 -0
- package/src/app/components/seo/ServerProductCard.tsx +91 -0
- package/src/app/components/seo/ServerProductGrid.tsx +45 -0
- package/src/app/components/shop/CategoryFilter.tsx +184 -0
- package/src/app/components/shop/ItemsPerPageSelect.tsx +69 -0
- package/src/app/components/shop/ItemsPerPageSelectClient.tsx +58 -0
- package/src/app/components/shop/MobileFilters.tsx +103 -0
- package/src/app/components/shop/ProductGridSkeleton.tsx +16 -0
- package/src/app/components/shop/ProductsGrid.tsx +230 -0
- package/src/app/components/shop/SearchFilter.tsx +218 -0
- package/src/app/components/shop/SearchFilterClient.tsx +122 -0
- package/src/app/components/shop/SearchLoadingOverlay.tsx +32 -0
- package/src/app/components/shop/ShopMobileFilters.tsx +205 -0
- package/src/app/components/showroom/VehicleSearchDropdowns.tsx +187 -0
- package/src/app/components/showroom/brandsSwiper.tsx +49 -0
- package/src/app/components/showroom/brandsSwiperClient copy.tsx +93 -0
- package/src/app/components/showroom/brandsSwiperClient.tsx +122 -0
- package/src/app/components/showroom/brandsSwiperServer.tsx +42 -0
- package/src/app/components/showroom/bundleProducts.tsx +120 -0
- package/src/app/components/showroom/categoryGrid.tsx +51 -0
- package/src/app/components/showroom/categoryGridServer.tsx +45 -0
- package/src/app/components/showroom/categorySwiper.tsx +115 -0
- package/src/app/components/showroom/featureStrip.tsx +139 -0
- package/src/app/components/showroom/offersSwiper.tsx +181 -0
- package/src/app/components/showroom/productGrid.tsx +56 -0
- package/src/app/components/showroom/productSwiper.tsx +119 -0
- package/src/app/components/showroom/promotion-slider.tsx +138 -0
- package/src/app/components/showroom/promotion.tsx +207 -0
- package/src/app/components/showroom/promotionsSwiper.tsx +174 -0
- package/src/app/components/showroom/showroomHeroCarousel.tsx +141 -0
- package/src/app/components/showroom/testimonialsGrid.tsx +106 -0
- package/src/app/components/skeletons/ContentSkeleton.tsx +14 -0
- package/src/app/components/sortDropdown/index.tsx +116 -0
- package/src/app/components/tertiaryButton/index.tsx +25 -0
- package/src/app/components/theme/theme-provider.tsx +82 -0
- package/src/app/contact/layout.tsx +32 -0
- package/src/app/contact/page.tsx +591 -0
- package/src/app/content/[slug]/layout.tsx +17 -0
- package/src/app/content/[slug]/page.tsx +159 -0
- package/src/app/content/layout.tsx +31 -0
- package/src/app/content/page.tsx +88 -0
- package/src/app/core-policies/page.tsx +55 -0
- package/src/app/discounts/page.tsx +54 -0
- package/src/app/frequently-asked-questions/page.tsx +57 -0
- package/src/app/globals.css +440 -0
- package/src/app/hooks/useDealerLocations.ts +259 -0
- package/src/app/hooks/useGTMEngagement.ts +71 -0
- package/src/app/hooks/useGoogleAnalytics.ts +145 -0
- package/src/app/layout.tsx +149 -0
- package/src/app/not-found.tsx +31 -0
- package/src/app/order-confirmation/layout.tsx +19 -0
- package/src/app/order-confirmation/page.tsx +12 -0
- package/src/app/order-confirmation/summary.tsx +1775 -0
- package/src/app/page.tsx +194 -0
- package/src/app/privacy-policy/loading.tsx +17 -0
- package/src/app/privacy-policy/page.tsx +56 -0
- package/src/app/product/[id]/ProductDetailClient.tsx +2448 -0
- package/src/app/product/[id]/components/itemInquiryModal.tsx +461 -0
- package/src/app/product/[id]/layout.tsx +116 -0
- package/src/app/product/[id]/page.tsx +200 -0
- package/src/app/product/layout.tsx +15 -0
- package/src/app/products/all/AllProductsClient.tsx +743 -0
- package/src/app/products/all/page.tsx +176 -0
- package/src/app/products/components/shopEmptyState.tsx +29 -0
- package/src/app/request-return/layout.tsx +36 -0
- package/src/app/request-return/page.tsx +597 -0
- package/src/app/robots.txt/route.ts +27 -0
- package/src/app/search/layout.tsx +16 -0
- package/src/app/search/page.tsx +736 -0
- package/src/app/shipping-returns/page.tsx +60 -0
- package/src/app/site-map/layout.tsx +33 -0
- package/src/app/site-map/page.tsx +113 -0
- package/src/app/sitemap-index.xml/route.ts +20 -0
- package/src/app/sitemap.ts +10 -0
- package/src/app/terms-and-conditions/loading.tsx +17 -0
- package/src/app/terms-and-conditions/page.tsx +56 -0
- package/src/app/utils/appConfiguration.ts +327 -0
- package/src/app/utils/branding.ts +52 -0
- package/src/app/utils/configurationService.ts +202 -0
- package/src/app/utils/constant.tsx +242 -0
- package/src/app/utils/editorJsUtils.tsx +249 -0
- package/src/app/utils/functions.ts +146 -0
- package/src/app/utils/googleAnalytics.ts +168 -0
- package/src/app/utils/googleTagManager.ts +475 -0
- package/src/app/utils/ipDetection.ts +270 -0
- package/src/app/utils/serverConfigurationService.ts +209 -0
- package/src/app/utils/svgs/GridIcon.tsx +45 -0
- package/src/app/utils/svgs/account/myAccount/listDotIcon.tsx +3 -0
- package/src/app/utils/svgs/account/myAccount/tickIcon.tsx +10 -0
- package/src/app/utils/svgs/account/orderHistory/InfoIcon.tsx +49 -0
- package/src/app/utils/svgs/arrowDownIcon.tsx +17 -0
- package/src/app/utils/svgs/arrowIcon.tsx +25 -0
- package/src/app/utils/svgs/arrowUpIcon.tsx +16 -0
- package/src/app/utils/svgs/brandsSearchIcon.tsx +25 -0
- package/src/app/utils/svgs/cart/cartIcon.tsx +31 -0
- package/src/app/utils/svgs/cart/plusIcon.tsx +13 -0
- package/src/app/utils/svgs/cart/subtractIcon.tsx +13 -0
- package/src/app/utils/svgs/cart/successTickIcon.tsx +14 -0
- package/src/app/utils/svgs/chevronDownIcon.tsx +21 -0
- package/src/app/utils/svgs/closeEyeIcon.tsx +47 -0
- package/src/app/utils/svgs/crossIcon.tsx +25 -0
- package/src/app/utils/svgs/eyeIcon.tsx +29 -0
- package/src/app/utils/svgs/featureTag.tsx +20 -0
- package/src/app/utils/svgs/filterIcon.tsx +3 -0
- package/src/app/utils/svgs/globleIcon.tsx +41 -0
- package/src/app/utils/svgs/infoIcon.tsx +34 -0
- package/src/app/utils/svgs/listIcon.tsx +50 -0
- package/src/app/utils/svgs/logOutIcon.tsx +35 -0
- package/src/app/utils/svgs/menuIcon.tsx +8 -0
- package/src/app/utils/svgs/minusIcon.tsx +18 -0
- package/src/app/utils/svgs/newsletterIcon.tsx +19 -0
- package/src/app/utils/svgs/noDataFoundIcon-.tsx +26 -0
- package/src/app/utils/svgs/noProductFoundIcon.tsx +43 -0
- package/src/app/utils/svgs/passwordIcons/errorIcon.tsx +31 -0
- package/src/app/utils/svgs/passwordIcons/successIcon.tsx +24 -0
- package/src/app/utils/svgs/paymentProcessingIcons/hourglassIcon.tsx +43 -0
- package/src/app/utils/svgs/paymentProcessingIcons/modalCrossIcon.tsx +23 -0
- package/src/app/utils/svgs/paymentProcessingIcons/paymentFailedIcon.tsx +47 -0
- package/src/app/utils/svgs/pencilIcon.tsx +11 -0
- package/src/app/utils/svgs/plusIcon.tsx +25 -0
- package/src/app/utils/svgs/productInquiryIcon.tsx +40 -0
- package/src/app/utils/svgs/searchIcon.tsx +31 -0
- package/src/app/utils/svgs/shoppingCart.tsx +32 -0
- package/src/app/utils/svgs/spinnerIcon.tsx +22 -0
- package/src/app/utils/svgs/spinnerLoadingIcon.tsx +26 -0
- package/src/app/utils/svgs/successTickIcon.tsx +40 -0
- package/src/app/utils/svgs/swiperArrowIconLeft.tsx +18 -0
- package/src/app/utils/svgs/swiperArrowIconRight.tsx +18 -0
- package/src/app/utils/svgs/userProfileIcon.tsx +31 -0
- package/src/app/utils/svgs/warningCircleIcon.tsx +15 -0
- package/src/app/warranty/constant.tsx +63 -0
- package/src/app/warranty/loading.tsx +17 -0
- package/src/app/warranty/page.tsx +56 -0
- package/src/graphql/client.ts +288 -0
- package/src/graphql/mutations/accountAddressCreate.ts +56 -0
- package/src/graphql/mutations/accountAddressDelete.ts +23 -0
- package/src/graphql/mutations/accountAddressUpdate.ts +55 -0
- package/src/graphql/mutations/accountSetDefaultAddress.ts +32 -0
- package/src/graphql/mutations/accountUpdate.ts +34 -0
- package/src/graphql/mutations/changePassword.ts +25 -0
- package/src/graphql/mutations/checkout.ts +117 -0
- package/src/graphql/mutations/checkoutAddVoucher.ts +63 -0
- package/src/graphql/mutations/checkoutComplete.ts +79 -0
- package/src/graphql/mutations/checkoutCreate.ts +131 -0
- package/src/graphql/mutations/checkoutCustomerAttach.ts +50 -0
- package/src/graphql/mutations/checkoutEmailUpdate.ts +15 -0
- package/src/graphql/mutations/checkoutLineMetadataUpdate.ts +52 -0
- package/src/graphql/mutations/checkoutPaymentCreate.ts +82 -0
- package/src/graphql/mutations/paymentGatewayInitialize.ts +58 -0
- package/src/graphql/mutations/registerAccount.ts +65 -0
- package/src/graphql/mutations/requestPasswordReset.ts +32 -0
- package/src/graphql/mutations/setPassword.ts +49 -0
- package/src/graphql/mutations/signIn.ts +50 -0
- package/src/graphql/mutations/tokenRefresh.ts +19 -0
- package/src/graphql/mutations/updateCheckoutMetadata.ts +49 -0
- package/src/graphql/mutations/updateProfile.ts +18 -0
- package/src/graphql/mutations/willCallDeliveryMethod.ts +81 -0
- package/src/graphql/queries/checkout.ts +168 -0
- package/src/graphql/queries/findProductByOldSlug.ts +58 -0
- package/src/graphql/queries/getAboutPage.ts +24 -0
- package/src/graphql/queries/getAboutPageId.ts +9 -0
- package/src/graphql/queries/getAboutUs.ts +38 -0
- package/src/graphql/queries/getAddressInformation.ts +38 -0
- package/src/graphql/queries/getAllCategories.ts +41 -0
- package/src/graphql/queries/getAllCategoriesTree.ts +67 -0
- package/src/graphql/queries/getAllCategoriesWithProducts.ts +29 -0
- package/src/graphql/queries/getAllCollectionsWithProducts.ts +16 -0
- package/src/graphql/queries/getBlogs.ts +222 -0
- package/src/graphql/queries/getBrands.ts +17 -0
- package/src/graphql/queries/getBundles.ts +43 -0
- package/src/graphql/queries/getCategories.ts +20 -0
- package/src/graphql/queries/getChannels.ts +77 -0
- package/src/graphql/queries/getCheckoutQuestions.ts +115 -0
- package/src/graphql/queries/getCheckoutTermsAndConditions.ts +37 -0
- package/src/graphql/queries/getContactPage.ts +117 -0
- package/src/graphql/queries/getContentPage.ts +191 -0
- package/src/graphql/queries/getDiscountOffers.ts +18 -0
- package/src/graphql/queries/getDynamicPageBySlug.ts +251 -0
- package/src/graphql/queries/getFeaturedProducts.ts +48 -0
- package/src/graphql/queries/getHeroMetadata.ts +23 -0
- package/src/graphql/queries/getMenuBySlug.ts +84 -0
- package/src/graphql/queries/getMyProfile.ts +23 -0
- package/src/graphql/queries/getNewsletter.ts +122 -0
- package/src/graphql/queries/getNewsletterPage.ts +111 -0
- package/src/graphql/queries/getPageBySlug.ts +52 -0
- package/src/graphql/queries/getPageTypeId.ts +27 -0
- package/src/graphql/queries/getPaymentMethods.ts +61 -0
- package/src/graphql/queries/getProducts.ts +78 -0
- package/src/graphql/queries/getPromotions.ts +24 -0
- package/src/graphql/queries/getRequestReturnPage.ts +121 -0
- package/src/graphql/queries/getSiteInfo.ts +54 -0
- package/src/graphql/queries/getSocialLinks.ts +52 -0
- package/src/graphql/queries/getTestimonials.ts +25 -0
- package/src/graphql/queries/getUserWithCheckout.ts +27 -0
- package/src/graphql/queries/getVehicleMakes.ts +21 -0
- package/src/graphql/queries/getVehicleModels.ts +21 -0
- package/src/graphql/queries/getVehicleYears.ts +21 -0
- package/src/graphql/queries/meAddresses.ts +56 -0
- package/src/graphql/queries/myOrders.ts +37 -0
- package/src/graphql/queries/orderDetail.ts +231 -0
- package/src/graphql/queries/productDetailsById.ts +197 -0
- package/src/graphql/queries/productInquiry.ts +115 -0
- package/src/graphql/queries/productsByCategoriesAndCollections.ts +39 -0
- package/src/graphql/queries/willCallCollectionPoints.ts +55 -0
- package/src/graphql/server-client.ts +54 -0
- package/src/graphql/types/categories.ts +9 -0
- package/src/graphql/types/checkout.ts +168 -0
- package/src/graphql/types/offer.ts +12 -0
- package/src/graphql/types/product.ts +44 -0
- package/src/hooks/scrollPageTop.ts +9 -0
- package/src/hooks/serverNavbarData.ts +79 -0
- package/src/hooks/useCartSync.ts +24 -0
- package/src/hooks/useRecaptcha.ts +33 -0
- package/src/hooks/useTokenExpiration.ts +81 -0
- package/src/hooks/useVehicleData.ts +346 -0
- package/src/lib/api/kount.ts +165 -0
- package/src/lib/api/shop.ts +1445 -0
- package/src/lib/saleor/getSaleorApiUrl.ts +25 -0
- package/src/lib/schema.ts +303 -0
- package/src/lib/seo/extractTextFromEditorJs.ts +58 -0
- package/src/lib/seo/site.ts +10 -0
- package/src/lib/urls/normalizeInternalUrl.ts +53 -0
- package/src/middleware.ts +134 -0
- package/src/sitemaps/README.md +105 -0
- package/src/sitemaps/dynamic-pages-sitemap.ts +247 -0
- package/src/sitemaps/sitemap-index.ts +21 -0
- package/src/sitemaps/static-pages-sitemap.ts +36 -0
- package/src/store/useGlobalStore.tsx +1656 -0
- package/src/types/global.d.ts +148 -0
- package/tsconfig.json +27 -0
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import Link from "next/link";
|
|
3
|
+
|
|
4
|
+
export type EditorJsBlock = {
|
|
5
|
+
id: string;
|
|
6
|
+
type: string;
|
|
7
|
+
data: {
|
|
8
|
+
text?: string;
|
|
9
|
+
level?: number;
|
|
10
|
+
style?: "ordered" | "unordered";
|
|
11
|
+
items?: string[];
|
|
12
|
+
caption?: string;
|
|
13
|
+
alignment?: "left" | "center" | "right";
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// Helper function to decode HTML entities
|
|
18
|
+
function decodeHtmlEntities(text: string): string {
|
|
19
|
+
const textarea =
|
|
20
|
+
typeof document !== "undefined" ? document.createElement("textarea") : null;
|
|
21
|
+
|
|
22
|
+
if (textarea) {
|
|
23
|
+
textarea.innerHTML = text;
|
|
24
|
+
return textarea.value;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Fallback for server-side rendering
|
|
28
|
+
return text
|
|
29
|
+
.replace(/</g, "<")
|
|
30
|
+
.replace(/>/g, ">")
|
|
31
|
+
.replace(/&/g, "&")
|
|
32
|
+
.replace(/"/g, '"')
|
|
33
|
+
.replace(/'/g, "'")
|
|
34
|
+
.replace(/ /g, " ");
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Helper function to make all anchor tags open in new tab
|
|
38
|
+
function processAnchorsForNewTab(html: string): string {
|
|
39
|
+
// Match <a tags and add target="_blank" rel="noopener noreferrer"
|
|
40
|
+
return html.replace(
|
|
41
|
+
/<a\s+([^>]*?)>/gi,
|
|
42
|
+
(match, attributes) => {
|
|
43
|
+
// Check if target already exists
|
|
44
|
+
if (/target\s*=/i.test(attributes)) {
|
|
45
|
+
return match;
|
|
46
|
+
}
|
|
47
|
+
return `<a ${attributes} target="_blank" rel="noopener noreferrer">`;
|
|
48
|
+
}
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Helper function to check if text contains HTML table elements
|
|
53
|
+
function isTableFragment(text: string): boolean {
|
|
54
|
+
const tablePatterns = [
|
|
55
|
+
/<table/i,
|
|
56
|
+
/<\/table>/i,
|
|
57
|
+
/<thead/i,
|
|
58
|
+
/<\/thead>/i,
|
|
59
|
+
/<tbody/i,
|
|
60
|
+
/<\/tbody>/i,
|
|
61
|
+
/<tr/i,
|
|
62
|
+
/<\/tr>/i,
|
|
63
|
+
/<td/i,
|
|
64
|
+
/<\/td>/i,
|
|
65
|
+
/<th/i,
|
|
66
|
+
/<\/th>/i,
|
|
67
|
+
/<colgroup/i,
|
|
68
|
+
/<\/colgroup>/i,
|
|
69
|
+
/<col/i,
|
|
70
|
+
];
|
|
71
|
+
return tablePatterns.some((pattern) => pattern.test(text));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Helper function to merge table fragments
|
|
75
|
+
function mergeTableBlocks(blocks: EditorJsBlock[]): EditorJsBlock[] {
|
|
76
|
+
const merged: EditorJsBlock[] = [];
|
|
77
|
+
let tableBuffer: string[] = [];
|
|
78
|
+
let tableStartId: string | null = null;
|
|
79
|
+
let isInTable = false;
|
|
80
|
+
|
|
81
|
+
for (let i = 0; i < blocks.length; i++) {
|
|
82
|
+
const block = blocks[i];
|
|
83
|
+
const text = block.data?.text || "";
|
|
84
|
+
const decodedText = decodeHtmlEntities(text);
|
|
85
|
+
|
|
86
|
+
// Check if this starts a table
|
|
87
|
+
if (/<table/i.test(decodedText)) {
|
|
88
|
+
isInTable = true;
|
|
89
|
+
tableStartId = block.id;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (
|
|
93
|
+
block.type === "paragraph" &&
|
|
94
|
+
(isInTable || isTableFragment(decodedText))
|
|
95
|
+
) {
|
|
96
|
+
// Start or continue accumulating table content
|
|
97
|
+
if (!tableStartId) {
|
|
98
|
+
tableStartId = block.id;
|
|
99
|
+
isInTable = true;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Clean up individual fragments: remove <br> tags and preserve whitespace
|
|
103
|
+
const cleanedText = decodedText
|
|
104
|
+
.replace(/<br\s*\/?>/gi, "")
|
|
105
|
+
.replace(/^\s+|\s+$/g, ""); // Trim leading/trailing whitespace only
|
|
106
|
+
|
|
107
|
+
if (cleanedText) {
|
|
108
|
+
tableBuffer.push(cleanedText);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Check if this closes the table
|
|
112
|
+
if (/<\/table>/i.test(decodedText)) {
|
|
113
|
+
isInTable = false;
|
|
114
|
+
// Create merged table block
|
|
115
|
+
const mergedHTML = tableBuffer.join("");
|
|
116
|
+
merged.push({
|
|
117
|
+
id: tableStartId || block.id,
|
|
118
|
+
type: "table",
|
|
119
|
+
data: {
|
|
120
|
+
text: mergedHTML,
|
|
121
|
+
},
|
|
122
|
+
});
|
|
123
|
+
tableBuffer = [];
|
|
124
|
+
tableStartId = null;
|
|
125
|
+
}
|
|
126
|
+
} else {
|
|
127
|
+
// If we have accumulated table content, create a merged block
|
|
128
|
+
if (tableBuffer.length > 0 && tableStartId) {
|
|
129
|
+
// Join without extra newlines to preserve HTML structure
|
|
130
|
+
const mergedHTML = tableBuffer.join("");
|
|
131
|
+
merged.push({
|
|
132
|
+
id: tableStartId,
|
|
133
|
+
type: "table",
|
|
134
|
+
data: {
|
|
135
|
+
text: mergedHTML,
|
|
136
|
+
},
|
|
137
|
+
});
|
|
138
|
+
tableBuffer = [];
|
|
139
|
+
tableStartId = null;
|
|
140
|
+
isInTable = false;
|
|
141
|
+
}
|
|
142
|
+
// Add the non-table block
|
|
143
|
+
merged.push(block);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Handle any remaining table content
|
|
148
|
+
if (tableBuffer.length > 0 && tableStartId) {
|
|
149
|
+
const mergedHTML = tableBuffer.join("");
|
|
150
|
+
merged.push({
|
|
151
|
+
id: tableStartId,
|
|
152
|
+
type: "table",
|
|
153
|
+
data: {
|
|
154
|
+
text: mergedHTML,
|
|
155
|
+
},
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return merged;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export default function BlogEditorRenderer({
|
|
163
|
+
content,
|
|
164
|
+
}: {
|
|
165
|
+
content: string | null | undefined;
|
|
166
|
+
}) {
|
|
167
|
+
let blocks: EditorJsBlock[] = [];
|
|
168
|
+
if (content) {
|
|
169
|
+
try {
|
|
170
|
+
const parsed = JSON.parse(content);
|
|
171
|
+
if (Array.isArray(parsed?.blocks)) {
|
|
172
|
+
blocks = parsed.blocks as EditorJsBlock[];
|
|
173
|
+
}
|
|
174
|
+
} catch (_) {}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (!blocks.length) {
|
|
178
|
+
return (
|
|
179
|
+
<div className="w-full p-6">
|
|
180
|
+
<h2 className="text-xl font-semibold">Call for Availability</h2>
|
|
181
|
+
</div>
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Merge table fragments before rendering
|
|
186
|
+
const mergedBlocks = mergeTableBlocks(blocks);
|
|
187
|
+
|
|
188
|
+
const renderBlock = (block: EditorJsBlock) => {
|
|
189
|
+
const { type, data } = block;
|
|
190
|
+
const html = processAnchorsForNewTab(decodeHtmlEntities(data?.text || ""));
|
|
191
|
+
|
|
192
|
+
switch (type) {
|
|
193
|
+
case "table":
|
|
194
|
+
return (
|
|
195
|
+
<div
|
|
196
|
+
key={block.id}
|
|
197
|
+
className="table-wrapper my-6"
|
|
198
|
+
dangerouslySetInnerHTML={{ __html: data?.text || "" }}
|
|
199
|
+
/>
|
|
200
|
+
);
|
|
201
|
+
case "header": {
|
|
202
|
+
const level = Math.min(Math.max(Number(data?.level) || 3, 1), 6);
|
|
203
|
+
return React.createElement(`h${level}`, {
|
|
204
|
+
key: block.id,
|
|
205
|
+
className: "text-2xl font-semibold leading-8 tracking-[-0.06px] my-6",
|
|
206
|
+
dangerouslySetInnerHTML: { __html: html },
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
case "list": {
|
|
210
|
+
const style = data?.style || "unordered";
|
|
211
|
+
const items = Array.isArray(data?.items) ? data.items : [];
|
|
212
|
+
const ListTag = style === "ordered" ? "ol" : "ul";
|
|
213
|
+
const listClassName =
|
|
214
|
+
style === "ordered"
|
|
215
|
+
? "list-decimal list-inside my-4 space-y-2 text-lg leading-7 tracking-[-0.045px]"
|
|
216
|
+
: "list-disc list-inside my-4 space-y-2 text-lg leading-7 tracking-[-0.045px]";
|
|
217
|
+
|
|
218
|
+
return (
|
|
219
|
+
<ListTag key={block.id} className={listClassName}>
|
|
220
|
+
{items.map((item, idx) => (
|
|
221
|
+
<li
|
|
222
|
+
key={`${block.id}-${idx}`}
|
|
223
|
+
dangerouslySetInnerHTML={{ __html: processAnchorsForNewTab(decodeHtmlEntities(item)) }}
|
|
224
|
+
/>
|
|
225
|
+
))}
|
|
226
|
+
</ListTag>
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
case "quote": {
|
|
230
|
+
const alignment = data?.alignment || "left";
|
|
231
|
+
const alignmentClass = alignment === "center" ? "text-center" : alignment === "right" ? "text-right" : "text-left";
|
|
232
|
+
return (
|
|
233
|
+
<blockquote
|
|
234
|
+
key={block.id}
|
|
235
|
+
className={`my-6 pl-4 border-l-4 border-[var(--color-primary-600)] italic text-lg leading-7 text-[var(--color-secondary-700)] ${alignmentClass}`}
|
|
236
|
+
>
|
|
237
|
+
<p dangerouslySetInnerHTML={{ __html: html }} />
|
|
238
|
+
{data?.caption && data.caption !== data.text && (
|
|
239
|
+
<cite
|
|
240
|
+
className="block mt-2 text-sm not-italic text-[var(--color-secondary-500)]"
|
|
241
|
+
dangerouslySetInnerHTML={{ __html: processAnchorsForNewTab(decodeHtmlEntities(data.caption)) }}
|
|
242
|
+
/>
|
|
243
|
+
)}
|
|
244
|
+
</blockquote>
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
case "paragraph":
|
|
248
|
+
default:
|
|
249
|
+
// Check if this paragraph contains table HTML that wasn't merged
|
|
250
|
+
if (isTableFragment(html)) {
|
|
251
|
+
return (
|
|
252
|
+
<div
|
|
253
|
+
key={block.id}
|
|
254
|
+
className="table-wrapper my-6"
|
|
255
|
+
dangerouslySetInnerHTML={{ __html: html }}
|
|
256
|
+
/>
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
return (
|
|
260
|
+
<p
|
|
261
|
+
key={block.id}
|
|
262
|
+
className="text-lg leading-7 tracking-[-0.045px] my-4"
|
|
263
|
+
dangerouslySetInnerHTML={{ __html: html }}
|
|
264
|
+
/>
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
return (
|
|
270
|
+
<>
|
|
271
|
+
<style
|
|
272
|
+
dangerouslySetInnerHTML={{
|
|
273
|
+
__html: `
|
|
274
|
+
.editor-content a {
|
|
275
|
+
color: var(--color-primary-600);
|
|
276
|
+
text-decoration: underline;
|
|
277
|
+
transition: opacity 0.2s;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
.editor-content strong,
|
|
281
|
+
.editor-content b {
|
|
282
|
+
font-weight: 600;
|
|
283
|
+
}
|
|
284
|
+
.editor-content em,
|
|
285
|
+
.editor-content i {
|
|
286
|
+
font-style: italic;
|
|
287
|
+
}
|
|
288
|
+
.editor-content blockquote {
|
|
289
|
+
background-color: var(--color-secondary-50);
|
|
290
|
+
padding: 1rem 1.5rem;
|
|
291
|
+
border-radius: 0 0.5rem 0.5rem 0;
|
|
292
|
+
}
|
|
293
|
+
.editor-content img {
|
|
294
|
+
max-width: 100%;
|
|
295
|
+
width: 100%;
|
|
296
|
+
height: auto;
|
|
297
|
+
display: block;
|
|
298
|
+
margin: 1.5rem 0;
|
|
299
|
+
border-radius: 0.5rem;
|
|
300
|
+
}
|
|
301
|
+
.editor-content table {
|
|
302
|
+
width: 100% !important;
|
|
303
|
+
max-width: 100% !important;
|
|
304
|
+
border-collapse: collapse;
|
|
305
|
+
margin: 2rem 0;
|
|
306
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
307
|
+
display: table;
|
|
308
|
+
table-layout: auto !important;
|
|
309
|
+
}
|
|
310
|
+
.editor-content .table-wrapper {
|
|
311
|
+
width: 100%;
|
|
312
|
+
overflow-x: auto;
|
|
313
|
+
}
|
|
314
|
+
.editor-content table col {
|
|
315
|
+
width: auto !important;
|
|
316
|
+
}
|
|
317
|
+
.editor-content table colgroup {
|
|
318
|
+
display: table-column-group;
|
|
319
|
+
}
|
|
320
|
+
.editor-content table thead {
|
|
321
|
+
background-color: var(--color-primary-600);
|
|
322
|
+
color: white;
|
|
323
|
+
}
|
|
324
|
+
.editor-content table th,
|
|
325
|
+
.editor-content table td {
|
|
326
|
+
padding: 1rem 1.5rem;
|
|
327
|
+
text-align: left;
|
|
328
|
+
border: 1px solid var(--color-secondary-300);
|
|
329
|
+
line-height: 1.6;
|
|
330
|
+
}
|
|
331
|
+
.editor-content table th {
|
|
332
|
+
font-weight: 600;
|
|
333
|
+
font-size: 1rem;
|
|
334
|
+
}
|
|
335
|
+
.editor-content table tbody tr:nth-child(even) {
|
|
336
|
+
background-color: var(--color-secondary-50);
|
|
337
|
+
}
|
|
338
|
+
.editor-content .table-header {
|
|
339
|
+
font-weight: 600;
|
|
340
|
+
font-size: 1.1rem;
|
|
341
|
+
padding: 1.25rem 1.5rem;
|
|
342
|
+
}
|
|
343
|
+
.editor-content .table-data {
|
|
344
|
+
font-size: 0.95rem;
|
|
345
|
+
padding: 0.875rem 1.25rem;
|
|
346
|
+
}
|
|
347
|
+
.ezo-table {
|
|
348
|
+
width: 100%;
|
|
349
|
+
border-collapse: collapse;
|
|
350
|
+
margin: 2rem 0;
|
|
351
|
+
}
|
|
352
|
+
@media (max-width: 768px) {
|
|
353
|
+
.editor-content table {
|
|
354
|
+
font-size: 0.875rem;
|
|
355
|
+
}
|
|
356
|
+
.editor-content table th,
|
|
357
|
+
.editor-content table td {
|
|
358
|
+
padding: 0.75rem 1rem;
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
`,
|
|
362
|
+
}}
|
|
363
|
+
/>
|
|
364
|
+
<div className="editor-content overflow-x-auto">
|
|
365
|
+
{mergedBlocks.map(renderBlock)}
|
|
366
|
+
</div>
|
|
367
|
+
</>
|
|
368
|
+
);
|
|
369
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Metadata } from "next"
|
|
2
|
+
import { getStoreName } from "@/app/utils/branding"
|
|
3
|
+
|
|
4
|
+
export async function generateMetadata(): Promise<Metadata> {
|
|
5
|
+
return {
|
|
6
|
+
title: `Blog Post - ${getStoreName()}`,
|
|
7
|
+
description: `Read this article with expert insights, tips, and advice from ${getStoreName()}'s team.`,
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export default function BlogPostLayout({
|
|
12
|
+
children,
|
|
13
|
+
}: {
|
|
14
|
+
children: React.ReactNode
|
|
15
|
+
}) {
|
|
16
|
+
return children
|
|
17
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
import type { Metadata } from "next"
|
|
3
|
+
import Link from "next/link"
|
|
4
|
+
import Breadcrumb from "@/app/components/reuseableUI/breadcrumb"
|
|
5
|
+
import Heading from "@/app/components/reuseableUI/heading"
|
|
6
|
+
import EditorRenderer from "@/app/components/richText/EditorRenderer"
|
|
7
|
+
import { fetchBlogBySlug } from "@/graphql/queries/getBlogs"
|
|
8
|
+
import { getStoreName } from "@/app/utils/branding"
|
|
9
|
+
import {
|
|
10
|
+
generateBlogPostingSchema,
|
|
11
|
+
generateBreadcrumbSchema,
|
|
12
|
+
} from "@/lib/schema"
|
|
13
|
+
import { notFound } from "next/navigation"
|
|
14
|
+
import { extractTextFromEditorJs } from "@/lib/seo/extractTextFromEditorJs"
|
|
15
|
+
|
|
16
|
+
function truncate(input: string, maxLen: number): string {
|
|
17
|
+
const v = input.trim()
|
|
18
|
+
if (v.length <= maxLen) return v
|
|
19
|
+
return v.slice(0, Math.max(0, maxLen - 1)).trimEnd() + "…"
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export async function generateMetadata({
|
|
23
|
+
params,
|
|
24
|
+
}: {
|
|
25
|
+
params: Promise<{ slug: string }>
|
|
26
|
+
}): Promise<Metadata> {
|
|
27
|
+
const { slug } = await params
|
|
28
|
+
const post = await fetchBlogBySlug(slug)
|
|
29
|
+
|
|
30
|
+
if (!post || !post.title) {
|
|
31
|
+
return {
|
|
32
|
+
title: `Blog Post Not Found - ${getStoreName()}`,
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const desc =
|
|
37
|
+
truncate(extractTextFromEditorJs(post.content), 160) ||
|
|
38
|
+
`${post.title} - ${getStoreName()}`
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
title: `${post.title} - ${getStoreName()}`,
|
|
42
|
+
description: desc,
|
|
43
|
+
alternates: {
|
|
44
|
+
canonical: `/blog/${slug}`,
|
|
45
|
+
},
|
|
46
|
+
openGraph: {
|
|
47
|
+
title: `${post.title} - ${getStoreName()}`,
|
|
48
|
+
description: desc,
|
|
49
|
+
type: "article",
|
|
50
|
+
url: `/blog/${slug}`,
|
|
51
|
+
images: [{ url: "/Logo.png" }],
|
|
52
|
+
},
|
|
53
|
+
twitter: {
|
|
54
|
+
card: "summary_large_image",
|
|
55
|
+
title: `${post.title} - ${getStoreName()}`,
|
|
56
|
+
description: desc,
|
|
57
|
+
images: ["/Logo.png"],
|
|
58
|
+
},
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export default async function BlogPostPage({
|
|
63
|
+
params,
|
|
64
|
+
}: {
|
|
65
|
+
params: Promise<{ slug: string }>
|
|
66
|
+
}) {
|
|
67
|
+
const { slug } = await params
|
|
68
|
+
const post = await fetchBlogBySlug(slug)
|
|
69
|
+
|
|
70
|
+
if (!post || !post.title) {
|
|
71
|
+
notFound()
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Format date if available
|
|
75
|
+
const formattedDate = post.created
|
|
76
|
+
? new Date(post.created).toLocaleDateString("en-US", {
|
|
77
|
+
year: "numeric",
|
|
78
|
+
month: "long",
|
|
79
|
+
day: "numeric",
|
|
80
|
+
})
|
|
81
|
+
: ""
|
|
82
|
+
|
|
83
|
+
// Generate schema.org structured data
|
|
84
|
+
const desc =
|
|
85
|
+
truncate(extractTextFromEditorJs(post.content), 200) ||
|
|
86
|
+
`${post.title} - ${getStoreName()}`
|
|
87
|
+
|
|
88
|
+
const blogSchema = generateBlogPostingSchema(
|
|
89
|
+
post.title,
|
|
90
|
+
desc,
|
|
91
|
+
`/blog/${slug}`,
|
|
92
|
+
post.created || new Date().toISOString(),
|
|
93
|
+
post.created || new Date().toISOString()
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
const breadcrumbSchema = generateBreadcrumbSchema([
|
|
97
|
+
{ name: "Home", url: "/" },
|
|
98
|
+
{ name: "Blog", url: "/blog" },
|
|
99
|
+
{ name: post.title, url: `/blog/${slug}` },
|
|
100
|
+
]);
|
|
101
|
+
|
|
102
|
+
return (
|
|
103
|
+
<main className="h-full w-full">
|
|
104
|
+
{/* Schema.org structured data */}
|
|
105
|
+
<script
|
|
106
|
+
type="application/ld+json"
|
|
107
|
+
dangerouslySetInnerHTML={{ __html: JSON.stringify(blogSchema) }}
|
|
108
|
+
/>
|
|
109
|
+
<script
|
|
110
|
+
type="application/ld+json"
|
|
111
|
+
dangerouslySetInnerHTML={{ __html: JSON.stringify(breadcrumbSchema) }}
|
|
112
|
+
/>
|
|
113
|
+
<div className="container mx-auto max-w-[1276px]">
|
|
114
|
+
<div className="flex flex-col items-start w-full gap-8 px-4 md:px-6 py-12 md:py-16 lg:py-24">
|
|
115
|
+
<div className="flex flex-col items-start gap-5 w-full">
|
|
116
|
+
<Breadcrumb
|
|
117
|
+
items={[
|
|
118
|
+
{ text: "Home", link: "/" },
|
|
119
|
+
{ text: "BLOGS", link: "/blog" },
|
|
120
|
+
{ text: post.title, link: `/blog/${post.slug}` },
|
|
121
|
+
]}
|
|
122
|
+
/>
|
|
123
|
+
<Heading as="h1" content={post.title} />
|
|
124
|
+
{formattedDate && (
|
|
125
|
+
<p className="text-sm text-[var(--color-secondary-500)]">
|
|
126
|
+
Published on {formattedDate}
|
|
127
|
+
</p>
|
|
128
|
+
)}
|
|
129
|
+
</div>
|
|
130
|
+
|
|
131
|
+
<div className="w-full flex flex-col items-start gap-6">
|
|
132
|
+
{/* Blog Content */}
|
|
133
|
+
<div className="w-full">
|
|
134
|
+
<EditorRenderer content={post.content} />
|
|
135
|
+
</div>
|
|
136
|
+
|
|
137
|
+
{/* Back to Blog Link */}
|
|
138
|
+
<div className="w-full pt-8 border-t border-[var(--color-secondary-200)]">
|
|
139
|
+
<Link
|
|
140
|
+
href="/blog"
|
|
141
|
+
className="text-[var(--color-primary-600)] hover:underline inline-flex items-center gap-2"
|
|
142
|
+
>
|
|
143
|
+
← Back to all blogs
|
|
144
|
+
</Link>
|
|
145
|
+
</div>
|
|
146
|
+
</div>
|
|
147
|
+
</div>
|
|
148
|
+
</div>
|
|
149
|
+
</main>
|
|
150
|
+
)
|
|
151
|
+
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
export const blogPosts = [
|
|
2
|
+
{
|
|
3
|
+
id: 1,
|
|
4
|
+
title: "The Future of Performance Exhaust Systems",
|
|
5
|
+
excerpt:
|
|
6
|
+
"Exploring the latest innovations in exhaust technology and what it means for car enthusiasts.",
|
|
7
|
+
date: "August 15, 2025",
|
|
8
|
+
readTime: "5 min read",
|
|
9
|
+
image: "/no-image-avail-large.png",
|
|
10
|
+
slug: "future-of-performance-exhaust-systems",
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
id: 2,
|
|
14
|
+
title: "Top 5 Exhaust Upgrades for Your Vehicle",
|
|
15
|
+
excerpt:
|
|
16
|
+
"Discover the best exhaust upgrades to enhance your vehicle's performance and sound.",
|
|
17
|
+
date: "July 28, 2025",
|
|
18
|
+
readTime: "4 min read",
|
|
19
|
+
image: "/no-image-avail-large.png",
|
|
20
|
+
slug: "top-5-exhaust-upgrades",
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
id: 3,
|
|
24
|
+
title: "Maintaining Your Exhaust System: A Complete Guide",
|
|
25
|
+
excerpt:
|
|
26
|
+
"Learn essential maintenance tips to keep your exhaust system in top condition.",
|
|
27
|
+
date: "July 10, 2025",
|
|
28
|
+
readTime: "6 min read",
|
|
29
|
+
image: "/no-image-avail-large.png",
|
|
30
|
+
slug: "exhaust-system-maintenance-guide",
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
id: 4,
|
|
34
|
+
title: "The Future of Performance Exhaust Systems",
|
|
35
|
+
excerpt:
|
|
36
|
+
"Exploring the latest innovations in exhaust technology and what it means for car enthusiasts.",
|
|
37
|
+
date: "August 15, 2025",
|
|
38
|
+
readTime: "5 min read",
|
|
39
|
+
image: "/no-image-avail-large.png",
|
|
40
|
+
slug: "future-of-performance-exhaust-systems",
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
id: 5,
|
|
44
|
+
title: "Top 5 Exhaust Upgrades for Your Vehicle",
|
|
45
|
+
excerpt:
|
|
46
|
+
"Discover the best exhaust upgrades to enhance your vehicle's performance and sound.",
|
|
47
|
+
date: "July 28, 2025",
|
|
48
|
+
readTime: "4 min read",
|
|
49
|
+
image: "/no-image-avail-large.png",
|
|
50
|
+
slug: "top-5-exhaust-upgrades",
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
id: 6,
|
|
54
|
+
title: "Maintaining Your Exhaust System: A Complete Guide",
|
|
55
|
+
excerpt:
|
|
56
|
+
"Learn essential maintenance tips to keep your exhaust system in top condition.",
|
|
57
|
+
date: "July 10, 2025",
|
|
58
|
+
readTime: "6 min read",
|
|
59
|
+
image: "/no-image-avail-large.png",
|
|
60
|
+
slug: "exhaust-system-maintenance-guide",
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
id: 7,
|
|
64
|
+
title: "The Future of Performance Exhaust Systems",
|
|
65
|
+
excerpt:
|
|
66
|
+
"Exploring the latest innovations in exhaust technology and what it means for car enthusiasts.",
|
|
67
|
+
date: "August 15, 2025",
|
|
68
|
+
readTime: "5 min read",
|
|
69
|
+
image: "/no-image-avail-large.png",
|
|
70
|
+
slug: "future-of-performance-exhaust-systems",
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
id: 8,
|
|
74
|
+
title: "Top 5 Exhaust Upgrades for Your Vehicle",
|
|
75
|
+
excerpt:
|
|
76
|
+
"Discover the best exhaust upgrades to enhance your vehicle's performance and sound.",
|
|
77
|
+
date: "July 28, 2025",
|
|
78
|
+
readTime: "4 min read",
|
|
79
|
+
image: "/no-image-avail-large.png",
|
|
80
|
+
slug: "top-5-exhaust-upgrades",
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
id: 9,
|
|
84
|
+
title: "Maintaining Your Exhaust System: A Complete Guide",
|
|
85
|
+
excerpt:
|
|
86
|
+
"Learn essential maintenance tips to keep your exhaust system in top condition.",
|
|
87
|
+
date: "July 10, 2025",
|
|
88
|
+
readTime: "6 min read",
|
|
89
|
+
image: "/no-image-avail-large.png",
|
|
90
|
+
slug: "exhaust-system-maintenance-guide",
|
|
91
|
+
},
|
|
92
|
+
];
|
|
93
|
+
|
|
94
|
+
export const BlogPostContent = {
|
|
95
|
+
title: "COMPONENTS OF THE BULKHEAD FITTING KIT",
|
|
96
|
+
description:
|
|
97
|
+
"This kit includes various parts that ensure a secure, leak-proof connection between the air supply line and the ARB differential. Here's a comprehensive overview of each component, along with their part numbers and roles in the assembly.",
|
|
98
|
+
subtitle:"6mm Banjo Bolt",
|
|
99
|
+
desc: "This kit comprises multiple components designed to ensure a secure and leak-free connection between the air supply line and the ARB differential. Below is a thorough overview of each part, their part numbers, and their functions in the assembly.",
|
|
100
|
+
components: [
|
|
101
|
+
{
|
|
102
|
+
id: 1,
|
|
103
|
+
title: "6MM BANJO BOLT",
|
|
104
|
+
description:
|
|
105
|
+
"The banjo bolt (Item #01) fastens the banjo elbow and washer to the differential body. This bolt is crucial for maintaining pressure and airflow to the differential.",
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
id: 2,
|
|
109
|
+
title: "6MM BANJO BOLT",
|
|
110
|
+
description:
|
|
111
|
+
"The banjo bolt (Item #01) secures the banjo elbow and washer to the differential body. This bolt is vital for ensuring proper pressure and airflow to the differential.",
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
id: 3,
|
|
115
|
+
title: "6MM BANJO BOLT",
|
|
116
|
+
description:
|
|
117
|
+
"The banjo bolt (Item #01) secures the banjo elbow and washer to the differential body. This bolt is essential for maintaining pressure and airflow to the differential.",
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
id: 4,
|
|
121
|
+
title: "6MM BANJO BOLT",
|
|
122
|
+
description:
|
|
123
|
+
"The banjo bolt (Item #01) fastens the banjo elbow and washer to the differential body. This bolt is key to ensuring proper pressure and airflow to the differential.",
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
id: 5,
|
|
127
|
+
title: "6MM BANJO BOLT",
|
|
128
|
+
description:
|
|
129
|
+
"The banjo bolt (Item #01) secures the banjo elbow and washer to the differential body. This bolt plays a vital role in maintaining pressure and airflow to the differential.",
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
id: 6,
|
|
133
|
+
title: "6MM BANJO BOLT",
|
|
134
|
+
description:
|
|
135
|
+
"The banjo bolt (Item #01) fastens the banjo elbow and washer to the differential body. This bolt is essential for ensuring pressure and airflow to the differential.",
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
id: 7,
|
|
139
|
+
title: "6MM BANJO BOLT",
|
|
140
|
+
specs: [
|
|
141
|
+
"External Tube Diameter: 6mm",
|
|
142
|
+
"Internal Tube Diameter: 3mm",
|
|
143
|
+
"Bulkhead Thread: 1/4\" NPT",
|
|
144
|
+
],
|
|
145
|
+
},
|
|
146
|
+
],
|
|
147
|
+
};
|