@akinon/projectzero 2.0.0-beta.2 → 2.0.0-beta.21

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 (300) hide show
  1. package/CHANGELOG.md +155 -6
  2. package/app-template/.env.example +8 -0
  3. package/app-template/.github/instructions/account.instructions.md +749 -0
  4. package/app-template/.github/instructions/checkout.instructions.md +678 -0
  5. package/app-template/.github/instructions/default.instructions.md +279 -0
  6. package/app-template/.github/instructions/edge-cases.instructions.md +73 -0
  7. package/app-template/.github/instructions/routing.instructions.md +603 -0
  8. package/app-template/.github/instructions/settings.instructions.md +338 -0
  9. package/app-template/.gitignore +5 -0
  10. package/app-template/AGENTS.md +7 -0
  11. package/app-template/CHANGELOG.md +1783 -61
  12. package/app-template/Procfile +1 -1
  13. package/app-template/README.md +6 -0
  14. package/app-template/akinon.json +1 -4
  15. package/app-template/build.sh +10 -0
  16. package/app-template/config/prebuild-tests.json +5 -0
  17. package/app-template/docs/advanced-usage.md +111 -0
  18. package/app-template/docs/plugins.md +60 -25
  19. package/app-template/docs/sentry-usage.md +35 -0
  20. package/app-template/jest.config.ts +2 -2
  21. package/app-template/next-env.d.ts +1 -0
  22. package/app-template/next.config.mjs +8 -6
  23. package/app-template/package.json +61 -50
  24. package/app-template/postcss.config.mjs +5 -0
  25. package/app-template/public/amex.svg +12 -0
  26. package/app-template/public/apple-pay.svg +16 -0
  27. package/app-template/public/assets/images/product-placeholder-1.jpg +0 -0
  28. package/app-template/public/assets/images/product-placeholder-2.jpg +0 -0
  29. package/app-template/public/assets/images/product-placeholder-3.jpg +0 -0
  30. package/app-template/public/assets/images/product-placeholder-4.jpg +0 -0
  31. package/app-template/public/google-pay.svg +16 -0
  32. package/app-template/public/locales/en/account.json +13 -4
  33. package/app-template/public/locales/en/auth.json +6 -7
  34. package/app-template/public/locales/en/basket.json +6 -6
  35. package/app-template/public/locales/en/blog.json +7 -0
  36. package/app-template/public/locales/en/category.json +3 -1
  37. package/app-template/public/locales/en/checkout.json +17 -4
  38. package/app-template/public/locales/en/common.json +71 -3
  39. package/app-template/public/locales/en/forgot_password.json +6 -7
  40. package/app-template/public/locales/en/product.json +84 -4
  41. package/app-template/public/locales/tr/account.json +13 -4
  42. package/app-template/public/locales/tr/auth.json +16 -17
  43. package/app-template/public/locales/tr/basket.json +4 -4
  44. package/app-template/public/locales/tr/blog.json +7 -0
  45. package/app-template/public/locales/tr/category.json +3 -1
  46. package/app-template/public/locales/tr/checkout.json +48 -36
  47. package/app-template/public/locales/tr/common.json +70 -2
  48. package/app-template/public/locales/tr/forgot_password.json +12 -13
  49. package/app-template/public/locales/tr/product.json +82 -0
  50. package/app-template/public/logo.svg +3 -27
  51. package/app-template/public/mastercard.svg +14 -0
  52. package/app-template/public/masterpass-javascript-sdk-web.min.js +1 -0
  53. package/app-template/public/promotion-banner.jpg +0 -0
  54. package/app-template/public/shop-pay.svg +12 -0
  55. package/app-template/public/visa.svg +12 -0
  56. package/app-template/src/__tests__/middleware-matcher.test.ts +135 -0
  57. package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/[...prettyurl]/page.tsx +11 -11
  58. package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/account/layout.tsx +4 -3
  59. package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/account/orders/[id]/cancellation/page.tsx +105 -13
  60. package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/account/orders/[id]/page.tsx +136 -52
  61. package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/account/profile/page.tsx +2 -2
  62. package/app-template/src/app/[pz]/blog/[slug]/page.tsx +120 -0
  63. package/app-template/src/app/[pz]/category/[pk]/page.tsx +37 -0
  64. package/app-template/src/app/[pz]/error.tsx +17 -0
  65. package/app-template/src/app/[pz]/flat-page/[pk]/page.tsx +23 -0
  66. package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/forms/[pk]/generate/page.tsx +1 -2
  67. package/app-template/src/app/[pz]/group-product/[pk]/page.tsx +93 -0
  68. package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/landing-page/[pk]/page.tsx +2 -4
  69. package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/layout.tsx +6 -11
  70. package/app-template/src/app/[pz]/list/page.tsx +26 -0
  71. package/app-template/src/app/{[commerce]/[locale]/[currency]/pz-not-found/page.tsx → [pz]/not-found.tsx} +5 -7
  72. package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/orders/checkout/page.tsx +7 -4
  73. package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/orders/completed/[token]/page.tsx +6 -4
  74. package/app-template/src/app/[pz]/page.tsx +28 -0
  75. package/app-template/src/app/[pz]/pages/[slug]/page.tsx +19 -0
  76. package/app-template/src/app/[pz]/product/[pk]/page.tsx +102 -0
  77. package/app-template/src/app/[pz]/special-page/[pk]/page.tsx +35 -0
  78. package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/users/email-set-primary/[[...id]]/page.tsx +3 -4
  79. package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/users/registration/account-confirm-email/[[...id]]/page.tsx +3 -3
  80. package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/users/reset/[[...id]]/page.tsx +41 -5
  81. package/app-template/src/app/[pz]/xml-sitemap/[node]/route.ts +73 -0
  82. package/app-template/src/app/api/auth/[...nextauth]/route.ts +3 -0
  83. package/app-template/src/app/api/barcode-search/route.ts +1 -0
  84. package/app-template/src/app/api/cache/route.ts +1 -1
  85. package/app-template/src/app/api/form/[...id]/route.ts +1 -7
  86. package/app-template/src/app/api/image-proxy/route.ts +1 -0
  87. package/app-template/src/app/api/logout/route.ts +1 -1
  88. package/app-template/src/app/api/product-categories/route.ts +1 -0
  89. package/app-template/src/app/api/similar-product-list/route.ts +1 -0
  90. package/app-template/src/app/api/similar-products/route.ts +1 -0
  91. package/app-template/src/app/api/theme-settings/route.ts +12 -0
  92. package/app-template/src/app/api/virtual-try-on/limited-categories/route.ts +1 -0
  93. package/app-template/src/app/api/virtual-try-on/route.ts +1 -0
  94. package/app-template/src/app/api/web-vitals/route.ts +1 -1
  95. package/app-template/src/assets/fonts/pz-icon.css +211 -49
  96. package/app-template/src/assets/fonts/pz-icon.eot +0 -0
  97. package/app-template/src/assets/fonts/pz-icon.html +486 -0
  98. package/app-template/src/assets/fonts/pz-icon.scss +373 -49
  99. package/app-template/src/assets/fonts/pz-icon.svg +215 -53
  100. package/app-template/src/assets/fonts/pz-icon.ttf +0 -0
  101. package/app-template/src/assets/fonts/pz-icon.woff +0 -0
  102. package/app-template/src/assets/fonts/pz-icon.woff2 +0 -0
  103. package/app-template/src/assets/globals.scss +37 -34
  104. package/app-template/src/assets/icons/arrow-right.svg +3 -0
  105. package/app-template/src/assets/icons/cart.svg +4 -12
  106. package/app-template/src/assets/icons/check.svg +2 -18
  107. package/app-template/src/assets/icons/chevron-down.svg +2 -7
  108. package/app-template/src/assets/icons/delete.svg +3 -0
  109. package/app-template/src/assets/icons/facebook.svg +2 -8
  110. package/app-template/src/assets/icons/fav-off.svg +5 -0
  111. package/app-template/src/assets/icons/fav-on.svg +5 -0
  112. package/app-template/src/assets/icons/filter-and-sort.svg +3 -0
  113. package/app-template/src/assets/icons/heart.svg +3 -0
  114. package/app-template/src/assets/icons/instagram.svg +2 -13
  115. package/app-template/src/assets/icons/materials.svg +3 -0
  116. package/app-template/src/assets/icons/person.svg +4 -0
  117. package/app-template/src/assets/icons/pinterest.svg +5 -11
  118. package/app-template/src/assets/icons/ruler.svg +3 -0
  119. package/app-template/src/assets/icons/search.svg +8 -11
  120. package/app-template/src/assets/icons/share.svg +2 -9
  121. package/app-template/src/assets/icons/snapchat.svg +3 -0
  122. package/app-template/src/assets/icons/tiktok.svg +3 -0
  123. package/app-template/src/assets/icons/tumblr.svg +6 -0
  124. package/app-template/src/assets/icons/twitter.svg +2 -10
  125. package/app-template/src/assets/icons/vimeo.svg +3 -0
  126. package/app-template/src/assets/icons/youtube.svg +3 -0
  127. package/app-template/src/assets/icons/zoom.svg +8 -0
  128. package/app-template/src/auth.ts +3 -0
  129. package/app-template/src/components/__tests__/link.test.tsx +2 -0
  130. package/app-template/src/components/accordion.tsx +48 -23
  131. package/app-template/src/components/action-tooltip.tsx +160 -0
  132. package/app-template/src/components/button.tsx +50 -35
  133. package/app-template/src/components/carousel-core.tsx +4 -11
  134. package/app-template/src/components/checkbox.tsx +2 -1
  135. package/app-template/src/components/currency-select.tsx +150 -4
  136. package/app-template/src/components/file-input.tsx +64 -2
  137. package/app-template/src/components/generate-form-fields.tsx +49 -10
  138. package/app-template/src/components/icon.tsx +5 -6
  139. package/app-template/src/components/index.ts +4 -1
  140. package/app-template/src/components/input.tsx +8 -2
  141. package/app-template/src/components/language-select.tsx +88 -2
  142. package/app-template/src/components/modal.tsx +34 -16
  143. package/app-template/src/components/pagination.tsx +133 -20
  144. package/app-template/src/components/price.tsx +1 -1
  145. package/app-template/src/components/pwa-tags.tsx +1 -0
  146. package/app-template/src/components/quantity-input.tsx +63 -0
  147. package/app-template/src/components/quantity-selector.tsx +215 -0
  148. package/app-template/src/components/route-handler.tsx +50 -0
  149. package/app-template/src/components/select.tsx +86 -54
  150. package/app-template/src/components/tabs.tsx +2 -2
  151. package/app-template/src/components/types/index.ts +55 -2
  152. package/app-template/src/components/widget-content.tsx +323 -0
  153. package/app-template/src/data/server/theme.ts +70 -0
  154. package/app-template/src/hooks/use-fav-button.tsx +9 -10
  155. package/app-template/src/hooks/use-product-cart.ts +80 -0
  156. package/app-template/src/hooks/use-stock-alert.ts +74 -0
  157. package/app-template/src/hooks/use-theme-settings.ts +42 -0
  158. package/app-template/src/lib/fonts.ts +149 -0
  159. package/app-template/src/middleware.ts +1 -0
  160. package/app-template/src/plugins.js +13 -2
  161. package/app-template/src/redux/middlewares/category.ts +6 -5
  162. package/app-template/src/redux/reducers/category.ts +1 -1
  163. package/app-template/src/redux/store.ts +21 -1
  164. package/app-template/src/routes/index.ts +8 -7
  165. package/app-template/src/settings.js +5 -3
  166. package/app-template/src/types/hookform-resolvers-yup.d.ts +28 -0
  167. package/app-template/src/types/index.ts +74 -3
  168. package/app-template/src/types/next-auth.d.ts +2 -2
  169. package/app-template/src/types/widget.ts +169 -0
  170. package/app-template/src/utils/__tests__/theme-page-context.test.ts +145 -0
  171. package/app-template/src/utils/convert-facet-search-params.ts +1 -1
  172. package/app-template/src/utils/formatDate.ts +48 -0
  173. package/app-template/src/utils/styles.ts +71 -0
  174. package/app-template/src/utils/theme-page-context.ts +309 -0
  175. package/app-template/src/utils/variant-validation.ts +41 -0
  176. package/app-template/src/views/account/address-form.tsx +8 -4
  177. package/app-template/src/views/account/contact-form.tsx +148 -136
  178. package/app-template/src/views/account/content-header.tsx +2 -2
  179. package/app-template/src/views/account/faq/faq-tabs.tsx +8 -2
  180. package/app-template/src/views/account/favorite-item.tsx +1 -1
  181. package/app-template/src/views/account/order.tsx +10 -8
  182. package/app-template/src/views/account/orders/order-cancellation-item.tsx +4 -3
  183. package/app-template/src/views/account/orders/order-detail-header.tsx +1 -1
  184. package/app-template/src/views/anonymous-tracking/order-detail/index.tsx +44 -37
  185. package/app-template/src/views/basket/basket-item.tsx +7 -1
  186. package/app-template/src/views/basket/summary.tsx +16 -0
  187. package/app-template/src/views/breadcrumb.tsx +2 -2
  188. package/app-template/src/views/category/category-active-filters.tsx +1 -1
  189. package/app-template/src/views/category/category-banner.tsx +4 -23
  190. package/app-template/src/views/category/category-header.tsx +12 -6
  191. package/app-template/src/views/category/category-info.tsx +4 -3
  192. package/app-template/src/views/category/filters/filter-item.tsx +138 -42
  193. package/app-template/src/views/category/filters/index.tsx +3 -3
  194. package/app-template/src/views/category/layout.tsx +1 -0
  195. package/app-template/src/views/checkout/auth.tsx +64 -40
  196. package/app-template/src/views/checkout/layout/header.tsx +9 -5
  197. package/app-template/src/views/checkout/steps/payment/index.tsx +1 -1
  198. package/app-template/src/views/checkout/steps/payment/options/credit-card/index.tsx +21 -5
  199. package/app-template/src/views/checkout/steps/payment/options/funds-transfer.tsx +25 -5
  200. package/app-template/src/views/checkout/steps/payment/options/loyalty.tsx +21 -2
  201. package/app-template/src/views/checkout/steps/payment/options/redirection.tsx +27 -5
  202. package/app-template/src/views/checkout/steps/payment/options/store-credit.tsx +464 -0
  203. package/app-template/src/views/checkout/steps/shipping/address-box.tsx +30 -17
  204. package/app-template/src/views/checkout/steps/shipping/addresses.tsx +1 -1
  205. package/app-template/src/views/checkout/summary.tsx +10 -0
  206. package/app-template/src/views/find-in-store/index.tsx +2 -2
  207. package/app-template/src/views/guest-login/index.tsx +62 -58
  208. package/app-template/src/views/header/action-menu.tsx +7 -4
  209. package/app-template/src/views/header/index.tsx +1 -1
  210. package/app-template/src/views/header/mini-basket.tsx +14 -3
  211. package/app-template/src/views/header/mobile-hamburger-button.tsx +5 -8
  212. package/app-template/src/views/header/mobile-menu.tsx +12 -0
  213. package/app-template/src/views/header/search/index.tsx +23 -1
  214. package/app-template/src/views/installment-options/index.tsx +1 -1
  215. package/app-template/src/views/login/index.tsx +89 -56
  216. package/app-template/src/views/otp-login/index.tsx +23 -20
  217. package/app-template/src/views/product/index.ts +1 -0
  218. package/app-template/src/views/product/layout.tsx +26 -6
  219. package/app-template/src/views/product/price-wrapper.tsx +3 -24
  220. package/app-template/src/views/product/product-actions.tsx +165 -0
  221. package/app-template/src/views/product/product-info.tsx +75 -237
  222. package/app-template/src/views/product/product-share.tsx +58 -0
  223. package/app-template/src/views/product/product-variants.tsx +26 -0
  224. package/app-template/src/views/product/slider.tsx +21 -0
  225. package/app-template/src/views/product/variant.tsx +69 -41
  226. package/app-template/src/views/register/index.tsx +54 -44
  227. package/app-template/src/views/share/index.tsx +9 -6
  228. package/app-template/src/views/widgets/home-hero-slider-content.tsx +41 -39
  229. package/app-template/src/widgets/flatpages/about-us/index.tsx +78 -0
  230. package/app-template/src/widgets/flatpages/blog-list/index.tsx +129 -0
  231. package/app-template/src/widgets/footer-menu.tsx +6 -2
  232. package/app-template/src/widgets/footer-subscription/footer-subscription-form.tsx +17 -14
  233. package/app-template/src/widgets/home-stories-eng.tsx +42 -34
  234. package/app-template/src/widgets/index.ts +7 -0
  235. package/app-template/src/widgets/schemas/about-us.json +46 -0
  236. package/app-template/src/widgets/schemas/blog-list.json +37 -0
  237. package/app-template/src/widgets/schemas/blog.json +29 -0
  238. package/app-template/tailwind.config.js +19 -7
  239. package/app-template/tsconfig.json +29 -11
  240. package/codemods/migrate-segments/index.js +591 -0
  241. package/codemods/sentry-9/index.js +30 -0
  242. package/codemods/sentry-9/remove-sentry-configs.js +14 -0
  243. package/codemods/sentry-9/remove-sentry-dependency.js +25 -0
  244. package/codemods/sentry-9/replace-error-page.js +32 -0
  245. package/codemods/update-tailwind-config/index.js +30 -0
  246. package/codemods/update-tailwind-config/transform.js +102 -0
  247. package/commands/codemod.ts +17 -0
  248. package/commands/index.ts +3 -1
  249. package/commands/plugins.ts +115 -46
  250. package/dist/codemods/sentry-9/templates/error.js +14 -0
  251. package/dist/commands/codemod.js +15 -0
  252. package/dist/commands/index.js +3 -1
  253. package/dist/commands/plugins.js +108 -36
  254. package/package.json +3 -2
  255. package/app-template/postcss.config.js +0 -6
  256. package/app-template/sentry.client.config.ts +0 -16
  257. package/app-template/sentry.edge.config.ts +0 -3
  258. package/app-template/sentry.properties +0 -4
  259. package/app-template/sentry.server.config.ts +0 -3
  260. package/app-template/src/app/[commerce]/[locale]/[currency]/category/[pk]/page.tsx +0 -22
  261. package/app-template/src/app/[commerce]/[locale]/[currency]/error.tsx +0 -20
  262. package/app-template/src/app/[commerce]/[locale]/[currency]/flat-page/[pk]/page.tsx +0 -20
  263. package/app-template/src/app/[commerce]/[locale]/[currency]/group-product/[pk]/page.tsx +0 -74
  264. package/app-template/src/app/[commerce]/[locale]/[currency]/list/page.tsx +0 -18
  265. package/app-template/src/app/[commerce]/[locale]/[currency]/page.tsx +0 -50
  266. package/app-template/src/app/[commerce]/[locale]/[currency]/product/[pk]/loading.tsx +0 -67
  267. package/app-template/src/app/[commerce]/[locale]/[currency]/product/[pk]/page.tsx +0 -84
  268. package/app-template/src/app/[commerce]/[locale]/[currency]/special-page/[pk]/page.tsx +0 -27
  269. package/app-template/src/app/[commerce]/[locale]/[currency]/xml-sitemap/[node]/route.ts +0 -25
  270. package/app-template/src/pages/api/auth/[...nextauth].ts +0 -3
  271. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/account/address/page.tsx +0 -0
  272. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/account/change-email/page.tsx +0 -0
  273. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/account/change-password/page.tsx +0 -0
  274. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/account/contact/page.tsx +0 -0
  275. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/account/coupons/page.tsx +0 -0
  276. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/account/email-verification/page.tsx +0 -0
  277. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/account/faq/page.tsx +0 -0
  278. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/account/favourite-products/page.tsx +0 -0
  279. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/account/my-quotations/page.tsx +0 -0
  280. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/account/orders/[id]/layout.tsx +0 -0
  281. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/account/orders/page.tsx +0 -0
  282. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/account/page.tsx +0 -0
  283. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/address/stores/page.tsx +0 -0
  284. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/anonymous-tracking/page.tsx +0 -0
  285. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/auth/oauth-login/page.tsx +0 -0
  286. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/auth/page.tsx +0 -0
  287. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/basket/page.tsx +0 -0
  288. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/basket-b2b/page.tsx +0 -0
  289. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/category/[pk]/loading.tsx +0 -0
  290. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/client-root.tsx +0 -0
  291. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/contact-us/page.tsx +0 -0
  292. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/flat-page/[pk]/loading.tsx +0 -0
  293. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/group-product/[pk]/loading.tsx +0 -0
  294. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/landing-page/[pk]/loading.tsx +0 -0
  295. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/list/loading.tsx +0 -0
  296. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/orders/completed/[token]/layout.tsx +0 -0
  297. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/special-page/[pk]/loading.tsx +0 -0
  298. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/template.tsx +0 -0
  299. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/users/password/reset/page.tsx +0 -0
  300. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/xml-sitemap/route.ts +0 -0
@@ -1,10 +1,8 @@
1
1
  import { getLandingPageData } from '@akinon/next/data/server';
2
2
  import { withSegmentDefaults } from '@akinon/next/hocs/server';
3
- import { PageProps } from '@akinon/next/types';
4
-
5
- async function Page(props: PageProps<{ pk: number }>) {
6
- const params = await props.params;
3
+ import { ResolvedPageProps } from '@akinon/next/types';
7
4
 
5
+ async function Page({ params }: ResolvedPageProps<{ pk: number }>) {
8
6
  const data = await getLandingPageData({ pk: params.pk });
9
7
 
10
8
  const content = data.landing_page;
@@ -6,13 +6,14 @@ import Footer from '@theme/views/footer';
6
6
  import Header from '@theme/views/header';
7
7
  import RootModal from '@theme/views/root-modal';
8
8
  import ClientRoot from './client-root';
9
- import { RootLayoutProps, Metadata } from '@akinon/next/types';
9
+ import { ResolvedRootLayoutProps, Metadata } from '@akinon/next/types';
10
10
  import { getSeoData } from '@akinon/next/data/server';
11
11
  import PzRoot from '@akinon/next/components/pz-root';
12
12
  import MobileAppToggler from '@akinon/next/components/mobile-app-toggler';
13
13
  import pwaTags from '@theme/components/pwa-tags';
14
14
  import type { Viewport } from 'next';
15
15
  import settings from '@theme/settings';
16
+ import RouteHandler from '@theme/components/route-handler';
16
17
 
17
18
  export const viewport: Viewport = {
18
19
  width: 'device-width',
@@ -42,26 +43,20 @@ export async function generateMetadata() {
42
43
  return result;
43
44
  }
44
45
 
45
- async function RootLayout({
46
- params,
47
- locale,
48
- translations,
49
- children
50
- }: RootLayoutProps) {
51
- const layoutParams = await params;
52
-
46
+ async function RootLayout({ locale, translations, children }: ResolvedRootLayoutProps) {
53
47
  return (
54
48
  <html lang={locale.isoCode} {...(locale.rtl ? { dir: 'rtl' } : {})}>
55
49
  <head />
56
50
  <body className="overflow-x-hidden">
57
- <PzRoot translations={translations} {...layoutParams}>
51
+ <PzRoot translations={translations} locale={locale.value}>
58
52
  <ClientRoot>
59
- <div className="overflow-x-hidden">
53
+ <div className="overflow-x-clip">
60
54
  <MobileAppToggler>
61
55
  <Header />
62
56
  </MobileAppToggler>
63
57
  <main>
64
58
  {children}
59
+ <RouteHandler />
65
60
  <RootModal />
66
61
  </main>
67
62
  <MobileAppToggler>
@@ -0,0 +1,26 @@
1
+ import { getListData } from '@akinon/next/data/server';
2
+ import { withSegmentDefaults } from '@akinon/next/hocs/server';
3
+ import { ResolvedPageProps } from '@akinon/next/types';
4
+ import CategoryLayout from '@theme/views/category/layout';
5
+ import { ROUTES } from '@theme/routes';
6
+ import { buildListingPageContext } from '@theme/utils/theme-page-context';
7
+
8
+ async function Page({ searchParams }: ResolvedPageProps) {
9
+ const data = await getListData({ searchParams });
10
+ const pageType = data.search_text ? 'search' : 'plp';
11
+
12
+ return (
13
+ <>
14
+ <CategoryLayout
15
+ data={data}
16
+ pageContext={buildListingPageContext({
17
+ data,
18
+ pageType,
19
+ path: ROUTES.LIST
20
+ })}
21
+ />
22
+ </>
23
+ );
24
+ }
25
+
26
+ export default withSegmentDefaults(Page, { segmentType: 'page' });
@@ -4,14 +4,14 @@ import React from 'react';
4
4
  import { Button, Link } from '@theme/components';
5
5
  import { useLocalization } from '@akinon/next/hooks';
6
6
 
7
- const NotFound = () => {
7
+ export default function NotFound() {
8
8
  const { t } = useLocalization();
9
9
 
10
10
  return (
11
- <div className="py-6 flex flex-col items-center justify-center">
12
- <div className="text-8xl font-bold">404</div>
11
+ <div className="py-10 flex flex-col items-center justify-center">
12
+ <div className="text-8xl font-bold text-gray-400">404</div>
13
13
  <h1 className="text-4xl font-bold mb-4">{t('not_found.title')}</h1>
14
- <p className="text-lg mb-6">{t('not_found.sub_title')}</p>
14
+ <p className="text-lg mb-6 text-gray-600">{t('not_found.sub_title')}</p>
15
15
  <Link href={'/'}>
16
16
  <Button className="h-auto mt-4 text-base py-3 px-6">
17
17
  {t('not_found.button')}
@@ -19,6 +19,4 @@ const NotFound = () => {
19
19
  </Link>
20
20
  </div>
21
21
  );
22
- };
23
-
24
- export default NotFound;
22
+ }
@@ -11,7 +11,7 @@ import {
11
11
  } from '@akinon/next/redux/reducers/checkout';
12
12
  import { RootState } from '@theme/redux/store';
13
13
  import { ROUTES } from '@theme/routes';
14
- import { useFetchCheckoutQuery } from '@akinon/next/data/client/checkout';
14
+ import { useFetchCheckoutQuery, useResetCheckoutStateQuery } from '@akinon/next/data/client/checkout';
15
15
  import { Button, LoaderSpinner } from '@theme/components';
16
16
  import { pushAddPaymentInfo, pushAddShippingInfo } from '@theme/utils/gtm';
17
17
  import { CheckoutStep } from '@akinon/next/types';
@@ -25,6 +25,8 @@ const Checkout = () => {
25
25
  (state: RootState) => state.checkout
26
26
  );
27
27
 
28
+ const { data: indexData, isLoading: isResetStateLoading } = useResetCheckoutStateQuery(null);
29
+
28
30
  const {
29
31
  data: checkoutData,
30
32
  isFetching,
@@ -32,7 +34,8 @@ const Checkout = () => {
32
34
  isSuccess,
33
35
  refetch: refetchCheckout
34
36
  } = useFetchCheckoutQuery(null, {
35
- refetchOnMountOrArgChange: true
37
+ refetchOnMountOrArgChange: true,
38
+ skip: isResetStateLoading || !indexData
36
39
  });
37
40
  const initialStepChanged = useRef<boolean>(false);
38
41
  const router = useRouter();
@@ -94,10 +97,10 @@ const Checkout = () => {
94
97
  );
95
98
  }
96
99
 
97
- if (isFetching || isError) {
100
+ if (isResetStateLoading || isFetching || isError) {
98
101
  return (
99
102
  <div className="flex flex-col items-center justify-center h-80">
100
- {isFetching ? (
103
+ {isResetStateLoading || isFetching ? (
101
104
  <LoaderSpinner />
102
105
  ) : (
103
106
  <>
@@ -1,11 +1,12 @@
1
1
  'use client';
2
2
 
3
- import { useEffect, useRef, use } from 'react';
3
+ import { useEffect, useRef } from 'react';
4
+ import { useParams } from 'next/navigation';
4
5
  import { useAppDispatch } from '@akinon/next/redux/hooks';
5
6
  import { setCurrentStep } from '@akinon/next/redux/reducers/checkout';
6
7
  import { ROUTES } from '@theme/routes';
7
8
  import { useFetchCheckoutResultQuery } from '@akinon/next/data/client/checkout';
8
- import { CheckoutStep, PageProps } from '@akinon/next/types';
9
+ import { CheckoutStep } from '@akinon/next/types';
9
10
  import { CarouselCore } from '@theme/components/carousel-core';
10
11
  import { Price, Button, Icon, LoaderSpinner, Link } from '@theme/components';
11
12
  import { CheckoutStepList } from '@theme/views/checkout';
@@ -14,10 +15,11 @@ import { Image } from '@akinon/next/components/image';
14
15
  import { Trans } from '@akinon/next/components/trans';
15
16
  import { pushPurchaseEvent } from '@theme/utils/gtm';
16
17
 
17
- const CheckoutCompleted = (props) => {
18
+ const CheckoutCompleted = () => {
19
+ const params = useParams<{ token: string }>();
18
20
  const { t } = useLocalization();
19
21
  const dispatch = useAppDispatch();
20
- const params = use(props.params) as { token: string };
22
+
21
23
  const { data, isLoading } = useFetchCheckoutResultQuery(String(params.token));
22
24
 
23
25
  const carouselRef = useRef(null);
@@ -0,0 +1,28 @@
1
+ import 'server-only';
2
+
3
+ import { withSegmentDefaults } from '@akinon/next/hocs/server';
4
+ import ThemePlaceholder from '@akinon/pz-theme/src/theme-placeholder';
5
+ import { buildHomepagePageContext } from '@theme/utils/theme-page-context';
6
+
7
+ export const dynamic = 'force-static';
8
+ export const revalidate = 600;
9
+
10
+ const getPlaceholderSlug = (baseSlug: string) => {
11
+ const isDevelopment = process.env.NODE_ENV === 'development';
12
+ return isDevelopment ? `${baseSlug}-dev` : baseSlug;
13
+ };
14
+
15
+ async function Page() {
16
+ return (
17
+ <ThemePlaceholder
18
+ slug={getPlaceholderSlug('homepage-body-new')}
19
+ pageContext={buildHomepagePageContext()}
20
+ />
21
+ );
22
+ }
23
+
24
+ export async function generateStaticParams() {
25
+ return [];
26
+ }
27
+
28
+ export default withSegmentDefaults(Page, { segmentType: 'page' });
@@ -0,0 +1,19 @@
1
+ import ThemePlaceholder from '@akinon/pz-theme/src/theme-placeholder';
2
+ import { withSegmentDefaults } from '@akinon/next/hocs/server';
3
+ import { ResolvedPageProps } from '@akinon/next/types';
4
+ import { buildCustomPageContext } from '@theme/utils/theme-page-context';
5
+
6
+ export const dynamic = 'force-dynamic';
7
+
8
+ async function Page({ params }: ResolvedPageProps<{ slug: string }>) {
9
+ return (
10
+ <div className="min-h-screen">
11
+ <ThemePlaceholder
12
+ slug={`page-${params.slug}`}
13
+ pageContext={buildCustomPageContext(params.slug)}
14
+ />
15
+ </div>
16
+ );
17
+ }
18
+
19
+ export default withSegmentDefaults(Page, { segmentType: 'page' });
@@ -0,0 +1,102 @@
1
+ import { getProductData, getWidgetData } from '@akinon/next/data/server';
2
+ import { withSegmentDefaults } from '@akinon/next/hocs/server';
3
+ import { AsyncPageProps, ResolvedPageProps, Metadata } from '@akinon/next/types';
4
+ import { generateJsonLd } from '@theme/utils/generate-jsonld';
5
+ import { AccordionWrapper, ProductInfo } from '@theme/views/product';
6
+ import ProductLayout from '@theme/views/product/layout';
7
+ import { notFound } from 'next/navigation';
8
+
9
+ export async function generateMetadata(
10
+ props: AsyncPageProps<{ pk: number }>
11
+ ) {
12
+ const params = await props.params;
13
+ const searchParams = await props.searchParams;
14
+ let result: Metadata = {};
15
+
16
+ try {
17
+ const { data } = await getProductData({
18
+ pk: params.pk,
19
+ searchParams
20
+ });
21
+
22
+ const product = data?.product;
23
+
24
+ if (!product) {
25
+ return {};
26
+ }
27
+
28
+ result = {
29
+ title: product.name,
30
+ description: String(product.attributes?.description),
31
+ twitter: {
32
+ title: product.name,
33
+ description: String(product.attributes?.description)
34
+ },
35
+ openGraph: {
36
+ title: product.name,
37
+ description: String(product.attributes?.description),
38
+ images: product.productimage_set?.map((item) => ({
39
+ url: item.image
40
+ }))
41
+ }
42
+ };
43
+ } catch (error: unknown) {
44
+ if ((error as Error & { status?: number })?.status === 404) {
45
+ notFound();
46
+ }
47
+ throw error;
48
+ }
49
+
50
+ return result;
51
+ }
52
+
53
+ async function Page({ params, searchParams }: ResolvedPageProps<{ pk: number }>) {
54
+ if (params.pk === undefined || isNaN(Number(params.pk))) {
55
+ notFound();
56
+ }
57
+
58
+ try {
59
+ const [{ data, breadcrumbData }, deliveryReturn] = await Promise.all([
60
+ getProductData({
61
+ pk: params.pk,
62
+ searchParams
63
+ }),
64
+ getWidgetData({ slug: 'product-delivery-returns' })
65
+ ]);
66
+
67
+ const jsonLd = generateJsonLd(data.product);
68
+
69
+ return (
70
+ <>
71
+ <ProductLayout data={data} breadcrumbData={breadcrumbData}>
72
+ <div className="flex flex-col items-center">
73
+ <h1
74
+ className="mt-4 text-2xl text-center md:mt-0"
75
+ data-testid="product-name"
76
+ >
77
+ {data.product.name}
78
+ </h1>
79
+
80
+ <ProductInfo data={data} />
81
+
82
+ <AccordionWrapper
83
+ data={data}
84
+ deliveryReturn={deliveryReturn?.attributes}
85
+ />
86
+ </div>
87
+ </ProductLayout>
88
+ <script
89
+ type="application/ld+json"
90
+ dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
91
+ />
92
+ </>
93
+ );
94
+ } catch (error: unknown) {
95
+ if ((error as Error & { status?: number })?.status === 404) {
96
+ notFound();
97
+ }
98
+ throw error;
99
+ }
100
+ }
101
+
102
+ export default withSegmentDefaults(Page, { segmentType: 'page' });
@@ -0,0 +1,35 @@
1
+ import { getSpecialPageData } from '@akinon/next/data/server';
2
+ import { withSegmentDefaults } from '@akinon/next/hocs/server';
3
+ import { ResolvedPageProps } from '@akinon/next/types';
4
+ import CategoryLayout from '@theme/views/category/layout';
5
+ import SpecialPageBanner from '@theme/widgets/special-page-banner';
6
+ import SpecialPageCarousel from '@theme/widgets/special-page-carousel';
7
+ import { notFound } from 'next/navigation';
8
+
9
+ async function Page({ params, searchParams }: ResolvedPageProps<{ pk: number }>) {
10
+ try {
11
+ const data = await getSpecialPageData({ pk: params.pk, searchParams });
12
+
13
+ return (
14
+ <>
15
+ <CategoryLayout data={data}>
16
+ {data.special_page && (
17
+ <>
18
+ <SpecialPageBanner data={data.special_page} />
19
+ <SpecialPageCarousel
20
+ slug={data.special_page.video_embedded_code}
21
+ />
22
+ </>
23
+ )}
24
+ </CategoryLayout>
25
+ </>
26
+ );
27
+ } catch (error: unknown) {
28
+ if ((error as Error & { status?: number })?.status === 404) {
29
+ notFound();
30
+ }
31
+ throw error;
32
+ }
33
+ }
34
+
35
+ export default withSegmentDefaults(Page, { segmentType: 'page' });
@@ -2,11 +2,10 @@
2
2
  import { Icon, Link, LoaderSpinner } from '@theme/components';
3
3
  import { useLocalization } from '@akinon/next/hooks';
4
4
  import { useChangeEmailVerificationQuery } from '@akinon/next/data/client/user';
5
- import { use } from 'react';
6
-
7
- export default function Page(props) {
8
- const { id } = use(props.params) as { id: string[] };
5
+ import { useParams } from 'next/navigation';
9
6
 
7
+ export default function Page() {
8
+ const { id } = useParams<{ id: string[] }>();
10
9
  const { t } = useLocalization();
11
10
  const { isSuccess, isLoading } = useChangeEmailVerificationQuery(
12
11
  id.join('/')
@@ -2,10 +2,10 @@
2
2
  import { Icon, Link, LoaderSpinner } from '@theme/components';
3
3
  import { useLocalization } from '@akinon/next/hooks';
4
4
  import { useConfirmEmailVerificationQuery } from '@akinon/next/data/client/user';
5
- import { use } from 'react';
5
+ import { useParams } from 'next/navigation';
6
6
 
7
- export default function Page(props) {
8
- const { id } = use(props.params) as { id: string[] };
7
+ export default function Page() {
8
+ const { id } = useParams<{ id: string[] }>();
9
9
  const { t } = useLocalization();
10
10
  const { isSuccess, isLoading } = useConfirmEmailVerificationQuery(
11
11
  id.join('/')
@@ -1,22 +1,33 @@
1
1
  'use client';
2
2
 
3
- import { Button, Input, Link } from '@theme/components';
3
+ import { Button, Input, Link, LoaderSpinner } from '@theme/components';
4
4
  import { useForm, SubmitHandler } from 'react-hook-form';
5
5
  import { yupResolver } from '@hookform/resolvers/yup';
6
6
  import * as yup from 'yup';
7
7
  import {
8
8
  usePasswordResetMutation,
9
+ useGetValidatePasswordResetTokenQuery,
9
10
  PasswordResetType
10
11
  } from '@akinon/next/data/client/account';
11
12
  import { useLocalization } from '@akinon/next/hooks';
13
+ import { useParams } from 'next/navigation';
12
14
  import PasswordRulesFeedback from '@theme/components/password-rules-feedback';
13
- import { use } from 'react';
14
15
 
15
- export default function NewPassword(props) {
16
- const { id } = use(props.params) as { id: string };
16
+ export default function NewPassword() {
17
+ const { id } = useParams<{ id: string[] }>();
17
18
  const { t } = useLocalization();
18
19
  const [newPassword, { isSuccess: formSuccess }] = usePasswordResetMutation();
19
20
 
21
+ const token = id?.join('/');
22
+
23
+ const {
24
+ data: validationData,
25
+ isLoading: isValidating,
26
+ error: validationError
27
+ } = useGetValidatePasswordResetTokenQuery(token, {
28
+ skip: !token
29
+ });
30
+
20
31
  const newPasswordFormSchema = (t) =>
21
32
  yup.object().shape({
22
33
  password: yup
@@ -46,10 +57,35 @@ export default function NewPassword(props) {
46
57
  const passwordValue = watch('password', '');
47
58
 
48
59
  const onSubmit: SubmitHandler<PasswordResetType> = (data) => {
49
- data.slug = id;
60
+ data.slug = id?.join('/') ?? '';
50
61
  newPassword(data);
51
62
  };
52
63
 
64
+ if (isValidating) {
65
+ return (
66
+ <div className="container my-12 flex flex-col items-center px-4">
67
+ <div className="w-full xl:w-5/12 text-center">
68
+ <LoaderSpinner />
69
+ </div>
70
+ </div>
71
+ );
72
+ }
73
+
74
+ if (validationError || !validationData?.validlink) {
75
+ return (
76
+ <div className="container my-12 flex flex-col items-center px-4">
77
+ <div className="w-full xl:w-5/12 text-center">
78
+ <div className="text-2xl mb-4 text-error">
79
+ {t('account.password_reset.expired_link_message')}
80
+ </div>
81
+ <Link href="/users/password/reset" className="underline text-primary">
82
+ {t('account.password_reset.continue_password_reset')}
83
+ </Link>
84
+ </div>
85
+ </div>
86
+ );
87
+ }
88
+
53
89
  return (
54
90
  <div className="container my-12 flex flex-col items-center px-4">
55
91
  <div className="w-full xl:w-5/12 text-center">
@@ -0,0 +1,73 @@
1
+ import { urlLocaleMatcherRegex } from '@akinon/next/utils';
2
+
3
+ /**
4
+ * XML Sitemap Route
5
+ *
6
+ * This route serves XML sitemaps from an S3 bucket.
7
+ *
8
+ * Required environment variables:
9
+ * - SITEMAP_S3_BUCKET_NAME: The name of the S3 bucket containing the sitemaps
10
+ * Example: "0fb534"
11
+ *
12
+ * If the environment variable is not set, the route will return a 503 Service Unavailable
13
+ * response with a JSON error message.
14
+ */
15
+
16
+ export const dynamic = 'force-dynamic';
17
+
18
+ export async function GET(request: Request, context: { params: Promise<{ node: string }> }) {
19
+ const { node } = await context.params;
20
+ const url = new URL(request.url);
21
+ const matchedLocale = url.pathname.match(urlLocaleMatcherRegex);
22
+
23
+ const s3BucketName = process.env.SITEMAP_S3_BUCKET_NAME;
24
+
25
+ if (!s3BucketName) {
26
+ return new Response(
27
+ JSON.stringify({
28
+ error: 'Configuration error',
29
+ message: 'Please set the SITEMAP_S3_BUCKET_NAME environment variable'
30
+ }),
31
+ {
32
+ status: 503,
33
+ headers: {
34
+ 'Content-Type': 'application/json'
35
+ }
36
+ }
37
+ );
38
+ }
39
+
40
+ const sitemap = await fetch(
41
+ `https://s3.eu-central-1.amazonaws.com/${s3BucketName}/sitemaps/sitemaps/sitemap-${node}.xml.gz`
42
+ );
43
+
44
+ if (!sitemap.ok) {
45
+ return new Response(
46
+ JSON.stringify({
47
+ error: 'Sitemap not found',
48
+ message: `Failed to fetch sitemap for node: ${node}`
49
+ }),
50
+ {
51
+ status: 503,
52
+ headers: {
53
+ 'Content-Type': 'application/json'
54
+ }
55
+ }
56
+ );
57
+ }
58
+
59
+ let sitemapContent = await sitemap.text();
60
+
61
+ if (matchedLocale?.[0]) {
62
+ const localeSuffix = matchedLocale[0];
63
+ const domainRegex = /(?<=<loc>\s*)https?:\/\/[^/<\s]+/g;
64
+
65
+ sitemapContent = sitemapContent.replace(domainRegex, `$&${localeSuffix}`);
66
+ }
67
+
68
+ return new Response(sitemapContent, {
69
+ headers: {
70
+ 'Content-Type': 'application/xml'
71
+ }
72
+ });
73
+ }
@@ -0,0 +1,3 @@
1
+ import { handlers } from 'auth';
2
+
3
+ export const { GET, POST } = handlers;
@@ -0,0 +1 @@
1
+ export { GET } from "@akinon/next/api/barcode-search";
@@ -1 +1 @@
1
- export * from '@akinon/next/api/cache';
1
+ export { POST, PUT } from "@akinon/next/api/cache";
@@ -1,7 +1 @@
1
- import { NextResponse } from 'next/server';
2
-
3
- export async function POST() {
4
- // TODO: Handle Form Data
5
-
6
- return NextResponse.json({ message: 'ok' });
7
- }
1
+ export * from '@akinon/next/api/form';
@@ -0,0 +1 @@
1
+ export { GET, POST } from "@akinon/next/api/image-proxy";
@@ -1 +1 @@
1
- export * from '@akinon/next/api/logout';
1
+ export { POST } from "@akinon/next/api/logout";
@@ -0,0 +1 @@
1
+ export { GET } from "@akinon/next/api/product-categories";
@@ -0,0 +1 @@
1
+ export { GET } from "@akinon/next/api/similar-product-list";
@@ -0,0 +1 @@
1
+ export { GET, POST } from "@akinon/next/api/similar-products";
@@ -0,0 +1,12 @@
1
+ import { NextResponse } from 'next/server';
2
+ import { getThemeSettings } from '@theme/data/server/theme';
3
+
4
+ export async function GET() {
5
+ try {
6
+ const settings = await getThemeSettings();
7
+ return NextResponse.json(settings);
8
+ } catch (error) {
9
+ console.error('Error in theme-settings API:', error);
10
+ return NextResponse.json({}, { status: 500 });
11
+ }
12
+ }
@@ -0,0 +1 @@
1
+ export * from '@akinon/next/api/virtual-try-on';
@@ -0,0 +1 @@
1
+ export { GET, POST, PUT, OPTIONS } from "@akinon/next/api/virtual-try-on";
@@ -1 +1 @@
1
- export * from '@akinon/next/api/web-vitals';
1
+ export { POST } from "@akinon/next/api/web-vitals";