@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,418 @@
1
+ "use client";
2
+
3
+ import { SearchIcon } from "@/app/utils/svgs/searchIcon";
4
+ import {
5
+ shopApi,
6
+ type GlobalSearchProduct,
7
+ type GlobalSearchCategory,
8
+ type GlobalSearchProductType,
9
+ type PLSearchProductsResponse,
10
+ } from "@/lib/api/shop";
11
+ import { useRouter, usePathname, useSearchParams } from "next/navigation";
12
+ import React, { useEffect, useRef, useState } from "react";
13
+ import InputField from "../../reuseableUI/defaultInputField";
14
+ import { ArrowUpIcon } from "@/app/utils/svgs/arrowUpIcon";
15
+ import { cn, getFullImageUrl } from "@/app/utils/functions";
16
+ import { useGlobalStore } from "@/store/useGlobalStore";
17
+
18
+ const Search = ({ className }: { className?: string }) => {
19
+ const router = useRouter();
20
+ const pathname = usePathname();
21
+ const isYMMActive = useGlobalStore((state) => state.isYMMActive);
22
+ const channel = process.env.NEXT_PUBLIC_SALEOR_CHANNEL || "default-channel";
23
+ const searchParams = useSearchParams();
24
+ const fitmentPairs = searchParams.get("fitment_pairs");
25
+ const [term, setTerm] = useState("");
26
+ const [open, setOpen] = useState(false);
27
+ const [loading, setLoading] = useState(false);
28
+ const [products, setProducts] = useState<GlobalSearchProduct[]>([]);
29
+ const [categories, setCategories] = useState<GlobalSearchCategory[]>([]);
30
+ const [productTypes, setProductTypes] = useState<GlobalSearchProductType[]>(
31
+ []
32
+ );
33
+
34
+ // All refs declared together
35
+ const abortRef = useRef<AbortController | null>(null);
36
+ const containerRef = useRef<HTMLDivElement | null>(null);
37
+ const justSelectedRef = useRef<boolean>(false);
38
+
39
+ // Debounced fetch
40
+ useEffect(() => {
41
+ // Don't open if user just selected an item
42
+ if (justSelectedRef.current) {
43
+ justSelectedRef.current = false;
44
+ return;
45
+ }
46
+
47
+ // Clear results if search term is too short
48
+ if (!term || term.trim().length < 2) {
49
+ setProducts([]);
50
+ setCategories([]);
51
+ setProductTypes([]);
52
+ setLoading(false);
53
+ return;
54
+ }
55
+
56
+ setLoading(true);
57
+ if (term.trim().length >= 2) {
58
+ setOpen(true);
59
+ }
60
+
61
+ const controller = new AbortController();
62
+ abortRef.current?.abort();
63
+ abortRef.current = controller;
64
+
65
+ const t = setTimeout(async () => {
66
+ try {
67
+ // If YMM status is OK/Active → Use PartsLogic search products API
68
+ // If YMM status is NOT OK → Use GraphQL search
69
+ if (isYMMActive) {
70
+ const resp: PLSearchProductsResponse = await shopApi.searchProductsPL(
71
+ {
72
+ q: term.trim(),
73
+ page: 1,
74
+ per_page: 10,
75
+ fitment_pairs: fitmentPairs ? fitmentPairs : "",
76
+ }
77
+ );
78
+
79
+ if (!controller.signal.aborted) {
80
+ // Transform PartsLogic response to match component's expected format
81
+ const restProducts = resp.products.map((product) => ({
82
+ id: product.id,
83
+ name: product.name,
84
+ slug: product.slug,
85
+ updatedAt: "",
86
+ // Map category from PartsLogic format or GraphQL format
87
+ category:
88
+ product.category ||
89
+ (product.category_name
90
+ ? {
91
+ id: product.category_id || "",
92
+ name: product.category_name,
93
+ }
94
+ : null),
95
+ // Use primary_image from PartsLogic API or thumbnail from GraphQL
96
+ thumbnail:
97
+ product.thumbnail ||
98
+ (product.primary_image
99
+ ? {
100
+ url: product.primary_image,
101
+ alt: product.name,
102
+ }
103
+ : null),
104
+ }));
105
+
106
+ const restCategories = resp.facets.categories.map((cat) => ({
107
+ id: cat.id,
108
+ name: cat.value,
109
+ slug: cat.value.toLowerCase().replace(/\s+/g, "-"),
110
+ level: 0,
111
+ parent: null,
112
+ backgroundImage: cat.media
113
+ ? { url: cat.media, alt: cat.value }
114
+ : null,
115
+ products: {
116
+ totalCount: cat.count,
117
+ },
118
+ }));
119
+
120
+ const restProductTypes = resp.facets.brands.map((brand) => ({
121
+ id: brand.id,
122
+ name: brand.value,
123
+ slug: brand.value.toLowerCase().replace(/\s+/g, "-"),
124
+ hasVariants: false,
125
+ }));
126
+
127
+ setProducts(restProducts);
128
+ setCategories(restCategories);
129
+ setProductTypes(restProductTypes);
130
+ }
131
+ } else {
132
+ const resp = await shopApi.globalSearchStorefront({
133
+ query: term.trim(),
134
+ channel,
135
+ includeProducts: true,
136
+ includeCategories: true,
137
+ includeCollections: false,
138
+ includeProductTypes: true,
139
+ });
140
+
141
+ if (!controller.signal.aborted) {
142
+ setProducts(resp.products?.edges.map((edge) => edge.node) || []);
143
+ setCategories(
144
+ resp.categories?.edges.map((edge) => edge.node) || []
145
+ );
146
+ setProductTypes(
147
+ resp.productTypes?.edges.map((edge) => edge.node) || []
148
+ );
149
+ }
150
+ }
151
+ } catch (e) {
152
+ if (!controller.signal.aborted) {
153
+ setProducts([]);
154
+ setCategories([]);
155
+ setProductTypes([]);
156
+ }
157
+ } finally {
158
+ if (!controller.signal.aborted) {
159
+ setLoading(false);
160
+ }
161
+ }
162
+ }, 300); // Slightly longer debounce
163
+
164
+ return () => {
165
+ clearTimeout(t);
166
+ controller.abort();
167
+ };
168
+ }, [term, isYMMActive]);
169
+
170
+ // Close on outside click or Escape
171
+ useEffect(() => {
172
+ const onDocClick = (e: MouseEvent) => {
173
+ if (!open) return;
174
+ const t = e.target as Node;
175
+ if (containerRef.current && !containerRef.current.contains(t)) {
176
+ setOpen(false);
177
+ }
178
+ };
179
+ const onKey = (e: KeyboardEvent) => {
180
+ if (e.key === "Escape") setOpen(false);
181
+ };
182
+ document.addEventListener("click", onDocClick);
183
+ document.addEventListener("keydown", onKey);
184
+ return () => {
185
+ document.removeEventListener("click", onDocClick);
186
+ document.removeEventListener("keydown", onKey);
187
+ };
188
+ }, [open]);
189
+
190
+ const goToShop = (params: Record<string, string | undefined>) => {
191
+ // Navigate to shop page with search query
192
+ if (pathname === "/products/all") {
193
+ // If on shop page, replace with new search query
194
+ const sp = new URLSearchParams();
195
+ if (params.q) sp.set("q", params.q);
196
+ const newUrl = sp.toString()
197
+ ? `/products/all?${sp.toString()}`
198
+ : "/products/all";
199
+ router.replace(newUrl, { scroll: false });
200
+ } else {
201
+ // If not on shop page, navigate to shop with search query
202
+ const sp = new URLSearchParams();
203
+ if (params.q) sp.set("q", params.q);
204
+ const url = sp.toString()
205
+ ? `/products/all?${sp.toString()}`
206
+ : "/products/all";
207
+ router.push(url);
208
+ }
209
+ setOpen(false);
210
+ };
211
+
212
+ const onSubmit = (e: React.FormEvent) => {
213
+ e.preventDefault();
214
+ if (!term.trim()) return;
215
+ goToShop({ q: term.trim() });
216
+ };
217
+
218
+ return (
219
+ <div
220
+ ref={containerRef}
221
+ style={{
222
+ border: "1px solid var(--color-secondary-700)",
223
+ backgroundColor: "transparent",
224
+ }}
225
+ className={cn(
226
+ "flex items-center relative max-w-[560px] px-4 py-2.5 max-h-10 justify-between w-full",
227
+ className
228
+ )}
229
+ >
230
+ <form
231
+ onSubmit={onSubmit}
232
+ className="flex items-center w-full bg-transparent h-full justify-center"
233
+ >
234
+ <InputField
235
+ className="leading-none !bg-transparent w-full !px-0 !py-0"
236
+ name="Search-product"
237
+ type="text"
238
+ placeholder="Search product"
239
+ value={term}
240
+ onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
241
+ setTerm(e.target.value)
242
+ }
243
+ onFocus={() => term.trim().length >= 2 && setOpen(true)}
244
+ />
245
+ <button type="submit" className="h-full">
246
+ <span className="text-black">
247
+ {SearchIcon}
248
+ </span>
249
+ </button>
250
+ </form>
251
+
252
+ {open && term?.length > 1 && (
253
+ <div className="absolute top-full left-0 right-0 mt-2 bg-white border shadow-[0_10px_20px_0_rgba(0,0,0,0.10)] border-[var(--color-secondary-200)] z-40">
254
+ <div className=" max-h-80 flex flex-col gap-2 overflow-auto p-3">
255
+ {/* Products Section - Now at the top */}
256
+ {products.length > 0 && (
257
+ <div className="border-b border-gray-200 pb-2 mb-2">
258
+ <div className="text-xs font-semibold text-gray-500 uppercase tracking-wide mb-2">
259
+ Products
260
+ </div>
261
+ {products.slice(0, 8).map((p) => (
262
+ <button
263
+ key={p.id}
264
+ onClick={(e) => {
265
+ e.stopPropagation();
266
+ // Navigate to product detail page using original slug
267
+ router.push(`/product/${encodeURIComponent(p.slug)}`);
268
+ setOpen(false);
269
+ }}
270
+ className="w-full font-secondary p-3 text-left hover:bg-[var(--color-secondary-200)] flex items-center justify-between -tracking-[0.035px] text-[var(--color-secondary-800)] text-base transition-colors duration-200"
271
+ >
272
+ <div className="flex items-center gap-3 flex-1 min-w-0">
273
+ <div className="w-12 h-12 flex-shrink-0 bg-gray-100 rounded overflow-hidden">
274
+ {p.thumbnail ? (
275
+ <img
276
+ src={getFullImageUrl(p.thumbnail.url)}
277
+ alt={p.thumbnail.alt || p.name}
278
+ className="w-full h-full object-cover"
279
+ onError={(e) => {
280
+ // Fallback to placeholder if image fails to load
281
+ const target = e.target as HTMLImageElement;
282
+ target.style.display = "none";
283
+ target.parentElement!.innerHTML =
284
+ '<div class="w-full h-full bg-gray-200 flex items-center justify-center text-gray-400 text-xs">No Image</div>';
285
+ }}
286
+ />
287
+ ) : (
288
+ <div className="w-full h-full bg-gray-200 flex items-center justify-center text-gray-400 text-xs">
289
+ No Image
290
+ </div>
291
+ )}
292
+ </div>
293
+ <div className="flex-1 min-w-0">
294
+ <div className="truncate font-medium text-sm">
295
+ {p.name}
296
+ </div>
297
+ {p.category && (
298
+ <div className="text-xs text-gray-500 mt-1">
299
+ Category: {p.category.name}
300
+ </div>
301
+ )}
302
+ <div className="text-xs text-blue-600 mt-1">
303
+ View Product Details →
304
+ </div>
305
+ </div>
306
+ </div>
307
+ <span className="rotate-90 ml-2 text-gray-400">
308
+ {ArrowUpIcon}
309
+ </span>
310
+ </button>
311
+ ))}
312
+ </div>
313
+ )}
314
+
315
+ {/* Categories Section */}
316
+ {categories.length > 0 && (
317
+ <div className="border-b border-gray-200 pb-2 mb-2">
318
+ <div className="text-xs font-semibold text-gray-500 uppercase tracking-wide mb-2">
319
+ Categories
320
+ </div>
321
+ {categories.slice(0, 4).map((c) => (
322
+ <button
323
+ key={`cat-${c.id}`}
324
+ onClick={(e) => {
325
+ e.stopPropagation();
326
+ justSelectedRef.current = true;
327
+ setTerm(c.name);
328
+ setOpen(false);
329
+ try {
330
+ (document.activeElement as HTMLElement | null)?.blur();
331
+ } catch {}
332
+ // Navigate to category page directly using slug
333
+ router.push(`/category/${encodeURIComponent(c.slug)}`);
334
+ }}
335
+ className="w-full font-secondary p-2 text-left hover:bg-[var(--color-secondary-200)] flex items-center justify-between -tracking-[0.035px] text-[var(--color-secondary-800)] text-base "
336
+ >
337
+ <div className="flex items-center gap-3 flex-1">
338
+ {/* Category Image */}
339
+ {c.backgroundImage?.url && (
340
+ <div className="w-10 h-10 flex-shrink-0 bg-gray-100 rounded overflow-hidden">
341
+ <img
342
+ src={getFullImageUrl(c.backgroundImage.url)}
343
+ alt={c.backgroundImage.alt || c.name}
344
+ className="w-full h-full object-cover"
345
+ onError={(e) => {
346
+ const target = e.target as HTMLImageElement;
347
+ target.style.display = "none";
348
+ }}
349
+ />
350
+ </div>
351
+ )}
352
+ <div className="flex items-center justify-between flex-1">
353
+ <span>{c.name}</span>
354
+ <span className="text-xs text-gray-500">
355
+ ({c.products.totalCount})
356
+ </span>
357
+ </div>
358
+ </div>
359
+ <span className=" rotate-90 ml-2">{ArrowUpIcon}</span>
360
+ </button>
361
+ ))}
362
+ </div>
363
+ )}
364
+
365
+ {/* Product Types Section */}
366
+ {productTypes.length > 0 && (
367
+ <div>
368
+ <div className="text-xs font-semibold text-gray-500 uppercase tracking-wide mb-2">
369
+ Product Types
370
+ </div>
371
+ {productTypes.slice(0, 3).map((pt) => (
372
+ <button
373
+ key={`pt-${pt.id}`}
374
+ onClick={(e) => {
375
+ e.stopPropagation();
376
+ justSelectedRef.current = true;
377
+ setTerm(pt.name);
378
+ setOpen(false);
379
+ try {
380
+ (document.activeElement as HTMLElement | null)?.blur();
381
+ } catch {}
382
+ // Navigate to brand page directly using slug
383
+ router.push(`/brand/${encodeURIComponent(pt.slug)}`);
384
+ }}
385
+ className="w-full font-secondary p-2 text-left hover:bg-[var(--color-secondary-200)] flex items-center justify-between -tracking-[0.035px] text-[var(--color-secondary-800)] text-base "
386
+ >
387
+ <div className="flex items-center justify-between w-full">
388
+ <span>{pt.name}</span>
389
+ <span className="text-xs text-gray-500 bg-blue-100 px-1 rounded">
390
+ Type
391
+ </span>
392
+ </div>
393
+ <span className=" rotate-90 ml-2">{ArrowUpIcon}</span>
394
+ </button>
395
+ ))}
396
+ </div>
397
+ )}
398
+
399
+ {!loading &&
400
+ !products.length &&
401
+ !categories.length &&
402
+ !productTypes.length &&
403
+ term.trim().length >= 2 && (
404
+ <div className=" text-sm text-gray-500">No Results found</div>
405
+ )}
406
+ {loading && (
407
+ <div className="text-sm text-gray-500 font-secondary">
408
+ Searching…
409
+ </div>
410
+ )}
411
+ </div>
412
+ </div>
413
+ )}
414
+ </div>
415
+ );
416
+ };
417
+
418
+ export default Search;
@@ -0,0 +1,27 @@
1
+ export const navbarStyles = {
2
+ navLinkBase: "font-secondary text-white font-extrabold w-auto hover:text-zinc-300 transition-all ease-in-out duration-300 relative group",
3
+
4
+ navLinkActive: "text-[var(--color-primary-500)] after:w-full",
5
+
6
+ navLinkInactive: "text-black",
7
+
8
+ navLinkWithUnderline: "after:bg-[var(--color-primary-500)] after:transition-all after:duration-300",
9
+
10
+ iconBtnBase: "cursor-pointer transition-all ease-in-out duration-300 hover:text-[var(--color-primary-500)]",
11
+
12
+ mobileContainer: "xl:hidden flex items-center py-5 px-4 md:px-6 bg-[#e8e8e8] text-sm font-semibold border-b border-zinc-600",
13
+
14
+ desktopContainer: "hidden xl:flex w-full items-center justify-between gap-10 py-5 px-20 bg-[#e8e8e8] text-sm font-semibold",
15
+
16
+ linksContainer: "grid auto-cols-auto grid-flow-col gap-10 items-center w-auto",
17
+
18
+ actionsContainer: "ml-auto flex w-full items-center gap-5",
19
+
20
+ brandContainer: "flex-shrink-0 relative w-[160px] flex items-center h-[48px]",
21
+
22
+ dropdown: {
23
+ container: "absolute top-full left-0 mt-2 p-2 bg-white shadow-lg overflow-y-auto min-w-48 max-h-80 z-50",
24
+ item: "block px-4 py-2 text-gray-800 hover:bg-gray-100 hover:text-[var(--color-primary-500)] transition-colors"
25
+ },
26
+ cartBadge: "absolute -right-0.5 -top-1.5 lg:-right-1 lg:-top-1.5 flex size-5 items-center justify-center rounded-full text-xs bg-white/80 text-[var(--color-primary)]"
27
+ } as const;
@@ -0,0 +1,214 @@
1
+ import Link from "next/link";
2
+ import { fetchSiteInfo } from "@/graphql/queries/getSiteInfo";
3
+ import EditorJsRenderer from "@/app/components/reuseableUI/EditorJsRenderer";
4
+
5
+ function normalizePhone(raw: string) {
6
+ const digits = raw.replace(/[^\d+]/g, "");
7
+ return digits ? `tel:${digits}` : null;
8
+ }
9
+
10
+ function IconMail(props: React.SVGProps<SVGSVGElement>) {
11
+ return (
12
+ <svg
13
+ viewBox="0 0 24 24"
14
+ width={14}
15
+ height={14}
16
+ fill="none"
17
+ stroke="currentColor"
18
+ strokeWidth="2"
19
+ strokeLinecap="round"
20
+ strokeLinejoin="round"
21
+ aria-hidden="true"
22
+ {...props}
23
+ >
24
+ <path d="M4 4h16a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2z" />
25
+ <path d="m22 6-10 7L2 6" />
26
+ </svg>
27
+ );
28
+ }
29
+
30
+ function IconPhone(props: React.SVGProps<SVGSVGElement>) {
31
+ return (
32
+ <svg
33
+ viewBox="0 0 24 24"
34
+ width={14}
35
+ height={14}
36
+ fill="none"
37
+ stroke="currentColor"
38
+ strokeWidth="2"
39
+ strokeLinecap="round"
40
+ strokeLinejoin="round"
41
+ aria-hidden="true"
42
+ {...props}
43
+ >
44
+ <path d="M22 16.92v3a2 2 0 0 1-2.18 2 19.8 19.8 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6A19.8 19.8 0 0 1 2.08 4.18 2 2 0 0 1 4.06 2h3a2 2 0 0 1 2 1.72c.12.9.33 1.77.62 2.6a2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.48-1.14a2 2 0 0 1 2.11-.45c.83.29 1.7.5 2.6.62A2 2 0 0 1 22 16.92z" />
45
+ </svg>
46
+ );
47
+ }
48
+
49
+ function IconClock(props: React.SVGProps<SVGSVGElement>) {
50
+ return (
51
+ <svg
52
+ viewBox="0 0 24 24"
53
+ width={14}
54
+ height={14}
55
+ fill="none"
56
+ stroke="currentColor"
57
+ strokeWidth="2"
58
+ strokeLinecap="round"
59
+ strokeLinejoin="round"
60
+ aria-hidden="true"
61
+ {...props}
62
+ >
63
+ <circle cx="12" cy="12" r="10" />
64
+ <path d="M12 6v6l4 2" />
65
+ </svg>
66
+ );
67
+ }
68
+
69
+ function IconMapPin(props: React.SVGProps<SVGSVGElement>) {
70
+ return (
71
+ <svg
72
+ viewBox="0 0 24 24"
73
+ width={14}
74
+ height={14}
75
+ fill="none"
76
+ stroke="currentColor"
77
+ strokeWidth="2"
78
+ strokeLinecap="round"
79
+ strokeLinejoin="round"
80
+ aria-hidden="true"
81
+ {...props}
82
+ >
83
+ <path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z" />
84
+ <circle cx="12" cy="10" r="3" />
85
+ </svg>
86
+ );
87
+ }
88
+
89
+ export default async function TopBar() {
90
+ const page = await fetchSiteInfo();
91
+ const content = page?.content;
92
+
93
+ const get = (k: string) =>
94
+ page?.metadata
95
+ ?.find((m) => m.key.toLowerCase() === k.toLowerCase())
96
+ ?.value?.trim() || "";
97
+
98
+ const email = get("Email");
99
+ const phone = get("Phone");
100
+ const timings = get("Timings");
101
+ const address = get("Address");
102
+
103
+ // If nothing to show, render nothing
104
+ if (![email, phone, timings, address, content].some(Boolean)) return null;
105
+
106
+ const phoneHref = phone
107
+ ? normalizePhone(phone.split(/\r?\n/).pop() || phone)
108
+ : null;
109
+
110
+ return (
111
+ <div
112
+ className="w-full border-b"
113
+ style={{
114
+ backgroundColor: "var(--color-secondary-950)",
115
+ borderColor: "var(--color-secondary-800)",
116
+ }}
117
+ >
118
+ <div className="container mx-auto px-4 md:px-6">
119
+ <div className="flex flex-col sm:flex-row items-center justify-center sm:justify-between gap-2 py-2 md:py-2.5">
120
+ {/* Left: Contact */}
121
+ <div
122
+ className="flex items-center gap-4 text-xs sm:text-sm"
123
+ style={{ color: "var(--color-secondary-50)" }}
124
+ >
125
+ {/* {email && (
126
+ <div className="group flex items-center gap-1.5">
127
+ <IconMail className="text-white group-hover:text-[var(--color-primary)] transition-colors" />
128
+ <Link
129
+ aria-label="Email"
130
+ prefetch={false}
131
+ href={`mailto:${email}`}
132
+ className="text-white hover:text-[var(--color-primary)] transition-colors"
133
+ >
134
+ {email}
135
+ </Link>
136
+ </div>
137
+ )}
138
+ {phone && (
139
+ <div className="group flex items-center gap-1.5">
140
+ <IconPhone className="text-white group-hover:text-[var(--color-primary)] transition-colors" />
141
+ {phoneHref ? (
142
+ <Link
143
+ aria-label="Phone"
144
+ prefetch={false}
145
+ href={phoneHref}
146
+ className="text-white group-hover:text-[var(--color-primary)] transition-colors"
147
+ >
148
+ {phone}
149
+ </Link>
150
+ ) : (
151
+ <span className="text-white group-hover:text-[var(--color-primary)] transition-colors">
152
+ {phone}
153
+ </span>
154
+ )}
155
+ </div>
156
+ )} */}
157
+ {content && (
158
+ <div className="text-xs sm:text-sm text-white [&_p]:m-0 [&_a]:font-semibold [&_a]:underline [&_a]:underline-offset-2 [&_a]:text-white [&_a:hover]:text-[var(--color-primary)] [&_a]:transition-colors">
159
+ <EditorJsRenderer content={content} />
160
+ </div>
161
+ )}
162
+ </div>
163
+
164
+ {/* Free Shipping Notification */}
165
+
166
+ {/* Right: Timings */}
167
+ {/* {timings && (
168
+ <div
169
+ className="group flex items-center gap-1.5 text-xs sm:text-sm"
170
+ style={{ color: "var(--color-secondary-50)" }}
171
+ >
172
+ <div className="mb-[2px]">
173
+ <IconClock className="opacity-80 transition-colors group-hover:text-[var(--color-primary-500)]" />
174
+ </div>
175
+ <span className="transition-colors group-hover:text-[var(--color-primary-500)]">
176
+ {timings}
177
+ </span>
178
+ </div>
179
+ )} */}
180
+ <div
181
+ className="flex items-center gap-4 text-xs sm:text-sm"
182
+ style={{ color: "var(--color-secondary-50)" }}
183
+ >
184
+ {phone && (
185
+ <div className="group flex items-center gap-1.5">
186
+ <IconPhone className="text-white group-hover:text-[var(--color-primary)] transition-colors" />
187
+ {phoneHref ? (
188
+ <Link
189
+ aria-label="Phone"
190
+ prefetch={false}
191
+ href={phoneHref}
192
+ className="text-white group-hover:text-[var(--color-primary)] transition-colors"
193
+ >
194
+ {phone}
195
+ </Link>
196
+ ) : (
197
+ <span className="text-white group-hover:text-[var(--color-primary)] transition-colors">
198
+ {phone}
199
+ </span>
200
+ )}
201
+ </div>
202
+ )}
203
+ {address && (
204
+ <div className="group flex items-center gap-1.5 text-white group-hover:text-[var(--color-primary)] transition-colors">
205
+ <IconMapPin className="text-white group-hover:text-[var(--color-primary)] transition-colors" />
206
+ {address}
207
+ </div>
208
+ )}
209
+ </div>
210
+ </div>
211
+ </div>
212
+ </div>
213
+ );
214
+ }