@akinon/projectzero 2.0.0-beta.12 → 2.0.0-beta.13

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 (176) hide show
  1. package/CHANGELOG.md +100 -23
  2. package/app-template/.env.example +1 -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 +3 -0
  10. package/app-template/AGENTS.md +7 -0
  11. package/app-template/CHANGELOG.md +1348 -310
  12. package/app-template/Procfile +1 -1
  13. package/app-template/akinon.json +0 -3
  14. package/app-template/build.sh +10 -0
  15. package/app-template/docs/advanced-usage.md +101 -0
  16. package/app-template/docs/sentry-usage.md +35 -0
  17. package/app-template/next-env.d.ts +1 -0
  18. package/app-template/{next.config.ts → next.config.mjs} +6 -6
  19. package/app-template/package.json +58 -51
  20. package/app-template/postcss.config.mjs +1 -4
  21. package/app-template/public/locales/en/checkout.json +6 -0
  22. package/app-template/public/locales/en/common.json +50 -1
  23. package/app-template/public/locales/en/product.json +62 -1
  24. package/app-template/public/locales/tr/checkout.json +6 -0
  25. package/app-template/public/locales/tr/common.json +50 -1
  26. package/app-template/public/locales/tr/product.json +63 -0
  27. package/app-template/public/masterpass-javascript-sdk-web.min.js +1 -0
  28. package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/[...prettyurl]/page.tsx +9 -9
  29. package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/account/layout.tsx +2 -2
  30. package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/account/orders/[id]/cancellation/page.tsx +6 -6
  31. package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/account/orders/[id]/page.tsx +6 -6
  32. package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/account/page.tsx +1 -1
  33. package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/account/profile/page.tsx +2 -2
  34. package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/address/stores/page.tsx +2 -2
  35. package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/auth/page.tsx +1 -1
  36. package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/basket/page.tsx +2 -2
  37. package/app-template/src/app/[pz]/category/[pk]/page.tsx +27 -0
  38. package/app-template/src/app/[pz]/flat-page/[pk]/page.tsx +23 -0
  39. package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/forms/[pk]/generate/page.tsx +2 -3
  40. package/app-template/src/app/[pz]/group-product/[pk]/page.tsx +93 -0
  41. package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/landing-page/[pk]/page.tsx +2 -4
  42. package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/layout.tsx +3 -10
  43. package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/list/page.tsx +2 -4
  44. package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/not-found.tsx +5 -7
  45. package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/orders/completed/[token]/page.tsx +6 -4
  46. package/app-template/src/app/[pz]/product/[pk]/page.tsx +102 -0
  47. package/app-template/src/app/[pz]/special-page/[pk]/page.tsx +35 -0
  48. package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/users/email-set-primary/[[...id]]/page.tsx +3 -4
  49. package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/users/registration/account-confirm-email/[[...id]]/page.tsx +3 -3
  50. package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/users/reset/[[...id]]/page.tsx +6 -12
  51. package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/xml-sitemap/[node]/route.ts +2 -2
  52. package/app-template/src/app/api/auth/[...nextauth]/route.ts +3 -0
  53. package/app-template/src/app/api/form/[...id]/route.ts +1 -7
  54. package/app-template/src/app/api/image-proxy/route.ts +1 -0
  55. package/app-template/src/app/api/product-categories/route.ts +1 -0
  56. package/app-template/src/app/api/similar-product-list/route.ts +1 -0
  57. package/app-template/src/app/api/similar-products/route.ts +1 -0
  58. package/app-template/src/app/api/virtual-try-on/limited-categories/route.ts +1 -0
  59. package/app-template/src/app/api/virtual-try-on/route.ts +1 -0
  60. package/app-template/src/assets/globals.scss +4 -133
  61. package/app-template/src/auth.ts +3 -0
  62. package/app-template/src/components/__tests__/badge.test.tsx +2 -2
  63. package/app-template/src/components/__tests__/link.test.tsx +2 -0
  64. package/app-template/src/components/accordion.tsx +23 -20
  65. package/app-template/src/components/button.tsx +1 -1
  66. package/app-template/src/components/carousel-core.tsx +4 -11
  67. package/app-template/src/components/checkbox.tsx +1 -1
  68. package/app-template/src/components/currency-select.tsx +1 -0
  69. package/app-template/src/components/file-input.tsx +27 -7
  70. package/app-template/src/components/generate-form-fields.tsx +49 -10
  71. package/app-template/src/components/input.tsx +11 -5
  72. package/app-template/src/components/modal.tsx +32 -16
  73. package/app-template/src/components/pagination.tsx +1 -0
  74. package/app-template/src/components/price.tsx +1 -1
  75. package/app-template/src/components/pwa-tags.tsx +1 -0
  76. package/app-template/src/components/select.tsx +39 -27
  77. package/app-template/src/components/shimmer.tsx +1 -1
  78. package/app-template/src/components/types/index.ts +25 -1
  79. package/app-template/src/hooks/use-fav-button.tsx +4 -8
  80. package/app-template/src/hooks/use-product-cart.ts +77 -0
  81. package/app-template/src/hooks/use-stock-alert.ts +74 -0
  82. package/app-template/src/plugins.js +12 -2
  83. package/app-template/src/redux/middlewares/category.ts +5 -4
  84. package/app-template/src/redux/store.ts +21 -1
  85. package/app-template/src/routes/index.ts +2 -1
  86. package/app-template/src/settings.js +3 -1
  87. package/app-template/src/types/index.ts +74 -3
  88. package/app-template/src/types/next-auth.d.ts +2 -2
  89. package/app-template/src/utils/variant-validation.ts +41 -0
  90. package/app-template/src/views/account/address-form.tsx +8 -4
  91. package/app-template/src/views/account/contact-form.tsx +2 -2
  92. package/app-template/src/views/account/content-header.tsx +4 -3
  93. package/app-template/src/views/account/faq/faq-tabs.tsx +8 -2
  94. package/app-template/src/views/account/order.tsx +1 -1
  95. package/app-template/src/views/account/orders/order-cancellation-item.tsx +1 -1
  96. package/app-template/src/views/anonymous-tracking/order-detail/index.tsx +1 -1
  97. package/app-template/src/views/basket/basket-item.tsx +6 -1
  98. package/app-template/src/views/basket/summary.tsx +16 -0
  99. package/app-template/src/views/breadcrumb.tsx +2 -2
  100. package/app-template/src/views/category/category-info.tsx +2 -1
  101. package/app-template/src/views/category/filters/index.tsx +1 -1
  102. package/app-template/src/views/checkout/auth.tsx +1 -1
  103. package/app-template/src/views/checkout/layout/header.tsx +1 -1
  104. package/app-template/src/views/checkout/steps/payment/options/credit-card/index.tsx +1 -1
  105. package/app-template/src/views/checkout/steps/payment/options/store-credit.tsx +121 -0
  106. package/app-template/src/views/checkout/steps/payment/payment-option-buttons.tsx +4 -4
  107. package/app-template/src/views/checkout/steps/shipping/address-box.tsx +3 -3
  108. package/app-template/src/views/checkout/steps/shipping/addresses.tsx +1 -1
  109. package/app-template/src/views/checkout/summary.tsx +12 -2
  110. package/app-template/src/views/find-in-store/index.tsx +2 -2
  111. package/app-template/src/views/header/action-menu.tsx +2 -6
  112. package/app-template/src/views/header/band.tsx +2 -2
  113. package/app-template/src/views/header/index.tsx +1 -1
  114. package/app-template/src/views/header/mini-basket.tsx +2 -2
  115. package/app-template/src/views/header/mobile-menu.tsx +6 -6
  116. package/app-template/src/views/header/navbar.tsx +1 -1
  117. package/app-template/src/views/header/pwa-back-button.tsx +1 -1
  118. package/app-template/src/views/header/search/index.tsx +13 -3
  119. package/app-template/src/views/header/search/results.tsx +1 -1
  120. package/app-template/src/views/header/user-menu.tsx +1 -3
  121. package/app-template/src/views/login/index.tsx +14 -13
  122. package/app-template/src/views/otp-login/index.tsx +11 -6
  123. package/app-template/src/views/product/layout.tsx +15 -1
  124. package/app-template/src/views/product/product-actions.tsx +165 -0
  125. package/app-template/src/views/product/product-info.tsx +69 -261
  126. package/app-template/src/views/product/product-share.tsx +56 -0
  127. package/app-template/src/views/product/product-variants.tsx +26 -0
  128. package/app-template/src/views/product/slider.tsx +22 -1
  129. package/app-template/src/views/product-pointer-banner-item.tsx +1 -1
  130. package/app-template/src/views/register/index.tsx +17 -21
  131. package/app-template/src/views/sales-contract-modal/index.tsx +17 -17
  132. package/app-template/src/widgets/footer-info.tsx +1 -1
  133. package/app-template/src/widgets/footer-menu.tsx +7 -3
  134. package/app-template/src/widgets/footer-subscription/index.tsx +1 -1
  135. package/app-template/src/widgets/home-stories-eng.tsx +43 -35
  136. package/app-template/tailwind.config.js +129 -1
  137. package/app-template/tsconfig.json +29 -11
  138. package/codemods/migrate-segments/index.js +591 -0
  139. package/commands/plugins.ts +62 -14
  140. package/dist/commands/plugins.js +62 -14
  141. package/package.json +1 -1
  142. package/app-template/src/app/[commerce]/[locale]/[currency]/category/[pk]/page.tsx +0 -22
  143. package/app-template/src/app/[commerce]/[locale]/[currency]/flat-page/[pk]/page.tsx +0 -20
  144. package/app-template/src/app/[commerce]/[locale]/[currency]/group-product/[pk]/page.tsx +0 -74
  145. package/app-template/src/app/[commerce]/[locale]/[currency]/product/[pk]/page.tsx +0 -84
  146. package/app-template/src/app/[commerce]/[locale]/[currency]/special-page/[pk]/page.tsx +0 -27
  147. package/app-template/src/pages/api/auth/[...nextauth].ts +0 -3
  148. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/account/address/page.tsx +0 -0
  149. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/account/change-email/page.tsx +0 -0
  150. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/account/change-password/page.tsx +0 -0
  151. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/account/contact/page.tsx +0 -0
  152. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/account/coupons/page.tsx +0 -0
  153. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/account/email-verification/page.tsx +0 -0
  154. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/account/faq/page.tsx +0 -0
  155. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/account/favourite-products/page.tsx +0 -0
  156. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/account/my-quotations/page.tsx +0 -0
  157. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/account/orders/[id]/layout.tsx +0 -0
  158. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/account/orders/page.tsx +0 -0
  159. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/anonymous-tracking/page.tsx +0 -0
  160. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/auth/oauth-login/page.tsx +0 -0
  161. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/basket-b2b/page.tsx +0 -0
  162. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/category/[pk]/loading.tsx +0 -0
  163. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/client-root.tsx +0 -0
  164. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/contact-us/page.tsx +0 -0
  165. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/error.tsx +0 -0
  166. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/flat-page/[pk]/loading.tsx +0 -0
  167. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/group-product/[pk]/loading.tsx +0 -0
  168. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/landing-page/[pk]/loading.tsx +0 -0
  169. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/list/loading.tsx +0 -0
  170. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/orders/checkout/page.tsx +0 -0
  171. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/orders/completed/[token]/layout.tsx +0 -0
  172. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/page.tsx +0 -0
  173. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/special-page/[pk]/loading.tsx +0 -0
  174. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/template.tsx +0 -0
  175. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/users/password/reset/page.tsx +0 -0
  176. /package/app-template/src/app/{[commerce]/[locale]/[currency] → [pz]}/xml-sitemap/route.ts +0 -0
@@ -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-10 lg:py-8 flex flex-col items-center justify-center px-4 lg:px-0">
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 text-center">{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
+ }
@@ -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,102 @@
1
+ import { getProductData, getWidgetData } from '@akinon/next/data/server';
2
+ import { withSegmentDefaults } from '@akinon/next/hocs/server';
3
+ import { PageProps, 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: PageProps<{ 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('/')
@@ -9,11 +9,11 @@ import {
9
9
  PasswordResetType
10
10
  } from '@akinon/next/data/client/account';
11
11
  import { useLocalization } from '@akinon/next/hooks';
12
+ import { useParams } from 'next/navigation';
12
13
  import PasswordRulesFeedback from '@theme/components/password-rules-feedback';
13
- import { use } from 'react';
14
14
 
15
- export default function NewPassword(props) {
16
- const { id } = use(props.params) as { id: string };
15
+ export default function NewPassword() {
16
+ const { id } = useParams<{ id: string[] }>();
17
17
  const { t } = useLocalization();
18
18
  const [newPassword, { isSuccess: formSuccess }] = usePasswordResetMutation();
19
19
 
@@ -26,10 +26,7 @@ export default function NewPassword(props) {
26
26
  /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^a-zA-Z\d]).{6,}$/,
27
27
  t('forgot_password.create_new_password.form.error.password_rule')
28
28
  )
29
- .max(
30
- 50,
31
- t('forgot_password.create_new_password.form.error.password_max')
32
- ),
29
+ .max(50, t('forgot_password.create_new_password.form.error.password_max')),
33
30
  repeatPassword: yup
34
31
  .string()
35
32
  .required(t('forgot_password.create_new_password.form.error.required'))
@@ -46,7 +43,7 @@ export default function NewPassword(props) {
46
43
  const passwordValue = watch('password', '');
47
44
 
48
45
  const onSubmit: SubmitHandler<PasswordResetType> = (data) => {
49
- data.slug = id;
46
+ data.slug = id?.join('/') ?? '';
50
47
  newPassword(data);
51
48
  };
52
49
 
@@ -84,10 +81,7 @@ export default function NewPassword(props) {
84
81
  required
85
82
  />
86
83
 
87
- <PasswordRulesFeedback
88
- password={passwordValue}
89
- isVisible={errors?.password ? true : false}
90
- />
84
+ <PasswordRulesFeedback password={passwordValue} isVisible={errors?.password ? true : false}/>
91
85
  </div>
92
86
 
93
87
  <div className="mb-2 text-left">
@@ -15,8 +15,8 @@ import { urlLocaleMatcherRegex } from '@akinon/next/utils';
15
15
 
16
16
  export const dynamic = 'force-dynamic';
17
17
 
18
- export async function GET(request: Request, context: { params }) {
19
- const node = (await context.params).node;
18
+ export async function GET(request: Request, context: { params: Promise<{ node: string }> }) {
19
+ const { node } = await context.params;
20
20
  const url = new URL(request.url);
21
21
  const matchedLocale = url.pathname.match(urlLocaleMatcherRegex);
22
22
 
@@ -0,0 +1,3 @@
1
+ import { handlers } from 'auth';
2
+
3
+ export const { GET, POST } = handlers;
@@ -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 * from '@akinon/next/api/image-proxy';
@@ -0,0 +1 @@
1
+ export * from '@akinon/next/api/product-categories';
@@ -0,0 +1 @@
1
+ export * from '@akinon/next/api/similar-product-list';
@@ -0,0 +1 @@
1
+ export * from '@akinon/next/api/similar-products';
@@ -0,0 +1 @@
1
+ export * from '@akinon/next/api/virtual-try-on';
@@ -0,0 +1 @@
1
+ export * from '@akinon/next/api/virtual-try-on';
@@ -2,128 +2,8 @@
2
2
  @use './fonts/index.scss' as fonts;
3
3
  @use './fonts/pz-icon.css' as icons;
4
4
 
5
- @import 'tailwindcss';
6
-
7
- @plugin '@tailwindcss/typography';
8
-
9
- @config '../../tailwind.config.js';
10
-
11
- @source '../app/**/*.{js,ts,jsx,tsx}';
12
- @source '../pages/**/*.{js,ts,jsx,tsx}';
13
- @source '../components/**/*.{js,ts,jsx,tsx}';
14
- @source '../views/**/*.{js,ts,jsx,tsx}';
15
- @source '../widgets/**/*.{js,ts,jsx,tsx}';
16
- @source '../hooks/**/*.{js,ts,jsx,tsx}';
17
- @source '../utils/**/*.{js,ts,jsx,tsx}';
18
-
19
- @theme {
20
- --font-sans: 'Jost', ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji',
21
- 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
22
- --font-size-2xs: 0.5rem;
23
- --outline-off: none;
24
-
25
- --width-1-10: 10%;
26
- --width-2-10: 20%;
27
- --width-3-10: 30%;
28
- --width-4-10: 40%;
29
- --width-5-10: 50%;
30
- --width-6-10: 60%;
31
- --width-7-10: 70%;
32
- --width-8-10: 80%;
33
- --width-9-10: 90%;
34
-
35
- --transition-property-max-width: max-width;
36
-
37
- --background-image-skeleton-shimmer: linear-gradient(
38
- 90deg,
39
- #d7d7d7 0%,
40
- #ebebeb 40%,
41
- #eeeeee 60%,
42
- #d7d7d7
43
- );
44
-
45
- --animate-skeleton-shimmer: skeleton-shimmer 2s linear infinite;
46
-
47
- @keyframes skeleton-shimmer {
48
- to {
49
- transform: translateX(100%);
50
- }
51
- }
52
-
53
- --color-transparent: transparent;
54
- --color-white: #ffffff;
55
- --color-primary: #000000;
56
- --color-primary-hover: #181818;
57
- --color-primary-foreground: #ffffff;
58
- --color-primary-100: #525252;
59
- --color-primary-200: #404040;
60
- --color-primary-300: #3d3d3d;
61
- --color-primary-400: #333333;
62
- --color-primary-500: #2d2d2d;
63
- --color-primary-600: #292929;
64
- --color-primary-700: #2b2b2b;
65
- --color-primary-800: #181818;
66
- --color-primary-900: #000000;
67
-
68
- --color-secondary: #e95151;
69
- --color-secondary-hover: #d03838;
70
- --color-secondary-foreground: #ffffff;
71
- --color-secondary-100: #ffb7b7;
72
- --color-secondary-200: #ff9e9e;
73
- --color-secondary-300: #ff8484;
74
- --color-secondary-400: #ff6b6b;
75
- --color-secondary-500: #e95151;
76
- --color-secondary-600: #d72b01;
77
- --color-secondary-700: #b61e1e;
78
- --color-secondary-800: #9d0505;
79
- --color-secondary-900: #830000;
80
-
81
- --color-black: #000000;
82
- --color-black-100: #525252;
83
- --color-black-200: #404040;
84
- --color-black-300: #3d3d3d;
85
- --color-black-400: #333333;
86
- --color-black-500: #2d2d2d;
87
- --color-black-600: #292929;
88
- --color-black-700: #2b2b2b;
89
- --color-black-800: #181818;
90
- --color-black-900: #000000;
91
-
92
- --color-gray: #ebebeb;
93
- --color-gray-25: #fdfdfd;
94
- --color-gray-50: #f7f7f7;
95
- --color-gray-100: #f5f5f5;
96
- --color-gray-150: #f4f4f4;
97
- --color-gray-200: #eeeeee;
98
- --color-gray-300: #ebebeb;
99
- --color-gray-400: #d7d7d7;
100
- --color-gray-450: #d4d4d4;
101
- --color-gray-500: #c9c9c9;
102
- --color-gray-600: #9d9d9d;
103
- --color-gray-700: #686868;
104
- --color-gray-800: #615f62;
105
- --color-gray-850: #58585a;
106
- --color-gray-900: #4a4f54;
107
- --color-gray-950: #424242;
108
-
109
- --color-error: #d72b01;
110
- --color-error-100: #e20008;
111
-
112
- --color-success: #7b9d76;
113
- --color-success-100: #7b9d76;
114
-
115
- --container-center: true;
116
- --container-padding: 0rem;
117
- --container-padding-sm: 2rem;
118
- --container-padding-2xl: 0rem;
119
-
120
- --breakpoint-xs: 575px;
121
- --breakpoint-sm: 640px;
122
- --breakpoint-md: 768px;
123
- --breakpoint-lg: 1024px;
124
- --breakpoint-xl: 1170px;
125
- --breakpoint-2xl: 1370px;
126
- }
5
+ @import "tailwindcss";
6
+ @config "../../tailwind.config.js";
127
7
 
128
8
  @utility header-grid-template-areas {
129
9
  grid-template-areas:
@@ -164,22 +44,13 @@
164
44
  grid-template-columns: auto 1fr 1fr;
165
45
  }
166
46
 
167
- @utility container {
168
- margin-inline: auto;
169
- }
170
-
171
- @layer base {
172
- input::placeholder,
173
- textarea::placeholder {
174
- color: theme(--color-gray-400);
175
- }
176
-
47
+ @layer utilities {
177
48
  input {
178
49
  &:focus,
179
50
  &:not(:placeholder-shown),
180
51
  &:autofill {
181
52
  & + label {
182
- top: calc(1 / 3 * 100%);
53
+ @apply top-1/3;
183
54
  }
184
55
  }
185
56
  }
@@ -0,0 +1,3 @@
1
+ import { createAuth } from '@akinon/next/api/auth';
2
+
3
+ export const { handlers, auth, signIn, signOut } = createAuth();
@@ -5,7 +5,7 @@ describe('Badge Component', () => {
5
5
  let badge;
6
6
 
7
7
  beforeEach(() => {
8
- render(<Badge className="text-md rounded-xs">%90 off</Badge>);
8
+ render(<Badge className="text-md rounded-sm">%90 off</Badge>);
9
9
 
10
10
  badge = screen.getByText('%90 off');
11
11
  });
@@ -15,6 +15,6 @@ describe('Badge Component', () => {
15
15
  });
16
16
 
17
17
  it('should be classname checked', () => {
18
- expect(badge).toHaveClass('text-md rounded-xs');
18
+ expect(badge).toHaveClass('text-md rounded-sm');
19
19
  });
20
20
  });
@@ -30,6 +30,8 @@ describe('Link Component', () => {
30
30
  wrapper = container;
31
31
  });
32
32
 
33
+ wrapper;
34
+
33
35
  it('should render without error', () => {
34
36
  const link = screen.getByRole('link');
35
37
 
@@ -1,53 +1,52 @@
1
1
  'use client';
2
2
 
3
- import { ReactNode, useState } from 'react';
3
+ import { useState } from 'react';
4
4
  import { Icon } from './icon';
5
5
  import { twMerge } from 'tailwind-merge';
6
-
7
- type AccordionProps = {
8
- isCollapse?: boolean;
9
- title?: string;
10
- subTitle?: string;
11
- icons?: string[];
12
- iconSize?: number;
13
- iconColor?: string;
14
- children?: ReactNode;
15
- className?: string;
16
- titleClassName?: string;
17
- dataTestId?: string;
18
- };
6
+ import { AccordionProps } from './types';
19
7
 
20
8
  export const Accordion = ({
21
9
  isCollapse = false,
10
+ collapseClassName,
22
11
  title,
23
12
  subTitle,
24
13
  icons = ['chevron-up', 'chevron-down'],
25
14
  iconSize = 16,
26
15
  iconColor = 'fill-[#000000]',
27
16
  children,
17
+ headerClassName,
28
18
  className,
29
19
  titleClassName,
30
- dataTestId
20
+ subTitleClassName,
21
+ dataTestId,
22
+ contentClassName
31
23
  }: AccordionProps) => {
32
24
  const [collapse, setCollapse] = useState(isCollapse);
33
25
 
34
26
  return (
35
27
  <div
36
28
  className={twMerge(
37
- 'flex flex-col justify-center border-b border-gray-200 pb-4 mb-4 last:border-none',
29
+ 'flex flex-col justify-center border-b pb-4 mb-4 last:border-none',
38
30
  className
39
31
  )}
40
32
  >
41
33
  <div
42
- className="flex items-center justify-between cursor-pointer"
34
+ className={twMerge(
35
+ 'flex items-center justify-between cursor-pointer',
36
+ headerClassName
37
+ )}
43
38
  onClick={() => setCollapse(!collapse)}
44
39
  data-testid={dataTestId}
45
40
  >
46
- <div className="flex flex-col">
41
+ <div className={twMerge('flex flex-col', contentClassName)}>
47
42
  {title && (
48
43
  <h3 className={twMerge('text-sm', titleClassName)}>{title}</h3>
49
44
  )}
50
- {subTitle && <h4 className="text-xs text-gray-700">{subTitle}</h4>}
45
+ {subTitle && (
46
+ <h4 className={twMerge('text-xs text-gray-700', subTitleClassName)}>
47
+ {subTitle}
48
+ </h4>
49
+ )}
51
50
  </div>
52
51
 
53
52
  {icons && (
@@ -58,7 +57,11 @@ export const Accordion = ({
58
57
  />
59
58
  )}
60
59
  </div>
61
- {collapse && <div className="mt-3 text-sm">{children}</div>}
60
+ {collapse && (
61
+ <div className={twMerge('mt-3 text-sm', collapseClassName)}>
62
+ {children}
63
+ </div>
64
+ )}
62
65
  </div>
63
66
  );
64
67
  };
@@ -35,7 +35,7 @@ export const Button = (props: ButtonProps) => {
35
35
 
36
36
  const buttonClasses = twMerge(
37
37
  clsx(
38
- 'px-4 text-xs transition-all duration-200 cursor-pointer',
38
+ 'px-4 text-xs transition-all duration-200',
39
39
  'inline-flex gap-2 justify-center items-center',
40
40
  variants[appearance],
41
41
  sizes[size],
@@ -1,11 +1,10 @@
1
1
  'use client';
2
2
 
3
- import { useEffect, useState } from 'react';
3
+ import { forwardRef } from 'react';
4
4
  import 'react-multi-carousel/lib/styles.css';
5
5
  import Carousel, {
6
6
  CarouselProps as BaseCarouselProps
7
7
  } from 'react-multi-carousel';
8
- import { forwardRef } from 'react';
9
8
  import { useMediaQuery } from '@akinon/next/hooks';
10
9
 
11
10
  type ContainerAspectRatioType = {
@@ -20,15 +19,9 @@ interface CarouselProps extends BaseCarouselProps {
20
19
  const CarouselCore = forwardRef<Carousel, CarouselProps>((props, ref) => {
21
20
  const { containerAspectRatio } = props;
22
21
  const matches = useMediaQuery('(min-width: 768px)');
23
- const [aspectRatio, setAspectRatio] = useState(containerAspectRatio.mobile);
24
-
25
- useEffect(() => {
26
- if (matches) {
27
- setAspectRatio(containerAspectRatio.desktop);
28
- } else {
29
- setAspectRatio(containerAspectRatio.mobile);
30
- }
31
- }, [matches, containerAspectRatio]);
22
+ const aspectRatio = matches
23
+ ? containerAspectRatio.desktop
24
+ : containerAspectRatio.mobile;
32
25
 
33
26
  return (
34
27
  <div className="w-full" style={{ aspectRatio }}>
@@ -19,7 +19,7 @@ const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>((props, ref) => {
19
19
  {children && <span className="ml-2">{children}</span>}
20
20
  </div>
21
21
  {error && (
22
- <span className="mt-1 text-sm text-error">{error.message}</span>
22
+ <span className="mt-1 text-sm text-error">{String(error.message)}</span>
23
23
  )}
24
24
  </label>
25
25
  );
@@ -70,6 +70,7 @@ export const CurrencySelect = (props: CurrencySelectProps) => {
70
70
  onClick={confirmModalHandleClick}
71
71
  appearance="filled"
72
72
  className="font-medium px-10 py-4 h-12"
73
+ data-testid="currency-modal-confirm"
73
74
  >
74
75
  {t('common.currency_modal.continue')}
75
76
  </Button>