@akinon/projectzero 1.44.0 → 1.45.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 +43 -0
- package/README.md +3 -2
- package/app-template/.lintstagedrc.js +5 -4
- package/app-template/CHANGELOG.md +972 -20
- package/app-template/docs/basic-setup.md +1 -1
- package/app-template/docs/plugins.md +7 -7
- package/app-template/package-lock.json +29303 -0
- package/app-template/package.json +23 -21
- 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/address/page.tsx +1 -1
- 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]/category/[pk]/page.tsx +5 -2
- package/app-template/src/app/[commerce]/[locale]/[currency]/orders/completed/[token]/page.tsx +12 -8
- package/app-template/src/components/checkbox.tsx +2 -2
- package/app-template/src/components/input.tsx +19 -7
- package/app-template/src/components/price.tsx +9 -4
- package/app-template/src/redux/reducers/category.ts +7 -1
- package/app-template/src/settings.js +6 -1
- package/app-template/src/views/account/address-card.tsx +2 -2
- package/app-template/src/views/account/address-form.tsx +22 -7
- package/app-template/src/views/account/contact-form.tsx +23 -6
- package/app-template/src/views/account/favorite-item.tsx +2 -2
- package/app-template/src/views/account/favourite-products/favourite-products-list.tsx +5 -1
- package/app-template/src/views/breadcrumb.tsx +4 -1
- package/app-template/src/views/category/category-info.tsx +31 -17
- package/app-template/src/views/category/filters/filter-item.tsx +131 -0
- package/app-template/src/views/category/filters/index.tsx +5 -105
- package/app-template/src/views/category/layout.tsx +5 -3
- package/app-template/src/views/checkout/steps/payment/options/credit-card/index.tsx +33 -4
- package/app-template/src/views/checkout/steps/payment/options/redirection.tsx +43 -37
- package/app-template/src/views/checkout/steps/payment/payment-option-buttons.tsx +19 -3
- package/app-template/src/views/checkout/steps/shipping/address-box.tsx +2 -2
- package/app-template/src/views/checkout/steps/shipping/addresses.tsx +1 -1
- package/app-template/src/views/checkout/steps/shipping/shipping-options.tsx +230 -37
- package/app-template/src/views/find-in-store/index.tsx +2 -3
- package/app-template/src/views/header/mobile-menu.tsx +25 -8
- package/app-template/tsconfig.json +14 -4
- package/app-template/yarn.lock +1824 -1953
- package/commands/create.ts +29 -5
- package/dist/commands/create.js +25 -2
- package/package.json +2 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "projectzeronext",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.45.0-rc.1",
|
|
4
4
|
"private": true,
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"scripts": {
|
|
@@ -22,27 +22,26 @@
|
|
|
22
22
|
"prestart": "pz-prestart"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@akinon/next": "1.
|
|
26
|
-
"@akinon/pz-akifast": "1.
|
|
27
|
-
"@akinon/pz-b2b": "1.
|
|
28
|
-
"@akinon/pz-basket-gift-pack": "1.
|
|
29
|
-
"@akinon/pz-bkm": "1.
|
|
30
|
-
"@akinon/pz-checkout-gift-pack": "1.
|
|
31
|
-
"@akinon/pz-click-collect": "1.
|
|
32
|
-
"@akinon/pz-credit-payment": "1.
|
|
33
|
-
"@akinon/pz-gpay": "1.
|
|
34
|
-
"@akinon/pz-masterpass": "1.
|
|
35
|
-
"@akinon/pz-one-click-checkout": "1.
|
|
36
|
-
"@akinon/pz-otp": "1.
|
|
37
|
-
"@akinon/pz-pay-on-delivery": "1.
|
|
25
|
+
"@akinon/next": "1.45.0-rc.1",
|
|
26
|
+
"@akinon/pz-akifast": "1.45.0-rc.1",
|
|
27
|
+
"@akinon/pz-b2b": "1.45.0-rc.1",
|
|
28
|
+
"@akinon/pz-basket-gift-pack": "1.45.0-rc.1",
|
|
29
|
+
"@akinon/pz-bkm": "1.45.0-rc.1",
|
|
30
|
+
"@akinon/pz-checkout-gift-pack": "1.45.0-rc.1",
|
|
31
|
+
"@akinon/pz-click-collect": "1.45.0-rc.1",
|
|
32
|
+
"@akinon/pz-credit-payment": "1.45.0-rc.1",
|
|
33
|
+
"@akinon/pz-gpay": "1.45.0-rc.1",
|
|
34
|
+
"@akinon/pz-masterpass": "1.45.0-rc.1",
|
|
35
|
+
"@akinon/pz-one-click-checkout": "1.45.0-rc.1",
|
|
36
|
+
"@akinon/pz-otp": "1.45.0-rc.1",
|
|
37
|
+
"@akinon/pz-pay-on-delivery": "1.45.0-rc.1",
|
|
38
38
|
"@hookform/resolvers": "2.9.0",
|
|
39
39
|
"@next/third-parties": "14.1.0",
|
|
40
40
|
"@react-google-maps/api": "2.17.1",
|
|
41
|
-
"@sentry/nextjs": "7.
|
|
41
|
+
"@sentry/nextjs": "7.116.0",
|
|
42
42
|
"dayjs": "1.11.5",
|
|
43
|
-
"eslint-config-next": "14.2.2",
|
|
44
43
|
"lossless-json": "2.0.5",
|
|
45
|
-
"next": "14.2.
|
|
44
|
+
"next": "14.2.5",
|
|
46
45
|
"next-auth": "4.24.5",
|
|
47
46
|
"next-pwa": "5.6.0",
|
|
48
47
|
"pino": "8.11.0",
|
|
@@ -59,7 +58,7 @@
|
|
|
59
58
|
"yup": "0.32.11"
|
|
60
59
|
},
|
|
61
60
|
"devDependencies": {
|
|
62
|
-
"@akinon/eslint-plugin-projectzero": "1.
|
|
61
|
+
"@akinon/eslint-plugin-projectzero": "1.45.0-rc.1",
|
|
63
62
|
"@semantic-release/changelog": "6.0.2",
|
|
64
63
|
"@semantic-release/exec": "6.0.3",
|
|
65
64
|
"@semantic-release/git": "10.0.1",
|
|
@@ -79,14 +78,14 @@
|
|
|
79
78
|
"clsx": "1.1.1",
|
|
80
79
|
"currency-symbol-map": "5.1.0",
|
|
81
80
|
"eslint": "8.14.0",
|
|
82
|
-
"eslint-config-next": "14.
|
|
81
|
+
"eslint-config-next": "14.2.3",
|
|
83
82
|
"eslint-config-prettier": "8.5.0",
|
|
84
83
|
"husky": "8.0.0",
|
|
85
84
|
"jest": "29.7.0",
|
|
86
85
|
"jest-css-modules-transform": "4.3.0",
|
|
87
86
|
"lint-staged": "13.1.0",
|
|
88
87
|
"prettier": "2.6.2",
|
|
89
|
-
"react-number-format": "
|
|
88
|
+
"react-number-format": "5.3.4",
|
|
90
89
|
"sass": "1.49.9",
|
|
91
90
|
"semantic-release": "19.0.5",
|
|
92
91
|
"server-only": "0.0.1",
|
|
@@ -95,10 +94,13 @@
|
|
|
95
94
|
"stylelint-config-standard": "25.0.0",
|
|
96
95
|
"stylelint-scss": "4.2.0",
|
|
97
96
|
"stylelint-selector-bem-pattern": "2.1.1",
|
|
98
|
-
"tailwindcss": "3.4.
|
|
97
|
+
"tailwindcss": "3.4.5",
|
|
99
98
|
"ts-jest": "29.1.1",
|
|
100
99
|
"ts-node": "10.7.0",
|
|
101
100
|
"typescript": "5.2.2"
|
|
102
101
|
},
|
|
102
|
+
"resolutions": {
|
|
103
|
+
"**/postcss": "8.4.31"
|
|
104
|
+
},
|
|
103
105
|
"packageManager": "yarn@1.22.17"
|
|
104
106
|
}
|
|
@@ -286,11 +286,11 @@
|
|
|
286
286
|
"empty_coupon": "You don't have any coupons"
|
|
287
287
|
},
|
|
288
288
|
"title": {
|
|
289
|
-
"
|
|
289
|
+
"campaigns": {
|
|
290
290
|
"active": "Active Campaigns",
|
|
291
|
-
"to_be_active": "
|
|
292
|
-
"expired": "Expired
|
|
293
|
-
"used": "Used
|
|
291
|
+
"to_be_active": "Campaigns to be Active",
|
|
292
|
+
"expired": "Expired Campaigns",
|
|
293
|
+
"used": "Used Campaigns"
|
|
294
294
|
},
|
|
295
295
|
"coupons": {
|
|
296
296
|
"active": "Active Coupons",
|
|
@@ -51,7 +51,7 @@ export default function Page() {
|
|
|
51
51
|
{basketOffersLoading && <LoaderSpinner className="mb-8" />}
|
|
52
52
|
{basketOffersSuccess && (
|
|
53
53
|
<CouponItem
|
|
54
|
-
mainTitle={t('account.my_vouchers.title.
|
|
54
|
+
mainTitle={t('account.my_vouchers.title.campaigns.active')}
|
|
55
55
|
subTitles={[
|
|
56
56
|
t('account.my_vouchers.card.campaign_name'),
|
|
57
57
|
t('account.my_vouchers.card.starting_date'),
|
|
@@ -68,7 +68,7 @@ export default function Page() {
|
|
|
68
68
|
{futureBasketOffersLoading && <LoaderSpinner className="mb-8" />}
|
|
69
69
|
{futureBasketOffersSuccess && (
|
|
70
70
|
<CouponItem
|
|
71
|
-
mainTitle={t('account.my_vouchers.title.
|
|
71
|
+
mainTitle={t('account.my_vouchers.title.campaigns.to_be_active')}
|
|
72
72
|
subTitles={[
|
|
73
73
|
t('account.my_vouchers.card.campaign_name'),
|
|
74
74
|
t('account.my_vouchers.card.starting_date'),
|
|
@@ -85,7 +85,7 @@ export default function Page() {
|
|
|
85
85
|
{expiredBasketOffersLoading && <LoaderSpinner className="mb-8" />}
|
|
86
86
|
{expiredBasketOffersSuccess && (
|
|
87
87
|
<CouponItem
|
|
88
|
-
mainTitle={t('account.my_vouchers.title.
|
|
88
|
+
mainTitle={t('account.my_vouchers.title.campaigns.expired')}
|
|
89
89
|
subTitles={[
|
|
90
90
|
t('account.my_vouchers.card.campaign_name'),
|
|
91
91
|
t('account.my_vouchers.card.starting_date'),
|
|
@@ -102,7 +102,7 @@ export default function Page() {
|
|
|
102
102
|
{discountItemsLoading && <LoaderSpinner className="mb-8" />}
|
|
103
103
|
{discountItemsSuccess && (
|
|
104
104
|
<CouponItem
|
|
105
|
-
mainTitle={t('account.my_vouchers.title.
|
|
105
|
+
mainTitle={t('account.my_vouchers.title.campaigns.used')}
|
|
106
106
|
subTitles={[
|
|
107
107
|
t('account.my_vouchers.card.campaign_name'),
|
|
108
108
|
t('account.my_vouchers.card.starting_date'),
|
|
@@ -4,11 +4,14 @@ import { PageProps } from '@akinon/next/types';
|
|
|
4
4
|
import CategoryLayout from '@theme/views/category/layout';
|
|
5
5
|
|
|
6
6
|
async function Page({ params, searchParams }: PageProps<{ pk: number }>) {
|
|
7
|
-
const { data } = await getCategoryData({
|
|
7
|
+
const { data, breadcrumbData } = await getCategoryData({
|
|
8
|
+
pk: params.pk,
|
|
9
|
+
searchParams
|
|
10
|
+
});
|
|
8
11
|
|
|
9
12
|
return (
|
|
10
13
|
<>
|
|
11
|
-
<CategoryLayout data={data} />
|
|
14
|
+
<CategoryLayout data={data} breadcrumbData={breadcrumbData} />
|
|
12
15
|
</>
|
|
13
16
|
);
|
|
14
17
|
}
|
package/app-template/src/app/[commerce]/[locale]/[currency]/orders/completed/[token]/page.tsx
CHANGED
|
@@ -182,21 +182,25 @@ const CheckoutCompleted = ({
|
|
|
182
182
|
}}
|
|
183
183
|
>
|
|
184
184
|
{data.order.orderitem_set.map((item) => (
|
|
185
|
-
<div
|
|
186
|
-
|
|
185
|
+
<div
|
|
186
|
+
key={`order-item-${item.id}`}
|
|
187
|
+
className="flex justify-between gap-x-4 w-full"
|
|
188
|
+
>
|
|
189
|
+
<Link
|
|
190
|
+
className="flex justify-between gap-x-4 flex-1 items-center transition-all text-xs text-black-800 hover:text-secondary"
|
|
191
|
+
href={item.product.absolute_url}
|
|
192
|
+
passHref
|
|
193
|
+
>
|
|
187
194
|
<Image
|
|
188
195
|
src={item.product.image}
|
|
189
196
|
alt={item.product.name}
|
|
190
197
|
width={64}
|
|
191
198
|
height={96}
|
|
192
199
|
/>
|
|
200
|
+
|
|
201
|
+
<span>{item.product.name}</span>
|
|
193
202
|
</Link>
|
|
194
|
-
<div className="flex justify-
|
|
195
|
-
<>
|
|
196
|
-
<div className="text-xs text-black-800 transition-all w-full hover:text-secondary">
|
|
197
|
-
{item.product.name}
|
|
198
|
-
</div>
|
|
199
|
-
</>
|
|
203
|
+
<div className="flex justify-end items-center">
|
|
200
204
|
<div>
|
|
201
205
|
{item.retail_price !== item.price && (
|
|
202
206
|
<div className="text-black-800 line-through text-xs min-w-max sm:text-sm">
|
|
@@ -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
|
/>
|
|
@@ -1,10 +1,11 @@
|
|
|
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
|
+
import Settings from '@theme/settings';
|
|
6
7
|
|
|
7
|
-
export const Price = (props:
|
|
8
|
+
export const Price = (props: NumericFormatProps & PriceProps) => {
|
|
8
9
|
const {
|
|
9
10
|
value,
|
|
10
11
|
currencyCode,
|
|
@@ -37,15 +38,19 @@ export const Price = (props: NumberFormatProps & PriceProps) => {
|
|
|
37
38
|
[currencyCode_, useCurrencySymbol, useCurrencyAfterPrice, useCurrencySpace]
|
|
38
39
|
);
|
|
39
40
|
|
|
41
|
+
const currentCurrencyDecimalScale = Settings.localization.currencies.find(
|
|
42
|
+
(currency) => currency.code === currencyCode_
|
|
43
|
+
).decimalScale;
|
|
44
|
+
|
|
40
45
|
return (
|
|
41
|
-
<
|
|
46
|
+
<NumericFormat
|
|
42
47
|
value={useNegative ? `-${useNegativeSpace}${_value}` : _value}
|
|
43
48
|
{...{
|
|
44
49
|
[useCurrencyAfterPrice ? 'suffix' : 'prefix']: currency
|
|
45
50
|
}}
|
|
46
51
|
displayType={displayType}
|
|
47
52
|
thousandSeparator={thousandSeparator}
|
|
48
|
-
decimalScale={decimalScale}
|
|
53
|
+
decimalScale={currentCurrencyDecimalScale ?? decimalScale}
|
|
49
54
|
decimalSeparator={decimalSeparator}
|
|
50
55
|
fixedDecimalScale={fixedDecimalScale}
|
|
51
56
|
{...rest}
|
|
@@ -7,11 +7,13 @@ import { WIDGET_TYPE } from '@theme/types';
|
|
|
7
7
|
export interface CategoryState {
|
|
8
8
|
facets: Facet[];
|
|
9
9
|
selectedFacets: Facet[];
|
|
10
|
+
isMenuOpen: boolean;
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
const initialState: CategoryState = {
|
|
13
14
|
facets: [],
|
|
14
|
-
selectedFacets: []
|
|
15
|
+
selectedFacets: [],
|
|
16
|
+
isMenuOpen: false
|
|
15
17
|
};
|
|
16
18
|
|
|
17
19
|
const categorySlice = createSlice({
|
|
@@ -24,6 +26,9 @@ const categorySlice = createSlice({
|
|
|
24
26
|
setSelectedFacets(state, action) {
|
|
25
27
|
state.selectedFacets = action.payload;
|
|
26
28
|
},
|
|
29
|
+
setMenuOpen(state, action) {
|
|
30
|
+
state.isMenuOpen = action.payload;
|
|
31
|
+
},
|
|
27
32
|
toggleFacet(state, action) {
|
|
28
33
|
const facets = JSON.parse(JSON.stringify(state.facets));
|
|
29
34
|
|
|
@@ -81,6 +86,7 @@ const categorySlice = createSlice({
|
|
|
81
86
|
export const {
|
|
82
87
|
setFacets,
|
|
83
88
|
setSelectedFacets,
|
|
89
|
+
setMenuOpen,
|
|
84
90
|
toggleFacet,
|
|
85
91
|
removeCategoryFacet,
|
|
86
92
|
resetSelectedFacets
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
const { LocaleUrlStrategy } = require('@akinon/next/localization');
|
|
2
2
|
const { ROUTES } = require('@theme/routes');
|
|
3
3
|
|
|
4
|
+
/* IMPORTANT *
|
|
5
|
+
* In order to use one locale in the locales array and hide the default locale in the URL, you need to set the localeUrlStrategy to LocaleUrlStrategy.ShowAllLocales.
|
|
6
|
+
*/
|
|
7
|
+
|
|
4
8
|
const commerceUrl = encodeURI(process.env.SERVICE_BACKEND_URL ?? 'default');
|
|
5
9
|
|
|
6
10
|
/** @type {import('@akinon/next/types').Settings} */
|
|
@@ -11,6 +15,7 @@ module.exports = {
|
|
|
11
15
|
{ translationKey: 'size', key: 'size' }
|
|
12
16
|
],
|
|
13
17
|
localization: {
|
|
18
|
+
// If there is one locale in the locales array, the default locale will be hidden in the URL.
|
|
14
19
|
locales: [
|
|
15
20
|
{
|
|
16
21
|
label: 'EN',
|
|
@@ -38,7 +43,7 @@ module.exports = {
|
|
|
38
43
|
}
|
|
39
44
|
],
|
|
40
45
|
defaultLocaleValue: 'en',
|
|
41
|
-
localeUrlStrategy: LocaleUrlStrategy.HideDefaultLocale,
|
|
46
|
+
localeUrlStrategy: LocaleUrlStrategy.HideDefaultLocale, // If there is one locale in the locales array, the default locale will be hidden in the URL and localeUrlStrategy should be set to LocaleUrlStrategy.ShowAllLocales.
|
|
42
47
|
redirectToDefaultLocale: true,
|
|
43
48
|
defaultCurrencyCode: 'usd'
|
|
44
49
|
},
|
|
@@ -27,13 +27,13 @@ export const AddressCard = (props: Props) => {
|
|
|
27
27
|
const [isRemoveAddressModalOpen, setRemoveAddressModalOpen] = useState(false);
|
|
28
28
|
|
|
29
29
|
const onSubmit = async (data) => {
|
|
30
|
-
await editAddress(data);
|
|
30
|
+
await editAddress({ ...data, invalidateTag: 'Addresses' });
|
|
31
31
|
setIsEditAddressModalOpen(false);
|
|
32
32
|
};
|
|
33
33
|
|
|
34
34
|
const handleRemoveAddress = async (pk: number) => {
|
|
35
35
|
try {
|
|
36
|
-
await removeAddress(pk)
|
|
36
|
+
await removeAddress({ id: pk, invalidateTag: 'Addresses' })
|
|
37
37
|
.unwrap()
|
|
38
38
|
.then(() => setRemoveAddressModalOpen(false))
|
|
39
39
|
.catch((err) => console.error(err));
|
|
@@ -31,7 +31,7 @@ interface Props {
|
|
|
31
31
|
onSubmit: (data: any) => void;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
const makeAddressFormSchema = (t, { phoneNumberLength }) =>
|
|
34
|
+
const makeAddressFormSchema = (t, { phoneNumberLength, postCodeLength }) =>
|
|
35
35
|
yup.object().shape({
|
|
36
36
|
title: yup.string().required(t('account.address_book.form.error.required')),
|
|
37
37
|
first_name: yup
|
|
@@ -65,8 +65,9 @@ const makeAddressFormSchema = (t, { phoneNumberLength }) =>
|
|
|
65
65
|
.max(255, t('account.address_book.form.error.line_max')),
|
|
66
66
|
postcode: yup
|
|
67
67
|
.string()
|
|
68
|
-
.
|
|
69
|
-
.
|
|
68
|
+
.transform((value: string) => value.replace(/_/g, '').replace(/ /g, ''))
|
|
69
|
+
.min(postCodeLength, t('account.address_book.form.error.postcode_min'))
|
|
70
|
+
.max(postCodeLength, t('account.address_book.form.error.postcode_max'))
|
|
70
71
|
.required(t('account.address_book.form.error.required')),
|
|
71
72
|
company_name: yup.string().nullable(),
|
|
72
73
|
tax_no: yup.string().nullable(),
|
|
@@ -80,7 +81,8 @@ export const AddressForm = (props: Props) => {
|
|
|
80
81
|
const { data, onSubmit } = props;
|
|
81
82
|
const config = useAppSelector((state) => state.config);
|
|
82
83
|
const addressFormSchema = makeAddressFormSchema(t, {
|
|
83
|
-
phoneNumberLength: config.user_phone_format.length
|
|
84
|
+
phoneNumberLength: config.user_phone_format.length,
|
|
85
|
+
postCodeLength: config.user_post_code_format.length
|
|
84
86
|
});
|
|
85
87
|
const {
|
|
86
88
|
register,
|
|
@@ -181,12 +183,22 @@ export const AddressForm = (props: Props) => {
|
|
|
181
183
|
if (data && country) {
|
|
182
184
|
reset({
|
|
183
185
|
...data,
|
|
184
|
-
is_corporate:
|
|
185
|
-
String(data.is_corporate) === AddressType.company ? 'true' : 'false' // TODO: Fix this! This hack for radio buttons can't be set to boolean value
|
|
186
|
+
is_corporate: String(data.is_corporate)
|
|
186
187
|
});
|
|
187
188
|
}
|
|
188
189
|
}, [data, country, reset]);
|
|
189
190
|
|
|
191
|
+
useEffect(() => {
|
|
192
|
+
if (selectedFormType !== AddressType.company) {
|
|
193
|
+
reset({
|
|
194
|
+
...watch(),
|
|
195
|
+
company_name: '',
|
|
196
|
+
tax_office: '',
|
|
197
|
+
tax_no: ''
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
}, [selectedFormType, reset, watch]);
|
|
201
|
+
|
|
190
202
|
return (
|
|
191
203
|
<form
|
|
192
204
|
onSubmit={handleSubmit(onSubmit)}
|
|
@@ -322,12 +334,15 @@ export const AddressForm = (props: Props) => {
|
|
|
322
334
|
)}
|
|
323
335
|
</label>
|
|
324
336
|
<Input
|
|
325
|
-
type="number"
|
|
326
337
|
label={t('account.address_book.form.post_code.placeholder')}
|
|
327
338
|
{...register('postcode')}
|
|
328
339
|
error={errors.postcode}
|
|
329
340
|
data-testid="address-form-post-code"
|
|
330
341
|
required
|
|
342
|
+
format={config.user_post_code_format.replaceAll(/\9/g, '#')}
|
|
343
|
+
control={control}
|
|
344
|
+
mask="_"
|
|
345
|
+
allowEmptyFormatting
|
|
331
346
|
/>
|
|
332
347
|
{selectedFormType === AddressType.company && (
|
|
333
348
|
<>
|
|
@@ -3,9 +3,9 @@ import {
|
|
|
3
3
|
Button,
|
|
4
4
|
FileInput,
|
|
5
5
|
Input,
|
|
6
|
+
Link,
|
|
6
7
|
LoaderSpinner,
|
|
7
|
-
Select
|
|
8
|
-
Link
|
|
8
|
+
Select
|
|
9
9
|
} from '@theme/components';
|
|
10
10
|
import { useSession } from 'next-auth/react';
|
|
11
11
|
import { useEffect, useState } from 'react';
|
|
@@ -45,7 +45,8 @@ const contactFormSchema = (t) =>
|
|
|
45
45
|
.when('subject', {
|
|
46
46
|
is: (value) => value === '2',
|
|
47
47
|
then: yup.string().required(t('account.contact.form.error.required'))
|
|
48
|
-
})
|
|
48
|
+
}),
|
|
49
|
+
file: yup.mixed()
|
|
49
50
|
});
|
|
50
51
|
|
|
51
52
|
const ContactForm = () => {
|
|
@@ -110,8 +111,18 @@ const ContactForm = () => {
|
|
|
110
111
|
resolver: yupResolver(contactFormSchema(t))
|
|
111
112
|
});
|
|
112
113
|
|
|
113
|
-
const onSubmit: SubmitHandler<ContactFormType> = (data) => {
|
|
114
|
-
|
|
114
|
+
const onSubmit: SubmitHandler<ContactFormType> = (data, event) => {
|
|
115
|
+
const formData = new FormData();
|
|
116
|
+
|
|
117
|
+
Object.keys(data ?? {}).forEach((key) => {
|
|
118
|
+
if (key === 'file' && data[key]) {
|
|
119
|
+
formData.append(key, data[key][0]);
|
|
120
|
+
} else if (data[key]) {
|
|
121
|
+
formData.append(key, data[key]);
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
sendContact(formData);
|
|
115
126
|
};
|
|
116
127
|
|
|
117
128
|
const handleChange = (e) => {
|
|
@@ -234,6 +245,7 @@ const ContactForm = () => {
|
|
|
234
245
|
className="border-gray-500 border w-full text-xs p-2.5 focus-visible:outline-none focus:border-black hover:border-black"
|
|
235
246
|
rows={7}
|
|
236
247
|
name="message"
|
|
248
|
+
// @ts-expect-error -- awaiting fix:
|
|
237
249
|
{...register('message')}
|
|
238
250
|
/>
|
|
239
251
|
{errors.message && (
|
|
@@ -242,7 +254,12 @@ const ContactForm = () => {
|
|
|
242
254
|
<label className="text-xs text-gray-800 mb-2 block">
|
|
243
255
|
{t('account.contact.form.file.title')}
|
|
244
256
|
</label>
|
|
245
|
-
<FileInput
|
|
257
|
+
<FileInput
|
|
258
|
+
name="file"
|
|
259
|
+
title="file"
|
|
260
|
+
className="w-full mb-5"
|
|
261
|
+
{...register('file')}
|
|
262
|
+
/>
|
|
246
263
|
<Button type="submit" className="w-full font-medium">
|
|
247
264
|
{t('account.contact.form.submit_button')}
|
|
248
265
|
</Button>
|
|
@@ -93,8 +93,8 @@ export const FavoriteItem = (props: Props) => {
|
|
|
93
93
|
className={clsx(
|
|
94
94
|
'absolute top-4 right-4 cursor-pointer',
|
|
95
95
|
isRemoveFavoriteLoading
|
|
96
|
-
? '
|
|
97
|
-
: '
|
|
96
|
+
? 'hover:cursor-wait'
|
|
97
|
+
: 'hover:cursor-pointer'
|
|
98
98
|
)}
|
|
99
99
|
data-testid="favorites-remove"
|
|
100
100
|
/>
|
|
@@ -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 (
|
|
@@ -5,6 +5,7 @@ import { Icon, Link } from '@theme/components';
|
|
|
5
5
|
import { ROUTES } from '@theme/routes';
|
|
6
6
|
import { useLocalization } from '@akinon/next/hooks';
|
|
7
7
|
import { BreadcrumbResultType } from '@akinon/next/types';
|
|
8
|
+
import { capitalize } from '@akinon/next/utils';
|
|
8
9
|
|
|
9
10
|
export interface BreadcrumbProps {
|
|
10
11
|
breadcrumbList?: BreadcrumbResultType[];
|
|
@@ -26,7 +27,9 @@ export default function Breadcrumb(props: BreadcrumbProps) {
|
|
|
26
27
|
<div className="flex items-center gap-3 text-xs leading-4 text-gray-950">
|
|
27
28
|
{list.map((item, index) => (
|
|
28
29
|
<Fragment key={index}>
|
|
29
|
-
<Link href={item.url}>
|
|
30
|
+
<Link href={item.url}>
|
|
31
|
+
{capitalize(item.text.toLocaleLowerCase())}
|
|
32
|
+
</Link>
|
|
30
33
|
{index !== list.length - 1 && <Icon name="chevron-end" size={8} />}
|
|
31
34
|
</Fragment>
|
|
32
35
|
))}
|