@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.
Files changed (441) hide show
  1. package/.env.example +57 -0
  2. package/APPLE_PAY_QUICK_START.md +165 -0
  3. package/APPLE_PAY_SETUP.md +331 -0
  4. package/README.md +46 -0
  5. package/SEO_AUDIT_CHECKLIST_STATUS.md +244 -0
  6. package/SEO_AUDIT_REPORT.md +66 -0
  7. package/eslint.config.mjs +16 -0
  8. package/next-env.d.ts +5 -0
  9. package/next.config.ts +109 -0
  10. package/package.json +47 -0
  11. package/postcss.config.mjs +5 -0
  12. package/public/.well-known/apple-developer-merchantid-domain-association +1 -0
  13. package/public/Logo.png +0 -0
  14. package/public/brand-video.mp4 +0 -0
  15. package/public/favicon.ico +0 -0
  16. package/public/file.svg +1 -0
  17. package/public/footer/facebook.tsx +34 -0
  18. package/public/footer/instagram.tsx +27 -0
  19. package/public/footer/mail.tsx +5 -0
  20. package/public/footer/x.tsx +35 -0
  21. package/public/globe.svg +1 -0
  22. package/public/icons/Authorize.net.webp +0 -0
  23. package/public/icons/amex.gif +0 -0
  24. package/public/icons/appIcon.png +0 -0
  25. package/public/icons/discover.gif +0 -0
  26. package/public/icons/master.gif +0 -0
  27. package/public/icons/paypal.png +0 -0
  28. package/public/icons/stripe.png +0 -0
  29. package/public/icons/visa.gif +0 -0
  30. package/public/images/BackgroundNoise.png +0 -0
  31. package/public/images/footer-background.png +0 -0
  32. package/public/next.svg +1 -0
  33. package/public/no-image-avail-large.png +0 -0
  34. package/public/random-car-1.jpeg +0 -0
  35. package/public/random-car-2.png +0 -0
  36. package/public/random-car-3.jpg +0 -0
  37. package/public/random-car-4.jpg +0 -0
  38. package/public/random-car-5.jpg +0 -0
  39. package/public/star.svg +3 -0
  40. package/public/vercel.svg +1 -0
  41. package/public/window.svg +1 -0
  42. package/scripts/seo-audit/generate-checklist.mjs +156 -0
  43. package/src/app/(auth)/account/forgot-password/layout.tsx +16 -0
  44. package/src/app/(auth)/account/forgot-password/page.tsx +135 -0
  45. package/src/app/(auth)/account/login/layout.tsx +16 -0
  46. package/src/app/(auth)/account/login/page.tsx +288 -0
  47. package/src/app/(auth)/account/otp/layout.tsx +16 -0
  48. package/src/app/(auth)/account/otp/page.tsx +108 -0
  49. package/src/app/(auth)/account/register/layout.tsx +16 -0
  50. package/src/app/(auth)/account/register/page.tsx +431 -0
  51. package/src/app/(auth)/account/reset-password/layout.tsx +16 -0
  52. package/src/app/(auth)/account/reset-password/page.tsx +222 -0
  53. package/src/app/[slug]/page.tsx +43 -0
  54. package/src/app/about/loading.tsx +17 -0
  55. package/src/app/about/page.tsx +61 -0
  56. package/src/app/account/address/layout.tsx +15 -0
  57. package/src/app/account/address/page.tsx +166 -0
  58. package/src/app/account/head.tsx +4 -0
  59. package/src/app/account/layout.tsx +62 -0
  60. package/src/app/account/orders/[id]/layout.tsx +17 -0
  61. package/src/app/account/orders/[id]/page.tsx +115 -0
  62. package/src/app/account/orders/components/orderDetailsModal.tsx +410 -0
  63. package/src/app/account/orders/layout.tsx +15 -0
  64. package/src/app/account/orders/page.tsx +146 -0
  65. package/src/app/account/page.tsx +39 -0
  66. package/src/app/account/settings/components/editProfileSuccessModal.tsx +28 -0
  67. package/src/app/account/settings/layout.tsx +15 -0
  68. package/src/app/account/settings/page.tsx +260 -0
  69. package/src/app/api/affirm/check-status/route.ts +94 -0
  70. package/src/app/api/affirm/create-checkout/route.ts +109 -0
  71. package/src/app/api/affirm/get-config/route.ts +108 -0
  72. package/src/app/api/affirm/process-payment/route.ts +244 -0
  73. package/src/app/api/affirm/test-connection/route.ts +45 -0
  74. package/src/app/api/auth/clear/route.ts +16 -0
  75. package/src/app/api/auth/clear-cookies/route.ts +42 -0
  76. package/src/app/api/auth/set/route.ts +47 -0
  77. package/src/app/api/configuration/route.ts +18 -0
  78. package/src/app/api/dynamic-page/[slug]/route.ts +24 -0
  79. package/src/app/api/form-submission/route.ts +237 -0
  80. package/src/app/api/paypal/capture-order/route.ts +303 -0
  81. package/src/app/api/paypal/create-order/route.ts +211 -0
  82. package/src/app/api/paypal/get-config/route.ts +240 -0
  83. package/src/app/api/search-proxy/route.ts +52 -0
  84. package/src/app/authorize-net-success/layout.tsx +19 -0
  85. package/src/app/authorize-net-success/page.tsx +12 -0
  86. package/src/app/authorize-net-success/summary.tsx +486 -0
  87. package/src/app/blog/[slug]/blogContentRenderer.tsx +369 -0
  88. package/src/app/blog/[slug]/layout.tsx +17 -0
  89. package/src/app/blog/[slug]/page.tsx +151 -0
  90. package/src/app/blog/constant.tsx +147 -0
  91. package/src/app/blog/layout.tsx +31 -0
  92. package/src/app/blog/page.tsx +81 -0
  93. package/src/app/brand/[id]/BrandPageClient.tsx +188 -0
  94. package/src/app/brand/[id]/layout.tsx +17 -0
  95. package/src/app/brand/[id]/page.tsx +176 -0
  96. package/src/app/brands/components/brandsListingClient.tsx +97 -0
  97. package/src/app/brands/layout.tsx +31 -0
  98. package/src/app/brands/page.tsx +40 -0
  99. package/src/app/cancellation-policy/page.tsx +53 -0
  100. package/src/app/cart/layout.tsx +19 -0
  101. package/src/app/cart/page.tsx +752 -0
  102. package/src/app/category/[slug]/CategoryPageClient.tsx +377 -0
  103. package/src/app/category/[slug]/layout.tsx +17 -0
  104. package/src/app/category/[slug]/page.tsx +224 -0
  105. package/src/app/category/page.tsx +114 -0
  106. package/src/app/checkout/components/addNewAddressModal.tsx +474 -0
  107. package/src/app/checkout/layout.tsx +19 -0
  108. package/src/app/checkout/page.tsx +3312 -0
  109. package/src/app/components/account/AccountTabs.tsx +40 -0
  110. package/src/app/components/ads/GoogleAdSense.tsx +74 -0
  111. package/src/app/components/analytics/AnalyticsScripts.tsx +78 -0
  112. package/src/app/components/analytics/ConditionalGTMNoscript.tsx +24 -0
  113. package/src/app/components/analytics/ConditionalGoogleAnalytics.tsx +16 -0
  114. package/src/app/components/ancillary/AncillaryContent.tsx +7 -0
  115. package/src/app/components/auth/TokenExpirationHandler.tsx +8 -0
  116. package/src/app/components/blog/BlogList.tsx +112 -0
  117. package/src/app/components/checkout/AddressInformationSection.tsx +34 -0
  118. package/src/app/components/checkout/AddressManagement.tsx +571 -0
  119. package/src/app/components/checkout/CheckoutHeader.tsx +51 -0
  120. package/src/app/components/checkout/CheckoutQuestions.tsx +454 -0
  121. package/src/app/components/checkout/CheckoutTermsModal.tsx +81 -0
  122. package/src/app/components/checkout/ContactDetailsSection.tsx +52 -0
  123. package/src/app/components/checkout/DealerShippingSection.tsx +359 -0
  124. package/src/app/components/checkout/DeliveryMethodSection.tsx +249 -0
  125. package/src/app/components/checkout/OrderSummary.tsx +386 -0
  126. package/src/app/components/checkout/TermsContentRenderer.tsx +147 -0
  127. package/src/app/components/checkout/WillCallSection.tsx +133 -0
  128. package/src/app/components/checkout/affirmPayment.tsx +383 -0
  129. package/src/app/components/checkout/checkoutProcessingModal.tsx +96 -0
  130. package/src/app/components/checkout/googlePayButton.tsx +334 -0
  131. package/src/app/components/checkout/paymentStep.tsx +180 -0
  132. package/src/app/components/checkout/paypalPayment.tsx +1083 -0
  133. package/src/app/components/checkout/saleorNativePayment.tsx +1758 -0
  134. package/src/app/components/dynamicPage/DynamicPageRenderer.tsx +13 -0
  135. package/src/app/components/dynamicPage/HtmlWidgetRenderer.tsx +144 -0
  136. package/src/app/components/filtersCollapsible/index.tsx +365 -0
  137. package/src/app/components/globalSearch/index.tsx +423 -0
  138. package/src/app/components/layout/cartDropDown.tsx +628 -0
  139. package/src/app/components/layout/components/FooterNewsletter.tsx +21 -0
  140. package/src/app/components/layout/footer.tsx +283 -0
  141. package/src/app/components/layout/header/accountMenuDropdown.tsx +53 -0
  142. package/src/app/components/layout/header/components/CartBadge.tsx +18 -0
  143. package/src/app/components/layout/header/components/LoadingState.tsx +17 -0
  144. package/src/app/components/layout/header/components/MenuItemDropdown.tsx +124 -0
  145. package/src/app/components/layout/header/components/MobileNavbar.tsx +123 -0
  146. package/src/app/components/layout/header/components/NavbarActions.tsx +125 -0
  147. package/src/app/components/layout/header/components/NavbarBrand.tsx +29 -0
  148. package/src/app/components/layout/header/components/NavigationLinks.tsx +131 -0
  149. package/src/app/components/layout/header/hamMenuSlide.tsx +318 -0
  150. package/src/app/components/layout/header/header.tsx +44 -0
  151. package/src/app/components/layout/header/hooks/useDropdown.ts +45 -0
  152. package/src/app/components/layout/header/hooks/useNavbarData.ts +138 -0
  153. package/src/app/components/layout/header/hooks/useNavbarState.ts +66 -0
  154. package/src/app/components/layout/header/megaMenuDropdown.tsx +116 -0
  155. package/src/app/components/layout/header/navBar.tsx +121 -0
  156. package/src/app/components/layout/header/search.tsx +418 -0
  157. package/src/app/components/layout/header/styles/navbarStyles.ts +27 -0
  158. package/src/app/components/layout/header/topBar.tsx +214 -0
  159. package/src/app/components/layout/joinNewsletterForm/index.tsx +72 -0
  160. package/src/app/components/layout/mobileAccordian/index.tsx +92 -0
  161. package/src/app/components/layout/paymentMethods.tsx +75 -0
  162. package/src/app/components/layout/rootLayout.tsx +23 -0
  163. package/src/app/components/layout/siteInfo.tsx +103 -0
  164. package/src/app/components/layout/socialLinks.tsx +65 -0
  165. package/src/app/components/newsletterSection/emailListSection.tsx +224 -0
  166. package/src/app/components/newsletterSection/emailSectionServer.tsx +8 -0
  167. package/src/app/components/providers/ApolloWrapper.tsx +12 -0
  168. package/src/app/components/providers/AppConfigurationProvider.tsx +108 -0
  169. package/src/app/components/providers/GoogleAnalyticsProvider.tsx +149 -0
  170. package/src/app/components/providers/GoogleTagManagerProvider.tsx +31 -0
  171. package/src/app/components/providers/RecaptchaProvider.tsx +18 -0
  172. package/src/app/components/providers/ServerAppConfigurationProvider.tsx +133 -0
  173. package/src/app/components/providers/YMMStatusProvider.tsx +15 -0
  174. package/src/app/components/reuseableUI/AboutUs.tsx +115 -0
  175. package/src/app/components/reuseableUI/AddToCartClient.tsx +125 -0
  176. package/src/app/components/reuseableUI/EditorJsRenderer.tsx +219 -0
  177. package/src/app/components/reuseableUI/HeroSectionsearchByVehicle.tsx +188 -0
  178. package/src/app/components/reuseableUI/ImageWithFallback.tsx +41 -0
  179. package/src/app/components/reuseableUI/Toast.tsx +101 -0
  180. package/src/app/components/reuseableUI/blogCard.tsx +52 -0
  181. package/src/app/components/reuseableUI/brandCard.tsx +68 -0
  182. package/src/app/components/reuseableUI/breadcrumb.tsx +38 -0
  183. package/src/app/components/reuseableUI/categoryCard.tsx +37 -0
  184. package/src/app/components/reuseableUI/categorySkeleton.tsx +31 -0
  185. package/src/app/components/reuseableUI/commonButton.tsx +48 -0
  186. package/src/app/components/reuseableUI/defaultInputField/index.tsx +84 -0
  187. package/src/app/components/reuseableUI/emptyState.tsx +29 -0
  188. package/src/app/components/reuseableUI/errorTag.tsx +15 -0
  189. package/src/app/components/reuseableUI/heading/index.tsx +20 -0
  190. package/src/app/components/reuseableUI/input.tsx +117 -0
  191. package/src/app/components/reuseableUI/listCard.tsx +137 -0
  192. package/src/app/components/reuseableUI/loadingUI.tsx +12 -0
  193. package/src/app/components/reuseableUI/modalLayout.tsx +76 -0
  194. package/src/app/components/reuseableUI/newsletter/newsletterClient.tsx +622 -0
  195. package/src/app/components/reuseableUI/newsletter/newslettersHomeModal.tsx +68 -0
  196. package/src/app/components/reuseableUI/offerCard.tsx +42 -0
  197. package/src/app/components/reuseableUI/passwordRules/passwordRules.tsx +56 -0
  198. package/src/app/components/reuseableUI/primaryButton/index.tsx +34 -0
  199. package/src/app/components/reuseableUI/productCard.tsx +118 -0
  200. package/src/app/components/reuseableUI/productSkeleton.tsx +34 -0
  201. package/src/app/components/reuseableUI/searchByVehicle.tsx +187 -0
  202. package/src/app/components/reuseableUI/secondaryButton/index.tsx +34 -0
  203. package/src/app/components/reuseableUI/section.tsx +20 -0
  204. package/src/app/components/reuseableUI/select/index.tsx +98 -0
  205. package/src/app/components/reuseableUI/skeletonLoader.tsx +117 -0
  206. package/src/app/components/reuseableUI/statusTag.tsx +24 -0
  207. package/src/app/components/reuseableUI/tags/saleTag.tsx +19 -0
  208. package/src/app/components/reuseableUI/testimonialCard.tsx +93 -0
  209. package/src/app/components/richText/EditorRenderer.tsx +318 -0
  210. package/src/app/components/search/HierarchicalCategoryFilter.tsx +155 -0
  211. package/src/app/components/search/SearchFilters.tsx +155 -0
  212. package/src/app/components/search/YMMSearchSidebar.tsx +187 -0
  213. package/src/app/components/seo/ServerProductCard.tsx +91 -0
  214. package/src/app/components/seo/ServerProductGrid.tsx +45 -0
  215. package/src/app/components/shop/CategoryFilter.tsx +184 -0
  216. package/src/app/components/shop/ItemsPerPageSelect.tsx +69 -0
  217. package/src/app/components/shop/ItemsPerPageSelectClient.tsx +58 -0
  218. package/src/app/components/shop/MobileFilters.tsx +103 -0
  219. package/src/app/components/shop/ProductGridSkeleton.tsx +16 -0
  220. package/src/app/components/shop/ProductsGrid.tsx +230 -0
  221. package/src/app/components/shop/SearchFilter.tsx +218 -0
  222. package/src/app/components/shop/SearchFilterClient.tsx +122 -0
  223. package/src/app/components/shop/SearchLoadingOverlay.tsx +32 -0
  224. package/src/app/components/shop/ShopMobileFilters.tsx +205 -0
  225. package/src/app/components/showroom/VehicleSearchDropdowns.tsx +187 -0
  226. package/src/app/components/showroom/brandsSwiper.tsx +49 -0
  227. package/src/app/components/showroom/brandsSwiperClient copy.tsx +93 -0
  228. package/src/app/components/showroom/brandsSwiperClient.tsx +122 -0
  229. package/src/app/components/showroom/brandsSwiperServer.tsx +42 -0
  230. package/src/app/components/showroom/bundleProducts.tsx +120 -0
  231. package/src/app/components/showroom/categoryGrid.tsx +51 -0
  232. package/src/app/components/showroom/categoryGridServer.tsx +45 -0
  233. package/src/app/components/showroom/categorySwiper.tsx +115 -0
  234. package/src/app/components/showroom/featureStrip.tsx +139 -0
  235. package/src/app/components/showroom/offersSwiper.tsx +181 -0
  236. package/src/app/components/showroom/productGrid.tsx +56 -0
  237. package/src/app/components/showroom/productSwiper.tsx +119 -0
  238. package/src/app/components/showroom/promotion-slider.tsx +138 -0
  239. package/src/app/components/showroom/promotion.tsx +207 -0
  240. package/src/app/components/showroom/promotionsSwiper.tsx +174 -0
  241. package/src/app/components/showroom/showroomHeroCarousel.tsx +141 -0
  242. package/src/app/components/showroom/testimonialsGrid.tsx +106 -0
  243. package/src/app/components/skeletons/ContentSkeleton.tsx +14 -0
  244. package/src/app/components/sortDropdown/index.tsx +116 -0
  245. package/src/app/components/tertiaryButton/index.tsx +25 -0
  246. package/src/app/components/theme/theme-provider.tsx +82 -0
  247. package/src/app/contact/layout.tsx +32 -0
  248. package/src/app/contact/page.tsx +591 -0
  249. package/src/app/content/[slug]/layout.tsx +17 -0
  250. package/src/app/content/[slug]/page.tsx +159 -0
  251. package/src/app/content/layout.tsx +31 -0
  252. package/src/app/content/page.tsx +88 -0
  253. package/src/app/core-policies/page.tsx +55 -0
  254. package/src/app/discounts/page.tsx +54 -0
  255. package/src/app/frequently-asked-questions/page.tsx +57 -0
  256. package/src/app/globals.css +440 -0
  257. package/src/app/hooks/useDealerLocations.ts +259 -0
  258. package/src/app/hooks/useGTMEngagement.ts +71 -0
  259. package/src/app/hooks/useGoogleAnalytics.ts +145 -0
  260. package/src/app/layout.tsx +149 -0
  261. package/src/app/not-found.tsx +31 -0
  262. package/src/app/order-confirmation/layout.tsx +19 -0
  263. package/src/app/order-confirmation/page.tsx +12 -0
  264. package/src/app/order-confirmation/summary.tsx +1775 -0
  265. package/src/app/page.tsx +194 -0
  266. package/src/app/privacy-policy/loading.tsx +17 -0
  267. package/src/app/privacy-policy/page.tsx +56 -0
  268. package/src/app/product/[id]/ProductDetailClient.tsx +2448 -0
  269. package/src/app/product/[id]/components/itemInquiryModal.tsx +461 -0
  270. package/src/app/product/[id]/layout.tsx +116 -0
  271. package/src/app/product/[id]/page.tsx +200 -0
  272. package/src/app/product/layout.tsx +15 -0
  273. package/src/app/products/all/AllProductsClient.tsx +743 -0
  274. package/src/app/products/all/page.tsx +176 -0
  275. package/src/app/products/components/shopEmptyState.tsx +29 -0
  276. package/src/app/request-return/layout.tsx +36 -0
  277. package/src/app/request-return/page.tsx +597 -0
  278. package/src/app/robots.txt/route.ts +27 -0
  279. package/src/app/search/layout.tsx +16 -0
  280. package/src/app/search/page.tsx +736 -0
  281. package/src/app/shipping-returns/page.tsx +60 -0
  282. package/src/app/site-map/layout.tsx +33 -0
  283. package/src/app/site-map/page.tsx +113 -0
  284. package/src/app/sitemap-index.xml/route.ts +20 -0
  285. package/src/app/sitemap.ts +10 -0
  286. package/src/app/terms-and-conditions/loading.tsx +17 -0
  287. package/src/app/terms-and-conditions/page.tsx +56 -0
  288. package/src/app/utils/appConfiguration.ts +327 -0
  289. package/src/app/utils/branding.ts +52 -0
  290. package/src/app/utils/configurationService.ts +202 -0
  291. package/src/app/utils/constant.tsx +242 -0
  292. package/src/app/utils/editorJsUtils.tsx +249 -0
  293. package/src/app/utils/functions.ts +146 -0
  294. package/src/app/utils/googleAnalytics.ts +168 -0
  295. package/src/app/utils/googleTagManager.ts +475 -0
  296. package/src/app/utils/ipDetection.ts +270 -0
  297. package/src/app/utils/serverConfigurationService.ts +209 -0
  298. package/src/app/utils/svgs/GridIcon.tsx +45 -0
  299. package/src/app/utils/svgs/account/myAccount/listDotIcon.tsx +3 -0
  300. package/src/app/utils/svgs/account/myAccount/tickIcon.tsx +10 -0
  301. package/src/app/utils/svgs/account/orderHistory/InfoIcon.tsx +49 -0
  302. package/src/app/utils/svgs/arrowDownIcon.tsx +17 -0
  303. package/src/app/utils/svgs/arrowIcon.tsx +25 -0
  304. package/src/app/utils/svgs/arrowUpIcon.tsx +16 -0
  305. package/src/app/utils/svgs/brandsSearchIcon.tsx +25 -0
  306. package/src/app/utils/svgs/cart/cartIcon.tsx +31 -0
  307. package/src/app/utils/svgs/cart/plusIcon.tsx +13 -0
  308. package/src/app/utils/svgs/cart/subtractIcon.tsx +13 -0
  309. package/src/app/utils/svgs/cart/successTickIcon.tsx +14 -0
  310. package/src/app/utils/svgs/chevronDownIcon.tsx +21 -0
  311. package/src/app/utils/svgs/closeEyeIcon.tsx +47 -0
  312. package/src/app/utils/svgs/crossIcon.tsx +25 -0
  313. package/src/app/utils/svgs/eyeIcon.tsx +29 -0
  314. package/src/app/utils/svgs/featureTag.tsx +20 -0
  315. package/src/app/utils/svgs/filterIcon.tsx +3 -0
  316. package/src/app/utils/svgs/globleIcon.tsx +41 -0
  317. package/src/app/utils/svgs/infoIcon.tsx +34 -0
  318. package/src/app/utils/svgs/listIcon.tsx +50 -0
  319. package/src/app/utils/svgs/logOutIcon.tsx +35 -0
  320. package/src/app/utils/svgs/menuIcon.tsx +8 -0
  321. package/src/app/utils/svgs/minusIcon.tsx +18 -0
  322. package/src/app/utils/svgs/newsletterIcon.tsx +19 -0
  323. package/src/app/utils/svgs/noDataFoundIcon-.tsx +26 -0
  324. package/src/app/utils/svgs/noProductFoundIcon.tsx +43 -0
  325. package/src/app/utils/svgs/passwordIcons/errorIcon.tsx +31 -0
  326. package/src/app/utils/svgs/passwordIcons/successIcon.tsx +24 -0
  327. package/src/app/utils/svgs/paymentProcessingIcons/hourglassIcon.tsx +43 -0
  328. package/src/app/utils/svgs/paymentProcessingIcons/modalCrossIcon.tsx +23 -0
  329. package/src/app/utils/svgs/paymentProcessingIcons/paymentFailedIcon.tsx +47 -0
  330. package/src/app/utils/svgs/pencilIcon.tsx +11 -0
  331. package/src/app/utils/svgs/plusIcon.tsx +25 -0
  332. package/src/app/utils/svgs/productInquiryIcon.tsx +40 -0
  333. package/src/app/utils/svgs/searchIcon.tsx +31 -0
  334. package/src/app/utils/svgs/shoppingCart.tsx +32 -0
  335. package/src/app/utils/svgs/spinnerIcon.tsx +22 -0
  336. package/src/app/utils/svgs/spinnerLoadingIcon.tsx +26 -0
  337. package/src/app/utils/svgs/successTickIcon.tsx +40 -0
  338. package/src/app/utils/svgs/swiperArrowIconLeft.tsx +18 -0
  339. package/src/app/utils/svgs/swiperArrowIconRight.tsx +18 -0
  340. package/src/app/utils/svgs/userProfileIcon.tsx +31 -0
  341. package/src/app/utils/svgs/warningCircleIcon.tsx +15 -0
  342. package/src/app/warranty/constant.tsx +63 -0
  343. package/src/app/warranty/loading.tsx +17 -0
  344. package/src/app/warranty/page.tsx +56 -0
  345. package/src/graphql/client.ts +288 -0
  346. package/src/graphql/mutations/accountAddressCreate.ts +56 -0
  347. package/src/graphql/mutations/accountAddressDelete.ts +23 -0
  348. package/src/graphql/mutations/accountAddressUpdate.ts +55 -0
  349. package/src/graphql/mutations/accountSetDefaultAddress.ts +32 -0
  350. package/src/graphql/mutations/accountUpdate.ts +34 -0
  351. package/src/graphql/mutations/changePassword.ts +25 -0
  352. package/src/graphql/mutations/checkout.ts +117 -0
  353. package/src/graphql/mutations/checkoutAddVoucher.ts +63 -0
  354. package/src/graphql/mutations/checkoutComplete.ts +79 -0
  355. package/src/graphql/mutations/checkoutCreate.ts +131 -0
  356. package/src/graphql/mutations/checkoutCustomerAttach.ts +50 -0
  357. package/src/graphql/mutations/checkoutEmailUpdate.ts +15 -0
  358. package/src/graphql/mutations/checkoutLineMetadataUpdate.ts +52 -0
  359. package/src/graphql/mutations/checkoutPaymentCreate.ts +82 -0
  360. package/src/graphql/mutations/paymentGatewayInitialize.ts +58 -0
  361. package/src/graphql/mutations/registerAccount.ts +65 -0
  362. package/src/graphql/mutations/requestPasswordReset.ts +32 -0
  363. package/src/graphql/mutations/setPassword.ts +49 -0
  364. package/src/graphql/mutations/signIn.ts +50 -0
  365. package/src/graphql/mutations/tokenRefresh.ts +19 -0
  366. package/src/graphql/mutations/updateCheckoutMetadata.ts +49 -0
  367. package/src/graphql/mutations/updateProfile.ts +18 -0
  368. package/src/graphql/mutations/willCallDeliveryMethod.ts +81 -0
  369. package/src/graphql/queries/checkout.ts +168 -0
  370. package/src/graphql/queries/findProductByOldSlug.ts +58 -0
  371. package/src/graphql/queries/getAboutPage.ts +24 -0
  372. package/src/graphql/queries/getAboutPageId.ts +9 -0
  373. package/src/graphql/queries/getAboutUs.ts +38 -0
  374. package/src/graphql/queries/getAddressInformation.ts +38 -0
  375. package/src/graphql/queries/getAllCategories.ts +41 -0
  376. package/src/graphql/queries/getAllCategoriesTree.ts +67 -0
  377. package/src/graphql/queries/getAllCategoriesWithProducts.ts +29 -0
  378. package/src/graphql/queries/getAllCollectionsWithProducts.ts +16 -0
  379. package/src/graphql/queries/getBlogs.ts +222 -0
  380. package/src/graphql/queries/getBrands.ts +17 -0
  381. package/src/graphql/queries/getBundles.ts +43 -0
  382. package/src/graphql/queries/getCategories.ts +20 -0
  383. package/src/graphql/queries/getChannels.ts +77 -0
  384. package/src/graphql/queries/getCheckoutQuestions.ts +115 -0
  385. package/src/graphql/queries/getCheckoutTermsAndConditions.ts +37 -0
  386. package/src/graphql/queries/getContactPage.ts +117 -0
  387. package/src/graphql/queries/getContentPage.ts +191 -0
  388. package/src/graphql/queries/getDiscountOffers.ts +18 -0
  389. package/src/graphql/queries/getDynamicPageBySlug.ts +251 -0
  390. package/src/graphql/queries/getFeaturedProducts.ts +48 -0
  391. package/src/graphql/queries/getHeroMetadata.ts +23 -0
  392. package/src/graphql/queries/getMenuBySlug.ts +84 -0
  393. package/src/graphql/queries/getMyProfile.ts +23 -0
  394. package/src/graphql/queries/getNewsletter.ts +122 -0
  395. package/src/graphql/queries/getNewsletterPage.ts +111 -0
  396. package/src/graphql/queries/getPageBySlug.ts +52 -0
  397. package/src/graphql/queries/getPageTypeId.ts +27 -0
  398. package/src/graphql/queries/getPaymentMethods.ts +61 -0
  399. package/src/graphql/queries/getProducts.ts +78 -0
  400. package/src/graphql/queries/getPromotions.ts +24 -0
  401. package/src/graphql/queries/getRequestReturnPage.ts +121 -0
  402. package/src/graphql/queries/getSiteInfo.ts +54 -0
  403. package/src/graphql/queries/getSocialLinks.ts +52 -0
  404. package/src/graphql/queries/getTestimonials.ts +25 -0
  405. package/src/graphql/queries/getUserWithCheckout.ts +27 -0
  406. package/src/graphql/queries/getVehicleMakes.ts +21 -0
  407. package/src/graphql/queries/getVehicleModels.ts +21 -0
  408. package/src/graphql/queries/getVehicleYears.ts +21 -0
  409. package/src/graphql/queries/meAddresses.ts +56 -0
  410. package/src/graphql/queries/myOrders.ts +37 -0
  411. package/src/graphql/queries/orderDetail.ts +231 -0
  412. package/src/graphql/queries/productDetailsById.ts +197 -0
  413. package/src/graphql/queries/productInquiry.ts +115 -0
  414. package/src/graphql/queries/productsByCategoriesAndCollections.ts +39 -0
  415. package/src/graphql/queries/willCallCollectionPoints.ts +55 -0
  416. package/src/graphql/server-client.ts +54 -0
  417. package/src/graphql/types/categories.ts +9 -0
  418. package/src/graphql/types/checkout.ts +168 -0
  419. package/src/graphql/types/offer.ts +12 -0
  420. package/src/graphql/types/product.ts +44 -0
  421. package/src/hooks/scrollPageTop.ts +9 -0
  422. package/src/hooks/serverNavbarData.ts +79 -0
  423. package/src/hooks/useCartSync.ts +24 -0
  424. package/src/hooks/useRecaptcha.ts +33 -0
  425. package/src/hooks/useTokenExpiration.ts +81 -0
  426. package/src/hooks/useVehicleData.ts +346 -0
  427. package/src/lib/api/kount.ts +165 -0
  428. package/src/lib/api/shop.ts +1445 -0
  429. package/src/lib/saleor/getSaleorApiUrl.ts +25 -0
  430. package/src/lib/schema.ts +303 -0
  431. package/src/lib/seo/extractTextFromEditorJs.ts +58 -0
  432. package/src/lib/seo/site.ts +10 -0
  433. package/src/lib/urls/normalizeInternalUrl.ts +53 -0
  434. package/src/middleware.ts +134 -0
  435. package/src/sitemaps/README.md +105 -0
  436. package/src/sitemaps/dynamic-pages-sitemap.ts +247 -0
  437. package/src/sitemaps/sitemap-index.ts +21 -0
  438. package/src/sitemaps/static-pages-sitemap.ts +36 -0
  439. package/src/store/useGlobalStore.tsx +1656 -0
  440. package/src/types/global.d.ts +148 -0
  441. package/tsconfig.json +27 -0
@@ -0,0 +1,25 @@
1
+ const GRAPHQL_SUFFIX_RE = /\/graphql\/?$/i;
2
+
3
+ /**
4
+ * Returns the Saleor GraphQL endpoint URL.
5
+ *
6
+ * This template requires `NEXT_PUBLIC_API_URL` to be set (single-tenant per deployment).
7
+ * We also normalize it so callers can safely pass either:
8
+ * - https://example.com/graphql
9
+ * - https://example.com/graphql/
10
+ * - https://example.com (we'll append /graphql/)
11
+ */
12
+ export function getSaleorApiUrl(): string {
13
+ const raw = process.env.NEXT_PUBLIC_API_URL?.trim();
14
+ if (!raw) {
15
+ throw new Error(
16
+ "Missing NEXT_PUBLIC_API_URL. Set it to your Saleor GraphQL endpoint (e.g. https://.../graphql/)."
17
+ );
18
+ }
19
+
20
+ if (GRAPHQL_SUFFIX_RE.test(raw)) {
21
+ return raw.endsWith("/") ? raw : `${raw}/`;
22
+ }
23
+
24
+ return `${raw.replace(/\/+$/, "")}/graphql/`;
25
+ }
@@ -0,0 +1,303 @@
1
+ /**
2
+ * Schema.org structured data utilities
3
+ * Generate JSON-LD markup for SEO
4
+ */
5
+
6
+ interface Product {
7
+ id: string;
8
+ slug?: string;
9
+ name: string;
10
+ description?: string;
11
+ image?: string | string[];
12
+ price?: number;
13
+ currency?: string;
14
+ availability?: string;
15
+ sku?: string;
16
+ brand?: string;
17
+ rating?: number;
18
+ reviewCount?: number;
19
+ category?: {
20
+ id: string;
21
+ name: string;
22
+ };
23
+ }
24
+
25
+ interface BreadcrumbItem {
26
+ name: string;
27
+ url: string;
28
+ }
29
+
30
+ /**
31
+ * Generate Organization schema for homepage
32
+ */
33
+ export function generateOrganizationSchema(
34
+ siteName: string,
35
+ siteUrl: string,
36
+ logoUrl?: string,
37
+ socialLinks?: string[]
38
+ ) {
39
+ const baseUrl = (process.env.NEXT_PUBLIC_SITE_URL || siteUrl).replace(
40
+ /\/$/,
41
+ ""
42
+ );
43
+
44
+ return {
45
+ "@context": "https://schema.org",
46
+ "@type": "Organization",
47
+ name: siteName,
48
+ url: baseUrl,
49
+ logo: logoUrl
50
+ ? {
51
+ "@type": "ImageObject",
52
+ url: `${baseUrl}${logoUrl}`,
53
+ }
54
+ : undefined,
55
+ sameAs: socialLinks || [],
56
+ };
57
+ }
58
+
59
+ /**
60
+ * Generate WebSite schema with search action
61
+ */
62
+ export function generateWebsiteSchema(
63
+ siteName: string,
64
+ siteUrl: string,
65
+ searchUrl?: string
66
+ ) {
67
+ const baseUrl = (process.env.NEXT_PUBLIC_SITE_URL || siteUrl).replace(
68
+ /\/$/,
69
+ ""
70
+ );
71
+
72
+ return {
73
+ "@context": "https://schema.org",
74
+ "@type": "WebSite",
75
+ name: siteName,
76
+ url: baseUrl,
77
+ potentialAction: searchUrl
78
+ ? {
79
+ "@type": "SearchAction",
80
+ target: {
81
+ "@type": "EntryPoint",
82
+ urlTemplate: `${baseUrl}${searchUrl}?q={search_term_string}`,
83
+ },
84
+ "query-input": "required name=search_term_string",
85
+ }
86
+ : undefined,
87
+ };
88
+ }
89
+
90
+ /**
91
+ * Generate Product schema
92
+ */
93
+ export function generateProductSchema(product: Product) {
94
+ const baseUrl = (process.env.NEXT_PUBLIC_SITE_URL || "http://localhost:3000").replace(
95
+ /\/$/,
96
+ ""
97
+ );
98
+
99
+ const images = Array.isArray(product.image)
100
+ ? product.image
101
+ : product.image
102
+ ? [product.image]
103
+ : [];
104
+
105
+ return {
106
+ "@context": "https://schema.org",
107
+ "@type": "Product",
108
+ name: product.name,
109
+ description: product.description || "",
110
+ image: images.map((img) =>
111
+ img.startsWith("http") ? img : `${baseUrl}${img}`
112
+ ),
113
+ sku: product.sku || product.id,
114
+ category: product.category?.name,
115
+ offers: {
116
+ "@type": "Offer",
117
+ price: product.price?.toString() || "0",
118
+ priceCurrency: product.currency || "USD",
119
+ availability: product.availability
120
+ ? `https://schema.org/${product.availability}`
121
+ : "https://schema.org/InStock",
122
+ url: `${baseUrl}/product/${product.slug}`,
123
+ },
124
+ brand: product.brand
125
+ ? {
126
+ "@type": "Brand",
127
+ name: product.brand,
128
+ }
129
+ : undefined,
130
+ aggregateRating:
131
+ product.rating && product.reviewCount
132
+ ? {
133
+ "@type": "AggregateRating",
134
+ ratingValue: product.rating.toString(),
135
+ reviewCount: product.reviewCount.toString(),
136
+ }
137
+ : undefined,
138
+ };
139
+ }
140
+
141
+ /**
142
+ * Generate ItemList schema for product listings
143
+ */
144
+ export function generateItemListSchema(products: Product[], listName: string) {
145
+ const baseUrl = (process.env.NEXT_PUBLIC_SITE_URL || "http://localhost:3000").replace(
146
+ /\/$/,
147
+ ""
148
+ );
149
+
150
+ return {
151
+ "@context": "https://schema.org",
152
+ "@type": "ItemList",
153
+ name: listName,
154
+ numberOfItems: products.length,
155
+ itemListElement: products.map((product, index) => ({
156
+ "@type": "ListItem",
157
+ position: index + 1,
158
+ item: {
159
+ "@type": "Product",
160
+ name: product.name,
161
+ url: `${baseUrl}/product/${product.id}`,
162
+ image: Array.isArray(product.image)
163
+ ? product.image[0]
164
+ : product.image || "",
165
+ category: product.category?.name,
166
+ offers: {
167
+ "@type": "Offer",
168
+ price: product.price?.toString() || "0",
169
+ priceCurrency: product.currency || "USD",
170
+ },
171
+ },
172
+ })),
173
+ };
174
+ }
175
+
176
+ /**
177
+ * Generate BreadcrumbList schema
178
+ */
179
+ export function generateBreadcrumbSchema(items: BreadcrumbItem[]) {
180
+ const baseUrl = (process.env.NEXT_PUBLIC_SITE_URL || "http://localhost:3000").replace(
181
+ /\/$/,
182
+ ""
183
+ );
184
+
185
+ return {
186
+ "@context": "https://schema.org",
187
+ "@type": "BreadcrumbList",
188
+ itemListElement: items.map((item, index) => ({
189
+ "@type": "ListItem",
190
+ position: index + 1,
191
+ name: item.name,
192
+ item: item.url.startsWith("http") ? item.url : `${baseUrl}${item.url}`,
193
+ })),
194
+ };
195
+ }
196
+
197
+ /**
198
+ * Generate CollectionPage schema for category/collection pages
199
+ */
200
+ export function generateCollectionPageSchema(
201
+ name: string,
202
+ description: string,
203
+ url: string
204
+ ) {
205
+ const baseUrl = (process.env.NEXT_PUBLIC_SITE_URL || "http://localhost:3000").replace(
206
+ /\/$/,
207
+ ""
208
+ );
209
+
210
+ return {
211
+ "@context": "https://schema.org",
212
+ "@type": "CollectionPage",
213
+ name,
214
+ description,
215
+ url: url.startsWith("http") ? url : `${baseUrl}${url}`,
216
+ };
217
+ }
218
+
219
+ /**
220
+ * Generate WebPage schema for product category pages with ItemList
221
+ */
222
+ export function generateProductCategoryPageSchema(
223
+ name: string,
224
+ description: string,
225
+ url: string
226
+ ) {
227
+ const baseUrl = (process.env.NEXT_PUBLIC_SITE_URL || "http://localhost:3000").replace(
228
+ /\/$/,
229
+ ""
230
+ );
231
+
232
+ return {
233
+ "@context": "https://schema.org",
234
+ "@type": "WebPage",
235
+ name: `${name} - Product Category`,
236
+ description,
237
+ url: url.startsWith("http") ? url : `${baseUrl}${url}`,
238
+ breadcrumb: {
239
+ "@type": "BreadcrumbList",
240
+ },
241
+ };
242
+ }
243
+
244
+ /**
245
+ * Generate Blog schema for blog listing pages
246
+ */
247
+ export function generateBlogSchema(
248
+ name: string,
249
+ description: string,
250
+ url: string
251
+ ) {
252
+ const baseUrl = (process.env.NEXT_PUBLIC_SITE_URL || "http://localhost:3000").replace(
253
+ /\/$/,
254
+ ""
255
+ );
256
+
257
+ return {
258
+ "@context": "https://schema.org",
259
+ "@type": "Blog",
260
+ name,
261
+ description,
262
+ url: url.startsWith("http") ? url : `${baseUrl}${url}`,
263
+ };
264
+ }
265
+
266
+ /**
267
+ * Generate BlogPosting schema for blog posts
268
+ */
269
+ export function generateBlogPostingSchema(
270
+ title: string,
271
+ description: string,
272
+ url: string,
273
+ datePublished: string,
274
+ dateModified?: string,
275
+ authorName?: string,
276
+ image?: string
277
+ ) {
278
+ const baseUrl = (process.env.NEXT_PUBLIC_SITE_URL || "http://localhost:3000").replace(
279
+ /\/$/,
280
+ ""
281
+ );
282
+
283
+ return {
284
+ "@context": "https://schema.org",
285
+ "@type": "BlogPosting",
286
+ headline: title,
287
+ description,
288
+ url: url.startsWith("http") ? url : `${baseUrl}${url}`,
289
+ datePublished,
290
+ dateModified: dateModified || datePublished,
291
+ author: authorName
292
+ ? {
293
+ "@type": "Person",
294
+ name: authorName,
295
+ }
296
+ : undefined,
297
+ image: image
298
+ ? image.startsWith("http")
299
+ ? image
300
+ : `${baseUrl}${image}`
301
+ : undefined,
302
+ };
303
+ }
@@ -0,0 +1,58 @@
1
+ function stripHtml(input: string): string {
2
+ return input.replace(/<[^>]*>/g, " ").replace(/\s+/g, " ").trim();
3
+ }
4
+
5
+ type EditorJsBlock =
6
+ | { type: "paragraph"; data?: { text?: string } }
7
+ | { type: "header"; data?: { text?: string } }
8
+ | { type: "quote"; data?: { text?: string } }
9
+ | { type: "list"; data?: { items?: string[] } }
10
+ | { type: string; data?: Record<string, unknown> };
11
+
12
+ type EditorJsDoc = { blocks?: EditorJsBlock[] };
13
+
14
+ /**
15
+ * Saleor pages/descriptions are often stored as Editor.js JSON (string).
16
+ * This extracts readable plaintext for meta descriptions / schema.
17
+ */
18
+ export function extractTextFromEditorJs(input?: string | null): string {
19
+ if (!input) return "";
20
+
21
+ const trimmed = input.trim();
22
+ if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) {
23
+ return stripHtml(trimmed);
24
+ }
25
+
26
+ try {
27
+ const parsed = JSON.parse(trimmed) as EditorJsDoc;
28
+ const blocks = Array.isArray(parsed?.blocks) ? parsed.blocks : [];
29
+
30
+ const parts: string[] = [];
31
+ for (const block of blocks) {
32
+ if (!block || typeof block !== "object") continue;
33
+
34
+ if (
35
+ block.type === "paragraph" ||
36
+ block.type === "header" ||
37
+ block.type === "quote"
38
+ ) {
39
+ const txt = stripHtml(String(block.data?.text || ""));
40
+ if (txt) parts.push(txt);
41
+ continue;
42
+ }
43
+
44
+ if (block.type === "list") {
45
+ const items = Array.isArray(block.data?.items) ? block.data?.items : [];
46
+ for (const item of items) {
47
+ const txt = stripHtml(String(item || ""));
48
+ if (txt) parts.push(txt);
49
+ }
50
+ }
51
+ }
52
+
53
+ return stripHtml(parts.join(" "));
54
+ } catch {
55
+ return stripHtml(trimmed);
56
+ }
57
+ }
58
+
@@ -0,0 +1,10 @@
1
+ export function getSiteUrl(): URL {
2
+ const raw = process.env.NEXT_PUBLIC_SITE_URL?.trim() || "http://localhost:3000";
3
+ return new URL(raw.endsWith("/") ? raw : `${raw}/`);
4
+ }
5
+
6
+ export function canonicalPath(pathname: string): string {
7
+ if (!pathname.startsWith("/")) return `/${pathname}`;
8
+ return pathname;
9
+ }
10
+
@@ -0,0 +1,53 @@
1
+ export function normalizeInternalUrl(input: string): string {
2
+ const raw = input.trim();
3
+ if (!raw) return raw;
4
+
5
+ // Already relative.
6
+ if (raw.startsWith("/") && !raw.startsWith("//")) return raw;
7
+
8
+ // Try to interpret absolute URLs and strip the host when it matches our site.
9
+ try {
10
+ const siteRaw = process.env.NEXT_PUBLIC_SITE_URL?.trim();
11
+ const site = siteRaw ? new URL(siteRaw.endsWith("/") ? siteRaw : `${siteRaw}/`) : null;
12
+
13
+ const url = new URL(raw);
14
+
15
+ if (site && url.hostname === site.hostname) {
16
+ return `${url.pathname}${url.search}${url.hash}` || "/";
17
+ }
18
+
19
+ // Saleor menus often include absolute storefront URLs; in templates the domain may differ
20
+ // between environments (localhost vs deployed). Only strip the host for *known* internal routes.
21
+ const internalPrefixes = [
22
+ "/",
23
+ "/about",
24
+ "/contact",
25
+ "/products",
26
+ "/product",
27
+ "/category",
28
+ "/blog",
29
+ "/content",
30
+ "/request-return",
31
+ "/account",
32
+ "/privacy-policy",
33
+ "/shipping-returns",
34
+ "/terms-and-conditions",
35
+ "/warranty",
36
+ "/frequently-asked-questions",
37
+ "/site-map",
38
+ "/discounts",
39
+ "/core-policies",
40
+ "/cancellation-policy",
41
+ "/brands",
42
+ "/brand",
43
+ ];
44
+
45
+ if (internalPrefixes.some((p) => url.pathname === p || url.pathname.startsWith(`${p}/`))) {
46
+ return `${url.pathname}${url.search}${url.hash}` || "/";
47
+ }
48
+ } catch {
49
+ // fall through
50
+ }
51
+
52
+ return raw;
53
+ }
@@ -0,0 +1,134 @@
1
+ import type { NextRequest } from 'next/server';
2
+ import { NextResponse } from 'next/server';
3
+ import { jwtDecode } from 'jwt-decode';
4
+ import { fetchConfigurationDirect, isFeatureActive } from '@/app/utils/configurationService';
5
+
6
+ type JwtPayload = { exp?: number };
7
+
8
+ const AUTH_ROUTES = [
9
+ '/account/login',
10
+ '/account/register',
11
+ '/account/forgot-password',
12
+ '/account/reset-password',
13
+ ];
14
+
15
+ const PROTECTED_PREFIXES = ['/account', '/orders', '/settings'];
16
+
17
+ // Feature route mappings
18
+ const FEATURE_ROUTES = {
19
+ '/locator': 'dealer_locator',
20
+ } as const;
21
+
22
+ export async function middleware(req: NextRequest) {
23
+ const { pathname } = req.nextUrl;
24
+
25
+ const normalizedPath =
26
+ pathname.endsWith('/') && pathname.length > 1
27
+ ? pathname.slice(0, -1)
28
+ : pathname;
29
+
30
+ // Check feature route protection first
31
+ const featureName = FEATURE_ROUTES[normalizedPath as keyof typeof FEATURE_ROUTES];
32
+ if (featureName) {
33
+ try {
34
+ const configuration = await fetchConfigurationDirect();
35
+ const isActive = isFeatureActive(configuration, featureName);
36
+
37
+ if (!isActive) {
38
+ // Redirect to home page if feature is not active
39
+ const homeUrl = new URL('/', req.url);
40
+ const response = NextResponse.redirect(homeUrl);
41
+
42
+ const isProd = process.env.NODE_ENV === 'production';
43
+ if (!isProd) {
44
+ response.headers.set('x-middleware-redirect', `home:feature-disabled:${featureName}`);
45
+ }
46
+ return response;
47
+ }
48
+ } catch (error) {
49
+ console.error('Error checking feature configuration:', error);
50
+ // Allow access if there's an error (fail open)
51
+ }
52
+ }
53
+
54
+ const tokenCookie = req.cookies.get('token');
55
+ const refreshCookie = req.cookies.get('refreshToken');
56
+
57
+ let isTokenValid = false;
58
+ if (tokenCookie?.value) {
59
+ try {
60
+ const { exp } = jwtDecode<JwtPayload>(tokenCookie.value);
61
+ isTokenValid = !!exp && exp * 1000 > Date.now();
62
+ } catch {
63
+ isTokenValid = false;
64
+ }
65
+ }
66
+
67
+ const isLoggedIn = !!tokenCookie && isTokenValid;
68
+
69
+ const isAuthRoute = AUTH_ROUTES.some(
70
+ route => normalizedPath === route || normalizedPath.startsWith(route + '/')
71
+ );
72
+
73
+ const isProtectedRoute =
74
+ PROTECTED_PREFIXES.some(
75
+ prefix => normalizedPath === prefix || normalizedPath.startsWith(prefix + '/')
76
+ ) && !isAuthRoute;
77
+
78
+ // Debug headers only in non-prod and NEVER include token value
79
+ const isProd = process.env.NODE_ENV === 'production';
80
+ const debugHeaders: Record<string, string> = {
81
+ 'x-pathname': normalizedPath,
82
+ 'x-has-token': tokenCookie ? '1' : '0',
83
+ 'x-has-refresh': refreshCookie ? '1' : '0',
84
+ 'x-is-logged-in': isLoggedIn ? '1' : '0',
85
+ 'x-is-auth-route': isAuthRoute ? '1' : '0',
86
+ 'x-is-protected-route': isProtectedRoute ? '1' : '0',
87
+ };
88
+
89
+ // If token exists but is expired, redirect to clear-cookies API which will then redirect to login
90
+ if (tokenCookie && !isTokenValid) {
91
+ const loginUrl = new URL('/api/auth/clear-cookies', req.url);
92
+ loginUrl.searchParams.set('redirect', '/account/login');
93
+ loginUrl.searchParams.set('reason', 'token-expired');
94
+ const response = NextResponse.redirect(loginUrl);
95
+ if (!isProd) {
96
+ response.headers.set('x-middleware-redirect', 'login:token-expired');
97
+ Object.entries(debugHeaders).forEach(([k, v]) => response.headers.set(k, v));
98
+ }
99
+ return response;
100
+ }
101
+
102
+ if (isLoggedIn && isAuthRoute) {
103
+ const res = NextResponse.redirect(new URL('/', req.url));
104
+ if (!isProd) {
105
+ res.headers.set('x-middleware-redirect', 'home:auth-while-logged-in');
106
+ Object.entries(debugHeaders).forEach(([k, v]) => res.headers.set(k, v));
107
+ }
108
+ return res;
109
+ }
110
+
111
+ if (!isLoggedIn && isProtectedRoute) {
112
+ const loginUrl = new URL('/account/login', req.url);
113
+ loginUrl.searchParams.set('next', normalizedPath);
114
+ const res = NextResponse.redirect(loginUrl);
115
+ if (!isProd) {
116
+ res.headers.set('x-middleware-redirect', 'login:protected-while-logged-out');
117
+ Object.entries(debugHeaders).forEach(([k, v]) => res.headers.set(k, v));
118
+ }
119
+ return res;
120
+ }
121
+
122
+ const res = NextResponse.next();
123
+ if (!isProd) {
124
+ res.headers.set('x-middleware-hit', '1');
125
+ Object.entries(debugHeaders).forEach(([k, v]) => res.headers.set(k, v));
126
+ }
127
+ return res;
128
+ }
129
+
130
+ export const config = {
131
+ matcher: [
132
+ '/((?!api|_next/static|_next/image|favicon.ico|images|manifest.webmanifest|sitemap.xml|robots.txt).*)',
133
+ ],
134
+ };
@@ -0,0 +1,105 @@
1
+ # Sitemap Integration
2
+
3
+ Complete sitemap setup for all pages including static and dynamic content for Google, Yahoo, and Bing.
4
+
5
+ ## Files Structure
6
+
7
+ ```
8
+ src/sitemaps/
9
+ ├── static-pages-sitemap.ts # All static pages sitemap
10
+ ├── dynamic-pages-sitemap.ts # Dynamic content (products, categories, etc.)
11
+ ├── sitemap-index.ts # Configuration and search engine URLs
12
+ └── README.md # This documentation
13
+
14
+ src/app/
15
+ ├── sitemap.ts # Main sitemap endpoint (combines all)
16
+ └── sitemap-index.xml/ # Sitemap index route
17
+ └── route.ts
18
+
19
+ public/robots.txt # Search engine crawler instructions
20
+ ```
21
+
22
+ ## Available URLs
23
+
24
+ - **Main Sitemap**: `http://localhost:3000/sitemap.xml`
25
+ - **Sitemap Index**: `http://localhost:3000/sitemap-index.xml`
26
+ - **Robots.txt**: `http://localhost:3000/robots.txt`
27
+
28
+ ## Current Pages in Sitemap
29
+
30
+ ### Static Pages (22 pages):
31
+ - **Main Pages**: Homepage, About, Contact, Shop, Categories, Products, Blog
32
+ - **Account Pages**: Login, Register, Password Reset, Account Dashboard, Orders, Settings, Addresses
33
+ - **Legal Pages**: Privacy Policy, Terms & Conditions, Warranty, Shipping & Returns
34
+ - **Support Pages**: FAQ, Store Locator
35
+ - **E-commerce**: Cart, Checkout, Order Confirmation
36
+
37
+ ### Dynamic Pages (Ready for implementation):
38
+ - **Products**: `/product/[id]` - Product detail pages
39
+ - **Categories**: `/category/[slug]` - Category listing pages
40
+ - **Brands**: `/brand/[id]` - Brand pages
41
+ - **Blog Posts**: `/blog/[slug]` - Individual blog posts
42
+ - **Dynamic Pages**: `/[slug]` - CMS-managed pages
43
+
44
+ ## Google Search Console Setup
45
+
46
+ 1. **Add Property**: Go to [Google Search Console](https://search.google.com/search-console) and add your domain
47
+ 2. **Verify Ownership**: Follow verification steps (HTML file upload, DNS, or meta tag)
48
+ 3. **Submit Sitemaps**:
49
+ - Go to "Sitemaps" in the left menu
50
+ - Add sitemaps:
51
+ - `https://yourdomain.com/sitemap.xml` (main sitemap)
52
+ - `https://yourdomain.com/sitemap-index.xml` (sitemap index)
53
+ - Click "Submit"
54
+
55
+ ## Other Search Engines
56
+
57
+ ### Bing Webmaster Tools
58
+ 1. Visit [Bing Webmaster Tools](https://www.bing.com/webmasters)
59
+ 2. Add your site and verify ownership
60
+ 3. Submit both sitemaps in "Sitemaps" section
61
+
62
+ ### Yahoo Search
63
+ Yahoo uses Bing's index, so submitting to Bing covers Yahoo as well.
64
+
65
+ ### Manual Ping (Optional)
66
+ Search engines can be notified directly:
67
+ - Google: `https://www.google.com/ping?sitemap=https://yourdomain.com/sitemap.xml`
68
+ - Bing: `https://www.bing.com/ping?sitemap=https://yourdomain.com/sitemap.xml`
69
+
70
+ ## Production Setup
71
+
72
+ 1. Update `NEXT_PUBLIC_SITE_URL` in your environment variables
73
+ 2. Update robots.txt with your actual domain
74
+ 3. Implement dynamic content fetching in `dynamic-pages-sitemap.ts`
75
+ 4. Submit sitemaps to search engines using your production URL
76
+
77
+ ## Adding Dynamic Content
78
+
79
+ To populate dynamic pages, implement the helper functions in `dynamic-pages-sitemap.ts`:
80
+
81
+ ```typescript
82
+ // Example: Connect to your GraphQL API
83
+ export async function getProductSitemapEntries(): Promise<MetadataRoute.Sitemap> {
84
+ const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || 'http://localhost:3000'
85
+ const { data } = await client.query({ query: GET_ALL_PRODUCTS })
86
+
87
+ return data.products.map(product => ({
88
+ url: `${baseUrl}/product/${product.id}`,
89
+ lastModified: new Date(product.updatedAt),
90
+ changeFrequency: 'weekly' as const,
91
+ priority: 0.8,
92
+ }))
93
+ }
94
+ ```
95
+
96
+ ## SEO Priority Guidelines
97
+
98
+ - **Priority 1.0**: Homepage
99
+ - **Priority 0.9**: Shop, Main category pages
100
+ - **Priority 0.8**: About, Product pages, Category pages, Blog
101
+ - **Priority 0.7**: Contact, Brand pages
102
+ - **Priority 0.6**: FAQ, Blog posts, Store locator
103
+ - **Priority 0.5**: Legal pages, Dynamic CMS pages
104
+ - **Priority 0.4**: Account pages (login, register)
105
+ - **Priority 0.3**: Password reset, OTP pages