@akinon/projectzero 1.35.0 → 1.36.0-rc.1

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 (46) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/app-template/CHANGELOG.md +352 -7
  3. package/app-template/eslint.config.js +10 -0
  4. package/app-template/package.json +10 -10
  5. package/app-template/public/locales/en/account.json +4 -4
  6. package/app-template/public/locales/tr/account.json +1 -1
  7. package/app-template/src/app/[commerce]/[locale]/[currency]/[...prettyurl]/page.tsx +8 -0
  8. package/app-template/src/app/[commerce]/[locale]/[currency]/account/change-email/page.tsx +0 -2
  9. package/app-template/src/app/[commerce]/[locale]/[currency]/account/coupons/page.tsx +4 -4
  10. package/app-template/src/app/[commerce]/[locale]/[currency]/account/profile/page.tsx +1 -0
  11. package/app-template/src/app/[commerce]/[locale]/[currency]/address/stores/page.tsx +2 -2
  12. package/app-template/src/app/[commerce]/[locale]/[currency]/auth/page.tsx +1 -1
  13. package/app-template/src/app/[commerce]/[locale]/[currency]/landing-page/[pk]/page.tsx +0 -2
  14. package/app-template/src/app/[commerce]/[locale]/[currency]/orders/checkout/page.tsx +2 -2
  15. package/app-template/src/app/[commerce]/[locale]/[currency]/orders/completed/[token]/page.tsx +13 -9
  16. package/app-template/src/app/api/form/[...id]/route.ts +3 -3
  17. package/app-template/src/components/carousel-core.tsx +1 -1
  18. package/app-template/src/components/checkbox.tsx +2 -2
  19. package/app-template/src/components/input.tsx +19 -7
  20. package/app-template/src/components/pagination.tsx +2 -2
  21. package/app-template/src/components/price.tsx +3 -3
  22. package/app-template/src/middleware.ts +13 -13
  23. package/app-template/src/redux/reducers/category.ts +23 -1
  24. package/app-template/src/redux/store.ts +1 -1
  25. package/app-template/src/types/next-auth.d.ts +1 -0
  26. package/app-template/src/views/account/account-menu.tsx +0 -1
  27. package/app-template/src/views/account/address-form.tsx +4 -4
  28. package/app-template/src/views/account/favourite-products/favourite-products-list.tsx +5 -1
  29. package/app-template/src/views/account/orders/order-detail-header.tsx +0 -7
  30. package/app-template/src/views/category/category-active-filters.tsx +24 -35
  31. package/app-template/src/views/category/category-info.tsx +3 -3
  32. package/app-template/src/views/category/filters/index.tsx +23 -13
  33. package/app-template/src/views/checkout/auth.tsx +1 -1
  34. package/app-template/src/views/checkout/steps/payment/options/credit-card/index.tsx +1 -1
  35. package/app-template/src/views/checkout/steps/payment/options/funds-transfer.tsx +2 -3
  36. package/app-template/src/views/checkout/steps/payment/options/redirection.tsx +2 -5
  37. package/app-template/src/views/find-in-store/index.tsx +2 -2
  38. package/app-template/src/views/header/mini-basket.tsx +2 -2
  39. package/app-template/src/views/product/product-info.tsx +9 -2
  40. package/app-template/src/views/product-item/index.tsx +1 -1
  41. package/app-template/src/widgets/footer-subscription/footer-subscription-form.tsx +1 -1
  42. package/app-template/tsconfig.json +14 -4
  43. package/commands/create.ts +29 -5
  44. package/dist/commands/create.js +25 -2
  45. package/package.json +1 -1
  46. package/app-template/.eslintrc.js +0 -31
@@ -1,7 +1,7 @@
1
- import { NextRequest, NextResponse } from 'next/server';
1
+ import { NextResponse } from 'next/server';
2
2
 
3
- export async function POST(req: NextRequest) {
3
+ export async function POST() {
4
4
  // TODO: Handle Form Data
5
5
 
6
- return NextResponse.json({message: 'ok'});
6
+ return NextResponse.json({ message: 'ok' });
7
7
  }
@@ -28,7 +28,7 @@ const CarouselCore = forwardRef<Carousel, CarouselProps>((props, ref) => {
28
28
  } else {
29
29
  setAspectRatio(containerAspectRatio.mobile);
30
30
  }
31
- }, [matches]);
31
+ }, [matches, containerAspectRatio]);
32
32
 
33
33
  return (
34
34
  <div className="w-full" style={{ aspectRatio }}>
@@ -3,7 +3,7 @@ import { CheckboxProps } from '@theme/components/types';
3
3
  import { twMerge } from 'tailwind-merge';
4
4
 
5
5
  const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>((props, ref) => {
6
- const { children, checked, error, ...rest } = props;
6
+ const { children, checked = false, error, ...rest } = props;
7
7
 
8
8
  return (
9
9
  <label className={twMerge('flex flex-col text-xs', props.className)}>
@@ -12,7 +12,7 @@ const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>((props, ref) => {
12
12
  type="checkbox"
13
13
  {...rest}
14
14
  ref={ref}
15
- checked={checked ?? false}
15
+ defaultChecked={checked}
16
16
  className="w-4 h-4 shrink-0"
17
17
  />
18
18
  {children && <span className="ml-2">{children}</span>}
@@ -1,17 +1,28 @@
1
1
  import clsx from 'clsx';
2
- import { forwardRef, FocusEvent, useState } from 'react';
2
+ import { forwardRef, FocusEvent, useState, Ref } from 'react';
3
3
  import { Controller } from 'react-hook-form';
4
- import NumberFormat, { NumberFormatProps } from 'react-number-format';
4
+ import { PatternFormat, PatternFormatProps } from 'react-number-format';
5
5
  import { InputProps } from '@theme/components/types';
6
6
  import { twMerge } from 'tailwind-merge';
7
7
 
8
+ const PatternFormatWithRef = forwardRef(
9
+ (props: PatternFormatProps, ref: Ref<HTMLInputElement>) => {
10
+ return <PatternFormat {...props} getInputRef={ref} />;
11
+ }
12
+ );
13
+ PatternFormatWithRef.displayName = 'PatternFormatWithRef';
14
+
8
15
  export const Input = forwardRef<
9
16
  HTMLInputElement,
10
17
  InputProps &
11
18
  Pick<
12
- NumberFormatProps,
13
- 'format' | 'mask' | 'allowEmptyFormatting' | 'onValueChange'
14
- >
19
+ PatternFormatProps,
20
+ 'mask' | 'allowEmptyFormatting' | 'onValueChange'
21
+ > & {
22
+ format?: string;
23
+ defaultValue?: string;
24
+ type?: string;
25
+ }
15
26
  >((props, ref) => {
16
27
  const [focused, setFocused] = useState(false);
17
28
  const [hasValue, setHasValue] = useState(false);
@@ -37,6 +48,7 @@ export const Input = forwardRef<
37
48
  ),
38
49
  props.className
39
50
  );
51
+
40
52
  const inputProps: any = {
41
53
  id,
42
54
  ref,
@@ -79,14 +91,14 @@ export const Input = forwardRef<
79
91
  <Controller
80
92
  name={props.name ?? ''}
81
93
  control={props.control}
82
- defaultValue={false}
83
94
  render={({ field }) => (
84
- <NumberFormat
95
+ <PatternFormatWithRef
85
96
  format={format}
86
97
  mask={mask ?? ''}
87
98
  {...rest}
88
99
  {...field}
89
100
  {...inputProps}
101
+ type={props.type as 'text' | 'password' | 'tel'}
90
102
  />
91
103
  )}
92
104
  />
@@ -123,13 +123,13 @@ export const Pagination = (props: PaginationProps) => {
123
123
  if (inView) {
124
124
  handlePageChange();
125
125
  }
126
- }, [inView]);
126
+ }, [inView]); // eslint-disable-line react-hooks/exhaustive-deps
127
127
 
128
128
  useEffect(() => {
129
129
  if (type === 'list') {
130
130
  createListItems();
131
131
  }
132
- }, []);
132
+ }, []); // eslint-disable-line react-hooks/exhaustive-deps
133
133
 
134
134
  useEffect(() => {
135
135
  if (total && total !== paginationTotal) {
@@ -1,10 +1,10 @@
1
1
  import { useMemo } from 'react';
2
- import NumberFormat, { NumberFormatProps } from 'react-number-format';
2
+ import { NumericFormat, NumericFormatProps } from 'react-number-format';
3
3
  import { getCurrency } from '@akinon/next/utils';
4
4
  import { PriceProps } from '@theme/types';
5
5
  import { useLocalization } from '@akinon/next/hooks';
6
6
 
7
- export const Price = (props: NumberFormatProps & PriceProps) => {
7
+ export const Price = (props: NumericFormatProps & PriceProps) => {
8
8
  const {
9
9
  value,
10
10
  currencyCode,
@@ -38,7 +38,7 @@ export const Price = (props: NumberFormatProps & PriceProps) => {
38
38
  );
39
39
 
40
40
  return (
41
- <NumberFormat
41
+ <NumericFormat
42
42
  value={useNegative ? `-${useNegativeSpace}${_value}` : _value}
43
43
  {...{
44
44
  [useCurrencyAfterPrice ? 'suffix' : 'prefix']: currency
@@ -1,5 +1,5 @@
1
- import { PzNextRequest, withPzDefault } from '@akinon/next/middlewares';
2
- import { NextFetchEvent, NextMiddleware, NextResponse } from 'next/server';
1
+ import { withPzDefault } from '@akinon/next/middlewares';
2
+ import { NextMiddleware, NextResponse } from 'next/server';
3
3
 
4
4
  /**
5
5
  * !IMPORTANT
@@ -16,18 +16,18 @@ export const config = {
16
16
  ]
17
17
  };
18
18
 
19
- const middleware: NextMiddleware = (
20
- req: PzNextRequest,
21
- event: NextFetchEvent
22
- ) => {
23
- // If you use a custom response such as NextResponse.json(),
24
- // you should set pz-override-response header to true as shown below.
25
- // Otherwise, you'll get a 404 error.
19
+ const middleware: NextMiddleware = () =>
20
+ // req: PzNextRequest,
21
+ // event: NextFetchEvent
22
+ {
23
+ // If you use a custom response such as NextResponse.json(),
24
+ // you should set pz-override-response header to true as shown below.
25
+ // Otherwise, you'll get a 404 error.
26
26
 
27
- // Example:
28
- // return NextResponse.json({ status: 'ok' }, { headers: { 'pz-override-response': 'true' } });
27
+ // Example:
28
+ // return NextResponse.json({ status: 'ok' }, { headers: { 'pz-override-response': 'true' } });
29
29
 
30
- return NextResponse.next();
31
- };
30
+ return NextResponse.next();
31
+ };
32
32
 
33
33
  export default withPzDefault(middleware);
@@ -31,7 +31,10 @@ const categorySlice = createSlice({
31
31
  if (facet.key === action.payload.facet.key) {
32
32
  facet.data.choices = facet.data.choices
33
33
  .map((choice) => {
34
- if (action.payload.facet.widget_type === WIDGET_TYPE.category) {
34
+ if (
35
+ action.payload.facet.widget_type === WIDGET_TYPE.category &&
36
+ choice.is_selected
37
+ ) {
35
38
  choice.is_selected = false;
36
39
  }
37
40
  return choice;
@@ -48,6 +51,24 @@ const categorySlice = createSlice({
48
51
  return facet;
49
52
  });
50
53
  },
54
+ removeCategoryFacet(state, action) {
55
+ state.selectedFacets = state.selectedFacets.map((facet) => {
56
+ if (facet.key === action.payload.facet.key) {
57
+ facet.data.choices = facet.data.choices.map((choice) => {
58
+ if (choice.value === action.payload.choice.value) {
59
+ choice.is_selected = false;
60
+ }
61
+ return choice;
62
+ });
63
+ }
64
+
65
+ return facet;
66
+ });
67
+
68
+ state.selectedFacets = state.selectedFacets.filter(
69
+ (facet) => facet.key !== action.payload.facet.key
70
+ );
71
+ },
51
72
  resetSelectedFacets(state) {
52
73
  return {
53
74
  ...state,
@@ -61,6 +82,7 @@ export const {
61
82
  setFacets,
62
83
  setSelectedFacets,
63
84
  toggleFacet,
85
+ removeCategoryFacet,
64
86
  resetSelectedFacets
65
87
  } = categorySlice.actions;
66
88
 
@@ -33,7 +33,7 @@ export type AppStore = ReturnType<typeof makeStore>;
33
33
 
34
34
  export type RootState = ReturnType<AppStore['getState']>;
35
35
 
36
- export type TypedDispatch = ThunkDispatch<RootState, any, AnyAction>;
36
+ export type TypedDispatch = ThunkDispatch<RootState, void, AnyAction>;
37
37
 
38
38
  export type AppDispatch = AppStore['dispatch'];
39
39
 
@@ -1,3 +1,4 @@
1
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
1
2
  import NextAuth from 'next-auth';
2
3
 
3
4
  declare module 'next-auth' {
@@ -63,7 +63,6 @@ export default function AccountMenu() {
63
63
  const [logout] = useLogoutMutation();
64
64
  const pathname = usePathname();
65
65
  const accountMenuRegex = new RegExp(`^${ROUTES.ACCOUNT}/?$`).test(pathname);
66
-
67
66
  const handleLogout = async () => {
68
67
  logout()
69
68
  .unwrap()
@@ -121,7 +121,7 @@ export const AddressForm = (props: Props) => {
121
121
  return options;
122
122
  }
123
123
  return [];
124
- }, [country]);
124
+ }, [country]); // eslint-disable-line react-hooks/exhaustive-deps
125
125
 
126
126
  const cityOptions = useMemo(() => {
127
127
  if (city) {
@@ -137,7 +137,7 @@ export const AddressForm = (props: Props) => {
137
137
  return options;
138
138
  }
139
139
  return [];
140
- }, [city]);
140
+ }, [city]); // eslint-disable-line react-hooks/exhaustive-deps
141
141
 
142
142
  const townshipOptions = useMemo(() => {
143
143
  if (township) {
@@ -156,7 +156,7 @@ export const AddressForm = (props: Props) => {
156
156
  return options;
157
157
  }
158
158
  return [];
159
- }, [township]);
159
+ }, [township]); // eslint-disable-line react-hooks/exhaustive-deps
160
160
 
161
161
  const districtOptions = useMemo(() => {
162
162
  if (district) {
@@ -175,7 +175,7 @@ export const AddressForm = (props: Props) => {
175
175
  return options;
176
176
  }
177
177
  return [];
178
- }, [district]);
178
+ }, [district]); // eslint-disable-line react-hooks/exhaustive-deps
179
179
 
180
180
  useEffect(() => {
181
181
  if (data && country) {
@@ -30,7 +30,11 @@ const FavoriteProductsList = () => {
30
30
  }
31
31
 
32
32
  if (isLoading || isFetching) {
33
- return <LoaderSpinner />; // TODO: Fix loader spinner position
33
+ return (
34
+ <div className="flex items-center justify-center h-80">
35
+ <LoaderSpinner />
36
+ </div>
37
+ );
34
38
  }
35
39
 
36
40
  return (
@@ -14,13 +14,6 @@ export interface Props {
14
14
  export const OrderDetailHeader = ({ title, order, children }: Props) => {
15
15
  const { t } = useLocalization();
16
16
 
17
- const orderDate = new Date(order.created_date)
18
- .toJSON()
19
- .slice(0, 10)
20
- .split('-')
21
- .reverse()
22
- .join(' ');
23
-
24
17
  return (
25
18
  <div className="bg-gray-150 flex flex-col items-center justify-center p-6 w-full mb-8 md:flex-row">
26
19
  <div className="flex flex-col items-center text-center gap-2">
@@ -1,11 +1,15 @@
1
1
  import { useAppDispatch, useAppSelector } from '@akinon/next/redux/hooks';
2
2
  import React, { useEffect, useMemo } from 'react';
3
- import { toggleFacet } from '@theme/redux/reducers/category';
3
+ import {
4
+ removeCategoryFacet,
5
+ toggleFacet
6
+ } from '@theme/redux/reducers/category';
4
7
  import { useRouter } from '@akinon/next/hooks';
5
8
  import convertFacetSearchParams from '@theme/utils/convert-facet-search-params';
6
9
  import { usePathname } from 'next/navigation';
7
10
  import { WIDGET_TYPE } from '@theme/types';
8
11
  import { useSearchParams } from 'next/dist/client/components/navigation';
12
+ import { Icon } from '@theme/components';
9
13
 
10
14
  const CategoryActiveFilters = () => {
11
15
  const dispatch = useAppDispatch();
@@ -16,6 +20,11 @@ const CategoryActiveFilters = () => {
16
20
  const { facets, selectedFacets } = useAppSelector((state) => state.category);
17
21
 
18
22
  const handleRemoveFilter = ({ facet, choice }) => {
23
+ if (facet.widget_type === WIDGET_TYPE.category) {
24
+ dispatch(removeCategoryFacet({ facet, choice }));
25
+ return;
26
+ }
27
+
19
28
  dispatch(toggleFacet({ facet, choice }));
20
29
  };
21
30
 
@@ -53,43 +62,23 @@ const CategoryActiveFilters = () => {
53
62
  {facets.map((facet) =>
54
63
  facet.data.choices
55
64
  .filter((choice) => choice.is_selected)
56
- .map((choice) => (
57
- <div
58
- className="flex justify-between text-xs text-black-800 py-3.5 px-4 border border-gray-400 hover:bg-gray-300 cursor-default items-center"
59
- key={`${facet.key}-${choice.label}`}
60
- >
61
- <div>
62
- {facet.widget_type !== WIDGET_TYPE.category && (
63
- <span>{facet.name}: </span>
64
- )}
65
- <span>{choice.label}</span>
66
- </div>
67
- {facet.widget_type !== WIDGET_TYPE.category && (
65
+ .map(
66
+ (choice) =>
67
+ Number(choice.real_depth) !== 2 && (
68
68
  <div
69
- className="cursor-pointer ml-4"
70
- onClick={() => handleRemoveFilter({ facet, choice })}
69
+ className="flex justify-between text-xs text-black-800 py-3.5 px-4 border border-gray-400 hover:bg-gray-300 cursor-default items-center"
70
+ key={`${facet.key}-${choice.label}`}
71
71
  >
72
- <svg
73
- xmlns="http://www.w3.org/2000/svg"
74
- viewBox="0 0 13.5 13.5"
75
- width="1em"
76
- height="1em"
77
- >
78
- <path
79
- d="m.75 12.75 12-12m-12 0 12 12"
80
- style={{
81
- fill: 'none',
82
- stroke: '#000',
83
- strokeLinecap: 'round',
84
- strokeLinejoin: 'round',
85
- strokeWidth: '1.5px'
86
- }}
87
- />
88
- </svg>
72
+ <span>{choice.label}</span>
73
+ <Icon
74
+ className="cursor-pointer"
75
+ name="close"
76
+ size={12}
77
+ onClick={() => handleRemoveFilter({ facet, choice })}
78
+ />
89
79
  </div>
90
- )}
91
- </div>
92
- ))
80
+ )
81
+ )
93
82
  )}
94
83
  </div>
95
84
  );
@@ -44,7 +44,7 @@ export default function ListPage(props: ListPageProps) {
44
44
  newUrl.searchParams.delete('page');
45
45
  router.push(newUrl.pathname + newUrl.search, undefined);
46
46
  }
47
- }, [searchParams, data.products, page]);
47
+ }, [searchParams, data.products, page]); // eslint-disable-line react-hooks/exhaustive-deps
48
48
 
49
49
  const dispatch = useAppDispatch();
50
50
  const { t } = useLocalization();
@@ -70,7 +70,7 @@ export default function ListPage(props: ListPageProps) {
70
70
  ></div>
71
71
  <div className="flex flex-col items-center lg:items-stretch col-span-2 lg:col-span-1">
72
72
  <CategoryHeader
73
- totalCount={data.pagination.total_count}
73
+ totalCount={data.pagination?.total_count}
74
74
  setMenuStatus={() => setIsMenuOpen(true)}
75
75
  sortOptions={data.sorters}
76
76
  />
@@ -78,7 +78,7 @@ export default function ListPage(props: ListPageProps) {
78
78
  <CategoryActiveFilters />
79
79
  </div>
80
80
 
81
- {data.products.length === 0 && page === 1 && (
81
+ {data.products?.length === 0 && page === 1 && (
82
82
  <div className="text-center bg-gray-200 px-5 py-20">
83
83
  <p className="pb-4">{t('category.search.not_found')}</p>
84
84
  <Link className="underline" href={ROUTES.HOME}>
@@ -6,7 +6,7 @@ import clsx from 'clsx';
6
6
  import { Accordion, Button, Checkbox, Icon, Radio } from '@theme/components';
7
7
  import { SizeFilter } from './size-filter';
8
8
 
9
- import { useLocalization } from '@akinon/next/hooks';
9
+ import { useLocalization, useRouter } from '@akinon/next/hooks';
10
10
  import { Facet, FacetChoice } from '@akinon/next/types';
11
11
  import { useAppDispatch, useAppSelector } from '@akinon/next/redux/hooks';
12
12
  import {
@@ -14,7 +14,8 @@ import {
14
14
  toggleFacet
15
15
  } from '@theme/redux/reducers/category';
16
16
  import CategoryActiveFilters from '@theme/views/category/category-active-filters';
17
- import { useMemo } from 'react';
17
+ import { useEffect, useMemo } from 'react';
18
+ import { commonProductAttributes } from '@theme/settings';
18
19
 
19
20
  const COMPONENT_TYPES = {
20
21
  [WIDGET_TYPE.category]: Radio,
@@ -27,6 +28,7 @@ interface Props {
27
28
  }
28
29
 
29
30
  export const Filters = (props: Props) => {
31
+ const router = useRouter();
30
32
  const facets = useAppSelector((state) => state.category.facets);
31
33
  const dispatch = useAppDispatch();
32
34
  const { t } = useLocalization();
@@ -39,12 +41,16 @@ export const Filters = (props: Props) => {
39
41
  facet: Facet;
40
42
  choice: FacetChoice;
41
43
  }) => {
42
- dispatch(
43
- toggleFacet({
44
- facet,
45
- choice
46
- })
47
- );
44
+ if (facet.key === 'category_ids') {
45
+ router.push(choice.url);
46
+ } else {
47
+ dispatch(
48
+ toggleFacet({
49
+ facet,
50
+ choice
51
+ })
52
+ );
53
+ }
48
54
  };
49
55
 
50
56
  const haveFilter = useMemo(() => {
@@ -60,6 +66,10 @@ export const Filters = (props: Props) => {
60
66
  dispatch(resetSelectedFacets());
61
67
  };
62
68
 
69
+ const sizeKey = commonProductAttributes.find(
70
+ (item) => item.translationKey === 'size'
71
+ ).key;
72
+
63
73
  return (
64
74
  <div
65
75
  className={clsx(
@@ -81,7 +91,7 @@ export const Filters = (props: Props) => {
81
91
  let Component = null;
82
92
  const choices = [...facet.data.choices];
83
93
 
84
- if (facet.key === 'integration_SizeId') {
94
+ if (facet.key === sizeKey) {
85
95
  // If it's a size facet, use the custom size filter component
86
96
  Component = SizeFilter;
87
97
 
@@ -107,8 +117,8 @@ export const Filters = (props: Props) => {
107
117
  >
108
118
  <div
109
119
  className={clsx(
110
- 'flex gap-4',
111
- facet.key === 'integration_SizeId' ? 'flex-row' : 'flex-col' // TODO: This condition must be refactor to a better way
120
+ 'flex gap-4 flex-wrap',
121
+ facet.key === sizeKey ? 'flex-row' : 'flex-col' // TODO: This condition must be refactor to a better way
112
122
  )}
113
123
  >
114
124
  {choices.map((choice, index) => (
@@ -117,13 +127,13 @@ export const Filters = (props: Props) => {
117
127
  data={choice}
118
128
  name={facet.key}
119
129
  onChange={() => {
120
- if (facet.key !== 'integration_SizeId') {
130
+ if (facet.key !== sizeKey) {
121
131
  // TODO: This condition must be refactor to a better way
122
132
  handleSelectFilter({ facet, choice });
123
133
  }
124
134
  }}
125
135
  onClick={() => {
126
- if (facet.key === 'integration_SizeId') {
136
+ if (facet.key === sizeKey) {
127
137
  // TODO: This condition must be refactor to a better way
128
138
  handleSelectFilter({ facet, choice });
129
139
  }
@@ -20,7 +20,7 @@ const CheckoutAuth = () => {
20
20
  } else if (status === 'unauthenticated') {
21
21
  router.replace(ROUTES.CHECKOUT + `?callbackUrl=${ROUTES.CHECKOUT}`);
22
22
  }
23
- }, [status]);
23
+ }, [status]); // eslint-disable-line react-hooks/exhaustive-deps
24
24
 
25
25
  return (
26
26
  <div className="flex flex-col w-full my-5 lg:flex-row">
@@ -151,7 +151,7 @@ const CheckoutCreditCard = () => {
151
151
 
152
152
  setMonths(months);
153
153
  setYears(years);
154
- }, []);
154
+ }, []); // eslint-disable-line react-hooks/exhaustive-deps
155
155
 
156
156
  useEffect(() => {
157
157
  if (installment?.pk) {
@@ -13,10 +13,9 @@ import CheckoutAgreements from '../agreements';
13
13
  import PaymentHeader from '../payment-header';
14
14
  import { useLocalization } from '@akinon/next/hooks';
15
15
  import { Image } from '@akinon/next/components/image';
16
- import PaymentOptionButtons from '../payment-option-buttons';
17
16
  import { Trans } from '@akinon/next/components/trans';
18
17
 
19
- const fundsTransferFormSchema = (t) =>
18
+ const fundsTransferFormSchema = () =>
20
19
  yup.object().shape({
21
20
  agreement: yup.boolean().oneOf([true], 'This field is required.')
22
21
  });
@@ -28,7 +27,7 @@ const CheckoutFundsTransfer = () => {
28
27
  control,
29
28
  formState: { errors }
30
29
  } = useForm({
31
- resolver: yupResolver(fundsTransferFormSchema(t))
30
+ resolver: yupResolver(fundsTransferFormSchema())
32
31
  });
33
32
  const [formError, setFormError] = useState(null);
34
33
  const { bankAccounts, selectedBankAccountPk, preOrder } = useAppSelector(
@@ -1,14 +1,12 @@
1
1
  'use client';
2
2
 
3
3
  import { useCompleteRedirectionPaymentMutation } from '@akinon/next/data/client/checkout';
4
- import { useLocalization } from '@akinon/next/hooks';
5
4
  import { useAppSelector } from '@akinon/next/redux/hooks';
6
5
  import { yupResolver } from '@hookform/resolvers/yup';
7
6
  import { Button, Checkbox } from '@theme/components';
8
7
  import { useForm } from 'react-hook-form';
9
8
  import { twMerge } from 'tailwind-merge';
10
9
  import * as yup from 'yup';
11
- import PaymentOptionButtons from '../payment-option-buttons';
12
10
  import { useEffect, useState } from 'react';
13
11
  import { getPosError } from '@akinon/next/utils';
14
12
 
@@ -16,7 +14,7 @@ interface FormValues {
16
14
  agreement: boolean;
17
15
  }
18
16
 
19
- const formSchema = (t) =>
17
+ const formSchema = () =>
20
18
  yup.object().shape({
21
19
  agreement: yup
22
20
  .boolean()
@@ -28,13 +26,12 @@ export default function RedirectionPayment() {
28
26
  const { payment_option } = useAppSelector((state) => state.checkout.preOrder);
29
27
  const [formError, setFormError] = useState(null);
30
28
 
31
- const { t } = useLocalization();
32
29
  const {
33
30
  register,
34
31
  handleSubmit,
35
32
  formState: { errors }
36
33
  } = useForm<FormValues>({
37
- resolver: yupResolver(formSchema(t))
34
+ resolver: yupResolver(formSchema())
38
35
  });
39
36
  const [completeRedirectionPayment] = useCompleteRedirectionPaymentMutation();
40
37
 
@@ -63,7 +63,7 @@ export const FindInStore = ({ productPk, productName, variants }) => {
63
63
  return options;
64
64
  }
65
65
  return [];
66
- }, [retailStore]);
66
+ }, [retailStore]); // eslint-disable-line react-hooks/exhaustive-deps
67
67
 
68
68
  const sizeOptions = useMemo(() => {
69
69
  if (variants) {
@@ -80,7 +80,7 @@ export const FindInStore = ({ productPk, productName, variants }) => {
80
80
  return options;
81
81
  }
82
82
  return [];
83
- }, [variants]);
83
+ }, [variants, sizeKey]); // eslint-disable-line react-hooks/exhaustive-deps
84
84
 
85
85
  const createStockStatus = (stock) => {
86
86
  const status = {
@@ -46,7 +46,7 @@ function MiniBasketItem(props: MiniBasketItemProps) {
46
46
  miniBasketList.scrollTop = 0;
47
47
  }
48
48
  }
49
- }, [highlightedItem, basketItem.product.pk]);
49
+ }, [highlightedItem, basketItem.product.pk, miniBasketListRef]);
50
50
 
51
51
  const removeItem = () => {
52
52
  updateQuantityMutation({
@@ -183,7 +183,7 @@ export default function MiniBasket() {
183
183
  setSortedBasket(basket.basketitem_set);
184
184
  }
185
185
  }
186
- }, [isSuccess, highlightedItem, basket]);
186
+ }, [isSuccess, highlightedItem, basket, highlightedItemPk]);
187
187
 
188
188
  return (
189
189
  <>
@@ -36,7 +36,7 @@ export default function ProductInfo({ data }: ProductPageProps) {
36
36
 
37
37
  useEffect(() => {
38
38
  pushProductViewed(data?.product);
39
- }, []);
39
+ }, []); // eslint-disable-line react-hooks/exhaustive-deps
40
40
 
41
41
  const addProductToCart = async () => {
42
42
  if (!variantsSelectionCheck()) {
@@ -206,7 +206,14 @@ export default function ProductInfo({ data }: ProductPageProps) {
206
206
  'after:bg-[#d02c2f]',
207
207
  'after:rounded-xl',
208
208
  'after:left-1'
209
- ])
209
+ ]),
210
+ onError: (error) =>
211
+ setProductError(
212
+ error?.data?.non_field_errors ||
213
+ Object.keys(error?.data).map(
214
+ (key) => `${key}: ${error?.data[key].join(', ')}`
215
+ )
216
+ )
210
217
  }}
211
218
  />
212
219