@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
Binary file
Binary file
Binary file
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,3 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
2
+ <path d="M11.048 2.927c.3-.921 1.603-.921 1.902 0l1.518 4.674a1 1 0 00.95.69h4.915c.969 0 1.371 1.24.588 1.81l-3.976 2.89a1 1 0 00-.364 1.118l1.518 4.674c.3.922-.755 1.688-1.54 1.118l-3.976-2.89a1 1 0 00-1.176 0l-3.976 2.89c-.784.57-1.838-.196-1.539-1.118l1.518-4.674a1 1 0 00-.364-1.118l-3.976-2.89c-.783-.57-.38-1.81.588-1.81h4.915a1 1 0 00.95-.69l1.518-4.674z"/>
3
+ </svg>
@@ -0,0 +1 @@
1
+ <svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1155 1000"><path d="m577.3 0 577.4 1000H0z" fill="#fff"/></svg>
@@ -0,0 +1 @@
1
+ <svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5" fill="#666"/></svg>
@@ -0,0 +1,156 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+
4
+ const repoRoot = path.resolve(import.meta.dirname, "../..");
5
+ const auditPath = path.join(repoRoot, "docs", "SEO-Audit-Critical.md");
6
+ const outPath = path.join(repoRoot, "SEO_AUDIT_CHECKLIST_STATUS.md");
7
+
8
+ const input = fs.readFileSync(auditPath, "utf8");
9
+
10
+ /**
11
+ * Status legend:
12
+ * ✅ Pass, ❌ Fail, ⚠️ Partial, ⏭️ N/A (or requires external tooling/deployment), ⬜ Not tested yet
13
+ */
14
+ const statusOverrides = {
15
+ // Crawlability & indexation
16
+ "C-001": { status: "✅", notes: "robots.txt route exists at /robots.txt" },
17
+ "C-002": { status: "⚠️", notes: "Localhost blocks crawling; production should allow." },
18
+ "C-003": { status: "✅", notes: "robots references /sitemap.xml" },
19
+ "C-005": { status: "✅", notes: "Non-prod (localhost) blocks crawling." },
20
+
21
+ "C-008": { status: "✅", notes: "Sitemap served at /sitemap.xml" },
22
+ "C-009": { status: "✅", notes: "Valid XML output from Next metadata route" },
23
+ "C-010": { status: "⚠️", notes: "Includes key indexable routes; dynamic coverage depends on API data." },
24
+ "C-011": { status: "✅", notes: "Noindex routes excluded (cart/checkout/auth/account/search/etc)." },
25
+ "C-012": { status: "✅", notes: "Redirects dropped by template requirement." },
26
+ "C-013": { status: "⚠️", notes: "Dynamic URLs can go stale if upstream slugs change." },
27
+ "C-014": { status: "✅", notes: "Sitemap loc uses NEXT_PUBLIC_SITE_URL" },
28
+ "C-019": { status: "✅", notes: "Products included via dynamic sitemap." },
29
+ "C-020": { status: "✅", notes: "Categories included via dynamic sitemap." },
30
+ "C-021": { status: "✅", notes: "Blog/content pages included via dynamic sitemap." },
31
+ "C-022": { status: "✅", notes: "Transactional pages excluded." },
32
+ "C-023": { status: "✅", notes: "Auth pages excluded." },
33
+ "C-024": { status: "✅", notes: "Account pages excluded." },
34
+
35
+ "C-026": { status: "✅", notes: "Cart noindex/nofollow via metadata." },
36
+ "C-027": { status: "✅", notes: "Checkout noindex/nofollow via metadata." },
37
+ "C-028": { status: "✅", notes: "Account subtree noindex/nofollow." },
38
+ "C-029": { status: "✅", notes: "Order confirmation noindex/nofollow." },
39
+ "C-030": { status: "✅", notes: "Search results default noindex/nofollow." },
40
+ "C-034": { status: "✅", notes: "Payment callback/success pages noindex/nofollow." },
41
+
42
+ "C-025": { status: "✅", notes: "Indexable pages default to index,follow." },
43
+ "C-038": { status: "✅", notes: "metadataBase configured in root layout." },
44
+
45
+ "C-035": { status: "⚠️", notes: "Canonicals added on major indexable routes; verify full coverage as routes evolve." },
46
+ "C-036": { status: "⚠️", notes: "Generally self-referencing; validate on edge-case templates." },
47
+ "C-037": { status: "✅", notes: "Canonicals render as absolute URLs via metadataBase." },
48
+
49
+ // Redirects (template requirement)
50
+ "C-046": { status: "⏭️", notes: "Redirects intentionally removed from template." },
51
+ "C-047": { status: "⏭️", notes: "Redirects intentionally removed from template." },
52
+ "C-048": { status: "⏭️", notes: "Redirects intentionally removed from template." },
53
+ "C-049": { status: "⏭️", notes: "Redirects intentionally removed from template." },
54
+ "C-050": { status: "⏭️", notes: "Handled at hosting (Vercel) / deployment layer." },
55
+ "C-051": { status: "⏭️", notes: "Handled at hosting (Vercel) / deployment layer." },
56
+
57
+ // Status codes
58
+ "T-009": { status: "✅", notes: "Dynamic routes call notFound() for invalid slugs." },
59
+ "T-010": { status: "✅", notes: "Custom app/not-found.tsx present." },
60
+ "T-012": { status: "✅", notes: "Soft-404s fixed for key dynamic routes." },
61
+ "T-001": { status: "⚠️", notes: "Key pages include SSR fallbacks; interactive/catalog content still enhanced client-side." },
62
+ "T-002": { status: "⚠️", notes: "Key catalog + PDP pages include visible SSR content without JS." },
63
+ "T-003": { status: "⚠️", notes: "Reduced client-only SEO content by adding SSR fallbacks and server-side schema." },
64
+ "T-027": { status: "⚠️", notes: "Removed explicit force-dynamic on key pages; some routes remain dynamic due to searchParams/fetch." },
65
+
66
+ // On-page SEO
67
+ "O-007": { status: "✅", notes: "PDP title uses product name via generateMetadata." },
68
+ "O-008": { status: "✅", notes: "Category titles include category name." },
69
+ "O-010": { status: "✅", notes: "generateMetadata used for key dynamic routes." },
70
+ "O-019": { status: "⚠️", notes: "One H1 on key audited pages; continue spot checks across all routes." },
71
+
72
+ // Structured data
73
+ "S-001": { status: "✅", notes: "Organization JSON-LD in root layout." },
74
+ "S-002": { status: "✅", notes: "Organization JSON-LD is site-wide." },
75
+ "S-003": { status: "✅", notes: "WebSite + SearchAction JSON-LD in root layout." },
76
+ "S-007": { status: "✅", notes: "Product JSON-LD rendered server-side on PDP." },
77
+ "S-019": { status: "✅", notes: "Offer nested under Product." },
78
+ "S-021": { status: "⚠️", notes: "BreadcrumbList present on many key pages; not guaranteed on every route yet." },
79
+ "S-022": { status: "⚠️", notes: "Breadcrumb URLs validated on major routes; continue spot checks." },
80
+ "S-024": { status: "⚠️", notes: "Breadcrumb hierarchy matches visible UI on major routes." },
81
+ "S-027": { status: "✅", notes: "BlogPosting JSON-LD on blog/content posts." },
82
+ "S-031": { status: "⏭️", notes: "Requires Google Rich Results Test." },
83
+ "S-032": { status: "⏭️", notes: "Requires Search Console." },
84
+ "S-034": { status: "✅", notes: "JSON-LD used for schema." },
85
+
86
+ // Social
87
+ "SOC-001": { status: "⚠️", notes: "og:title is present; ensure all routes override defaults when needed." },
88
+ "SOC-002": { status: "⚠️", notes: "og:description is present; ensure all routes override defaults when needed." },
89
+ "SOC-003": { status: "✅", notes: "og:image present (defaults + per-page overrides)." },
90
+ "SOC-004": { status: "⚠️", notes: "og:url present; validate correctness across all routes." },
91
+
92
+ "SOC-009": { status: "⚠️", notes: "twitter:card present on major routes." },
93
+ "SOC-010": { status: "⚠️", notes: "twitter:title present on major routes." },
94
+ "SOC-011": { status: "⚠️", notes: "twitter:description present on major routes." },
95
+ "SOC-012": { status: "⚠️", notes: "twitter:image present on major routes." },
96
+
97
+ // Mobile
98
+ "M-002": { status: "✅", notes: "Viewport meta tag present." },
99
+
100
+ // Security
101
+ "SEC-001": { status: "⏭️", notes: "Requires HTTPS deployment." },
102
+ "SEC-002": { status: "⏭️", notes: "Requires HTTPS deployment." },
103
+ "SEC-003": { status: "⏭️", notes: "Requires HTTPS deployment + browser checks." },
104
+ "SEC-004": { status: "⚠️", notes: "HSTS header set in next.config.ts for production only." },
105
+
106
+ // Analytics
107
+ "A-001": { status: "⏭️", notes: "Template leaves GA/Tag Manager to project integration." },
108
+ "A-006": { status: "⏭️", notes: "Requires Search Console verification." },
109
+ "A-007": { status: "⏭️", notes: "Requires Search Console submission." },
110
+ };
111
+
112
+ function isTableRow(line) {
113
+ return /^\|\s*[A-Z]+-\d+/.test(line);
114
+ }
115
+
116
+ function parseRow(line) {
117
+ const parts = line
118
+ .split("|")
119
+ .map((s) => s.trim())
120
+ .filter(Boolean);
121
+
122
+ // Expected: [ID, Check, Priority, Status, Notes]
123
+ const [id, check, priority] = parts;
124
+ if (!id || !check || !priority) return null;
125
+ return { id, check, priority };
126
+ }
127
+
128
+ const rows = [];
129
+ for (const line of input.split("\n")) {
130
+ if (!isTableRow(line)) continue;
131
+ if (line.includes("-----")) continue;
132
+ const row = parseRow(line);
133
+ if (!row) continue;
134
+ rows.push(row);
135
+ }
136
+
137
+ const now = new Date();
138
+ const ymd = now.toISOString().slice(0, 10);
139
+
140
+ let out = "";
141
+ out += "# SEO Audit Checklist Status (Advance Template)\n\n";
142
+ out += `Generated: ${ymd}\n\n`;
143
+ out += "Legend: ✅ Pass, ❌ Fail, ⚠️ Partial, ⏭️ N/A or requires external tooling/deployment, ⬜ Not tested yet\n\n";
144
+ out += "| ID | Priority | Status | Check | Notes |\n";
145
+ out += "| --- | --- | --- | --- | --- |\n";
146
+
147
+ for (const r of rows) {
148
+ const ov = statusOverrides[r.id];
149
+ const status = ov?.status || "⬜";
150
+ const notes = ov?.notes || "";
151
+ const check = r.check.replace(/\s+/g, " ").trim();
152
+ out += `| ${r.id} | ${r.priority} | ${status} | ${check} | ${notes} |\n`;
153
+ }
154
+
155
+ fs.writeFileSync(outPath, out, "utf8");
156
+ console.log(`Wrote ${path.relative(repoRoot, outPath)} (${rows.length} checks)`);
@@ -0,0 +1,16 @@
1
+ import type { Metadata } from "next"
2
+ import { getStoreName } from "@/app/utils/branding"
3
+
4
+ export const metadata: Metadata = {
5
+ title: `Forgot Password - ${getStoreName()}`,
6
+ description: "Reset your password securely. Enter your email address and we'll send you instructions to create a new password.",
7
+ robots: { index: false, follow: false },
8
+ }
9
+
10
+ export default function ForgotPasswordLayout({
11
+ children,
12
+ }: {
13
+ children: React.ReactNode
14
+ }) {
15
+ return children
16
+ }
@@ -0,0 +1,135 @@
1
+ 'use client';
2
+
3
+ import { useState } from 'react';
4
+ import Link from 'next/link';
5
+ import { useMutation } from '@apollo/client';
6
+ import { REQUEST_PASSWORD_RESET_MUTATION, type RequestPasswordResetData, type RequestPasswordResetVariables } from '@/graphql/mutations/requestPasswordReset';
7
+ import Input from '@/app/components/reuseableUI/input';
8
+ import PrimaryButton from '@/app/components/reuseableUI/primaryButton';
9
+ import { SpinnerIcon } from '@/app/utils/svgs/spinnerIcon';
10
+ import { ErrorTag } from '@/app/components/reuseableUI/errorTag';
11
+
12
+ export default function ForgotPasswordPage() {
13
+ const [email, setEmail] = useState('');
14
+ const [message, setMessage] = useState<string | null>(null);
15
+ const [apiError, setApiError] = useState<string | null>(null);
16
+ const [fieldError, setFieldError] = useState('');
17
+ const [requestReset, { loading }] = useMutation<RequestPasswordResetData, RequestPasswordResetVariables>(REQUEST_PASSWORD_RESET_MUTATION);
18
+
19
+ const handleSubmit = async (e: React.FormEvent) => {
20
+ e.preventDefault();
21
+ setMessage(null);
22
+ setApiError(null);
23
+ setFieldError('');
24
+
25
+ if (!email.trim()) {
26
+ setFieldError('Email is required.');
27
+ return;
28
+ } else if (!/^\S+@\S+\.\S+$/.test(email)) {
29
+ setFieldError('Invalid email address.');
30
+ return;
31
+ }
32
+
33
+ try {
34
+ const siteUrl = process.env.NEXT_PUBLIC_SITE_URL || (typeof window !== 'undefined' ? window.location.origin : '');
35
+ const redirectUrl = `${siteUrl}/account/reset-password`;
36
+
37
+ const { data } = await requestReset({
38
+ variables: { email, redirectUrl },
39
+ });
40
+
41
+ const errs = data?.requestPasswordReset.errors || [];
42
+ if (errs.length) {
43
+ setApiError(errs[0]?.message || 'Unable to send reset email.');
44
+ return;
45
+ }
46
+
47
+ setMessage('If an account exists for this email, a password reset link has been sent. Please check your inbox and spam folder.');
48
+ } catch (err) {
49
+ console.error('Request password reset error', err);
50
+ setApiError('Unable to send reset email. Please try again.');
51
+ }
52
+ };
53
+
54
+ return (
55
+ <div className="flex items-center justify-center min-h-screen bg-white px-4 md:px-6 py-12 md:py-16 lg:py-24">
56
+ <div className="flex w-full max-w-[756px] flex-col justify-center items-start gap-6">
57
+ {/* Title */}
58
+ <div className="flex flex-col items-start gap-1 md:gap-2 self-stretch">
59
+ <h1 className="text-lg md:text-xl font-semibold tracking-[-0.05px] uppercase mb-1">
60
+ FORGOT PASSWORD
61
+ </h1>
62
+ <p
63
+ style={{ color: "var(--color-secondary-600)" }}
64
+ className="text-sm mb-6"
65
+ >
66
+ Please enter the email linked to your account, and we&apos;ll send
67
+ you a code to reset your password.
68
+ </p>
69
+ </div>
70
+ {/* Form */}
71
+ <form onSubmit={handleSubmit} className="space-y-6 w-full">
72
+ {/* Email Field */}
73
+ <div className="w-full">
74
+ <Input
75
+ label="EMAIL"
76
+ type="email"
77
+ name="email"
78
+ placeholder="jamescharles@gmail.com"
79
+ value={email}
80
+ onChange={(e) => setEmail(e.target.value)}
81
+ className="mt-1.5 border border-[var(--color-secondary-200)] leading-5"
82
+ hasError={!!fieldError}
83
+ />
84
+ {fieldError && (
85
+ <div
86
+ style={{ color: "var(--color-primary-600)" }}
87
+ className="text-sm leading-5 tracking-[-0.035px] mt-1.5"
88
+ >
89
+ {fieldError}
90
+ </div>
91
+ )}
92
+ {message && (
93
+ <div
94
+ style={{ color: "var(--color-secondary-700)" }}
95
+ className="mb-2 text-xs md:text-sm mt-2"
96
+ role="status"
97
+ >
98
+ {message}
99
+ </div>
100
+ )}
101
+ </div>
102
+ {apiError && <ErrorTag message={apiError} />}
103
+ {/* Buttons */}
104
+ <div className="flex gap-3">
105
+ <Link
106
+ href="/account/login"
107
+ style={{
108
+ backgroundColor: "var(--color-secondary-200)",
109
+ color: "var(--color-secondary-700)",
110
+ }}
111
+ className="w-1/2 text-center text-sm lg:text-base py-2.5 md:py-3 px-4 font-semibold uppercase"
112
+ >
113
+ Cancel
114
+ </Link>
115
+
116
+ <PrimaryButton
117
+ type="submit"
118
+ disabled={loading}
119
+ content={
120
+ loading ? (
121
+ <span className="flex items-center justify-center w-full">
122
+ {SpinnerIcon}
123
+ </span>
124
+ ) : (
125
+ "Send Email"
126
+ )
127
+ }
128
+ className="w-1/2 text-sm lg:text-base py-2.5 md:py-3 px-4 font-semibold "
129
+ />
130
+ </div>
131
+ </form>
132
+ </div>
133
+ </div>
134
+ );
135
+ }
@@ -0,0 +1,16 @@
1
+ import type { Metadata } from "next"
2
+ import { getStoreName } from "@/app/utils/branding"
3
+
4
+ export const metadata: Metadata = {
5
+ title: `Login - ${getStoreName()}`,
6
+ description: "Sign in to your account to access order history, saved addresses, and faster checkout. New customers can create an account.",
7
+ robots: { index: false, follow: false },
8
+ }
9
+
10
+ export default function LoginLayout({
11
+ children,
12
+ }: {
13
+ children: React.ReactNode
14
+ }) {
15
+ return children
16
+ }
@@ -0,0 +1,288 @@
1
+ "use client";
2
+
3
+ import React, { Suspense, useState } from "react";
4
+ import Link from "next/link";
5
+ import { useRouter } from "next/navigation";
6
+ import { useSearchParams } from "next/navigation";
7
+ import { useGlobalStore } from "@/store/useGlobalStore";
8
+ import { useMutation } from "@apollo/client";
9
+ import {
10
+ SIGN_IN_MUTATION,
11
+ type SignInData,
12
+ type SignInVariables,
13
+ } from "@/graphql/mutations/signIn";
14
+ import client from "@/graphql/client";
15
+ import AccountTabs from "@/app/components/account/AccountTabs";
16
+ import Input from "@/app/components/reuseableUI/input";
17
+ import PrimaryButton from "@/app/components/reuseableUI/primaryButton";
18
+ import { SpinnerIcon } from "@/app/utils/svgs/spinnerIcon";
19
+ import { ErrorTag } from "@/app/components/reuseableUI/errorTag";
20
+ import { useRecaptcha } from "@/hooks/useRecaptcha";
21
+ import ReCAPTCHA from "react-google-recaptcha";
22
+ import { useAppConfiguration } from "@/app/components/providers/ServerAppConfigurationProvider";
23
+
24
+ function LoginInner() {
25
+ const [formData, setFormData] = useState({
26
+ email: "",
27
+ password: "",
28
+ });
29
+ const [apiError, setApiError] = useState<string | null>(null);
30
+ const [fieldErrors, setFieldErrors] = useState({
31
+ email: "",
32
+ password: "",
33
+ recaptcha: "",
34
+ });
35
+ const [recaptchaValue, setRecaptchaValue] = useState<string | null>(null);
36
+
37
+ const searchParams = useSearchParams();
38
+ const sessionExpired = searchParams.get("sessionExpired") === "true";
39
+
40
+ const router = useRouter();
41
+ const { login } = useGlobalStore();
42
+ const { recaptchaRef, resetRecaptcha } = useRecaptcha();
43
+ const config = useAppConfiguration();
44
+ const [signIn, { loading }] = useMutation<SignInData, SignInVariables>(
45
+ SIGN_IN_MUTATION
46
+ );
47
+
48
+ const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
49
+ setFormData({
50
+ ...formData,
51
+ [e.target.name]: e.target.value,
52
+ });
53
+ };
54
+
55
+ const handleSubmit = async (e: React.FormEvent) => {
56
+ e.preventDefault();
57
+ setApiError(null);
58
+ setFieldErrors({ email: "", password: "", recaptcha: "" });
59
+ const errors = { email: "", password: "", recaptcha: "" };
60
+
61
+ let hasError = false;
62
+
63
+ if (!formData.email.trim()) {
64
+ errors.email = "Email is required.";
65
+ hasError = true;
66
+ }
67
+ if (!formData.password.trim()) {
68
+ errors.password = "Password is required.";
69
+ hasError = true;
70
+ }
71
+
72
+ // Only validate reCAPTCHA if enabled for login
73
+ if (config.isRecaptchaEnabledFor('login') && !recaptchaValue) {
74
+ errors.recaptcha = "Please complete the reCAPTCHA verification.";
75
+ hasError = true;
76
+ }
77
+
78
+ if (hasError) {
79
+ setFieldErrors(errors);
80
+ return;
81
+ }
82
+
83
+ try {
84
+ const { data } = await signIn({
85
+ variables: {
86
+ email: formData.email,
87
+ password: formData.password,
88
+ },
89
+ });
90
+
91
+ const result = data?.tokenCreate;
92
+ if (!result) {
93
+ setApiError("Unexpected error. Please try again.");
94
+ return;
95
+ }
96
+
97
+ if (result.errors && result.errors.length > 0) {
98
+ // Prefer first error message
99
+ setApiError(
100
+ result.errors[0]?.message ||
101
+ "Seems like your entered Incorrect Email or Password. Please try again."
102
+ );
103
+ return;
104
+ }
105
+
106
+ // Save tokens for subsequent requests
107
+ if (result.token) localStorage.setItem("token", result.token);
108
+ if (result.refreshToken)
109
+ localStorage.setItem("refreshToken", result.refreshToken);
110
+ // Set HttpOnly cookies via API for middleware/SSR
111
+ try {
112
+ await fetch("/api/auth/set", {
113
+ method: "POST",
114
+ headers: { "Content-Type": "application/json" },
115
+ body: JSON.stringify({
116
+ token: result.token,
117
+ refreshToken: result.refreshToken,
118
+ }),
119
+ });
120
+ } catch {}
121
+
122
+ // Update global user state
123
+ if (result.user) {
124
+ const { id, email, firstName, lastName } = result.user;
125
+ const displayName =
126
+ [firstName, lastName].filter(Boolean).join(" ") || email;
127
+ login({ id, email, name: displayName });
128
+ }
129
+
130
+ // IMPORTANT: clear Apollo cache so user-specific queries (e.g., me) don't show stale data
131
+ try {
132
+ await client.clearStore();
133
+ } catch (e) {
134
+ // non-fatal; proceed with navigation
135
+ console.warn("Apollo clearStore failed", e);
136
+ }
137
+
138
+ const next = searchParams.get("next") || "/";
139
+ router.push(next);
140
+ } catch (err) {
141
+ console.error("Sign-in error", err);
142
+ setApiError("Unable to sign in. Please try again.");
143
+
144
+ // Reset reCAPTCHA on error
145
+ if (config.isRecaptchaEnabledFor('login')) {
146
+ setRecaptchaValue(null);
147
+ resetRecaptcha();
148
+ }
149
+ }
150
+ };
151
+
152
+ return (
153
+ <div className="flex items-center justify-center min-h-screen w-full px-4 md:px-6 py-12 md:py-16 lg:py-24 ">
154
+ <div className="flex w-full max-w-[756px] flex-col justify-center items-start gap-6">
155
+ {/* Session expired message */}
156
+ {sessionExpired && (
157
+ <div className="w-full p-4 mb-4 text-red-700 bg-red-100 rounded-md">
158
+ Your session has expired. Please log in again to continue.
159
+ </div>
160
+ )}
161
+
162
+ {/* Tabs */}
163
+ <AccountTabs />
164
+ {/* Form */}
165
+ <form
166
+ onSubmit={handleSubmit}
167
+ className="flex flex-col items-start gap-4 md:gap-5 self-stretch"
168
+ >
169
+ <div className="w-full">
170
+ <Input
171
+ label="EMAIL"
172
+ type="email"
173
+ name="email"
174
+ placeholder="jamescharles@gmail.com"
175
+ value={formData.email}
176
+ onChange={handleChange}
177
+ hasError={!!fieldErrors.email}
178
+ className="mt-1.5 py-3 px-4 text-sm leading-5 tracking-[-0.035px]"
179
+ />
180
+ {fieldErrors.email && (
181
+ <div
182
+ style={{ color: "var(--color-primary-600)" }}
183
+ className="text-sm leading-5 tracking-[-0.035px] mt-1.5"
184
+ >
185
+ {fieldErrors.email}
186
+ </div>
187
+ )}
188
+ </div>
189
+ <div className="w-full relative">
190
+ <Input
191
+ label="PASSWORD"
192
+ type="password"
193
+ name="password"
194
+ placeholder="********"
195
+ value={formData.password}
196
+ onChange={handleChange}
197
+ hasError={!!fieldErrors.password}
198
+ className="w-full mt-1.5 py-3 px-4 text-sm leading-5 tracking-[-0.035px]"
199
+ />
200
+ {fieldErrors.password && (
201
+ <div
202
+ style={{ color: "var(--color-primary-600)" }}
203
+ className="text-sm leading-5 tracking-[-0.035px] mt-1.5"
204
+ >
205
+ {fieldErrors.password}
206
+ </div>
207
+ )}
208
+ </div>
209
+ <div className="text-right w-full">
210
+ <Link
211
+ href="/account/forgot-password"
212
+ style={{ color: "var(--color-secondary-800)" }}
213
+ className="font-secondary text-xs md:text-sm font-semibold leading-5 tracking-[-0.035px]"
214
+ >
215
+ FORGOT PASSWORD?
216
+ </Link>
217
+ </div>
218
+
219
+ {/* Conditional reCAPTCHA for login */}
220
+ {config.isRecaptchaEnabledFor('login') && (
221
+ <div className="w-full flex flex-col items-start">
222
+ <ReCAPTCHA
223
+ ref={recaptchaRef}
224
+ sitekey={config.getGoogleRecaptchaConfig()?.site_key || ''}
225
+ theme="light"
226
+ size="normal"
227
+ onChange={(value) => {
228
+ setRecaptchaValue(value);
229
+ // Clear recaptcha error when user completes it
230
+ if (value && fieldErrors.recaptcha) {
231
+ setFieldErrors(prev => ({
232
+ ...prev,
233
+ recaptcha: ""
234
+ }));
235
+ }
236
+ }}
237
+ onExpired={() => {
238
+ setRecaptchaValue(null);
239
+ resetRecaptcha();
240
+ }}
241
+ onError={() => {
242
+ setRecaptchaValue(null);
243
+ resetRecaptcha();
244
+ }}
245
+ />
246
+ {fieldErrors.recaptcha && (
247
+ <div
248
+ style={{ color: "var(--color-primary-600)" }}
249
+ className="text-sm leading-5 tracking-[-0.035px] mt-1.5"
250
+ >
251
+ {fieldErrors.recaptcha}
252
+ </div>
253
+ )}
254
+ </div>
255
+ )}
256
+
257
+ {apiError && (
258
+ <ErrorTag message={apiError } />
259
+ )}
260
+ <PrimaryButton
261
+ type="submit"
262
+ disabled={loading}
263
+ content={
264
+ loading ? (
265
+ <span className="flex items-center justify-center w-full">
266
+ {SpinnerIcon}
267
+ </span>
268
+ ) : (
269
+ "Login"
270
+ )
271
+ }
272
+ className="w-full text-sm lg:text-base font-semibold leading-[24px] tracking-[-0.04px] py-2 md:py-3 px-4 mt-1"
273
+ />
274
+ </form>
275
+ </div>
276
+ </div>
277
+ );
278
+ }
279
+
280
+ export default function LoginPage() {
281
+ return (
282
+ <Suspense
283
+ fallback={<div className="max-w-7xl mx-auto px-4 py-8">Loading...</div>}
284
+ >
285
+ <LoginInner />
286
+ </Suspense>
287
+ );
288
+ }