@akinon/next 1.91.0-rc.2 → 1.91.0-rc.4
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 +67 -0
- package/api/auth.ts +55 -5
- package/api/client.ts +18 -1
- package/components/accordion.tsx +20 -5
- package/components/file-input.tsx +65 -3
- package/components/modal.tsx +32 -16
- package/components/plugin-module.tsx +8 -3
- package/middlewares/default.ts +25 -2
- package/middlewares/locale.ts +5 -2
- package/package.json +6 -2
- package/plugins.js +2 -1
- package/tailwind/content.js +16 -0
- package/types/index.ts +28 -1
- package/utils/get-root-hostname.ts +28 -0
- package/with-pz-config.js +1 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,72 @@
|
|
|
1
1
|
# @akinon/next
|
|
2
2
|
|
|
3
|
+
## 1.91.0-rc.4
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 7eb51ca: ZERO-3424 :Update package versions
|
|
8
|
+
- c39c700: ZERO-3420: Refactor Modal component
|
|
9
|
+
- 0de5573: ZERO-3418: Update remotePatterns hostname to allow all subdomains
|
|
10
|
+
- 9dc7298: ZERO-3416: Refactor Accordion component to enhance props and improve styling flexibility
|
|
11
|
+
- 2d3f178: ZERO-3417: Enhance FileInput component with additional props for customization
|
|
12
|
+
|
|
13
|
+
## 1.91.0-rc.3
|
|
14
|
+
|
|
15
|
+
### Minor Changes
|
|
16
|
+
|
|
17
|
+
- 5dfeea04: ZERO-2801: Revert ZERO-2801
|
|
18
|
+
- 823d82f9: ZERO-3393: Enhance error handling in checkout middleware to ensure errors are checked for existence before processing
|
|
19
|
+
- 63774a6a: ZERO-3351: Add commerce redirection ignore list functionality and related utility
|
|
20
|
+
- 2d9b2b2c: ZERO-2816: Add segment to headers
|
|
21
|
+
- 5e1feca6: Revert "ZERO-3286: Add notFound handling for chunk URLs starting with \_next"
|
|
22
|
+
- d8fad39f: ZERO-3370: include plugins test to build stage
|
|
23
|
+
- 40a46853: ZERO-3182: Optimize basket update mutation with optimistic update
|
|
24
|
+
- 68bbcb27: ZERO-3393: Fix error handling in checkout middleware to check for errors array length
|
|
25
|
+
- 25524867: ZERO-3391: Add subdomain support to setLocale function
|
|
26
|
+
- f49bb74f: ZERO-3097: Add setCookie to logging in payment redirection middlewares
|
|
27
|
+
- e9541a13: ZERO-2816: Add headers to url
|
|
28
|
+
- 9b7d0de6: ZERO-3393: Improve error handling in checkout middleware to support both object and array error formats
|
|
29
|
+
- 72fd4d67: ZERO-3084: Fix URL search parameters encoding in default middleware
|
|
30
|
+
- c53ef7b9: ZERO-2668: The Link component has been updated to improve the logic for handling href values. Previously, if the href was not a string or started with 'http', it would return the href as is. Now, if the href is not provided, it will default to '#' to prevent any potential errors. Additionally, if the href is a string and does not start with 'http', it will be formatted with the locale and pathname, based on the localeUrlStrategy and defaultLocaleValue. This ensures that the correct href is generated based on the localization settings.
|
|
31
|
+
- f8e4cac6: ZERO-3343: restrict root hostname to only locale subdomains
|
|
32
|
+
- 64699d3f: ZERO-2761: Fix invalid import for plugin module
|
|
33
|
+
- 832bee36: ZERO-3343: add domain to cookie for subdomain locale strategy
|
|
34
|
+
- e974d8e8: ZERO-3406: Fix rc build
|
|
35
|
+
- 28a59d49: ZERO-3400: refactor cookie domain logic using fallback host
|
|
36
|
+
- 8feabe9a: ZERO-3343: add custom NextAuth options support
|
|
37
|
+
- bf354de4: ZERO-3321: add babel compiler for akinon/next test
|
|
38
|
+
- 7727ae55: ZERO-3073: Refactor basket page to use server-side data fetching and simplify component structure
|
|
39
|
+
- d552629f: ZERO-3182: Refactor basketApi to use invalidatesTags and comment out onQueryStarted logic
|
|
40
|
+
- 448adefb: ZERO-3321: move csp test to akinon-next
|
|
41
|
+
- 17f87524: ZERO-2816: Make the incoming currency lowercase
|
|
42
|
+
- 65d3b862: ZERO-3054: Update headers in appFetch
|
|
43
|
+
- bbe18b9f: ZERO-2575: Fix build error
|
|
44
|
+
- 17bfadc4: ZERO-3275: Disable OpenTelemetry monitoring in production environment
|
|
45
|
+
- 4920742c: Disable getCachedTranslations
|
|
46
|
+
- b6e5b624: ZERO-3257: Enhance locale middleware to redirect using existing or default locale and support 303 status for POST requests
|
|
47
|
+
- 6c3629c2: ZERO-3321: fix jest tests in akinon-next for standalone projects
|
|
48
|
+
- 6bc260be: ZERO-3295: update default tailwind content list
|
|
49
|
+
- 7e56d6b6: ZERO-2841: Update api tagTypes
|
|
50
|
+
- dfaceffd: ZERO-3356: Add useLoyaltyAvailability hook and update checkout state management
|
|
51
|
+
- 33377cfd: ZERO-3267: Refactor import statement for ROUTES in error-page component
|
|
52
|
+
- 43c182ee: ZERO-3054: Update Redis variable checks to conditionally include CACHE_SECRET
|
|
53
|
+
- 943a239e: ZERO-3370: add allowJs in akinon-next test tsconfig
|
|
54
|
+
- 068dc394: ZERO-3343: update get-root-hostname logic
|
|
55
|
+
- 942490f2: ZERO-3295: move third party tailwind content list to akinon-next
|
|
56
|
+
- eeb20bea: Revert "ZERO-3054: Refactor cache handler to use custom Redis handler and implement key hashing"
|
|
57
|
+
- 3bf63c8a: ZERO-3286: Add notFound handling for chunk URLs starting with \_next
|
|
58
|
+
- 9be2c081: ZERO-3243: Improve basket update query handling with optimistic updates
|
|
59
|
+
- f2c92d5c: ZERO-2816: Update cookie name
|
|
60
|
+
- 7bd3d992: ZERO-2801: Refactor locale middleware to handle single locale configuration
|
|
61
|
+
- b6d5bda2: ZERO-3343: update changeset config
|
|
62
|
+
- fdd255ee: ZERO-3054: Refactor cache handler to use custom Redis handler and implement key hashing
|
|
63
|
+
- acf03209: ZERO-3321: remove babel config
|
|
64
|
+
- 387356b6: ZERO-3323: Refactor locale filtering logic in URL matcher regex
|
|
65
|
+
- 49eeebfa: ZERO-2909: Add deleteCollectionItem query to wishlistApi
|
|
66
|
+
- 3f9b8d7e: ZERO-2761: Update plugins.js for akinon-next
|
|
67
|
+
- b2ee69b9: ZERO-3321: delete unnecessary files
|
|
68
|
+
- 0cabbda3: ZERO-3370: replace inline monorepo check with reusable utility
|
|
69
|
+
|
|
3
70
|
## 1.91.0-rc.2
|
|
4
71
|
|
|
5
72
|
### Minor Changes
|
package/api/auth.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { NextApiRequest, NextApiResponse } from 'next';
|
|
2
|
-
import NextAuth, { Session } from 'next-auth';
|
|
2
|
+
import NextAuth, { Session, NextAuthOptions } from 'next-auth';
|
|
3
3
|
import CredentialProvider from 'next-auth/providers/credentials';
|
|
4
4
|
import { ROUTES } from 'routes';
|
|
5
5
|
import { URLS, user } from '../data/urls';
|
|
@@ -7,6 +7,8 @@ import Settings from 'settings';
|
|
|
7
7
|
import { urlLocaleMatcherRegex } from '../utils';
|
|
8
8
|
import logger from '@akinon/next/utils/log';
|
|
9
9
|
import { AuthError } from '../types';
|
|
10
|
+
import getRootHostname from '../utils/get-root-hostname';
|
|
11
|
+
import { LocaleUrlStrategy } from '../localization';
|
|
10
12
|
|
|
11
13
|
async function getCurrentUser(sessionId: string, currency = '') {
|
|
12
14
|
const headers = {
|
|
@@ -40,7 +42,15 @@ async function getCurrentUser(sessionId: string, currency = '') {
|
|
|
40
42
|
};
|
|
41
43
|
}
|
|
42
44
|
|
|
43
|
-
|
|
45
|
+
type CustomNextAuthOptions = (
|
|
46
|
+
req: NextApiRequest,
|
|
47
|
+
res: NextApiResponse
|
|
48
|
+
) => Partial<NextAuthOptions>;
|
|
49
|
+
|
|
50
|
+
const defaultNextAuthOptions = (
|
|
51
|
+
req: NextApiRequest,
|
|
52
|
+
res: NextApiResponse
|
|
53
|
+
): NextAuthOptions => {
|
|
44
54
|
return {
|
|
45
55
|
providers: [
|
|
46
56
|
CredentialProvider({
|
|
@@ -150,7 +160,19 @@ const nextAuthOptions = (req: NextApiRequest, res: NextApiResponse) => {
|
|
|
150
160
|
|
|
151
161
|
if (sessionId) {
|
|
152
162
|
const maxAge = 30 * 24 * 60 * 60; // 30 days in seconds
|
|
153
|
-
const
|
|
163
|
+
const { localeUrlStrategy } = Settings.localization;
|
|
164
|
+
|
|
165
|
+
const fallbackHost =
|
|
166
|
+
req.headers['x-forwarded-host']?.toString() ||
|
|
167
|
+
req.headers.host?.toString();
|
|
168
|
+
const hostname =
|
|
169
|
+
process.env.NEXT_PUBLIC_URL || `https://${fallbackHost}`;
|
|
170
|
+
const rootHostname =
|
|
171
|
+
localeUrlStrategy === LocaleUrlStrategy.Subdomain
|
|
172
|
+
? getRootHostname(hostname)
|
|
173
|
+
: null;
|
|
174
|
+
const domainOption = rootHostname ? `Domain=${rootHostname};` : '';
|
|
175
|
+
const cookieOptions = `Path=/; HttpOnly; Secure; Max-Age=${maxAge}; ${domainOption}`;
|
|
154
176
|
|
|
155
177
|
res.setHeader('Set-Cookie', [
|
|
156
178
|
`osessionid=${sessionId}; ${cookieOptions}`,
|
|
@@ -253,8 +275,36 @@ const nextAuthOptions = (req: NextApiRequest, res: NextApiResponse) => {
|
|
|
253
275
|
};
|
|
254
276
|
};
|
|
255
277
|
|
|
256
|
-
const Auth = (
|
|
257
|
-
|
|
278
|
+
const Auth = (
|
|
279
|
+
req: NextApiRequest,
|
|
280
|
+
res: NextApiResponse,
|
|
281
|
+
customOptions?: CustomNextAuthOptions
|
|
282
|
+
) => {
|
|
283
|
+
const baseOptions = defaultNextAuthOptions(req, res);
|
|
284
|
+
const customOptionsResult = customOptions ? customOptions(req, res) : {};
|
|
285
|
+
|
|
286
|
+
const mergedOptions = {
|
|
287
|
+
...baseOptions,
|
|
288
|
+
...customOptionsResult,
|
|
289
|
+
providers: [
|
|
290
|
+
...baseOptions.providers,
|
|
291
|
+
...(customOptionsResult.providers || [])
|
|
292
|
+
],
|
|
293
|
+
callbacks: {
|
|
294
|
+
...baseOptions.callbacks,
|
|
295
|
+
...customOptionsResult.callbacks
|
|
296
|
+
},
|
|
297
|
+
events: {
|
|
298
|
+
...baseOptions.events,
|
|
299
|
+
...customOptionsResult.events
|
|
300
|
+
},
|
|
301
|
+
pages: {
|
|
302
|
+
...baseOptions.pages,
|
|
303
|
+
...customOptionsResult.pages
|
|
304
|
+
}
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
return NextAuth(req, res, mergedOptions);
|
|
258
308
|
};
|
|
259
309
|
|
|
260
310
|
export default Auth;
|
package/api/client.ts
CHANGED
|
@@ -5,6 +5,8 @@ import logger from '../utils/log';
|
|
|
5
5
|
import formatCookieString from '../utils/format-cookie-string';
|
|
6
6
|
import cookieParser from 'set-cookie-parser';
|
|
7
7
|
import { cookies } from 'next/headers';
|
|
8
|
+
import getRootHostname from '../utils/get-root-hostname';
|
|
9
|
+
import { LocaleUrlStrategy } from '../localization';
|
|
8
10
|
|
|
9
11
|
interface RouteParams {
|
|
10
12
|
params: {
|
|
@@ -190,8 +192,23 @@ async function proxyRequest(...args) {
|
|
|
190
192
|
const responseHeaders: any = {};
|
|
191
193
|
|
|
192
194
|
if (filteredCookies.length > 0) {
|
|
195
|
+
const { localeUrlStrategy } = settings.localization;
|
|
196
|
+
|
|
197
|
+
const fallbackHost =
|
|
198
|
+
req.headers.get('x-forwarded-host') || req.headers.get('host');
|
|
199
|
+
const hostname = process.env.NEXT_PUBLIC_URL || `https://${fallbackHost}`;
|
|
200
|
+
const rootHostname =
|
|
201
|
+
localeUrlStrategy === LocaleUrlStrategy.Subdomain
|
|
202
|
+
? getRootHostname(hostname)
|
|
203
|
+
: null;
|
|
204
|
+
|
|
193
205
|
responseHeaders['set-cookie'] = filteredCookies
|
|
194
|
-
.map(
|
|
206
|
+
.map((cookie) => {
|
|
207
|
+
if (!cookie.domain && rootHostname) {
|
|
208
|
+
cookie.domain = rootHostname;
|
|
209
|
+
}
|
|
210
|
+
return formatCookieString(cookie);
|
|
211
|
+
})
|
|
195
212
|
.join(', ');
|
|
196
213
|
}
|
|
197
214
|
|
package/components/accordion.tsx
CHANGED
|
@@ -7,15 +7,19 @@ import { AccordionProps } from '../types';
|
|
|
7
7
|
|
|
8
8
|
export const Accordion = ({
|
|
9
9
|
isCollapse = false,
|
|
10
|
+
collapseClassName,
|
|
10
11
|
title,
|
|
11
12
|
subTitle,
|
|
12
13
|
icons = ['chevron-up', 'chevron-down'],
|
|
13
14
|
iconSize = 16,
|
|
14
15
|
iconColor = 'fill-[#000000]',
|
|
15
16
|
children,
|
|
17
|
+
headerClassName,
|
|
16
18
|
className,
|
|
17
19
|
titleClassName,
|
|
18
|
-
|
|
20
|
+
subTitleClassName,
|
|
21
|
+
dataTestId,
|
|
22
|
+
contentClassName
|
|
19
23
|
}: AccordionProps) => {
|
|
20
24
|
const [collapse, setCollapse] = useState(isCollapse);
|
|
21
25
|
|
|
@@ -27,15 +31,22 @@ export const Accordion = ({
|
|
|
27
31
|
)}
|
|
28
32
|
>
|
|
29
33
|
<div
|
|
30
|
-
className=
|
|
34
|
+
className={twMerge(
|
|
35
|
+
'flex items-center justify-between cursor-pointer',
|
|
36
|
+
headerClassName
|
|
37
|
+
)}
|
|
31
38
|
onClick={() => setCollapse(!collapse)}
|
|
32
39
|
data-testid={dataTestId}
|
|
33
40
|
>
|
|
34
|
-
<div className=
|
|
41
|
+
<div className={twMerge('flex flex-col', contentClassName)}>
|
|
35
42
|
{title && (
|
|
36
43
|
<h3 className={twMerge('text-sm', titleClassName)}>{title}</h3>
|
|
37
44
|
)}
|
|
38
|
-
{subTitle &&
|
|
45
|
+
{subTitle && (
|
|
46
|
+
<h4 className={twMerge('text-xs text-gray-700', subTitleClassName)}>
|
|
47
|
+
{subTitle}
|
|
48
|
+
</h4>
|
|
49
|
+
)}
|
|
39
50
|
</div>
|
|
40
51
|
|
|
41
52
|
{icons && (
|
|
@@ -46,7 +57,11 @@ export const Accordion = ({
|
|
|
46
57
|
/>
|
|
47
58
|
)}
|
|
48
59
|
</div>
|
|
49
|
-
{collapse &&
|
|
60
|
+
{collapse && (
|
|
61
|
+
<div className={twMerge('mt-3 text-sm', collapseClassName)}>
|
|
62
|
+
{children}
|
|
63
|
+
</div>
|
|
64
|
+
)}
|
|
50
65
|
</div>
|
|
51
66
|
);
|
|
52
67
|
};
|
|
@@ -1,8 +1,70 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
1
2
|
import { forwardRef } from 'react';
|
|
2
|
-
import {
|
|
3
|
+
import { useLocalization } from '@akinon/next/hooks';
|
|
4
|
+
import { twMerge } from 'tailwind-merge';
|
|
5
|
+
import { FileInputProps } from '../types';
|
|
3
6
|
|
|
4
7
|
export const FileInput = forwardRef<HTMLInputElement, FileInputProps>(
|
|
5
|
-
function
|
|
6
|
-
|
|
8
|
+
function FileInput(
|
|
9
|
+
{
|
|
10
|
+
buttonClassName,
|
|
11
|
+
onChange,
|
|
12
|
+
fileClassName,
|
|
13
|
+
fileNameWrapperClassName,
|
|
14
|
+
fileInputClassName,
|
|
15
|
+
...props
|
|
16
|
+
},
|
|
17
|
+
ref
|
|
18
|
+
) {
|
|
19
|
+
const { t } = useLocalization();
|
|
20
|
+
const [fileNames, setFileNames] = useState<string[]>([]);
|
|
21
|
+
|
|
22
|
+
const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
23
|
+
const files = Array.from(event.target.files || []);
|
|
24
|
+
setFileNames(files.map((file) => file.name));
|
|
25
|
+
|
|
26
|
+
if (onChange) {
|
|
27
|
+
onChange(event);
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
return (
|
|
32
|
+
<div className="relative">
|
|
33
|
+
<input
|
|
34
|
+
type="file"
|
|
35
|
+
{...props}
|
|
36
|
+
ref={ref}
|
|
37
|
+
className={twMerge(
|
|
38
|
+
'absolute inset-0 w-full h-full opacity-0 cursor-pointer',
|
|
39
|
+
fileInputClassName
|
|
40
|
+
)}
|
|
41
|
+
onChange={handleFileChange}
|
|
42
|
+
/>
|
|
43
|
+
<button
|
|
44
|
+
type="button"
|
|
45
|
+
className={twMerge(
|
|
46
|
+
'bg-primary text-white py-2 px-4 text-sm',
|
|
47
|
+
buttonClassName
|
|
48
|
+
)}
|
|
49
|
+
>
|
|
50
|
+
{t('common.file_input.select_file')}
|
|
51
|
+
</button>
|
|
52
|
+
<div
|
|
53
|
+
className={twMerge('mt-1 text-gray-500', fileNameWrapperClassName)}
|
|
54
|
+
>
|
|
55
|
+
{fileNames.length > 0 ? (
|
|
56
|
+
<ul className={twMerge('list-disc pl-4 text-xs', fileClassName)}>
|
|
57
|
+
{fileNames.map((name, index) => (
|
|
58
|
+
<li key={index}>{name}</li>
|
|
59
|
+
))}
|
|
60
|
+
</ul>
|
|
61
|
+
) : (
|
|
62
|
+
<span className={twMerge('text-xs', fileClassName)}>
|
|
63
|
+
{t('common.file_input.no_file')}
|
|
64
|
+
</span>
|
|
65
|
+
)}
|
|
66
|
+
</div>
|
|
67
|
+
</div>
|
|
68
|
+
);
|
|
7
69
|
}
|
|
8
70
|
);
|
package/components/modal.tsx
CHANGED
|
@@ -4,16 +4,7 @@ import { ReactPortal } from './react-portal';
|
|
|
4
4
|
import { Icon } from './icon';
|
|
5
5
|
import { twMerge } from 'tailwind-merge';
|
|
6
6
|
import { useEffect } from 'react';
|
|
7
|
-
|
|
8
|
-
export interface ModalProps {
|
|
9
|
-
portalId: string;
|
|
10
|
-
children?: React.ReactNode;
|
|
11
|
-
open?: boolean;
|
|
12
|
-
setOpen?: (open: boolean) => void;
|
|
13
|
-
title?: React.ReactNode;
|
|
14
|
-
showCloseButton?: React.ReactNode;
|
|
15
|
-
className?: string;
|
|
16
|
-
}
|
|
7
|
+
import { ModalProps } from '../types';
|
|
17
8
|
|
|
18
9
|
export const Modal = (props: ModalProps) => {
|
|
19
10
|
const {
|
|
@@ -23,7 +14,14 @@ export const Modal = (props: ModalProps) => {
|
|
|
23
14
|
setOpen,
|
|
24
15
|
title = '',
|
|
25
16
|
showCloseButton = true,
|
|
26
|
-
className
|
|
17
|
+
className,
|
|
18
|
+
overlayClassName,
|
|
19
|
+
headerWrapperClassName,
|
|
20
|
+
titleClassName,
|
|
21
|
+
closeButtonClassName,
|
|
22
|
+
iconName = 'close',
|
|
23
|
+
iconSize = 16,
|
|
24
|
+
iconClassName
|
|
27
25
|
} = props;
|
|
28
26
|
|
|
29
27
|
useEffect(() => {
|
|
@@ -38,7 +36,12 @@ export const Modal = (props: ModalProps) => {
|
|
|
38
36
|
|
|
39
37
|
return (
|
|
40
38
|
<ReactPortal wrapperId={portalId}>
|
|
41
|
-
<div
|
|
39
|
+
<div
|
|
40
|
+
className={twMerge(
|
|
41
|
+
'fixed top-0 left-0 w-screen h-screen bg-primary bg-opacity-60 z-50',
|
|
42
|
+
overlayClassName
|
|
43
|
+
)}
|
|
44
|
+
/>
|
|
42
45
|
<section
|
|
43
46
|
className={twMerge(
|
|
44
47
|
'fixed top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 z-50 bg-white',
|
|
@@ -46,15 +49,28 @@ export const Modal = (props: ModalProps) => {
|
|
|
46
49
|
)}
|
|
47
50
|
>
|
|
48
51
|
{(showCloseButton || title) && (
|
|
49
|
-
<div
|
|
50
|
-
|
|
52
|
+
<div
|
|
53
|
+
className={twMerge(
|
|
54
|
+
'flex px-6 py-4 border-b border-gray-400',
|
|
55
|
+
headerWrapperClassName
|
|
56
|
+
)}
|
|
57
|
+
>
|
|
58
|
+
{title && (
|
|
59
|
+
<h3 className={twMerge('text-lg font-light', titleClassName)}>
|
|
60
|
+
{title}
|
|
61
|
+
</h3>
|
|
62
|
+
)}
|
|
51
63
|
{showCloseButton && (
|
|
52
64
|
<button
|
|
53
65
|
type="button"
|
|
54
66
|
onClick={() => setOpen(false)}
|
|
55
|
-
className=
|
|
67
|
+
className={twMerge('ml-auto', closeButtonClassName)}
|
|
56
68
|
>
|
|
57
|
-
<Icon
|
|
69
|
+
<Icon
|
|
70
|
+
name={iconName}
|
|
71
|
+
size={iconSize}
|
|
72
|
+
className={iconClassName}
|
|
73
|
+
/>
|
|
58
74
|
</button>
|
|
59
75
|
)}
|
|
60
76
|
</div>
|
|
@@ -20,7 +20,8 @@ enum Plugin {
|
|
|
20
20
|
B2B = 'pz-b2b',
|
|
21
21
|
Akifast = 'pz-akifast',
|
|
22
22
|
MultiBasket = 'pz-multi-basket',
|
|
23
|
-
SavedCard = 'pz-saved-card'
|
|
23
|
+
SavedCard = 'pz-saved-card',
|
|
24
|
+
Hepsipay = 'pz-hepsipay'
|
|
24
25
|
}
|
|
25
26
|
|
|
26
27
|
export enum Component {
|
|
@@ -45,7 +46,8 @@ export enum Component {
|
|
|
45
46
|
AkifastQuickLoginButton = 'QuickLoginButton',
|
|
46
47
|
AkifastCheckoutButton = 'CheckoutButton',
|
|
47
48
|
MultiBasket = 'MultiBasket',
|
|
48
|
-
SavedCard = 'SavedCardOption'
|
|
49
|
+
SavedCard = 'SavedCardOption',
|
|
50
|
+
Hepsipay = 'Hepsipay'
|
|
49
51
|
}
|
|
50
52
|
|
|
51
53
|
const PluginComponents = new Map([
|
|
@@ -78,7 +80,8 @@ const PluginComponents = new Map([
|
|
|
78
80
|
[Component.AkifastQuickLoginButton, Component.AkifastCheckoutButton]
|
|
79
81
|
],
|
|
80
82
|
[Plugin.MultiBasket, [Component.MultiBasket]],
|
|
81
|
-
[Plugin.SavedCard, [Component.SavedCard]]
|
|
83
|
+
[Plugin.SavedCard, [Component.SavedCard]],
|
|
84
|
+
[Plugin.Hepsipay, [Component.Hepsipay]]
|
|
82
85
|
]);
|
|
83
86
|
|
|
84
87
|
const getPlugin = (component: Component) => {
|
|
@@ -143,6 +146,8 @@ export default function PluginModule({
|
|
|
143
146
|
promise = import(`${'@akinon/pz-multi-basket'}`);
|
|
144
147
|
} else if (plugin === Plugin.SavedCard) {
|
|
145
148
|
promise = import(`${'@akinon/pz-saved-card'}`);
|
|
149
|
+
} else if (plugin === Plugin.Hepsipay) {
|
|
150
|
+
promise = import(`${'@akinon/pz-hepsipay'}`);
|
|
146
151
|
}
|
|
147
152
|
} catch (error) {
|
|
148
153
|
logger.error(error);
|
package/middlewares/default.ts
CHANGED
|
@@ -19,6 +19,8 @@ import withLocale from './locale';
|
|
|
19
19
|
import logger from '../utils/log';
|
|
20
20
|
import { user } from '../data/urls';
|
|
21
21
|
import { getUrlPathWithLocale } from '../utils/localization';
|
|
22
|
+
import getRootHostname from '../utils/get-root-hostname';
|
|
23
|
+
import { LocaleUrlStrategy } from '../localization';
|
|
22
24
|
|
|
23
25
|
const withPzDefault =
|
|
24
26
|
(middleware: NextMiddleware) =>
|
|
@@ -296,7 +298,7 @@ const withPzDefault =
|
|
|
296
298
|
!req.middlewareParams.found &&
|
|
297
299
|
Settings.customNotFoundEnabled
|
|
298
300
|
) {
|
|
299
|
-
|
|
301
|
+
const pathname = url.pathname
|
|
300
302
|
.replace(/\/+$/, '')
|
|
301
303
|
.split('/');
|
|
302
304
|
url.pathname = url.pathname.replace(
|
|
@@ -341,6 +343,21 @@ const withPzDefault =
|
|
|
341
343
|
middlewareResult = NextResponse.rewrite(url);
|
|
342
344
|
}
|
|
343
345
|
|
|
346
|
+
const { localeUrlStrategy } =
|
|
347
|
+
Settings.localization;
|
|
348
|
+
|
|
349
|
+
const fallbackHost =
|
|
350
|
+
req.headers.get('x-forwarded-host') ||
|
|
351
|
+
req.headers.get('host');
|
|
352
|
+
const hostname =
|
|
353
|
+
process.env.NEXT_PUBLIC_URL ||
|
|
354
|
+
`https://${fallbackHost}`;
|
|
355
|
+
const rootHostname =
|
|
356
|
+
localeUrlStrategy ===
|
|
357
|
+
LocaleUrlStrategy.Subdomain
|
|
358
|
+
? getRootHostname(hostname)
|
|
359
|
+
: null;
|
|
360
|
+
|
|
344
361
|
if (
|
|
345
362
|
!url.pathname.startsWith(`/${currency}/orders`)
|
|
346
363
|
) {
|
|
@@ -350,6 +367,7 @@ const withPzDefault =
|
|
|
350
367
|
? locale
|
|
351
368
|
: defaultLocaleValue,
|
|
352
369
|
{
|
|
370
|
+
domain: rootHostname,
|
|
353
371
|
sameSite: 'none',
|
|
354
372
|
secure: true,
|
|
355
373
|
expires: new Date(
|
|
@@ -358,10 +376,12 @@ const withPzDefault =
|
|
|
358
376
|
}
|
|
359
377
|
);
|
|
360
378
|
}
|
|
379
|
+
|
|
361
380
|
middlewareResult.cookies.set(
|
|
362
381
|
'pz-currency',
|
|
363
382
|
currency,
|
|
364
383
|
{
|
|
384
|
+
domain: rootHostname,
|
|
365
385
|
sameSite: 'none',
|
|
366
386
|
secure: true,
|
|
367
387
|
expires: new Date(
|
|
@@ -410,7 +430,10 @@ const withPzDefault =
|
|
|
410
430
|
).json();
|
|
411
431
|
middlewareResult.cookies.set(
|
|
412
432
|
'csrftoken',
|
|
413
|
-
csrf_token
|
|
433
|
+
csrf_token,
|
|
434
|
+
{
|
|
435
|
+
domain: rootHostname
|
|
436
|
+
}
|
|
414
437
|
);
|
|
415
438
|
}
|
|
416
439
|
} catch (error) {
|
package/middlewares/locale.ts
CHANGED
|
@@ -13,7 +13,8 @@ const getMatchedLocale = (pathname: string, req: PzNextRequest) => {
|
|
|
13
13
|
const { localeUrlStrategy, defaultLocaleValue } = settings.localization;
|
|
14
14
|
|
|
15
15
|
if (localeUrlStrategy === LocaleUrlStrategy.Subdomain) {
|
|
16
|
-
const host =
|
|
16
|
+
const host =
|
|
17
|
+
req.headers.get('x-forwarded-host') || req.headers.get('host') || '';
|
|
17
18
|
|
|
18
19
|
if (host) {
|
|
19
20
|
const subDomain = host.split('.')[0] || '';
|
|
@@ -44,7 +45,9 @@ const withLocale =
|
|
|
44
45
|
try {
|
|
45
46
|
const url = req.nextUrl.clone();
|
|
46
47
|
const matchedLocale = getMatchedLocale(url.pathname, req);
|
|
47
|
-
let { localeUrlStrategy
|
|
48
|
+
let { localeUrlStrategy } = settings.localization;
|
|
49
|
+
|
|
50
|
+
const { defaultLocaleValue, redirectToDefaultLocale } =
|
|
48
51
|
settings.localization;
|
|
49
52
|
|
|
50
53
|
localeUrlStrategy =
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@akinon/next",
|
|
3
3
|
"description": "Core package for Project Zero Next",
|
|
4
|
-
"version": "1.91.0-rc.
|
|
4
|
+
"version": "1.91.0-rc.4",
|
|
5
5
|
"private": false,
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"bin": {
|
|
@@ -34,7 +34,11 @@
|
|
|
34
34
|
"set-cookie-parser": "2.6.0"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
|
-
"@akinon/eslint-plugin-projectzero": "1.91.0-rc.
|
|
37
|
+
"@akinon/eslint-plugin-projectzero": "1.91.0-rc.4",
|
|
38
|
+
"@babel/core": "7.26.10",
|
|
39
|
+
"@babel/preset-env": "7.26.9",
|
|
40
|
+
"@babel/preset-typescript": "7.27.0",
|
|
41
|
+
"@types/jest": "29.5.14",
|
|
38
42
|
"@types/react-redux": "7.1.30",
|
|
39
43
|
"@types/set-cookie-parser": "2.4.7",
|
|
40
44
|
"@typescript-eslint/eslint-plugin": "6.7.4",
|
package/plugins.js
CHANGED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
const defaultPlugins = require('../plugins');
|
|
2
|
+
|
|
3
|
+
const getAkinonNextContent = (plugins = defaultPlugins) => {
|
|
4
|
+
return [
|
|
5
|
+
`./node_modules/@akinon/next/components/**/*.{js,ts,jsx,tsx}`,
|
|
6
|
+
`../../node_modules/@akinon/next/components/**/*.{js,ts,jsx,tsx}`,
|
|
7
|
+
...plugins
|
|
8
|
+
.map((plugin) => [
|
|
9
|
+
`./node_modules/@akinon/${plugin}/**/*.{js,ts,jsx,tsx}`,
|
|
10
|
+
`../../node_modules/@akinon/${plugin}/**/*.{js,ts,jsx,tsx}`
|
|
11
|
+
])
|
|
12
|
+
.flat()
|
|
13
|
+
];
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
module.exports = getAkinonNextContent;
|
package/types/index.ts
CHANGED
|
@@ -283,7 +283,13 @@ export interface ButtonProps
|
|
|
283
283
|
target?: '_blank' | '_self' | '_parent' | '_top';
|
|
284
284
|
}
|
|
285
285
|
|
|
286
|
-
export
|
|
286
|
+
export interface FileInputProps extends React.HTMLProps<HTMLInputElement> {
|
|
287
|
+
fileClassName?: string;
|
|
288
|
+
fileNameWrapperClassName?: string;
|
|
289
|
+
fileInputClassName?: string;
|
|
290
|
+
onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void;
|
|
291
|
+
buttonClassName?: string;
|
|
292
|
+
}
|
|
287
293
|
|
|
288
294
|
export interface PriceProps {
|
|
289
295
|
currencyCode?: string;
|
|
@@ -304,15 +310,19 @@ export interface InputProps extends React.HTMLProps<HTMLInputElement> {
|
|
|
304
310
|
|
|
305
311
|
export interface AccordionProps {
|
|
306
312
|
isCollapse?: boolean;
|
|
313
|
+
collapseClassName?: string;
|
|
307
314
|
title?: string;
|
|
308
315
|
subTitle?: string;
|
|
309
316
|
icons?: string[];
|
|
310
317
|
iconSize?: number;
|
|
311
318
|
iconColor?: string;
|
|
312
319
|
children?: ReactNode;
|
|
320
|
+
headerClassName?: string;
|
|
313
321
|
className?: string;
|
|
314
322
|
titleClassName?: string;
|
|
323
|
+
subTitleClassName?: string;
|
|
315
324
|
dataTestId?: string;
|
|
325
|
+
contentClassName?: string;
|
|
316
326
|
}
|
|
317
327
|
|
|
318
328
|
export interface PluginModuleComponentProps {
|
|
@@ -337,3 +347,20 @@ export interface PaginationProps {
|
|
|
337
347
|
direction?: 'next' | 'prev';
|
|
338
348
|
isLoading?: boolean;
|
|
339
349
|
}
|
|
350
|
+
|
|
351
|
+
export interface ModalProps {
|
|
352
|
+
portalId: string;
|
|
353
|
+
children?: React.ReactNode;
|
|
354
|
+
open?: boolean;
|
|
355
|
+
setOpen?: (open: boolean) => void;
|
|
356
|
+
title?: React.ReactNode;
|
|
357
|
+
showCloseButton?: React.ReactNode;
|
|
358
|
+
className?: string;
|
|
359
|
+
overlayClassName?: string;
|
|
360
|
+
headerWrapperClassName?: string;
|
|
361
|
+
titleClassName?: string;
|
|
362
|
+
closeButtonClassName?: string;
|
|
363
|
+
iconName?: string;
|
|
364
|
+
iconSize?: number;
|
|
365
|
+
iconClassName?: string;
|
|
366
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import Settings from 'settings';
|
|
2
|
+
|
|
3
|
+
export default function getRootHostname(
|
|
4
|
+
url: string | undefined
|
|
5
|
+
): string | null {
|
|
6
|
+
if (!url) return null;
|
|
7
|
+
|
|
8
|
+
try {
|
|
9
|
+
const urlObj = new URL(url);
|
|
10
|
+
const hostname = urlObj.hostname;
|
|
11
|
+
const parts = hostname.split('.');
|
|
12
|
+
|
|
13
|
+
const firstPart = parts[0];
|
|
14
|
+
|
|
15
|
+
const isLocale = Settings.localization.locales.some(
|
|
16
|
+
(locale) => locale.value === firstPart
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
if (isLocale) {
|
|
20
|
+
const hostnameAfterLocale = parts.slice(1).join('.');
|
|
21
|
+
return `.${hostnameAfterLocale}`;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return `.${hostname}`;
|
|
25
|
+
} catch {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
}
|
package/with-pz-config.js
CHANGED