@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,72 @@
1
+ "use client";
2
+ import Image from "next/image";
3
+ import Link from "next/link";
4
+ import React, { useState } from "react";
5
+ import CommonButton from "../../reuseableUI/commonButton";
6
+ import Input from "../../reuseableUI/input";
7
+
8
+ type FormData = {
9
+ email: string;
10
+ };
11
+
12
+ const JoinNewsletterForm: React.FC = () => {
13
+ const [formData, setFormData] = useState<FormData>({ email: "" });
14
+
15
+ const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
16
+ e.preventDefault();
17
+ };
18
+ const TENANT_NAME = process.env.NEXT_PUBLIC_TENANT_NAME || "default";
19
+ const brandName = TENANT_NAME || "WSM";
20
+ const logo =
21
+ process.env.NEXT_PUBLIC_LOGO_URL ||
22
+ "https://webshopmanager.com/files/images/logo.png";
23
+ return (
24
+ <div className="w-full lg:max-w-[544px]">
25
+ <Link className="hidden lg:flex items-center justify-start" href="/">
26
+ <Image
27
+ src={logo}
28
+ alt={brandName}
29
+ width={133}
30
+ height={40}
31
+ />
32
+ </Link>
33
+ <p
34
+ style={{ color: "var(--color-secondary-100)" }}
35
+ className="font-secondary -tracking-wide text-2xl font-semibold mt-10 mb-5"
36
+ >
37
+ JOIN OUR NEWSLETTER
38
+ </p>
39
+
40
+ <form onSubmit={handleSubmit}>
41
+ <div className="w-full flex gap-3 items-end">
42
+ <Input
43
+ label="EMAIL"
44
+ name="email"
45
+ type="email"
46
+ darkTheme={true}
47
+ disabled
48
+ placeholder="Enter your Email Address"
49
+ value={formData.email}
50
+ onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
51
+ setFormData({ ...formData, email: e.target.value })
52
+ }
53
+ autoComplete="email"
54
+ parentClassName="w-full"
55
+ required
56
+ />
57
+
58
+ <CommonButton disabled variant="primary" className="py-3" content="SUBMIT" />
59
+ </div>
60
+ </form>
61
+
62
+ <p
63
+ style={{ color: "var(--color-secondary-400)" }}
64
+ className="mt-5 font-secondary text-xs lg:text-base font-semibold -tracking-wide"
65
+ >
66
+ By subscribing you agree to the Terms of Services and Privacy Policy.
67
+ </p>
68
+ </div>
69
+ );
70
+ };
71
+
72
+ export default JoinNewsletterForm;
@@ -0,0 +1,92 @@
1
+ "use client";
2
+
3
+ import { cn, SrcRoot, trimSlash } from "@/app/utils/functions";
4
+ import { ArrowDownIcon } from "@/app/utils/svgs/arrowDownIcon";
5
+ import Link from "next/link";
6
+ import { memo, useState } from "react";
7
+ import ImageWithFallback from "../../reuseableUI/ImageWithFallback";
8
+
9
+ type MenuItem = {
10
+ __typename: "MenuItem";
11
+ id: string;
12
+ level: number;
13
+ name: string;
14
+ url: string;
15
+ children: MenuItem[];
16
+ };
17
+
18
+ const AccordionSection = memo(function AccordionSection({ section }: { section: MenuItem }) {
19
+ const [open, setOpen] = useState(false);
20
+
21
+ const isExternal = (href: string) => /^https?:\/\//i.test(href);
22
+
23
+ return (
24
+ <div style={{ borderBottom: "1px solid var(--color-secondary-800)" }}>
25
+ <button
26
+ onClick={() => setOpen((prev) => !prev)}
27
+ className="flex w-full justify-between items-center py-6 lg:py-3 text-left"
28
+ >
29
+ <span
30
+ style={{ color: "var(--color-primary-600)" }}
31
+ className="font-semibold text-sm lg:text-lg font-secondary"
32
+ >
33
+ {section.name}
34
+ </span>
35
+ <span className={cn("transition ease-in-out duration-300", open ? "rotate-180" : "")}>
36
+ {ArrowDownIcon}
37
+ </span>
38
+ </button>
39
+
40
+ {open && (
41
+ <div className="flex flex-col gap-2 pb-6">
42
+ {section.children.map((child) => {
43
+ const href = child.url || "#";
44
+ const external = isExternal(href);
45
+ return (
46
+ <Link
47
+ prefetch={false}
48
+ key={child.id}
49
+ href={href}
50
+ rel={external ? "noopener noreferrer" : undefined}
51
+ style={{ color: "var(--color-secondary-100)" }}
52
+ className="text-base hover:opacity-80 transition-opacity"
53
+ >
54
+ {child.name}
55
+ </Link>
56
+ );
57
+ })}
58
+ </div>
59
+ )}
60
+ </div>
61
+ );
62
+ });
63
+
64
+ export const MobileAccordion = ({ sections }: { sections: MenuItem[] }) => {
65
+ const TENANT_NAME = process.env.NEXT_PUBLIC_TENANT_NAME || "default";
66
+ const BASE_URL = trimSlash(process.env.NEXT_PUBLIC_ASSETS_BASE_URL);
67
+ const brandName = TENANT_NAME || "WSM";
68
+
69
+ const logoSrcRoot: SrcRoot = {
70
+ basePath: `${BASE_URL}/storage/v1/object/public/storefront-assets/${TENANT_NAME}`,
71
+ fileName: "logo",
72
+ };
73
+
74
+ return (
75
+ <div className="block lg:hidden w-full">
76
+ <Link className="block lg:hidden items-center justify-start mb-4" href="/">
77
+ <ImageWithFallback
78
+ srcRoot={logoSrcRoot}
79
+ alt={brandName}
80
+ width={133}
81
+ height={40}
82
+ className="object-contain w-auto h-full max-h-10"
83
+ priority
84
+ />
85
+ </Link>
86
+
87
+ {sections.map((section) => (
88
+ <AccordionSection key={section.id} section={section} />
89
+ ))}
90
+ </div>
91
+ );
92
+ };
@@ -0,0 +1,75 @@
1
+ import Link from "next/link";
2
+ import { fetchPaymentMethods, PaymentMethodFlagKey } from "@/graphql/queries/getPaymentMethods";
3
+ import { paymentProvidersIconsConfig } from "@/app/utils/constant";
4
+
5
+ export type PaymentIconKey = keyof typeof paymentProvidersIconsConfig;
6
+
7
+ const apiKeyToIconKey: Record<PaymentMethodFlagKey, PaymentIconKey> = {
8
+ VISA: "Visa",
9
+ MasterCard: "Mastercard",
10
+ Amex: "Americanexpress",
11
+ PayPal: "Paypal",
12
+ GooglePay: "Gpay",
13
+ ApplePay: "Applepay",
14
+ };
15
+
16
+ const normalizeBool = (v: string | null | undefined) =>
17
+ String(v).trim().toLowerCase() === "true";
18
+
19
+ export default async function PaymentMethods({
20
+ fallback,
21
+ }: {
22
+ fallback?: { name: string; href: string; icon: PaymentIconKey }[];
23
+ }) {
24
+ const page = await fetchPaymentMethods();
25
+
26
+ let items: { name: string; href: string; icon: PaymentIconKey }[] | null = null;
27
+
28
+ if (page?.metadata?.length) {
29
+ const enabled = page.metadata
30
+ .filter((m) => normalizeBool(m.value))
31
+ .map((m) => m.key);
32
+
33
+ items = enabled
34
+ .map((key) => {
35
+ const icon = apiKeyToIconKey[key as PaymentMethodFlagKey];
36
+ if (!icon || !(icon in paymentProvidersIconsConfig)) return null;
37
+ // Use brand websites as generic hrefs; can be refined later per business rules
38
+ const brandHref: Record<PaymentIconKey, string> = {
39
+ Visa: "https://visa.com",
40
+ Mastercard: "https://mastercard.com",
41
+ Americanexpress: "https://americanexpress.com",
42
+ Paypal: "https://paypal.com",
43
+ Gpay: "https://pay.google.com",
44
+ Applepay: "https://www.apple.com/apple-pay/",
45
+ };
46
+ return { name: icon, href: brandHref[icon], icon };
47
+ })
48
+ .filter(Boolean) as { name: string; href: string; icon: PaymentIconKey }[];
49
+ }
50
+
51
+ const list = items && items.length > 0 ? items : fallback || [];
52
+
53
+ if (!list.length) return null;
54
+
55
+ return (
56
+ <div className="flex gap-2 items-center justify-center lg:justify-start flex-wrap">
57
+ {list.map((p) => {
58
+ const Icon = paymentProvidersIconsConfig[p.icon];
59
+ if (!Icon) return null;
60
+ return (
61
+ <Link
62
+ key={p.name}
63
+ href={p.href}
64
+ target="_blank"
65
+ rel="noopener noreferrer"
66
+ aria-label={p.name}
67
+ className="hover:opacity-80 transition-opacity"
68
+ >
69
+ <span className="inline-block size-8">{Icon}</span>
70
+ </Link>
71
+ );
72
+ })}
73
+ </div>
74
+ );
75
+ }
@@ -0,0 +1,23 @@
1
+ import React from "react";
2
+ import Footer from "./footer";
3
+ import { Header } from "./header/header";
4
+
5
+ interface LayoutProps {
6
+ children: React.ReactNode;
7
+ }
8
+
9
+ export default function RootLayout({ children }: LayoutProps) {
10
+
11
+ return (
12
+ <div className="min-h-screen">
13
+ {/* Sticky Header */}
14
+ <div className="sticky top-0 z-[101]">
15
+ <Header />
16
+ </div>
17
+
18
+ {/* Main Content */}
19
+ <main className="min-h-screen">{children}</main>
20
+ <Footer />
21
+ </div>
22
+ );
23
+ }
@@ -0,0 +1,103 @@
1
+ import Link from "next/link"
2
+ import { fetchSiteInfo } from "@/graphql/queries/getSiteInfo"
3
+
4
+ function normalizePhone(raw: string) {
5
+ const digits = raw.replace(/[^\d+]/g, "")
6
+ return digits ? `tel:${digits}` : null
7
+ }
8
+
9
+ function lines(v: string) {
10
+ return v.split(/\r?\n/).filter(Boolean)
11
+ }
12
+
13
+ export default async function SiteInfo() {
14
+ const page = await fetchSiteInfo()
15
+
16
+ const get = (k: string) =>
17
+ page?.metadata
18
+ ?.find((m) => m.key.toLowerCase() === k.toLowerCase())
19
+ ?.value?.trim() || ""
20
+
21
+ const address = get("Address")
22
+ const email = get("Email")
23
+ const phone = get("Phone")
24
+ const timings = get("Timings")
25
+
26
+ const allEmpty = [address, email, phone, timings].every((v) => !v)
27
+ if (allEmpty) return null
28
+
29
+ return (
30
+ <div className="flex flex-col w-full gap-3">
31
+ <span
32
+ style={{ color: "var(--color-primary-600)" }}
33
+ className="font-bold text-base text-center md:text-left lg:text-right"
34
+ >
35
+ SITE INFO
36
+ </span>
37
+ <div className="flex flex-col gap-2">
38
+ {address && (
39
+ <div
40
+ className="text-base text-center md:text-left lg:text-right"
41
+ style={{ color: "var(--color-secondary-100)" }}
42
+ >
43
+ {lines(address).map((l, i) => (
44
+ <div key={i}>{l}</div>
45
+ ))}
46
+ </div>
47
+ )}
48
+
49
+ {email && (
50
+ <Link
51
+ prefetch={false}
52
+ href={`mailto:${email}`}
53
+ className="text-base hover:opacity-80 transition-opacity text-center md:text-left lg:text-right"
54
+ style={{ color: "var(--color-secondary-100)" }}
55
+ >
56
+ {email}
57
+ </Link>
58
+ )}
59
+
60
+ {phone && (
61
+ <div
62
+ className="text-base text-center md:text-left lg:text-right"
63
+ style={{ color: "var(--color-secondary-100)" }}
64
+ >
65
+ {(() => {
66
+ const phLines = lines(phone)
67
+ const last = phLines[phLines.length - 1]
68
+ const telHref = normalizePhone(last || phone)
69
+ return (
70
+ <>
71
+ {phLines.slice(0, -1).map((l, i) => (
72
+ <div key={i}>{l}</div>
73
+ ))}
74
+ {last && telHref ? (
75
+ <Link
76
+ prefetch={false}
77
+ href={telHref}
78
+ className="hover:opacity-80 transition-opacity"
79
+ style={{ color: "var(--color-secondary-100)" }}
80
+ >
81
+ {last}
82
+ </Link>
83
+ ) : (
84
+ last && <div>{last}</div>
85
+ )}
86
+ </>
87
+ )
88
+ })()}
89
+ </div>
90
+ )}
91
+
92
+ {timings && (
93
+ <div
94
+ className="text-base text-center md:text-left lg:text-right"
95
+ style={{ color: "var(--color-secondary-100)" }}
96
+ >
97
+ {timings}
98
+ </div>
99
+ )}
100
+ </div>
101
+ </div>
102
+ )
103
+ }
@@ -0,0 +1,65 @@
1
+ import Link from "next/link"
2
+ import { fetchSocialLinks, SocialKey } from "@/graphql/queries/getSocialLinks"
3
+ import { socialIconsConfig } from "@/app/utils/constant"
4
+
5
+ export type SocialIconKey = keyof typeof socialIconsConfig
6
+
7
+ const apiKeyToIconKey: Partial<Record<SocialKey, SocialIconKey>> = {
8
+ facebook: "FB",
9
+ instagram: "IG",
10
+ twitter: "X",
11
+ youtube: "YT",
12
+ }
13
+
14
+ export default async function SocialLinks({
15
+ fallback,
16
+ }: {
17
+ fallback?: { name: string; href: string; icon: SocialIconKey }[]
18
+ }) {
19
+ const page = await fetchSocialLinks()
20
+
21
+ let items: { name: string; href: string; icon: SocialIconKey }[] | null = null
22
+
23
+ if (page?.metadata?.length) {
24
+ items = page.metadata
25
+ .map((m) => {
26
+ const icon = apiKeyToIconKey[m.key as SocialKey]
27
+ if (!icon || !(icon in socialIconsConfig)) return null
28
+ const friendlyName: Record<SocialIconKey, string> = {
29
+ X: "X",
30
+ IG: "Instagram",
31
+ FB: "Facebook",
32
+ YT: "YouTube",
33
+ }
34
+ const href = (m.value || "").trim()
35
+ if (!href) return null
36
+ return { name: friendlyName[icon], href, icon }
37
+ })
38
+ .filter(Boolean) as { name: string; href: string; icon: SocialIconKey }[]
39
+ }
40
+
41
+ const list = items && items.length > 0 ? items : fallback || []
42
+ if (!list.length) return null
43
+
44
+ return (
45
+ <div className="flex items-center justify-center lg:justify-start gap-4">
46
+ {list.map((s) => {
47
+ const Icon = socialIconsConfig[s.icon]
48
+ if (!Icon) return null
49
+ return (
50
+ <Link
51
+ key={s.name}
52
+ href={s.href}
53
+ target="_blank"
54
+ rel="noopener noreferrer"
55
+ className="hover:text-gray-400 transition-colors"
56
+ aria-label={s.name}
57
+ style={{ color: "var(--color-secondary-50)" }}
58
+ >
59
+ <span className="inline-block h-6 w-6">{Icon}</span>
60
+ </Link>
61
+ )
62
+ })}
63
+ </div>
64
+ )
65
+ }
@@ -0,0 +1,224 @@
1
+ "use client";
2
+
3
+ import { NewsletterIcon } from "@/app/utils/svgs/newsletterIcon";
4
+ import type { NewsletterPageData } from "@/graphql/queries/getNewsletterPage";
5
+ import React, { useState } from "react";
6
+ import { SpinnerIcon } from "../../utils/svgs/spinnerIcon";
7
+ import { ErrorTag } from "../reuseableUI/errorTag";
8
+ import Input from "../reuseableUI/input";
9
+ import PrimaryButton from "../reuseableUI/primaryButton";
10
+ import Toast from "../reuseableUI/Toast";
11
+
12
+ interface EmailListSectionProps {
13
+ newsletterData: NewsletterPageData | null;
14
+ }
15
+
16
+ const EmailListSection: React.FC<EmailListSectionProps> = ({
17
+ newsletterData,
18
+ }) => {
19
+ const [email, setEmail] = useState("");
20
+ const [emailError, setEmailError] = useState("");
21
+ const [apiError, setApiError] = useState<string | null>(null);
22
+ const [showSuccessToast, setShowSuccessToast] = useState(false);
23
+ const [loading, setLoading] = useState(false);
24
+
25
+ // Default fallback content
26
+ const title = newsletterData?.title || "JOIN OUR EMAIL LIST";
27
+ const description =
28
+ newsletterData?.description ||
29
+ "Be the first to know about new products, discounts and special events!";
30
+
31
+ const validateEmail = (email: string): boolean => {
32
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
33
+ return emailRegex.test(email);
34
+ };
35
+
36
+ const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
37
+ setEmail(e.target.value);
38
+ if (emailError) {
39
+ setEmailError("");
40
+ }
41
+ };
42
+
43
+ const handleSubmit = async (e: React.FormEvent) => {
44
+ e.preventDefault();
45
+
46
+ setApiError(null);
47
+ setShowSuccessToast(false);
48
+ setEmailError("");
49
+
50
+ // Validate email
51
+ if (!email || !email.trim()) {
52
+ setEmailError("Email is required.");
53
+ return;
54
+ }
55
+
56
+ if (!validateEmail(email)) {
57
+ setEmailError("Please enter a valid email address.");
58
+ return;
59
+ }
60
+
61
+ setLoading(true);
62
+
63
+ try {
64
+ // Prepare email message
65
+ const emailMessage: Record<string, string> = {
66
+ email: email,
67
+ };
68
+
69
+ // Prepare email recipients
70
+ const recipients: string[] = [];
71
+ if (newsletterData?.emailTo) {
72
+ recipients.push(
73
+ ...newsletterData.emailTo.split(",").map((e) => e.trim())
74
+ );
75
+ }
76
+
77
+ const ccRecipients: string[] = [];
78
+ if (newsletterData?.emailCc) {
79
+ ccRecipients.push(
80
+ ...newsletterData.emailCc.split(",").map((e) => e.trim())
81
+ );
82
+ }
83
+
84
+ const bccRecipients: string[] = [];
85
+ if (newsletterData?.emailBcc) {
86
+ bccRecipients.push(
87
+ ...newsletterData.emailBcc.split(",").map((e) => e.trim())
88
+ );
89
+ }
90
+
91
+ const response = await fetch("/api/form-submission", {
92
+ method: "POST",
93
+ headers: {
94
+ "Content-Type": "application/json",
95
+ },
96
+ body: JSON.stringify({
97
+ formType: "newsletter",
98
+ pageSlug: "newsletter-inline",
99
+ data: {
100
+ message: emailMessage,
101
+ to: recipients,
102
+ cc: ccRecipients,
103
+ bcc: bccRecipients,
104
+ subject: newsletterData?.emailSubject || "Newsletter Signup",
105
+ },
106
+ metadata: {},
107
+ timestamp: new Date().toISOString(),
108
+ }),
109
+ });
110
+
111
+ if (!response.ok) {
112
+ throw new Error("Failed to send email");
113
+ }
114
+
115
+ setShowSuccessToast(true);
116
+ setEmail("");
117
+ } catch (err) {
118
+ console.error("Newsletter signup error:", err);
119
+ setApiError("Failed to subscribe. Please try again later.");
120
+ } finally {
121
+ setLoading(false);
122
+ }
123
+ };
124
+
125
+ return (
126
+ <>
127
+ {/* Success Toast */}
128
+ {showSuccessToast && (
129
+ <>
130
+ <style jsx global>{`
131
+ @keyframes slideInRight {
132
+ from {
133
+ transform: translateX(100%);
134
+ opacity: 0;
135
+ }
136
+ to {
137
+ transform: translateX(0);
138
+ opacity: 1;
139
+ }
140
+ }
141
+ .animate-slide-in {
142
+ animation: slideInRight 0.3s ease-out;
143
+ }
144
+ `}</style>
145
+ <div className="fixed top-4 right-4 z-50 animate-slide-in">
146
+ <Toast
147
+ message="Successfully Subscribed!"
148
+ subParagraph={
149
+ newsletterData?.successMessage
150
+ ? newsletterData.successMessage.replace(/<[^>]*>/g, "")
151
+ : "Thank you for subscribing to our newsletter!"
152
+ }
153
+ type="success"
154
+ duration={5000}
155
+ onClose={() => setShowSuccessToast(false)}
156
+ />
157
+ </div>
158
+ </>
159
+ )}
160
+
161
+ <div className="bg-[#222325] w-full h-full flex flex-col lg:flex-row gap-4 items-center justify-center py-6 text-center px-4">
162
+ <div className="max-w-[470px] flex items-center gap-4">
163
+ <div className="space-y-1 hidden lg:block">
164
+ <span>{NewsletterIcon}</span>
165
+ </div>
166
+ <div className="flex flex-col">
167
+ <p className="text-xl font-secondary uppercase font-normal text-white">
168
+ {title}
169
+ </p>
170
+ <span className="text-zinc-400 font-secondary text-base uppercase break-words">
171
+ {description}
172
+ </span>
173
+ </div>
174
+ </div>
175
+ <form onSubmit={handleSubmit} className="w-full max-w-xl">
176
+ <div className="flex flex-col items-center gap-2">
177
+ <div className="flex items-center gap-1 w-full">
178
+ <Input
179
+ parentClassName="w-full"
180
+ name="email"
181
+ type="email"
182
+ placeholder="Enter your email"
183
+ value={email}
184
+ onChange={handleChange}
185
+ className="px-4 py-1.5 w-full h-10"
186
+ disabled={loading}
187
+ />
188
+ <PrimaryButton
189
+ type="submit"
190
+ disabled={loading}
191
+ content={
192
+ loading ? (
193
+ <span className="flex items-center justify-center gap-2">
194
+ <span className="animate-spin [&>svg]:size-5">
195
+ {SpinnerIcon}
196
+ </span>
197
+ </span>
198
+ ) : (
199
+ "Subscribe"
200
+ )
201
+ }
202
+ className="text-sm font-primary h-10 border-none font-normal min-w-28 capitalize"
203
+ />
204
+ </div>
205
+
206
+ {emailError && (
207
+ <div className="text-white text-sm w-full text-left px-1">
208
+ {emailError}
209
+ </div>
210
+ )}
211
+
212
+ {apiError && (
213
+ <div className="w-full">
214
+ <ErrorTag message={apiError} />
215
+ </div>
216
+ )}
217
+ </div>
218
+ </form>
219
+ </div>
220
+ </>
221
+ );
222
+ };
223
+
224
+ export default EmailListSection;
@@ -0,0 +1,8 @@
1
+ import { fetchNewsletterPage } from "@/graphql/queries/getNewsletterPage";
2
+ import EmailListSection from "./emailListSection";
3
+
4
+ export default async function EmailSectionServer() {
5
+ const newsletterData = await fetchNewsletterPage();
6
+
7
+ return <EmailListSection newsletterData={newsletterData} />;
8
+ }
@@ -0,0 +1,12 @@
1
+ 'use client';
2
+
3
+ import { ApolloProvider } from "@apollo/client";
4
+ import client from "@/graphql/client";
5
+
6
+ export default function ApolloWrapper({
7
+ children,
8
+ }: {
9
+ children: React.ReactNode;
10
+ }) {
11
+ return <ApolloProvider client={client}>{children}</ApolloProvider>;
12
+ }