@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,60 @@
1
+ import { Suspense } from "react";
2
+ import type { Metadata } from "next";
3
+ import Link from "next/link";
4
+ import AncillaryContent from "@/app/components/ancillary/AncillaryContent";
5
+ import ContentSkeleton from "@/app/components/skeletons/ContentSkeleton";
6
+ import { getStoreName } from "@/app/utils/branding";
7
+ import Breadcrumb from "../components/reuseableUI/breadcrumb";
8
+ import Heading from "../components/reuseableUI/heading";
9
+
10
+ export const metadata: Metadata = {
11
+ title: `Shipping & Returns - ${getStoreName()}`,
12
+ description:
13
+ "Learn about our shipping options, delivery times, return policy, and how to process returns for your products.",
14
+ alternates: {
15
+ canonical: "/shipping-returns",
16
+ },
17
+ openGraph: {
18
+ title: `Shipping & Returns - ${getStoreName()}`,
19
+ description:
20
+ "Learn about our shipping options, delivery times, return policy, and how to process returns for your products.",
21
+ type: "website",
22
+ url: "/shipping-returns",
23
+ images: [{ url: "/Logo.png" }],
24
+ },
25
+ twitter: {
26
+ card: "summary_large_image",
27
+ title: `Shipping & Returns - ${getStoreName()}`,
28
+ description:
29
+ "Learn about our shipping options, delivery times, return policy, and how to process returns for your products.",
30
+ images: ["/Logo.png"],
31
+ },
32
+ };
33
+
34
+ export default function ShippingAndReturnsPage() {
35
+ const pageTitle = "Shipping & Returns";
36
+
37
+ return (
38
+ <main className="h-full w-full">
39
+ <div className="container mx-auto max-w-[1276px]">
40
+ <div className="flex flex-col items-start w-full px-4 md:px-6 py-12 md:py-16 lg:py-24">
41
+ <div className="flex flex-col items-start gap-5 mb-6 w-full">
42
+ <Breadcrumb
43
+ items={[
44
+ { text: "Home", link: "/" },
45
+ { text: "Shipping & Returns", link: "/shipping-returns" },
46
+ ]}
47
+ />
48
+ <Heading as="h1" content={pageTitle} />
49
+ </div>
50
+
51
+ <section className="w-full">
52
+ <Suspense fallback={<ContentSkeleton />}>
53
+ <AncillaryContent slug="shipping-returns" />
54
+ </Suspense>
55
+ </section>
56
+ </div>
57
+ </div>
58
+ </main>
59
+ );
60
+ }
@@ -0,0 +1,33 @@
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: `Site Map - ${getStoreName()}`,
7
+ description: `Explore the comprehensive site map of ${getStoreName()} to easily navigate through all sections and find what you're looking for quickly.`,
8
+ alternates: {
9
+ canonical: "/site-map",
10
+ },
11
+ openGraph: {
12
+ title: `Site Map - ${getStoreName()}`,
13
+ description: `Explore the comprehensive site map of ${getStoreName()} to easily navigate through all sections and find what you're looking for quickly.`,
14
+ type: "website",
15
+ url: "/site-map",
16
+ images: [{ url: "/Logo.png" }],
17
+ },
18
+ twitter: {
19
+ card: "summary_large_image",
20
+ title: `Site Map - ${getStoreName()}`,
21
+ description: `Explore the comprehensive site map of ${getStoreName()} to easily navigate through all sections and find what you're looking for quickly.`,
22
+ images: ["/Logo.png"],
23
+ },
24
+ };
25
+ }
26
+
27
+ export default function SiteMapPostLayout({
28
+ children,
29
+ }: {
30
+ children: React.ReactNode;
31
+ }) {
32
+ return children;
33
+ }
@@ -0,0 +1,113 @@
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
+
14
+ export async function generateMetadata(): Promise<Metadata> {
15
+ const post = await fetchBlogBySlug("site-map");
16
+
17
+ if (!post || !post.title) {
18
+ return {
19
+ title: `Site Map - ${getStoreName()}`,
20
+ description: `Explore the comprehensive site map of ${getStoreName()} to easily navigate through all sections and find what you're looking for quickly.`,
21
+ alternates: {
22
+ canonical: "/site-map",
23
+ },
24
+ };
25
+ }
26
+
27
+ return {
28
+ title: `${post.title} - ${getStoreName()}`,
29
+ description:
30
+ post.title ||
31
+ `Explore the comprehensive site map of ${getStoreName()} to easily navigate through all sections and find what you're looking for quickly.`,
32
+ alternates: {
33
+ canonical: "/site-map",
34
+ },
35
+ };
36
+ }
37
+
38
+ export default async function SiteMapPage() {
39
+ const post = await fetchBlogBySlug("site-map");
40
+
41
+ if (!post || !post.title) {
42
+ return (
43
+ <main className="container mx-auto px-4 md:px-6 py-12 text-center">
44
+ <h1 className="text-3xl font-bold mb-4">Content Not Found</h1>
45
+ <p className="mb-6">The requested site map could not be found.</p>
46
+ <Link
47
+ href="/"
48
+ className="text-[var(--color-primary-600)] hover:underline"
49
+ >
50
+ ← Back to Home
51
+ </Link>
52
+ </main>
53
+ );
54
+ }
55
+
56
+ // Generate schema.org structured data
57
+ const blogSchema = generateBlogPostingSchema(
58
+ post.title,
59
+ post.title,
60
+ "/site-map",
61
+ post.created || new Date().toISOString(),
62
+ post.created || new Date().toISOString()
63
+ );
64
+
65
+ const breadcrumbSchema = generateBreadcrumbSchema([
66
+ { name: "Home", url: "/" },
67
+ { name: post.title, url: "/site-map" },
68
+ ]);
69
+
70
+ return (
71
+ <main className="h-full w-full">
72
+ {/* Schema.org structured data */}
73
+ <script
74
+ type="application/ld+json"
75
+ dangerouslySetInnerHTML={{ __html: JSON.stringify(blogSchema) }}
76
+ />
77
+ <script
78
+ type="application/ld+json"
79
+ dangerouslySetInnerHTML={{ __html: JSON.stringify(breadcrumbSchema) }}
80
+ />
81
+ <div className="container mx-auto max-w-[1276px]">
82
+ <div className="flex flex-col items-start w-full gap-8 px-4 md:px-6 py-12 md:py-16 lg:py-24">
83
+ <div className="flex flex-col items-start gap-5 w-full">
84
+ <Breadcrumb
85
+ items={[
86
+ { text: "Home", link: "/" },
87
+ { text: post.title, link: "/site-map" },
88
+ ]}
89
+ />
90
+ <Heading as="h1" content={post.title} />
91
+ </div>
92
+
93
+ <div className="w-full flex flex-col items-start gap-6">
94
+ {/* Site Map Content */}
95
+ <div className="w-full">
96
+ <EditorRenderer content={post.content} />
97
+ </div>
98
+
99
+ {/* Back to Home Link */}
100
+ <div className="w-full pt-8 border-t border-[var(--color-secondary-200)]">
101
+ <Link
102
+ href="/"
103
+ className="text-[var(--color-primary-600)] hover:underline inline-flex items-center gap-2"
104
+ >
105
+ ← Back to Home
106
+ </Link>
107
+ </div>
108
+ </div>
109
+ </div>
110
+ </div>
111
+ </main>
112
+ );
113
+ }
@@ -0,0 +1,20 @@
1
+ import { NextResponse } from 'next/server'
2
+
3
+ export async function GET() {
4
+ const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || 'http://localhost:3000'
5
+
6
+ const sitemapIndex = `<?xml version="1.0" encoding="UTF-8"?>
7
+ <sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
8
+ <sitemap>
9
+ <loc>${baseUrl}/sitemap.xml</loc>
10
+ <lastmod>${new Date().toISOString()}</lastmod>
11
+ </sitemap>
12
+ </sitemapindex>`
13
+
14
+ return new NextResponse(sitemapIndex, {
15
+ headers: {
16
+ 'Content-Type': 'application/xml',
17
+ 'Cache-Control': 'public, max-age=3600, s-maxage=3600',
18
+ },
19
+ })
20
+ }
@@ -0,0 +1,10 @@
1
+ import { MetadataRoute } from 'next'
2
+ import { generateStaticPagesSitemap } from '../sitemaps/static-pages-sitemap'
3
+ import { generateDynamicPagesSitemap } from '../sitemaps/dynamic-pages-sitemap'
4
+
5
+ export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
6
+ const staticPages = generateStaticPagesSitemap()
7
+ const dynamicPages = await generateDynamicPagesSitemap()
8
+
9
+ return [...staticPages, ...dynamicPages]
10
+ }
@@ -0,0 +1,17 @@
1
+ import ContentSkeleton from "@/app/components/skeletons/ContentSkeleton"
2
+
3
+ export default function Loading() {
4
+ return (
5
+ <main className="h-full w-full">
6
+ <div className="container mx-auto max-w-[1276px]">
7
+ <div className="flex flex-col items-start w-full py-24">
8
+ <div className="flex flex-col items-start gap-5 mb-6 w-full">
9
+ <div className="h-5 w-24 bg-[var(--color-secondary-800)]/30 rounded" />
10
+ <div className="h-9 w-64 bg-[var(--color-secondary-800)]/30 rounded" />
11
+ </div>
12
+ <ContentSkeleton />
13
+ </div>
14
+ </div>
15
+ </main>
16
+ )
17
+ }
@@ -0,0 +1,56 @@
1
+ import AncillaryContent from "@/app/components/ancillary/AncillaryContent";
2
+ import ContentSkeleton from "@/app/components/skeletons/ContentSkeleton";
3
+ import { getStoreName } from "@/app/utils/branding";
4
+ import type { Metadata } from "next";
5
+ import { Suspense } from "react";
6
+ import Breadcrumb from "../components/reuseableUI/breadcrumb";
7
+ import Heading from "../components/reuseableUI/heading";
8
+
9
+ export const metadata: Metadata = {
10
+ title: `Terms & Conditions - ${getStoreName()}`,
11
+ description: `Read our terms and conditions for purchasing products and using ${getStoreName()} services.`,
12
+ alternates: {
13
+ canonical: "/terms-and-conditions",
14
+ },
15
+ openGraph: {
16
+ title: `Terms & Conditions - ${getStoreName()}`,
17
+ description: `Read our terms and conditions for purchasing products and using ${getStoreName()} services.`,
18
+ type: "website",
19
+ url: "/terms-and-conditions",
20
+ images: [{ url: "/Logo.png" }],
21
+ },
22
+ twitter: {
23
+ card: "summary_large_image",
24
+ title: `Terms & Conditions - ${getStoreName()}`,
25
+ description: `Read our terms and conditions for purchasing products and using ${getStoreName()} services.`,
26
+ images: ["/Logo.png"],
27
+ },
28
+ };
29
+
30
+ export default function TermsAndConditionsPage() {
31
+ const derivedTitle = "Terms & Conditions";
32
+
33
+ return (
34
+ <main className="h-full w-full">
35
+ <div className="container mx-auto max-w-[1276px]">
36
+ <div className="flex flex-col items-start w-full px-4 md:px-6 py-12 md:py-16 lg:py-24">
37
+ <div className="flex flex-col items-start gap-5 mb-6 w-full">
38
+ <Breadcrumb
39
+ items={[
40
+ { text: "Home", link: "/" },
41
+ { text: "Terms & Conditions", link: "/terms-and-conditions" },
42
+ ]}
43
+ />
44
+ <Heading as="h1" content="Terms & Conditions" />
45
+ </div>
46
+
47
+ <section className="w-full">
48
+ <Suspense fallback={<ContentSkeleton />}>
49
+ <AncillaryContent slug="terms-and-conditions" />
50
+ </Suspense>
51
+ </section>
52
+ </div>
53
+ </div>
54
+ </main>
55
+ );
56
+ }
@@ -0,0 +1,327 @@
1
+ export interface GoogleRecaptchaConfig {
2
+ site_key: string;
3
+ locations?: {
4
+ login?: boolean;
5
+ signup?: boolean;
6
+ checkout?: boolean;
7
+ };
8
+ }
9
+
10
+ export interface GoogleTagManagerConfig {
11
+ container_id: string;
12
+ }
13
+
14
+ export interface GoogleMapsConfig {
15
+ api_key: string;
16
+ }
17
+
18
+ export interface GoogleAnalyticsConfig {
19
+ measurement_id: string;
20
+ }
21
+
22
+ export interface GoogleAdSenseConfig {
23
+ publisher_id: string;
24
+ }
25
+
26
+ export interface GoogleSearchConsoleConfig {
27
+ verification_meta_tag: string;
28
+ }
29
+
30
+ export interface DealerLocatorConfig {
31
+ token: string;
32
+ }
33
+
34
+ export interface WillCallConfig {
35
+ // Will call doesn't need any configurations for now, but we maintain this for consistency
36
+ enabled?: boolean;
37
+ }
38
+
39
+ export interface AppConfig {
40
+ app_name: string; // Allow any app name since API can return unknown apps
41
+ configurations?: DealerLocatorConfig | GoogleRecaptchaConfig | GoogleTagManagerConfig | GoogleMapsConfig | GoogleAnalyticsConfig | GoogleAdSenseConfig | GoogleSearchConsoleConfig | WillCallConfig | Record<string, unknown>;
42
+ is_active: boolean;
43
+ }
44
+
45
+ export interface ExternalAppConfig {
46
+ app_name: 'dealer_locator' | 'google_recaptcha' | 'google_tag_manager' | 'google_maps' | 'google_analytics' | 'google_adsense' | 'google_search_console' | 'will-call';
47
+ configurations: DealerLocatorConfig | GoogleRecaptchaConfig | GoogleTagManagerConfig | GoogleMapsConfig | GoogleAnalyticsConfig | GoogleAdSenseConfig | GoogleSearchConsoleConfig | WillCallConfig;
48
+ is_active: boolean;
49
+ }
50
+
51
+ export interface InternalAppConfig {
52
+ app_name: 'tiered_pricing';
53
+ is_active: boolean;
54
+ }
55
+
56
+ export interface AppConfigurationResponse {
57
+ internal?: InternalAppConfig[];
58
+ external?: ExternalAppConfig[];
59
+ }
60
+
61
+ class AppConfigurationService {
62
+ private configuration: AppConfigurationResponse | null = null;
63
+ private loading = false;
64
+ private error: string | null = null;
65
+ private lastFetch: number = 0;
66
+ private cacheTimeout: number = 10000; // 10 seconds cache timeout
67
+
68
+ async fetchConfiguration(): Promise<AppConfigurationResponse> {
69
+ if (this.configuration && !this.shouldRefetch()) {
70
+ return this.configuration;
71
+ }
72
+
73
+ if (this.loading) {
74
+ while (this.loading) {
75
+ await new Promise(resolve => setTimeout(resolve, 100));
76
+ }
77
+ return this.configuration!;
78
+ }
79
+
80
+ try {
81
+ this.loading = true;
82
+ this.error = null;
83
+
84
+ // Use the API proxy route instead of direct external API call
85
+ const configUrl = '/api/configuration';
86
+
87
+ const response = await fetch(configUrl, {
88
+ method: 'GET',
89
+ headers: {
90
+ 'Content-Type': 'application/json',
91
+ },
92
+ });
93
+
94
+ if (!response.ok) {
95
+ throw new Error(`Failed to fetch configuration: ${response.status}`);
96
+ }
97
+
98
+ const rawData = await response.json();
99
+
100
+ // Handle flat array format or empty array
101
+ let data: AppConfigurationResponse;
102
+ if (rawData.length === 0) {
103
+ data = this.getFallbackConfiguration();
104
+ } else {
105
+ data = this.transformFlatArrayToStructured(rawData);
106
+ }
107
+
108
+ this.configuration = data;
109
+ this.lastFetch = Date.now();
110
+
111
+ return data;
112
+ } catch (error) {
113
+ this.error = error instanceof Error ? error.message : 'Unknown error';
114
+ console.error('Failed to fetch app configuration:', error);
115
+
116
+ return this.getFallbackConfiguration();
117
+ } finally {
118
+ this.loading = false;
119
+ }
120
+ }
121
+
122
+ private shouldRefetch(): boolean {
123
+ const now = Date.now();
124
+ return (now - this.lastFetch) > this.cacheTimeout;
125
+ }
126
+
127
+ private getFallbackConfiguration(): AppConfigurationResponse {
128
+ // Return minimal fallback - all features disabled when API fails
129
+ return {
130
+ internal: [
131
+ {
132
+ app_name: 'tiered_pricing',
133
+ is_active: false
134
+ }
135
+ ],
136
+ external: [
137
+ {
138
+ app_name: 'dealer_locator',
139
+ configurations: {
140
+ token: ''
141
+ },
142
+ is_active: false
143
+ },
144
+ {
145
+ app_name: 'google_recaptcha',
146
+ configurations: {
147
+ site_key: '',
148
+ locations: {
149
+ login: false,
150
+ signup: false,
151
+ checkout: false
152
+ }
153
+ },
154
+ is_active: false
155
+ },
156
+ {
157
+ app_name: 'google_tag_manager',
158
+ configurations: {
159
+ container_id: ''
160
+ },
161
+ is_active: false
162
+ },
163
+ {
164
+ app_name: 'google_maps',
165
+ configurations: {
166
+ api_key: ''
167
+ },
168
+ is_active: false
169
+ },
170
+ {
171
+ app_name: 'google_analytics',
172
+ configurations: {
173
+ measurement_id: ''
174
+ },
175
+ is_active: false
176
+ },
177
+ {
178
+ app_name: 'google_adsense',
179
+ configurations: {
180
+ publisher_id: ''
181
+ },
182
+ is_active: false
183
+ },
184
+ {
185
+ app_name: 'google_search_console',
186
+ configurations: {
187
+ verification_meta_tag: ''
188
+ },
189
+ is_active: false
190
+ }
191
+ ]
192
+ };
193
+ }
194
+
195
+ private transformFlatArrayToStructured(flatArray: AppConfig[]): AppConfigurationResponse {
196
+ const internalApps: InternalAppConfig[] = [];
197
+ const externalApps: ExternalAppConfig[] = [];
198
+
199
+ flatArray.forEach(app => {
200
+ // Only apps without configurations go to internal
201
+ if (!app.configurations && app.app_name === 'tiered_pricing') {
202
+ internalApps.push({
203
+ app_name: app.app_name as 'tiered_pricing',
204
+ is_active: app.is_active
205
+ });
206
+ } else {
207
+ // Apps with configurations go to external (including dealer_locator)
208
+ const knownExternalApps = [
209
+ 'dealer_locator', 'google_recaptcha', 'google_tag_manager',
210
+ 'google_maps', 'google_analytics', 'google_adsense', 'google_search_console', 'will-call'
211
+ ];
212
+
213
+ if (knownExternalApps.includes(app.app_name)) {
214
+ externalApps.push({
215
+ app_name: app.app_name as 'dealer_locator' | 'google_recaptcha' | 'google_tag_manager' | 'google_maps' | 'google_analytics' | 'google_adsense' | 'google_search_console' | 'will-call',
216
+ configurations: app.configurations || {},
217
+ is_active: app.is_active
218
+ } as ExternalAppConfig);
219
+ }
220
+ }
221
+ });
222
+
223
+ return {
224
+ internal: internalApps,
225
+ external: externalApps
226
+ };
227
+ }
228
+
229
+ getExternalConfig<T>(appName: string): T | null {
230
+ if (!this.configuration) return null;
231
+
232
+ const app = this.configuration.external?.find(app => app.app_name === appName);
233
+ return app?.is_active ? (app.configurations as T) : null;
234
+ }
235
+
236
+ getInternalConfig(appName: string): boolean {
237
+ if (!this.configuration) return false;
238
+
239
+ const app = this.configuration.internal?.find(app => app.app_name === appName);
240
+ return app?.is_active ?? false;
241
+ }
242
+
243
+ isAppActive(appName: string): boolean {
244
+ if (!this.configuration) return false;
245
+
246
+ const externalApp = this.configuration.external?.find(app => app.app_name === appName);
247
+ if (externalApp) return externalApp.is_active;
248
+
249
+ const internalApp = this.configuration.internal?.find(app => app.app_name === appName);
250
+ return internalApp?.is_active ?? false;
251
+ }
252
+
253
+ getGoogleRecaptchaConfig(): GoogleRecaptchaConfig | null {
254
+ return this.getExternalConfig<GoogleRecaptchaConfig>('google_recaptcha');
255
+ }
256
+
257
+ isRecaptchaEnabledFor(location: 'login' | 'signup' | 'checkout'): boolean {
258
+ if (!this.isAppActive('google_recaptcha')) return false;
259
+
260
+ const config = this.getGoogleRecaptchaConfig();
261
+ return config?.locations?.[location] ?? false;
262
+ }
263
+
264
+ getGoogleTagManagerConfig(): GoogleTagManagerConfig | null {
265
+ return this.getExternalConfig<GoogleTagManagerConfig>('google_tag_manager');
266
+ }
267
+
268
+ getGoogleMapsConfig(): GoogleMapsConfig | null {
269
+ return this.getExternalConfig<GoogleMapsConfig>('google_maps');
270
+ }
271
+
272
+ getGoogleAnalyticsConfig(): GoogleAnalyticsConfig | null {
273
+ return this.getExternalConfig<GoogleAnalyticsConfig>('google_analytics');
274
+ }
275
+
276
+ getGoogleAdSenseConfig(): GoogleAdSenseConfig | null {
277
+ return this.getExternalConfig<GoogleAdSenseConfig>('google_adsense');
278
+ }
279
+
280
+ getGoogleSearchConsoleConfig(): GoogleSearchConsoleConfig | null {
281
+ return this.getExternalConfig<GoogleSearchConsoleConfig>('google_search_console');
282
+ }
283
+
284
+ isDealerLocatorEnabled(): boolean {
285
+ return this.getInternalConfig('dealer_locator');
286
+ }
287
+
288
+ isWillCallEnabled(): boolean {
289
+ return this.isAppActive('will-call');
290
+ }
291
+
292
+ getLastError(): string | null {
293
+ return this.error;
294
+ }
295
+
296
+ // Force refresh configuration (useful for development)
297
+ clearCache(): void {
298
+ this.configuration = null;
299
+ this.lastFetch = 0;
300
+ this.error = null;
301
+ }
302
+
303
+ // Reduce cache timeout for development
304
+ setCacheTimeout(timeoutMs: number): void {
305
+ this.cacheTimeout = timeoutMs;
306
+ }
307
+ }
308
+
309
+ export const appConfigService = new AppConfigurationService();
310
+
311
+ // Development helper: expose cache clearing functions to window for easy access
312
+ if (typeof window !== 'undefined' && process.env.NODE_ENV === 'development') {
313
+ (window as Record<string, unknown>).clearAppConfigCache = () => {
314
+ appConfigService.clearCache();
315
+ };
316
+
317
+ (window as Record<string, unknown>).refreshAppConfig = async () => {
318
+ appConfigService.clearCache();
319
+ const config = await appConfigService.fetchConfiguration();
320
+ return config;
321
+ };
322
+
323
+ (window as Record<string, unknown>).setAppConfigCacheTimeout = (timeoutMs: number) => {
324
+ appConfigService.setCacheTimeout(timeoutMs);
325
+
326
+ };
327
+ }
@@ -0,0 +1,52 @@
1
+ export function getTenantName() {
2
+ return process.env.NEXT_PUBLIC_TENANT_NAME || "default";
3
+ }
4
+
5
+ export function getStoreName() {
6
+ const tenantName = getTenantName();
7
+ return tenantName.charAt(0).toUpperCase() + tenantName.slice(1).replace(/-/g, ' ') + " Store";
8
+ }
9
+
10
+ export function getAssetsBaseUrl() {
11
+ // e.g. https://zgnluljrtbddtwugakuf.supabase.co
12
+ return process.env.NEXT_PUBLIC_ASSETS_BASE_URL || "";
13
+ }
14
+
15
+ function joinUrl(...parts: string[]) {
16
+ return parts
17
+ .filter(Boolean)
18
+ .map((p, i) => (i === 0 ? p.replace(/\/$/, "") : p.replace(/^\/+|\/+$/g, "")))
19
+ .join("/");
20
+ }
21
+
22
+ // Helpers to sanitize env inputs
23
+ function pickFirstUrlToken(input?: string) {
24
+ if (!input) return undefined;
25
+ const trimmed = input.trim();
26
+ // If there are accidental extra tokens (e.g., ".../favicon.png favicon.png"), take the first token
27
+ const firstToken = trimmed.split(/\s+/)[0];
28
+ return firstToken.replace(/^\"|\"$/g, "");
29
+ }
30
+
31
+ function cleanPathToken(input?: string, fallback = "") {
32
+ const v = (input ?? fallback).trim();
33
+ return v.replace(/^\"|\"$/g, "");
34
+ }
35
+
36
+ export function getAppIconUrl() {
37
+ const explicit = pickFirstUrlToken(process.env.NEXT_PUBLIC_APP_ICON);
38
+ if (explicit && /^https?:\/\//i.test(explicit)) return explicit;
39
+
40
+ const base = getAssetsBaseUrl();
41
+ const tenant = getTenantName();
42
+ const file = cleanPathToken(process.env.NEXT_PUBLIC_APP_ICON_FILENAME, "favicon.ico");
43
+ const dir = cleanPathToken(process.env.NEXT_PUBLIC_APP_ICON_DIR, ""); // optional subfolder
44
+ if (!base) return explicit || "/icons/appIcon.png";
45
+ const pathParts = file.includes("/") ? [file] : (dir ? [dir, file] : [file]);
46
+ return joinUrl(
47
+ base,
48
+ "storage/v1/object/public/storefront-assets",
49
+ tenant,
50
+ ...pathParts
51
+ );
52
+ }