@akinon/projectzero 1.77.0 → 1.78.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 (27) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/app-template/.gitignore +2 -0
  3. package/app-template/CHANGELOG.md +2655 -124
  4. package/app-template/package.json +18 -18
  5. package/app-template/public/locales/en/common.json +4 -0
  6. package/app-template/public/locales/tr/common.json +4 -0
  7. package/app-template/src/app/[commerce]/[locale]/[currency]/account/orders/[id]/cancellation/page.tsx +94 -5
  8. package/app-template/src/app/[commerce]/[locale]/[currency]/basket/page.tsx +9 -82
  9. package/app-template/src/app/[commerce]/[locale]/[currency]/orders/checkout/page.tsx +7 -4
  10. package/app-template/src/app/[commerce]/[locale]/[currency]/page.tsx +8 -0
  11. package/app-template/src/components/button.tsx +50 -35
  12. package/app-template/src/components/file-input.tsx +44 -2
  13. package/app-template/src/components/types/index.ts +4 -1
  14. package/app-template/src/components/widget/widget-placeholder.tsx +12 -0
  15. package/app-template/src/middleware.ts +1 -0
  16. package/app-template/src/settings.js +6 -1
  17. package/app-template/src/views/account/address-form.tsx +2 -2
  18. package/app-template/src/views/account/contact-form.tsx +3 -8
  19. package/app-template/src/views/account/orders/order-cancellation-item.tsx +4 -3
  20. package/app-template/src/views/basket/basket-content.tsx +106 -0
  21. package/app-template/src/views/basket/basket-item.tsx +16 -13
  22. package/app-template/src/views/basket/summary.tsx +10 -7
  23. package/app-template/src/views/login/index.tsx +28 -4
  24. package/app-template/src/views/register/index.tsx +30 -5
  25. package/package.json +1 -1
  26. package/app-template/sentry.edge.config.ts +0 -3
  27. package/app-template/sentry.server.config.ts +0 -3
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "projectzeronext",
3
- "version": "1.77.0",
3
+ "version": "1.78.0-rc.1",
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.77.0",
26
- "@akinon/pz-akifast": "1.77.0",
27
- "@akinon/pz-b2b": "1.77.0",
28
- "@akinon/pz-basket-gift-pack": "1.77.0",
29
- "@akinon/pz-bkm": "1.77.0",
30
- "@akinon/pz-checkout-gift-pack": "1.77.0",
31
- "@akinon/pz-click-collect": "1.77.0",
32
- "@akinon/pz-credit-payment": "1.77.0",
33
- "@akinon/pz-gpay": "1.77.0",
34
- "@akinon/pz-masterpass": "1.77.0",
35
- "@akinon/pz-one-click-checkout": "1.77.0",
36
- "@akinon/pz-otp": "1.77.0",
37
- "@akinon/pz-pay-on-delivery": "1.77.0",
38
- "@akinon/pz-saved-card": "1.77.0",
39
- "@akinon/pz-tabby-extension": "1.77.0",
25
+ "@akinon/next": "1.78.0-rc.1",
26
+ "@akinon/pz-akifast": "1.78.0-rc.1",
27
+ "@akinon/pz-b2b": "1.78.0-rc.1",
28
+ "@akinon/pz-basket-gift-pack": "1.78.0-rc.1",
29
+ "@akinon/pz-bkm": "1.78.0-rc.1",
30
+ "@akinon/pz-checkout-gift-pack": "1.78.0-rc.1",
31
+ "@akinon/pz-click-collect": "1.78.0-rc.1",
32
+ "@akinon/pz-credit-payment": "1.78.0-rc.1",
33
+ "@akinon/pz-gpay": "1.78.0-rc.1",
34
+ "@akinon/pz-masterpass": "1.78.0-rc.1",
35
+ "@akinon/pz-one-click-checkout": "1.78.0-rc.1",
36
+ "@akinon/pz-otp": "1.78.0-rc.1",
37
+ "@akinon/pz-pay-on-delivery": "1.78.0-rc.1",
38
+ "@akinon/pz-saved-card": "1.78.0-rc.1",
39
+ "@akinon/pz-tabby-extension": "1.78.0-rc.1",
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": "7.116.0",
43
+ "@sentry/nextjs": "8.35.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.77.0",
64
+ "@akinon/eslint-plugin-projectzero": "1.78.0-rc.1",
65
65
  "@semantic-release/changelog": "6.0.2",
66
66
  "@semantic-release/exec": "6.0.3",
67
67
  "@semantic-release/git": "10.0.1",
@@ -61,5 +61,9 @@
61
61
  "description": "The products in your cart will be deleted because the exchange rate has changed.",
62
62
  "close": "Close",
63
63
  "continue": "Continue"
64
+ },
65
+ "file_input": {
66
+ "select_file": "Select File",
67
+ "no_file": "No file selected"
64
68
  }
65
69
  }
@@ -61,5 +61,9 @@
61
61
  "description": "Döviz kuru değiştiği için sepetinizdeki ürünler silinecektir.",
62
62
  "close": "Kapat",
63
63
  "continue": "Devam Et"
64
+ },
65
+ "file_input": {
66
+ "select_file": "Dosya Seç",
67
+ "no_file": "Dosya seçilmedi"
64
68
  }
65
69
  }
@@ -8,14 +8,15 @@ import {
8
8
  useGetOrderQuery,
9
9
  useGetCancellationReasonsQuery
10
10
  } from '@akinon/next/data/client/account';
11
- import { AccountOrderCancellation } from '@akinon/next/types';
11
+ import type { 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
18
+ Link,
19
+ FileInput
19
20
  } from '@theme/components';
20
21
  import { useState } from 'react';
21
22
  import { OrderDetailHeader } from '@theme/views/account/orders/order-detail-header';
@@ -39,6 +40,7 @@ const AccountOrderCancellation = ({ params }) => {
39
40
  } = useForm<AccountOrderCancellation>({
40
41
  resolver: yupResolver(accountOrderCancellationSchema)
41
42
  });
43
+
42
44
  const { data: cancellationReasons, isSuccess: cancellationReasonsSuccess } =
43
45
  useGetCancellationReasonsQuery();
44
46
 
@@ -56,6 +58,9 @@ const AccountOrderCancellation = ({ params }) => {
56
58
  const watchAllFields = watch();
57
59
  const cancelItemsLength = watchAllFields?.cancel_order_items?.length;
58
60
  const [cancelOrder] = useCancelOrderMutation();
61
+ const [files, setFiles] = useState<
62
+ { itemId: string; image: string; description: string }[]
63
+ >([]);
59
64
 
60
65
  const modalHandleClick = () => {
61
66
  setIsModalOpen(false);
@@ -112,8 +117,68 @@ const AccountOrderCancellation = ({ params }) => {
112
117
  ]);
113
118
  };
114
119
 
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
+
115
169
  const onSubmit: SubmitHandler<AccountOrderCancellation> = (orderItems) => {
116
- cancelOrder({ id: order.number, ...orderItems })
170
+ const mergedData = {
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 })
117
182
  .unwrap()
118
183
  .then(() => {
119
184
  setResponseMessage({
@@ -122,10 +187,33 @@ const AccountOrderCancellation = ({ params }) => {
122
187
  });
123
188
  setIsModalOpen(true);
124
189
  })
125
- .catch(() => {
190
+ .catch((err) => {
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
+
126
214
  setResponseMessage({
127
215
  title: t('account.my_orders.return.error.title').toString(),
128
- content: t('account.my_orders.return.error.description').toString()
216
+ content: errorContent
129
217
  });
130
218
  setIsModalOpen(true);
131
219
  });
@@ -180,6 +268,7 @@ const AccountOrderCancellation = ({ params }) => {
180
268
  onChange={onChange}
181
269
  value={value}
182
270
  selectOption={selectOption}
271
+ fileInput={fileInputCondition(item, item.product.name)}
183
272
  />
184
273
  );
185
274
  }}
@@ -1,87 +1,14 @@
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';
1
+ import { BasketContent } from '@theme/views/basket/basket-content';
2
+ import { getBasketData } from '@akinon/next/data/server/basket';
11
3
  import settings from '@theme/settings';
12
4
 
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>
5
+ export default async function Page() {
6
+ const { basket } = await getBasketData();
72
7
 
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>
8
+ const multiBasket: boolean =
9
+ typeof settings.plugins?.multiBasket === 'boolean'
10
+ ? settings.plugins.multiBasket
11
+ : false;
77
12
 
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
- );
13
+ return <BasketContent initialBasket={basket} multiBasket={multiBasket} />;
87
14
  }
@@ -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 } from '@akinon/next/data/client/checkout';
14
+ import { useFetchCheckoutQuery, useResetCheckoutStateQuery } 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,6 +25,8 @@ const Checkout = () => {
25
25
  (state: RootState) => state.checkout
26
26
  );
27
27
 
28
+ const { data: indexData, isLoading: isResetStateLoading } = useResetCheckoutStateQuery(null);
29
+
28
30
  const {
29
31
  data: checkoutData,
30
32
  isFetching,
@@ -32,7 +34,8 @@ const Checkout = () => {
32
34
  isSuccess,
33
35
  refetch: refetchCheckout
34
36
  } = useFetchCheckoutQuery(null, {
35
- refetchOnMountOrArgChange: true
37
+ refetchOnMountOrArgChange: true,
38
+ skip: isResetStateLoading || !indexData
36
39
  });
37
40
  const initialStepChanged = useRef<boolean>(false);
38
41
  const router = useRouter();
@@ -94,10 +97,10 @@ const Checkout = () => {
94
97
  );
95
98
  }
96
99
 
97
- if (isFetching || isError) {
100
+ if (isResetStateLoading || isFetching || isError) {
98
101
  return (
99
102
  <div className="flex flex-col items-center justify-center h-80">
100
- {isFetching ? (
103
+ {isResetStateLoading || isFetching ? (
101
104
  <LoaderSpinner />
102
105
  ) : (
103
106
  <>
@@ -4,6 +4,7 @@ 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';
7
8
 
8
9
  type HomeWidgetOrderType = {
9
10
  widget_order: Array<{
@@ -16,6 +17,13 @@ export const dynamic = 'force-static';
16
17
  export const revalidate = 600;
17
18
 
18
19
  async function Page() {
20
+ return (
21
+ <>
22
+ <WidgetPlaceholder slug="home-widget-order" />
23
+ <WidgetPlaceholder slug="home-widget-order-2" />
24
+ </>
25
+ );
26
+
19
27
  const data = await getWidgetData<HomeWidgetOrderType>({
20
28
  slug: 'home-widget-order'
21
29
  });
@@ -1,46 +1,61 @@
1
1
  'use client';
2
2
 
3
+ import { Link } from '@theme/components';
3
4
  import { ButtonProps } from '@theme/components/types';
4
5
  import clsx from 'clsx';
5
6
  import { twMerge } from 'tailwind-merge';
6
7
 
7
8
  export const Button = (props: ButtonProps) => {
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
- )}
9
+ const {
10
+ appearance = 'filled',
11
+ size = 'md',
12
+ href,
13
+ target,
14
+ children,
15
+ className,
16
+ ...rest
17
+ } = props;
18
+
19
+ const variants = {
20
+ filled:
21
+ 'bg-primary text-primary-foreground border border-primary hover:bg-white hover:border-primary hover:text-primary',
22
+ outlined:
23
+ 'bg-transparent text-primary hover:bg-primary hover:text-primary-foreground',
24
+ ghost:
25
+ 'bg-transparent border-transparent text-primary hover:bg-primary hover:text-primary-foreground',
26
+ link: 'px-0 h-auto underline underline-offset-2'
27
+ };
28
+
29
+ const sizes = {
30
+ sm: 'h-8',
31
+ md: 'h-10',
32
+ lg: 'h-12',
33
+ xl: 'h-14'
34
+ };
35
+
36
+ const buttonClasses = twMerge(
37
+ clsx(
38
+ 'px-4 text-xs transition-all duration-200',
39
+ 'inline-flex gap-2 justify-center items-center',
40
+ variants[appearance],
41
+ sizes[size],
42
+ className
43
+ ),
44
+ className
45
+ );
46
+
47
+ return props.href ? (
48
+ <Link
49
+ prefetch={false}
50
+ target={target}
51
+ href={href}
52
+ className={buttonClasses}
42
53
  >
43
- {props.children}
54
+ {children}
55
+ </Link>
56
+ ) : (
57
+ <button {...rest} className={buttonClasses}>
58
+ {children}
44
59
  </button>
45
60
  );
46
61
  };
@@ -1,8 +1,50 @@
1
+ import { useState } from 'react';
1
2
  import { forwardRef } from 'react';
2
3
  import { FileInputProps } from '@theme/components/types';
4
+ import clsx from 'clsx';
5
+ import { useLocalization } from '@akinon/next/hooks';
3
6
 
4
7
  export const FileInput = forwardRef<HTMLInputElement, FileInputProps>(
5
- function fileInput(props, ref) {
6
- return <input type="file" {...props} ref={ref} />;
8
+ function FileInput({ className, onChange, ...props }, ref) {
9
+ const { t } = useLocalization();
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
+ );
7
49
  }
8
50
  );
@@ -4,7 +4,10 @@ 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';
7
+ appearance?: 'filled' | 'outlined' | 'ghost' | 'link' | string;
8
+ size?: 'sm' | 'md' | 'lg' | 'xl';
9
+ href?: string;
10
+ target?: '_blank' | '_self' | '_parent' | '_top';
8
11
  }
9
12
 
10
13
  export interface PaginationProps {
@@ -0,0 +1,12 @@
1
+ import 'server-only';
2
+ import { DynamicWidgetContainer } from '../dynamic-widget-renderer';
3
+
4
+ export default async function WidgetPlaceholder({ slug }: { slug: string }) {
5
+ return (
6
+ <>
7
+ <DynamicWidgetContainer />
8
+ </>
9
+ );
10
+
11
+ return <div>WidgetPlaceholder</div>;
12
+ }
@@ -9,6 +9,7 @@ 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)?)/',
12
13
  matcher: [
13
14
  '/((?!api|_next|[\\w-\\/*]+\\.\\w+).*)',
14
15
  '/(.*sitemap\\.xml)',
@@ -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} */
@@ -14,6 +18,7 @@ module.exports = {
14
18
  { translationKey: 'size', key: 'size' }
15
19
  ],
16
20
  localization: {
21
+ // If there is one locale in the locales array, the default locale will be hidden in the URL.
17
22
  locales: [
18
23
  {
19
24
  label: 'EN',
@@ -41,7 +46,7 @@ module.exports = {
41
46
  }
42
47
  ],
43
48
  defaultLocaleValue: 'en',
44
- localeUrlStrategy: LocaleUrlStrategy.HideDefaultLocale,
49
+ 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.
45
50
  redirectToDefaultLocale: true,
46
51
  defaultCurrencyCode: 'usd'
47
52
  },
@@ -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(/\9/g, '#')}
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(/\9/g, '#')}
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(/\9/g, '#')}
195
+ format={user_phone_format.replace(/9/g, '#')}
196
196
  mask="_"
197
197
  allowEmptyFormatting={true}
198
198
  control={control}
@@ -255,13 +255,8 @@ 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
- name="file"
260
- title="file"
261
- className="w-full mb-5"
262
- {...register('file')}
263
- />
264
- <Button type="submit" className="w-full font-medium">
258
+ <FileInput name="file" title="file" {...register('file')} />
259
+ <Button type="submit" className="w-full font-medium mt-4">
265
260
  {t('account.contact.form.submit_button')}
266
261
  </Button>
267
262
  </form>