@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.
- package/CHANGELOG.md +42 -0
- package/app-template/CHANGELOG.md +352 -7
- package/app-template/eslint.config.js +10 -0
- package/app-template/package.json +10 -10
- package/app-template/public/locales/en/account.json +4 -4
- package/app-template/public/locales/tr/account.json +1 -1
- package/app-template/src/app/[commerce]/[locale]/[currency]/[...prettyurl]/page.tsx +8 -0
- package/app-template/src/app/[commerce]/[locale]/[currency]/account/change-email/page.tsx +0 -2
- package/app-template/src/app/[commerce]/[locale]/[currency]/account/coupons/page.tsx +4 -4
- package/app-template/src/app/[commerce]/[locale]/[currency]/account/profile/page.tsx +1 -0
- package/app-template/src/app/[commerce]/[locale]/[currency]/address/stores/page.tsx +2 -2
- package/app-template/src/app/[commerce]/[locale]/[currency]/auth/page.tsx +1 -1
- package/app-template/src/app/[commerce]/[locale]/[currency]/landing-page/[pk]/page.tsx +0 -2
- package/app-template/src/app/[commerce]/[locale]/[currency]/orders/checkout/page.tsx +2 -2
- package/app-template/src/app/[commerce]/[locale]/[currency]/orders/completed/[token]/page.tsx +13 -9
- package/app-template/src/app/api/form/[...id]/route.ts +3 -3
- package/app-template/src/components/carousel-core.tsx +1 -1
- package/app-template/src/components/checkbox.tsx +2 -2
- package/app-template/src/components/input.tsx +19 -7
- package/app-template/src/components/pagination.tsx +2 -2
- package/app-template/src/components/price.tsx +3 -3
- package/app-template/src/middleware.ts +13 -13
- package/app-template/src/redux/reducers/category.ts +23 -1
- package/app-template/src/redux/store.ts +1 -1
- package/app-template/src/types/next-auth.d.ts +1 -0
- package/app-template/src/views/account/account-menu.tsx +0 -1
- package/app-template/src/views/account/address-form.tsx +4 -4
- package/app-template/src/views/account/favourite-products/favourite-products-list.tsx +5 -1
- package/app-template/src/views/account/orders/order-detail-header.tsx +0 -7
- package/app-template/src/views/category/category-active-filters.tsx +24 -35
- package/app-template/src/views/category/category-info.tsx +3 -3
- package/app-template/src/views/category/filters/index.tsx +23 -13
- package/app-template/src/views/checkout/auth.tsx +1 -1
- package/app-template/src/views/checkout/steps/payment/options/credit-card/index.tsx +1 -1
- package/app-template/src/views/checkout/steps/payment/options/funds-transfer.tsx +2 -3
- package/app-template/src/views/checkout/steps/payment/options/redirection.tsx +2 -5
- package/app-template/src/views/find-in-store/index.tsx +2 -2
- package/app-template/src/views/header/mini-basket.tsx +2 -2
- package/app-template/src/views/product/product-info.tsx +9 -2
- package/app-template/src/views/product-item/index.tsx +1 -1
- package/app-template/src/widgets/footer-subscription/footer-subscription-form.tsx +1 -1
- package/app-template/tsconfig.json +14 -4
- package/commands/create.ts +29 -5
- package/dist/commands/create.js +25 -2
- package/package.json +1 -1
- package/app-template/.eslintrc.js +0 -31
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { NextResponse } from 'next/server';
|
|
2
2
|
|
|
3
|
-
export async function POST(
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
13
|
-
'
|
|
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
|
-
<
|
|
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
|
|
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:
|
|
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
|
-
<
|
|
41
|
+
<NumericFormat
|
|
42
42
|
value={useNegative ? `-${useNegativeSpace}${_value}` : _value}
|
|
43
43
|
{...{
|
|
44
44
|
[useCurrencyAfterPrice ? 'suffix' : 'prefix']: currency
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
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
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
-
|
|
28
|
-
|
|
27
|
+
// Example:
|
|
28
|
+
// return NextResponse.json({ status: 'ok' }, { headers: { 'pz-override-response': 'true' } });
|
|
29
29
|
|
|
30
|
-
|
|
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 (
|
|
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,
|
|
36
|
+
export type TypedDispatch = ThunkDispatch<RootState, void, AnyAction>;
|
|
37
37
|
|
|
38
38
|
export type AppDispatch = AppStore['dispatch'];
|
|
39
39
|
|
|
@@ -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
|
|
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 {
|
|
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(
|
|
57
|
-
|
|
58
|
-
|
|
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-
|
|
70
|
-
|
|
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
|
-
<
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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 ===
|
|
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 ===
|
|
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 !==
|
|
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 ===
|
|
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">
|
|
@@ -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 = (
|
|
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(
|
|
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 = (
|
|
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(
|
|
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
|
|