@akinon/projectzero 1.78.0-rc.2 → 1.78.0
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 +1 -15
- package/app-template/.gitignore +0 -2
- package/app-template/CHANGELOG.md +166 -2734
- package/app-template/package.json +18 -18
- package/app-template/public/locales/en/common.json +0 -4
- package/app-template/public/locales/tr/common.json +0 -4
- package/app-template/sentry.edge.config.ts +3 -0
- package/app-template/sentry.server.config.ts +3 -0
- package/app-template/src/app/[commerce]/[locale]/[currency]/account/orders/[id]/cancellation/page.tsx +5 -94
- package/app-template/src/app/[commerce]/[locale]/[currency]/basket/page.tsx +82 -9
- package/app-template/src/app/[commerce]/[locale]/[currency]/orders/checkout/page.tsx +4 -7
- package/app-template/src/app/[commerce]/[locale]/[currency]/page.tsx +0 -8
- package/app-template/src/components/button.tsx +35 -50
- package/app-template/src/components/file-input.tsx +2 -44
- package/app-template/src/components/types/index.ts +1 -4
- package/app-template/src/middleware.ts +0 -1
- package/app-template/src/settings.js +1 -6
- package/app-template/src/views/account/address-form.tsx +2 -2
- package/app-template/src/views/account/contact-form.tsx +8 -3
- package/app-template/src/views/account/orders/order-cancellation-item.tsx +3 -4
- package/app-template/src/views/basket/basket-item.tsx +13 -16
- package/app-template/src/views/basket/summary.tsx +7 -10
- package/app-template/src/views/login/index.tsx +4 -28
- package/app-template/src/views/register/index.tsx +5 -30
- package/package.json +1 -1
- package/app-template/src/components/widget/widget-placeholder.tsx +0 -12
- package/app-template/src/views/basket/basket-content.tsx +0 -106
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "projectzeronext",
|
|
3
|
-
"version": "1.78.0
|
|
3
|
+
"version": "1.78.0",
|
|
4
4
|
"private": true,
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"scripts": {
|
|
@@ -22,25 +22,25 @@
|
|
|
22
22
|
"prestart": "pz-prestart"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@akinon/next": "1.78.0
|
|
26
|
-
"@akinon/pz-akifast": "1.78.0
|
|
27
|
-
"@akinon/pz-b2b": "1.78.0
|
|
28
|
-
"@akinon/pz-basket-gift-pack": "1.78.0
|
|
29
|
-
"@akinon/pz-bkm": "1.78.0
|
|
30
|
-
"@akinon/pz-checkout-gift-pack": "1.78.0
|
|
31
|
-
"@akinon/pz-click-collect": "1.78.0
|
|
32
|
-
"@akinon/pz-credit-payment": "1.78.0
|
|
33
|
-
"@akinon/pz-gpay": "1.78.0
|
|
34
|
-
"@akinon/pz-masterpass": "1.78.0
|
|
35
|
-
"@akinon/pz-one-click-checkout": "1.78.0
|
|
36
|
-
"@akinon/pz-otp": "1.78.0
|
|
37
|
-
"@akinon/pz-pay-on-delivery": "1.78.0
|
|
38
|
-
"@akinon/pz-saved-card": "1.78.0
|
|
39
|
-
"@akinon/pz-tabby-extension": "1.78.0
|
|
25
|
+
"@akinon/next": "1.78.0",
|
|
26
|
+
"@akinon/pz-akifast": "1.78.0",
|
|
27
|
+
"@akinon/pz-b2b": "1.78.0",
|
|
28
|
+
"@akinon/pz-basket-gift-pack": "1.78.0",
|
|
29
|
+
"@akinon/pz-bkm": "1.78.0",
|
|
30
|
+
"@akinon/pz-checkout-gift-pack": "1.78.0",
|
|
31
|
+
"@akinon/pz-click-collect": "1.78.0",
|
|
32
|
+
"@akinon/pz-credit-payment": "1.78.0",
|
|
33
|
+
"@akinon/pz-gpay": "1.78.0",
|
|
34
|
+
"@akinon/pz-masterpass": "1.78.0",
|
|
35
|
+
"@akinon/pz-one-click-checkout": "1.78.0",
|
|
36
|
+
"@akinon/pz-otp": "1.78.0",
|
|
37
|
+
"@akinon/pz-pay-on-delivery": "1.78.0",
|
|
38
|
+
"@akinon/pz-saved-card": "1.78.0",
|
|
39
|
+
"@akinon/pz-tabby-extension": "1.78.0",
|
|
40
40
|
"@hookform/resolvers": "2.9.0",
|
|
41
41
|
"@next/third-parties": "14.1.0",
|
|
42
42
|
"@react-google-maps/api": "2.17.1",
|
|
43
|
-
"@sentry/nextjs": "
|
|
43
|
+
"@sentry/nextjs": "7.116.0",
|
|
44
44
|
"dayjs": "1.11.5",
|
|
45
45
|
"lossless-json": "2.0.5",
|
|
46
46
|
"next": "14.2.5",
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
"yup": "0.32.11"
|
|
62
62
|
},
|
|
63
63
|
"devDependencies": {
|
|
64
|
-
"@akinon/eslint-plugin-projectzero": "1.78.0
|
|
64
|
+
"@akinon/eslint-plugin-projectzero": "1.78.0",
|
|
65
65
|
"@semantic-release/changelog": "6.0.2",
|
|
66
66
|
"@semantic-release/exec": "6.0.3",
|
|
67
67
|
"@semantic-release/git": "10.0.1",
|
|
@@ -8,15 +8,14 @@ import {
|
|
|
8
8
|
useGetOrderQuery,
|
|
9
9
|
useGetCancellationReasonsQuery
|
|
10
10
|
} from '@akinon/next/data/client/account';
|
|
11
|
-
import
|
|
11
|
+
import { AccountOrderCancellation } from '@akinon/next/types';
|
|
12
12
|
import {
|
|
13
13
|
Button,
|
|
14
14
|
Checkbox,
|
|
15
15
|
Select,
|
|
16
16
|
Modal,
|
|
17
17
|
LoaderSpinner,
|
|
18
|
-
Link
|
|
19
|
-
FileInput
|
|
18
|
+
Link
|
|
20
19
|
} from '@theme/components';
|
|
21
20
|
import { useState } from 'react';
|
|
22
21
|
import { OrderDetailHeader } from '@theme/views/account/orders/order-detail-header';
|
|
@@ -40,7 +39,6 @@ const AccountOrderCancellation = ({ params }) => {
|
|
|
40
39
|
} = useForm<AccountOrderCancellation>({
|
|
41
40
|
resolver: yupResolver(accountOrderCancellationSchema)
|
|
42
41
|
});
|
|
43
|
-
|
|
44
42
|
const { data: cancellationReasons, isSuccess: cancellationReasonsSuccess } =
|
|
45
43
|
useGetCancellationReasonsQuery();
|
|
46
44
|
|
|
@@ -58,9 +56,6 @@ const AccountOrderCancellation = ({ params }) => {
|
|
|
58
56
|
const watchAllFields = watch();
|
|
59
57
|
const cancelItemsLength = watchAllFields?.cancel_order_items?.length;
|
|
60
58
|
const [cancelOrder] = useCancelOrderMutation();
|
|
61
|
-
const [files, setFiles] = useState<
|
|
62
|
-
{ itemId: string; image: string; description: string }[]
|
|
63
|
-
>([]);
|
|
64
59
|
|
|
65
60
|
const modalHandleClick = () => {
|
|
66
61
|
setIsModalOpen(false);
|
|
@@ -117,68 +112,8 @@ const AccountOrderCancellation = ({ params }) => {
|
|
|
117
112
|
]);
|
|
118
113
|
};
|
|
119
114
|
|
|
120
|
-
const handleFileChange = async (
|
|
121
|
-
e: React.ChangeEvent<HTMLInputElement>,
|
|
122
|
-
itemId: string,
|
|
123
|
-
description: string
|
|
124
|
-
) => {
|
|
125
|
-
const selectedFiles = Array.from(e.target.files || []);
|
|
126
|
-
|
|
127
|
-
const base64Files = await Promise.all(
|
|
128
|
-
selectedFiles.map((file) => {
|
|
129
|
-
return new Promise<string>((resolve, reject) => {
|
|
130
|
-
const reader = new FileReader();
|
|
131
|
-
reader.onload = () => resolve(reader.result as string);
|
|
132
|
-
reader.onerror = reject;
|
|
133
|
-
reader.readAsDataURL(file);
|
|
134
|
-
});
|
|
135
|
-
})
|
|
136
|
-
);
|
|
137
|
-
|
|
138
|
-
const validFiles = base64Files.filter((file) =>
|
|
139
|
-
/^data:image\/(jpeg|png|jpg);base64,.+/.test(file)
|
|
140
|
-
);
|
|
141
|
-
|
|
142
|
-
const formattedFiles = validFiles.map((file) => ({
|
|
143
|
-
itemId,
|
|
144
|
-
image: file,
|
|
145
|
-
description
|
|
146
|
-
}));
|
|
147
|
-
|
|
148
|
-
setFiles((prevFiles) => [
|
|
149
|
-
...prevFiles.filter((f) => f.itemId !== itemId),
|
|
150
|
-
...formattedFiles
|
|
151
|
-
]);
|
|
152
|
-
};
|
|
153
|
-
|
|
154
|
-
const fileInputCondition = (item, description: string) => {
|
|
155
|
-
if (item.is_refundable && !item.active_cancellation_request) {
|
|
156
|
-
return (
|
|
157
|
-
<FileInput
|
|
158
|
-
name="files"
|
|
159
|
-
title="files"
|
|
160
|
-
multiple
|
|
161
|
-
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
|
|
162
|
-
handleFileChange(e, item.id, description)
|
|
163
|
-
}
|
|
164
|
-
/>
|
|
165
|
-
);
|
|
166
|
-
}
|
|
167
|
-
};
|
|
168
|
-
|
|
169
115
|
const onSubmit: SubmitHandler<AccountOrderCancellation> = (orderItems) => {
|
|
170
|
-
|
|
171
|
-
...orderItems,
|
|
172
|
-
cancel_order_items: orderItems.cancel_order_items.map((orderItem) => ({
|
|
173
|
-
...orderItem,
|
|
174
|
-
...(files.length > 0 && {
|
|
175
|
-
cancellation_request_image_set: files.filter(
|
|
176
|
-
(file) => file.itemId === orderItem.order_item
|
|
177
|
-
)
|
|
178
|
-
})
|
|
179
|
-
}))
|
|
180
|
-
};
|
|
181
|
-
cancelOrder({ id: order.number, ...mergedData })
|
|
116
|
+
cancelOrder({ id: order.number, ...orderItems })
|
|
182
117
|
.unwrap()
|
|
183
118
|
.then(() => {
|
|
184
119
|
setResponseMessage({
|
|
@@ -187,33 +122,10 @@ const AccountOrderCancellation = ({ params }) => {
|
|
|
187
122
|
});
|
|
188
123
|
setIsModalOpen(true);
|
|
189
124
|
})
|
|
190
|
-
.catch((
|
|
191
|
-
console.error('Err', err);
|
|
192
|
-
|
|
193
|
-
const errorMessages = new Set();
|
|
194
|
-
|
|
195
|
-
if (err?.data?.cancel_order_items) {
|
|
196
|
-
err.data.cancel_order_items.forEach((item) => {
|
|
197
|
-
if (item.cancellation_request_image_set) {
|
|
198
|
-
item.cancellation_request_image_set.forEach((error) => {
|
|
199
|
-
if (typeof error === 'string') {
|
|
200
|
-
errorMessages.add(error);
|
|
201
|
-
} else if (typeof error === 'object' && error?.image) {
|
|
202
|
-
error.image.forEach((msg) => errorMessages.add(msg));
|
|
203
|
-
}
|
|
204
|
-
});
|
|
205
|
-
}
|
|
206
|
-
});
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
const errorContent =
|
|
210
|
-
errorMessages.size > 0
|
|
211
|
-
? Array.from(errorMessages).join('\n')
|
|
212
|
-
: t('account.my_orders.return.error.description').toString();
|
|
213
|
-
|
|
125
|
+
.catch(() => {
|
|
214
126
|
setResponseMessage({
|
|
215
127
|
title: t('account.my_orders.return.error.title').toString(),
|
|
216
|
-
content:
|
|
128
|
+
content: t('account.my_orders.return.error.description').toString()
|
|
217
129
|
});
|
|
218
130
|
setIsModalOpen(true);
|
|
219
131
|
});
|
|
@@ -268,7 +180,6 @@ const AccountOrderCancellation = ({ params }) => {
|
|
|
268
180
|
onChange={onChange}
|
|
269
181
|
value={value}
|
|
270
182
|
selectOption={selectOption}
|
|
271
|
-
fileInput={fileInputCondition(item, item.product.name)}
|
|
272
183
|
/>
|
|
273
184
|
);
|
|
274
185
|
}}
|
|
@@ -1,14 +1,87 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useEffect } from 'react';
|
|
4
|
+
import { ROUTES } from '@theme/routes';
|
|
5
|
+
import { useGetBasketQuery } from '@akinon/next/data/client/basket';
|
|
6
|
+
import { pushCartView } from '@theme/utils/gtm';
|
|
7
|
+
import { Button, LoaderSpinner, Link } from '@theme/components';
|
|
8
|
+
import { BasketItem, Summary } from '@theme/views/basket';
|
|
9
|
+
import { useLocalization } from '@akinon/next/hooks';
|
|
10
|
+
import PluginModule, { Component } from '@akinon/next/components/plugin-module';
|
|
3
11
|
import settings from '@theme/settings';
|
|
4
12
|
|
|
5
|
-
export default
|
|
6
|
-
const { basket } =
|
|
13
|
+
export default function Page() {
|
|
14
|
+
const { data: basket, isLoading, isSuccess } = useGetBasketQuery();
|
|
15
|
+
const { t } = useLocalization();
|
|
16
|
+
const multiBasket = settings.plugins?.multiBasket ?? false;
|
|
17
|
+
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
if (isSuccess) {
|
|
20
|
+
const products = basket.basketitem_set.map((basketItem) => ({
|
|
21
|
+
...basketItem.product
|
|
22
|
+
}));
|
|
23
|
+
pushCartView(products);
|
|
24
|
+
}
|
|
25
|
+
}, [basket, isSuccess]);
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<div className="max-w-screen-xl p-4 flex flex-col text-primary-800 lg:p-8 xl:flex-row xl:mx-auto">
|
|
29
|
+
{isLoading && (
|
|
30
|
+
<div className="flex justify-center w-full">
|
|
31
|
+
<LoaderSpinner />
|
|
32
|
+
</div>
|
|
33
|
+
)}
|
|
34
|
+
{isSuccess &&
|
|
35
|
+
(basket && basket.basketitem_set && basket.basketitem_set.length > 0 ? (
|
|
36
|
+
<>
|
|
37
|
+
<div className="flex-1 xl:mr-16">
|
|
38
|
+
<div className="flex items-center justify-between py-2 border-b border-gray-200 lg:py-3">
|
|
39
|
+
<h2 className="text-xl lg:text-2xl font-light">
|
|
40
|
+
{t('basket.my_cart')}
|
|
41
|
+
</h2>
|
|
42
|
+
<Link
|
|
43
|
+
href={ROUTES.HOME}
|
|
44
|
+
className="text-xs hover:text-secondary-500"
|
|
45
|
+
>
|
|
46
|
+
{t('basket.back_to_shopping')}
|
|
47
|
+
</Link>
|
|
48
|
+
</div>
|
|
49
|
+
<ul>
|
|
50
|
+
{multiBasket ? (
|
|
51
|
+
<PluginModule
|
|
52
|
+
component={Component.MultiBasket}
|
|
53
|
+
props={{ BasketItem }}
|
|
54
|
+
/>
|
|
55
|
+
) : (
|
|
56
|
+
basket.basketitem_set.map((basketItem, index) => (
|
|
57
|
+
<BasketItem basketItem={basketItem} key={index} />
|
|
58
|
+
))
|
|
59
|
+
)}
|
|
60
|
+
</ul>
|
|
61
|
+
</div>
|
|
62
|
+
<Summary basket={basket} />
|
|
63
|
+
</>
|
|
64
|
+
) : (
|
|
65
|
+
<div className="flex flex-col items-center container max-w-screen-sm py-4 px-4 xs:py-6 xs:px-6 sm:py-8 sm:px-8 lg:max-w-screen-xl">
|
|
66
|
+
<h1
|
|
67
|
+
className="w-full text-xl font-light text-secondary text-center sm:text-2xl"
|
|
68
|
+
data-testid="basket-empty"
|
|
69
|
+
>
|
|
70
|
+
{t('basket.empty.title')}
|
|
71
|
+
</h1>
|
|
7
72
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
73
|
+
<div className="w-full text-sm text-black-800 text-center my-4 mb-2 sm:text-base">
|
|
74
|
+
<p>{t('basket.empty.content_first')}</p>
|
|
75
|
+
<p>{t('basket.empty.content_second')}.</p>
|
|
76
|
+
</div>
|
|
12
77
|
|
|
13
|
-
|
|
78
|
+
<Link href={ROUTES.HOME} passHref>
|
|
79
|
+
<Button className="px-10 mt-2" appearance="filled">
|
|
80
|
+
{t('basket.empty.button')}
|
|
81
|
+
</Button>
|
|
82
|
+
</Link>
|
|
83
|
+
</div>
|
|
84
|
+
))}
|
|
85
|
+
</div>
|
|
86
|
+
);
|
|
14
87
|
}
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
} from '@akinon/next/redux/reducers/checkout';
|
|
12
12
|
import { RootState } from '@theme/redux/store';
|
|
13
13
|
import { ROUTES } from '@theme/routes';
|
|
14
|
-
import { useFetchCheckoutQuery
|
|
14
|
+
import { useFetchCheckoutQuery } from '@akinon/next/data/client/checkout';
|
|
15
15
|
import { Button, LoaderSpinner } from '@theme/components';
|
|
16
16
|
import { pushAddPaymentInfo, pushAddShippingInfo } from '@theme/utils/gtm';
|
|
17
17
|
import { CheckoutStep } from '@akinon/next/types';
|
|
@@ -25,8 +25,6 @@ const Checkout = () => {
|
|
|
25
25
|
(state: RootState) => state.checkout
|
|
26
26
|
);
|
|
27
27
|
|
|
28
|
-
const { data: indexData, isLoading: isResetStateLoading } = useResetCheckoutStateQuery(null);
|
|
29
|
-
|
|
30
28
|
const {
|
|
31
29
|
data: checkoutData,
|
|
32
30
|
isFetching,
|
|
@@ -34,8 +32,7 @@ const Checkout = () => {
|
|
|
34
32
|
isSuccess,
|
|
35
33
|
refetch: refetchCheckout
|
|
36
34
|
} = useFetchCheckoutQuery(null, {
|
|
37
|
-
refetchOnMountOrArgChange: true
|
|
38
|
-
skip: isResetStateLoading || !indexData
|
|
35
|
+
refetchOnMountOrArgChange: true
|
|
39
36
|
});
|
|
40
37
|
const initialStepChanged = useRef<boolean>(false);
|
|
41
38
|
const router = useRouter();
|
|
@@ -97,10 +94,10 @@ const Checkout = () => {
|
|
|
97
94
|
);
|
|
98
95
|
}
|
|
99
96
|
|
|
100
|
-
if (
|
|
97
|
+
if (isFetching || isError) {
|
|
101
98
|
return (
|
|
102
99
|
<div className="flex flex-col items-center justify-center h-80">
|
|
103
|
-
{
|
|
100
|
+
{isFetching ? (
|
|
104
101
|
<LoaderSpinner />
|
|
105
102
|
) : (
|
|
106
103
|
<>
|
|
@@ -4,7 +4,6 @@ import { HOME_WIDGETS } from '@theme/widgets';
|
|
|
4
4
|
import { getWidgetData } from '@akinon/next/data/server';
|
|
5
5
|
import { withSegmentDefaults } from '@akinon/next/hocs/server';
|
|
6
6
|
import LazyComponent from '@akinon/next/components/lazy-component';
|
|
7
|
-
import WidgetPlaceholder from '@theme/components/widget/widget-placeholder';
|
|
8
7
|
|
|
9
8
|
type HomeWidgetOrderType = {
|
|
10
9
|
widget_order: Array<{
|
|
@@ -17,13 +16,6 @@ export const dynamic = 'force-static';
|
|
|
17
16
|
export const revalidate = 600;
|
|
18
17
|
|
|
19
18
|
async function Page() {
|
|
20
|
-
return (
|
|
21
|
-
<>
|
|
22
|
-
<WidgetPlaceholder slug="home-widget-order" />
|
|
23
|
-
<WidgetPlaceholder slug="home-widget-order-2" />
|
|
24
|
-
</>
|
|
25
|
-
);
|
|
26
|
-
|
|
27
19
|
const data = await getWidgetData<HomeWidgetOrderType>({
|
|
28
20
|
slug: 'home-widget-order'
|
|
29
21
|
});
|
|
@@ -1,61 +1,46 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import { Link } from '@theme/components';
|
|
4
3
|
import { ButtonProps } from '@theme/components/types';
|
|
5
4
|
import clsx from 'clsx';
|
|
6
5
|
import { twMerge } from 'tailwind-merge';
|
|
7
6
|
|
|
8
7
|
export const Button = (props: ButtonProps) => {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
),
|
|
44
|
-
className
|
|
45
|
-
);
|
|
46
|
-
|
|
47
|
-
return props.href ? (
|
|
48
|
-
<Link
|
|
49
|
-
prefetch={false}
|
|
50
|
-
target={target}
|
|
51
|
-
href={href}
|
|
52
|
-
className={buttonClasses}
|
|
8
|
+
return (
|
|
9
|
+
<button
|
|
10
|
+
{...props}
|
|
11
|
+
className={twMerge(
|
|
12
|
+
clsx(
|
|
13
|
+
[
|
|
14
|
+
'px-4',
|
|
15
|
+
'h-10',
|
|
16
|
+
'text-xs',
|
|
17
|
+
'bg-primary',
|
|
18
|
+
'text-primary-foreground',
|
|
19
|
+
'border',
|
|
20
|
+
'border-primary',
|
|
21
|
+
'transition-all',
|
|
22
|
+
'hover:bg-white',
|
|
23
|
+
'hover:border-primary',
|
|
24
|
+
'hover:text-primary'
|
|
25
|
+
],
|
|
26
|
+
props.appearance === 'outlined' && [
|
|
27
|
+
'bg-transparent ',
|
|
28
|
+
'text-primary ',
|
|
29
|
+
'hover:bg-primary ',
|
|
30
|
+
'hover:text-primary-foreground'
|
|
31
|
+
],
|
|
32
|
+
props.appearance === 'ghost' && [
|
|
33
|
+
'bg-transparent',
|
|
34
|
+
'border-transparent',
|
|
35
|
+
'text-primary',
|
|
36
|
+
'hover:bg-primary',
|
|
37
|
+
'hover:text-primary-foreground'
|
|
38
|
+
]
|
|
39
|
+
),
|
|
40
|
+
props.className
|
|
41
|
+
)}
|
|
53
42
|
>
|
|
54
|
-
{children}
|
|
55
|
-
</Link>
|
|
56
|
-
) : (
|
|
57
|
-
<button {...rest} className={buttonClasses}>
|
|
58
|
-
{children}
|
|
43
|
+
{props.children}
|
|
59
44
|
</button>
|
|
60
45
|
);
|
|
61
46
|
};
|
|
@@ -1,50 +1,8 @@
|
|
|
1
|
-
import { useState } from 'react';
|
|
2
1
|
import { forwardRef } from 'react';
|
|
3
2
|
import { FileInputProps } from '@theme/components/types';
|
|
4
|
-
import clsx from 'clsx';
|
|
5
|
-
import { useLocalization } from '@akinon/next/hooks';
|
|
6
3
|
|
|
7
4
|
export const FileInput = forwardRef<HTMLInputElement, FileInputProps>(
|
|
8
|
-
function
|
|
9
|
-
|
|
10
|
-
const [fileNames, setFileNames] = useState<string[]>([]);
|
|
11
|
-
|
|
12
|
-
const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
13
|
-
const files = Array.from(event.target.files || []);
|
|
14
|
-
setFileNames(files.map((file) => file.name));
|
|
15
|
-
|
|
16
|
-
if (onChange) {
|
|
17
|
-
onChange(event);
|
|
18
|
-
}
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
return (
|
|
22
|
-
<div className="relative">
|
|
23
|
-
<input
|
|
24
|
-
type="file"
|
|
25
|
-
{...props}
|
|
26
|
-
ref={ref}
|
|
27
|
-
className="absolute inset-0 w-full h-full opacity-0 cursor-pointer"
|
|
28
|
-
onChange={handleFileChange}
|
|
29
|
-
/>
|
|
30
|
-
<button
|
|
31
|
-
type="button"
|
|
32
|
-
className={clsx('bg-primary text-white py-2 px-4 text-sm', className)}
|
|
33
|
-
>
|
|
34
|
-
{t('common.file_input.select_file')}
|
|
35
|
-
</button>
|
|
36
|
-
<div className="mt-1 text-gray-500">
|
|
37
|
-
{fileNames.length > 0 ? (
|
|
38
|
-
<ul className="list-disc pl-4 text-xs">
|
|
39
|
-
{fileNames.map((name, index) => (
|
|
40
|
-
<li key={index}>{name}</li>
|
|
41
|
-
))}
|
|
42
|
-
</ul>
|
|
43
|
-
) : (
|
|
44
|
-
<span className="text-xs">{t('common.file_input.no_file')}</span>
|
|
45
|
-
)}
|
|
46
|
-
</div>
|
|
47
|
-
</div>
|
|
48
|
-
);
|
|
5
|
+
function fileInput(props, ref) {
|
|
6
|
+
return <input type="file" {...props} ref={ref} />;
|
|
49
7
|
}
|
|
50
8
|
);
|
|
@@ -4,10 +4,7 @@ import { UsePaginationType } from '@akinon/next/hooks/use-pagination';
|
|
|
4
4
|
|
|
5
5
|
export interface ButtonProps
|
|
6
6
|
extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
7
|
-
appearance?: 'filled' | 'outlined' | 'ghost'
|
|
8
|
-
size?: 'sm' | 'md' | 'lg' | 'xl';
|
|
9
|
-
href?: string;
|
|
10
|
-
target?: '_blank' | '_self' | '_parent' | '_top';
|
|
7
|
+
appearance?: 'filled' | 'outlined' | 'ghost';
|
|
11
8
|
}
|
|
12
9
|
|
|
13
10
|
export interface PaginationProps {
|
|
@@ -9,7 +9,6 @@ import { NextMiddleware, NextResponse } from 'next/server';
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
export const config = {
|
|
12
|
-
// For .xml.gz type sitemap urls, update '/(.*sitemap\\.xml)' with this regex '/(.*sitemap\\.xml|sitemap/.*.xml(?:.gz)?)/',
|
|
13
12
|
matcher: [
|
|
14
13
|
'/((?!api|_next|[\\w-\\/*]+\\.\\w+).*)',
|
|
15
14
|
'/(.*sitemap\\.xml)',
|
|
@@ -1,10 +1,6 @@
|
|
|
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
|
-
|
|
8
4
|
const commerceUrl = encodeURI(process.env.SERVICE_BACKEND_URL ?? 'default');
|
|
9
5
|
|
|
10
6
|
/** @type {import('@akinon/next/types').Settings} */
|
|
@@ -18,7 +14,6 @@ module.exports = {
|
|
|
18
14
|
{ translationKey: 'size', key: 'size' }
|
|
19
15
|
],
|
|
20
16
|
localization: {
|
|
21
|
-
// If there is one locale in the locales array, the default locale will be hidden in the URL.
|
|
22
17
|
locales: [
|
|
23
18
|
{
|
|
24
19
|
label: 'EN',
|
|
@@ -46,7 +41,7 @@ module.exports = {
|
|
|
46
41
|
}
|
|
47
42
|
],
|
|
48
43
|
defaultLocaleValue: 'en',
|
|
49
|
-
localeUrlStrategy: LocaleUrlStrategy.HideDefaultLocale,
|
|
44
|
+
localeUrlStrategy: LocaleUrlStrategy.HideDefaultLocale,
|
|
50
45
|
redirectToDefaultLocale: true,
|
|
51
46
|
defaultCurrencyCode: 'usd'
|
|
52
47
|
},
|
|
@@ -247,7 +247,7 @@ export const AddressForm = (props: Props) => {
|
|
|
247
247
|
/>
|
|
248
248
|
<Input
|
|
249
249
|
label={t('account.address_book.form.phone.placeholder')}
|
|
250
|
-
format={config.user_phone_format.replaceAll(
|
|
250
|
+
format={config.user_phone_format.replaceAll(/\9/g, '#')}
|
|
251
251
|
mask="_"
|
|
252
252
|
allowEmptyFormatting={true}
|
|
253
253
|
control={control}
|
|
@@ -339,7 +339,7 @@ export const AddressForm = (props: Props) => {
|
|
|
339
339
|
error={errors.postcode}
|
|
340
340
|
data-testid="address-form-post-code"
|
|
341
341
|
required
|
|
342
|
-
format={config.user_post_code_format.replaceAll(
|
|
342
|
+
format={config.user_post_code_format.replaceAll(/\9/g, '#')}
|
|
343
343
|
control={control}
|
|
344
344
|
mask="_"
|
|
345
345
|
allowEmptyFormatting
|
|
@@ -192,7 +192,7 @@ const ContactForm = () => {
|
|
|
192
192
|
label={t('account.contact.form.phone.placeholder')}
|
|
193
193
|
type="tel"
|
|
194
194
|
className="mb-1"
|
|
195
|
-
format={user_phone_format.replace(
|
|
195
|
+
format={user_phone_format.replace(/\9/g, '#')}
|
|
196
196
|
mask="_"
|
|
197
197
|
allowEmptyFormatting={true}
|
|
198
198
|
control={control}
|
|
@@ -255,8 +255,13 @@ const ContactForm = () => {
|
|
|
255
255
|
<label className="text-xs text-gray-800 mb-2 block">
|
|
256
256
|
{t('account.contact.form.file.title')}
|
|
257
257
|
</label>
|
|
258
|
-
<FileInput
|
|
259
|
-
|
|
258
|
+
<FileInput
|
|
259
|
+
name="file"
|
|
260
|
+
title="file"
|
|
261
|
+
className="w-full mb-5"
|
|
262
|
+
{...register('file')}
|
|
263
|
+
/>
|
|
264
|
+
<Button type="submit" className="w-full font-medium">
|
|
260
265
|
{t('account.contact.form.submit_button')}
|
|
261
266
|
</Button>
|
|
262
267
|
</form>
|