@akinon/pz-similar-products 1.108.0-rc.87 → 1.109.0-rc.11
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 +5 -383
- package/README.md +0 -218
- package/package.json +1 -1
- package/src/data/endpoints.ts +9 -37
- package/src/hooks/index.ts +0 -1
- package/src/hooks/use-similar-products.ts +27 -166
- package/src/types/index.ts +0 -51
- package/src/utils/index.ts +3 -38
- package/src/views/filters.tsx +3 -27
- package/src/views/header-image-search-feature.tsx +1 -13
- package/src/views/image-search-button.tsx +18 -20
- package/src/views/main.tsx +5 -82
- package/src/views/product-image-search-feature.tsx +3 -17
- package/src/views/search-button.tsx +2 -12
- package/src/views/search-modal.tsx +10 -169
- package/src/hooks/use-text-search-feature.ts +0 -41
package/src/views/filters.tsx
CHANGED
|
@@ -5,8 +5,7 @@ import {
|
|
|
5
5
|
Button,
|
|
6
6
|
Icon,
|
|
7
7
|
Accordion,
|
|
8
|
-
LoaderSpinner
|
|
9
|
-
Input
|
|
8
|
+
LoaderSpinner
|
|
10
9
|
} from '@akinon/next/components';
|
|
11
10
|
import { useLocalization } from '@akinon/next/hooks';
|
|
12
11
|
import { FilterSidebarProps } from '../types';
|
|
@@ -56,8 +55,6 @@ export function SimilarProductsFilterSidebar({
|
|
|
56
55
|
isLoading,
|
|
57
56
|
handleFacetChange,
|
|
58
57
|
removeFacetFilter,
|
|
59
|
-
searchText,
|
|
60
|
-
setSearchText,
|
|
61
58
|
currentImageUrl,
|
|
62
59
|
isCropping,
|
|
63
60
|
imageRef,
|
|
@@ -169,19 +166,6 @@ export function SimilarProductsFilterSidebar({
|
|
|
169
166
|
className
|
|
170
167
|
);
|
|
171
168
|
|
|
172
|
-
const modalCloseIconClassName = twMerge(
|
|
173
|
-
'',
|
|
174
|
-
settings?.customStyles?.modalCloseIcon
|
|
175
|
-
);
|
|
176
|
-
|
|
177
|
-
const filterRemoveIconClassName = twMerge(
|
|
178
|
-
'',
|
|
179
|
-
settings?.customStyles?.filterRemoveIcon
|
|
180
|
-
);
|
|
181
|
-
|
|
182
|
-
const modalCloseIconName = settings?.iconNames?.modalClose || 'close';
|
|
183
|
-
const filterRemoveIconName = settings?.iconNames?.filterRemove || 'close';
|
|
184
|
-
|
|
185
169
|
return (
|
|
186
170
|
<>
|
|
187
171
|
{isFilterMenuOpen && (
|
|
@@ -238,11 +222,7 @@ export function SimilarProductsFilterSidebar({
|
|
|
238
222
|
settings?.customStyles?.filterSidebarMobileCloseButton
|
|
239
223
|
)}
|
|
240
224
|
>
|
|
241
|
-
<Icon
|
|
242
|
-
name={modalCloseIconName}
|
|
243
|
-
size={16}
|
|
244
|
-
className={modalCloseIconClassName}
|
|
245
|
-
/>
|
|
225
|
+
<Icon name="close" size={16} />
|
|
246
226
|
</Button>
|
|
247
227
|
</div>
|
|
248
228
|
<div
|
|
@@ -948,11 +928,7 @@ export function SimilarProductsFilterSidebar({
|
|
|
948
928
|
disabled={isLoading}
|
|
949
929
|
className="hover:bg-gray-200 rounded-full p-0 w-5 h-5 disabled:opacity-50 disabled:cursor-not-allowed ml-1"
|
|
950
930
|
>
|
|
951
|
-
<Icon
|
|
952
|
-
name={filterRemoveIconName}
|
|
953
|
-
size={10}
|
|
954
|
-
className={filterRemoveIconClassName}
|
|
955
|
-
/>
|
|
931
|
+
<Icon name="close" size={10} />
|
|
956
932
|
</Button>
|
|
957
933
|
</div>
|
|
958
934
|
))}
|
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
import React, { useState, useRef } from 'react';
|
|
4
4
|
import { Product } from '@akinon/next/types';
|
|
5
5
|
import { useImageSearchFeature } from '../hooks/use-image-search-feature';
|
|
6
|
-
import { useTextSearchFeature } from '../hooks/use-text-search-feature';
|
|
7
6
|
import { ImageSearchButton } from './image-search-button';
|
|
8
7
|
import { SimilarProductsPlugin } from './main';
|
|
9
8
|
|
|
@@ -11,19 +10,14 @@ interface HeaderImageSearchFeatureProps {
|
|
|
11
10
|
className?: string;
|
|
12
11
|
isEnabled?: boolean;
|
|
13
12
|
settings?: any;
|
|
14
|
-
enableTextSearch?: boolean;
|
|
15
13
|
}
|
|
16
14
|
|
|
17
15
|
export function HeaderImageSearchFeature({
|
|
18
16
|
className,
|
|
19
17
|
isEnabled: isEnabledProp,
|
|
20
|
-
settings
|
|
21
|
-
enableTextSearch = false
|
|
18
|
+
settings
|
|
22
19
|
}: HeaderImageSearchFeatureProps) {
|
|
23
20
|
const { isEnabled: hookIsEnabled, isLoading } = useImageSearchFeature();
|
|
24
|
-
const { isEnabled: textSearchEnabled } = useTextSearchFeature({
|
|
25
|
-
enableTextSearch
|
|
26
|
-
});
|
|
27
21
|
|
|
28
22
|
const envEnabled = process.env.NEXT_PUBLIC_ENABLE_IMAGE_SEARCH === 'true';
|
|
29
23
|
const finalIsEnabled = envEnabled
|
|
@@ -36,11 +30,6 @@ export function HeaderImageSearchFeature({
|
|
|
36
30
|
const [isResultsModalOpen, setIsResultsModalOpen] = useState(false);
|
|
37
31
|
const [uploadedImageFile, setUploadedImageFile] = useState<File | null>(null);
|
|
38
32
|
|
|
39
|
-
const settings = {
|
|
40
|
-
...userSettings,
|
|
41
|
-
enableTextSearch: textSearchEnabled
|
|
42
|
-
};
|
|
43
|
-
|
|
44
33
|
if (isLoading || !finalIsEnabled) {
|
|
45
34
|
return null;
|
|
46
35
|
}
|
|
@@ -71,7 +60,6 @@ export function HeaderImageSearchFeature({
|
|
|
71
60
|
<ImageSearchButton
|
|
72
61
|
onClick={handleOpenImageSearch}
|
|
73
62
|
className={className}
|
|
74
|
-
settings={settings}
|
|
75
63
|
/>
|
|
76
64
|
<SimilarProductsPlugin
|
|
77
65
|
product={{} as Product}
|
|
@@ -1,32 +1,22 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import React from 'react';
|
|
4
|
-
import { Button
|
|
4
|
+
import { Button } from '@akinon/next/components';
|
|
5
5
|
import { useLocalization } from '@akinon/next/hooks';
|
|
6
6
|
import { useImageSearchFeature } from '../hooks/use-image-search-feature';
|
|
7
|
-
import { twMerge } from 'tailwind-merge';
|
|
8
7
|
|
|
9
8
|
interface ImageSearchButtonProps {
|
|
10
9
|
onClick: () => void;
|
|
11
10
|
className?: string;
|
|
12
|
-
settings?: any;
|
|
13
11
|
}
|
|
14
12
|
|
|
15
13
|
export function ImageSearchButton({
|
|
16
14
|
onClick,
|
|
17
|
-
className
|
|
18
|
-
settings
|
|
15
|
+
className
|
|
19
16
|
}: ImageSearchButtonProps) {
|
|
20
17
|
const { t } = useLocalization();
|
|
21
18
|
const { isEnabled, isLoading } = useImageSearchFeature();
|
|
22
19
|
|
|
23
|
-
const imageSearchButtonIconName =
|
|
24
|
-
settings?.iconNames?.imageSearchButton || 'search';
|
|
25
|
-
const imageSearchButtonIconClassName = twMerge(
|
|
26
|
-
'text-black',
|
|
27
|
-
settings?.customStyles?.imageSearchButtonIcon
|
|
28
|
-
);
|
|
29
|
-
|
|
30
20
|
if (isLoading || !isEnabled) {
|
|
31
21
|
return null;
|
|
32
22
|
}
|
|
@@ -34,16 +24,24 @@ export function ImageSearchButton({
|
|
|
34
24
|
return (
|
|
35
25
|
<Button
|
|
36
26
|
onClick={onClick}
|
|
37
|
-
className={`flex items-center justify-center mr-2 text-gray-500 focus:outline-none border-none bg-transparent ${
|
|
38
|
-
className || ''
|
|
39
|
-
}`}
|
|
27
|
+
className={`flex items-center justify-center mr-2 text-gray-500 focus:outline-none border-none bg-transparent ${className || ''}`}
|
|
40
28
|
title={t('common.search.image_search.title')}
|
|
41
29
|
>
|
|
42
|
-
<
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
30
|
+
<svg
|
|
31
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
32
|
+
width="20"
|
|
33
|
+
height="20"
|
|
34
|
+
viewBox="0 0 24 24"
|
|
35
|
+
fill="none"
|
|
36
|
+
stroke="currentColor"
|
|
37
|
+
strokeWidth="2"
|
|
38
|
+
strokeLinecap="round"
|
|
39
|
+
strokeLinejoin="round"
|
|
40
|
+
className="text-gray-500"
|
|
41
|
+
>
|
|
42
|
+
<path d="M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z"></path>
|
|
43
|
+
<circle cx="12" cy="13" r="4"></circle>
|
|
44
|
+
</svg>
|
|
47
45
|
</Button>
|
|
48
46
|
);
|
|
49
47
|
}
|
package/src/views/main.tsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import React, { useState, useRef, useEffect
|
|
3
|
+
import React, { useState, useRef, useEffect } from 'react';
|
|
4
4
|
import { Product } from '@akinon/next/types';
|
|
5
5
|
import { useImageCropper } from '../hooks/use-image-cropper';
|
|
6
6
|
import { SimilarProductsModal } from './search-modal';
|
|
@@ -37,39 +37,12 @@ export function SimilarProductsPlugin({
|
|
|
37
37
|
const [isFilterMenuOpen, setIsFilterMenuOpen] = useState(false);
|
|
38
38
|
|
|
39
39
|
const fileInputRef = useRef<HTMLInputElement>(null);
|
|
40
|
-
const hasUploadedImageRef = useRef<boolean>(false);
|
|
41
|
-
const [isImageUploadedViaFileInput, setIsImageUploadedViaFileInput] = useState(false);
|
|
42
|
-
|
|
43
|
-
const UPLOAD_FLAG_KEY = `similar-products-upload-${product.pk}`;
|
|
44
|
-
|
|
45
|
-
const setUploadFlag = (value: boolean) => {
|
|
46
|
-
try {
|
|
47
|
-
if (value) {
|
|
48
|
-
sessionStorage.setItem(UPLOAD_FLAG_KEY, 'true');
|
|
49
|
-
} else {
|
|
50
|
-
sessionStorage.removeItem(UPLOAD_FLAG_KEY);
|
|
51
|
-
}
|
|
52
|
-
} catch (error) {
|
|
53
|
-
console.error('Session storage error:', error);
|
|
54
|
-
}
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
const getUploadFlag = () => {
|
|
58
|
-
try {
|
|
59
|
-
return sessionStorage.getItem(UPLOAD_FLAG_KEY) === 'true';
|
|
60
|
-
} catch (error) {
|
|
61
|
-
console.error('Session storage error:', error);
|
|
62
|
-
return false;
|
|
63
|
-
}
|
|
64
|
-
};
|
|
65
40
|
|
|
66
41
|
const {
|
|
67
42
|
currentImageUrl,
|
|
68
43
|
setCurrentImageUrl,
|
|
69
44
|
isLoading,
|
|
70
45
|
fileError,
|
|
71
|
-
searchText,
|
|
72
|
-
setSearchText,
|
|
73
46
|
searchResults,
|
|
74
47
|
resultsKey,
|
|
75
48
|
hasUploadedImage,
|
|
@@ -84,47 +57,9 @@ export function SimilarProductsPlugin({
|
|
|
84
57
|
fetchSimilarProductsByImageUrl,
|
|
85
58
|
fetchSimilarProductsByImageCrop,
|
|
86
59
|
clearError,
|
|
87
|
-
clearResults
|
|
88
|
-
handleTextSearch,
|
|
89
|
-
handleClearText,
|
|
90
|
-
resetCropState
|
|
60
|
+
clearResults
|
|
91
61
|
} = useSimilarProducts(product);
|
|
92
62
|
|
|
93
|
-
useEffect(() => {
|
|
94
|
-
if (hasUploadedImage) {
|
|
95
|
-
setIsImageUploadedViaFileInput(true);
|
|
96
|
-
setUploadFlag(true);
|
|
97
|
-
} else {
|
|
98
|
-
setIsImageUploadedViaFileInput(false);
|
|
99
|
-
setUploadFlag(false);
|
|
100
|
-
}
|
|
101
|
-
}, [hasUploadedImage]);
|
|
102
|
-
|
|
103
|
-
const cropProcessImageFunction = useCallback(
|
|
104
|
-
(base64Image: string) => {
|
|
105
|
-
let shouldExclude = true;
|
|
106
|
-
|
|
107
|
-
if (base64Image.startsWith('data:application/x-cors-fallback;base64,')) {
|
|
108
|
-
try {
|
|
109
|
-
const fallbackDataEncoded = base64Image.replace('data:application/x-cors-fallback;base64,', '');
|
|
110
|
-
const fallbackData = JSON.parse(atob(fallbackDataEncoded));
|
|
111
|
-
|
|
112
|
-
if (fallbackData.originalUrl && fallbackData.originalUrl.startsWith('data:')) {
|
|
113
|
-
shouldExclude = false;
|
|
114
|
-
}
|
|
115
|
-
} catch (error) {
|
|
116
|
-
console.error('CORS fallback parse error:', error);
|
|
117
|
-
}
|
|
118
|
-
} else {
|
|
119
|
-
const isUploadedFromStorage = getUploadFlag();
|
|
120
|
-
shouldExclude = !isUploadedFromStorage;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
fetchSimilarProductsByImageCrop(base64Image, shouldExclude);
|
|
124
|
-
},
|
|
125
|
-
[fetchSimilarProductsByImageCrop, hasUploadedImage, currentImageUrl, uploadedImageFile, isImageUploadedViaFileInput]
|
|
126
|
-
);
|
|
127
|
-
|
|
128
63
|
const {
|
|
129
64
|
isCropping,
|
|
130
65
|
crop,
|
|
@@ -139,7 +74,7 @@ export function SimilarProductsPlugin({
|
|
|
139
74
|
resetCrop
|
|
140
75
|
} = useImageCropper(
|
|
141
76
|
(loading) => {},
|
|
142
|
-
|
|
77
|
+
fetchSimilarProductsByImageCrop,
|
|
143
78
|
clearError
|
|
144
79
|
);
|
|
145
80
|
|
|
@@ -150,12 +85,8 @@ export function SimilarProductsPlugin({
|
|
|
150
85
|
product.productimage_set[0].image;
|
|
151
86
|
setCurrentImageUrl(originalImageUrl);
|
|
152
87
|
setHasUploadedImage(false);
|
|
153
|
-
hasUploadedImageRef.current = false;
|
|
154
|
-
setUploadFlag(false);
|
|
155
|
-
setSearchText('');
|
|
156
88
|
resetCrop();
|
|
157
|
-
|
|
158
|
-
fetchSimilarProductsByImageUrl(originalImageUrl, '');
|
|
89
|
+
fetchSimilarProductsByImageUrl(originalImageUrl);
|
|
159
90
|
}
|
|
160
91
|
};
|
|
161
92
|
|
|
@@ -177,8 +108,6 @@ export function SimilarProductsPlugin({
|
|
|
177
108
|
|
|
178
109
|
if (!isOpen) {
|
|
179
110
|
setHasInitialSearchDone(false);
|
|
180
|
-
hasUploadedImageRef.current = false;
|
|
181
|
-
setUploadFlag(false);
|
|
182
111
|
clearError();
|
|
183
112
|
clearResults();
|
|
184
113
|
}
|
|
@@ -200,9 +129,7 @@ export function SimilarProductsPlugin({
|
|
|
200
129
|
if (result) {
|
|
201
130
|
setCurrentImageUrl(result);
|
|
202
131
|
setHasUploadedImage(true);
|
|
203
|
-
|
|
204
|
-
setUploadFlag(true);
|
|
205
|
-
fetchSimilarProductsByImageCrop(result, false);
|
|
132
|
+
fetchSimilarProductsByImageCrop(result);
|
|
206
133
|
}
|
|
207
134
|
};
|
|
208
135
|
reader.readAsDataURL(uploadedImageFile);
|
|
@@ -266,10 +193,6 @@ export function SimilarProductsPlugin({
|
|
|
266
193
|
settings={settings}
|
|
267
194
|
className={settings.customStyles?.modal}
|
|
268
195
|
showResetButton={showResetButton}
|
|
269
|
-
searchText={searchText}
|
|
270
|
-
setSearchText={setSearchText}
|
|
271
|
-
handleTextSearch={handleTextSearch}
|
|
272
|
-
handleClearText={handleClearText}
|
|
273
196
|
/>
|
|
274
197
|
|
|
275
198
|
<ImageSearchModalComponent
|
|
@@ -6,7 +6,6 @@ import { SimilarProductsButton } from './search-button';
|
|
|
6
6
|
import { SimilarProductsPlugin } from './main';
|
|
7
7
|
import { mergeSettings } from '../utils';
|
|
8
8
|
import { useImageSearchFeature } from '../hooks/use-image-search-feature';
|
|
9
|
-
import { useTextSearchFeature } from '../hooks/use-text-search-feature';
|
|
10
9
|
|
|
11
10
|
interface ProductImageSearchFeatureProps {
|
|
12
11
|
product: Product;
|
|
@@ -14,7 +13,6 @@ interface ProductImageSearchFeatureProps {
|
|
|
14
13
|
settings?: any;
|
|
15
14
|
className?: string;
|
|
16
15
|
isEnabled?: boolean;
|
|
17
|
-
enableTextSearch?: boolean;
|
|
18
16
|
}
|
|
19
17
|
|
|
20
18
|
export function ProductImageSearchFeature({
|
|
@@ -22,19 +20,11 @@ export function ProductImageSearchFeature({
|
|
|
22
20
|
activeIndex = 0,
|
|
23
21
|
settings: userSettings,
|
|
24
22
|
className = 'absolute top-6 left-6 z-[20]',
|
|
25
|
-
isEnabled: isEnabledProp
|
|
26
|
-
enableTextSearch = false
|
|
23
|
+
isEnabled: isEnabledProp
|
|
27
24
|
}: ProductImageSearchFeatureProps) {
|
|
25
|
+
const settings = mergeSettings(userSettings);
|
|
28
26
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
29
27
|
const { isEnabled: hookIsEnabled } = useImageSearchFeature();
|
|
30
|
-
const { isEnabled: textSearchEnabled } = useTextSearchFeature({
|
|
31
|
-
enableTextSearch
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
const settings = mergeSettings({
|
|
35
|
-
...userSettings,
|
|
36
|
-
enableTextSearch: textSearchEnabled
|
|
37
|
-
});
|
|
38
28
|
|
|
39
29
|
const envEnabled = process.env.NEXT_PUBLIC_ENABLE_IMAGE_SEARCH === 'true';
|
|
40
30
|
const finalIsEnabled = envEnabled
|
|
@@ -51,11 +41,7 @@ export function ProductImageSearchFeature({
|
|
|
51
41
|
<>
|
|
52
42
|
{finalIsEnabled && (
|
|
53
43
|
<>
|
|
54
|
-
<SimilarProductsButton
|
|
55
|
-
onClick={handleClick}
|
|
56
|
-
className={className}
|
|
57
|
-
settings={settings}
|
|
58
|
-
/>
|
|
44
|
+
<SimilarProductsButton onClick={handleClick} className={className} />
|
|
59
45
|
|
|
60
46
|
<SimilarProductsPlugin
|
|
61
47
|
product={product}
|
|
@@ -3,32 +3,22 @@
|
|
|
3
3
|
import React from 'react';
|
|
4
4
|
import { Icon } from '@akinon/next/components';
|
|
5
5
|
import { useLocalization } from '@akinon/next/hooks';
|
|
6
|
-
import { twMerge } from 'tailwind-merge';
|
|
7
6
|
|
|
8
7
|
interface SimilarProductsButtonProps {
|
|
9
8
|
onClick: () => void;
|
|
10
9
|
className?: string;
|
|
11
10
|
isLoading?: boolean;
|
|
12
11
|
disabled?: boolean;
|
|
13
|
-
settings?: any;
|
|
14
12
|
}
|
|
15
13
|
|
|
16
14
|
export function SimilarProductsButton({
|
|
17
15
|
onClick,
|
|
18
16
|
className = '',
|
|
19
17
|
isLoading = false,
|
|
20
|
-
disabled = false
|
|
21
|
-
settings
|
|
18
|
+
disabled = false
|
|
22
19
|
}: SimilarProductsButtonProps) {
|
|
23
20
|
const { t } = useLocalization();
|
|
24
21
|
|
|
25
|
-
const searchButtonIconClassName = twMerge(
|
|
26
|
-
'fill-black',
|
|
27
|
-
settings?.customStyles?.searchButtonIcon
|
|
28
|
-
);
|
|
29
|
-
|
|
30
|
-
const searchButtonIconName = settings?.iconNames?.searchButton || 'search';
|
|
31
|
-
|
|
32
22
|
return (
|
|
33
23
|
<button
|
|
34
24
|
onClick={onClick}
|
|
@@ -38,7 +28,7 @@ export function SimilarProductsButton({
|
|
|
38
28
|
} ${className}`}
|
|
39
29
|
>
|
|
40
30
|
<div className="flex items-center gap-2">
|
|
41
|
-
<Icon name=
|
|
31
|
+
<Icon name="search" size={16} className="fill-black" />
|
|
42
32
|
<span className="text-xs font-medium text-black uppercase">
|
|
43
33
|
{t('common.product.view_similar_styles')}
|
|
44
34
|
</span>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import React from 'react';
|
|
4
|
-
import { Modal, Button, Icon, Select
|
|
4
|
+
import { Modal, Button, Icon, Select } from '@akinon/next/components';
|
|
5
5
|
import { useLocalization } from '@akinon/next/hooks';
|
|
6
6
|
import { SimilarProductsFilterSidebar } from './filters';
|
|
7
7
|
import { SimilarProductsResultsGrid } from './results';
|
|
@@ -43,11 +43,7 @@ export function SimilarProductsModal({
|
|
|
43
43
|
fileError,
|
|
44
44
|
showResetButton = true,
|
|
45
45
|
settings,
|
|
46
|
-
className
|
|
47
|
-
searchText,
|
|
48
|
-
setSearchText,
|
|
49
|
-
handleTextSearch,
|
|
50
|
-
handleClearText
|
|
46
|
+
className
|
|
51
47
|
}: SimilarProductsModalProps) {
|
|
52
48
|
const { t } = useLocalization();
|
|
53
49
|
|
|
@@ -168,24 +164,6 @@ export function SimilarProductsModal({
|
|
|
168
164
|
})) || []
|
|
169
165
|
) || [];
|
|
170
166
|
|
|
171
|
-
const modalCloseIconName = settings?.iconNames?.modalClose || 'close';
|
|
172
|
-
const filterIconName = settings?.iconNames?.filter || 'filter';
|
|
173
|
-
const modalSearchIconName = settings?.iconNames?.modalSearch || 'search';
|
|
174
|
-
const modalSearchClearIconName =
|
|
175
|
-
settings?.iconNames?.modalSearchClear || 'close';
|
|
176
|
-
|
|
177
|
-
const modalCloseIconClassName = twMerge(
|
|
178
|
-
'',
|
|
179
|
-
settings?.customStyles?.modalCloseIcon
|
|
180
|
-
);
|
|
181
|
-
|
|
182
|
-
const filterIconClassName = twMerge('', settings?.customStyles?.filterIcon);
|
|
183
|
-
|
|
184
|
-
const modalSearchIconClassName = twMerge(
|
|
185
|
-
'text-gray-500',
|
|
186
|
-
settings?.customStyles?.modalSearchIcon
|
|
187
|
-
);
|
|
188
|
-
|
|
189
167
|
const renderHeader = () => {
|
|
190
168
|
if (settings?.customRenderers?.render?.modal?.renderHeader) {
|
|
191
169
|
return settings.customRenderers.render.modal.renderHeader({
|
|
@@ -268,11 +246,7 @@ export function SimilarProductsModal({
|
|
|
268
246
|
disabled={isLoading}
|
|
269
247
|
className={buttonClassName}
|
|
270
248
|
>
|
|
271
|
-
<Icon
|
|
272
|
-
name={modalCloseIconName}
|
|
273
|
-
size={12}
|
|
274
|
-
className={modalCloseIconClassName}
|
|
275
|
-
/>
|
|
249
|
+
<Icon name="close" size={12} />
|
|
276
250
|
</Button>
|
|
277
251
|
</div>
|
|
278
252
|
);
|
|
@@ -292,9 +266,7 @@ export function SimilarProductsModal({
|
|
|
292
266
|
'',
|
|
293
267
|
onSortChange: handleSortChange,
|
|
294
268
|
onFilterMenuToggle: () => setIsFilterMenuOpen(true),
|
|
295
|
-
isLoading
|
|
296
|
-
searchText,
|
|
297
|
-
setSearchText
|
|
269
|
+
isLoading
|
|
298
270
|
});
|
|
299
271
|
}
|
|
300
272
|
|
|
@@ -309,30 +281,15 @@ export function SimilarProductsModal({
|
|
|
309
281
|
);
|
|
310
282
|
|
|
311
283
|
const filterToggleClassName = twMerge(
|
|
312
|
-
'md:hidden text-xs
|
|
284
|
+
'md:hidden text-xs',
|
|
313
285
|
settings?.customStyles?.filterToggleButton
|
|
314
286
|
);
|
|
315
287
|
|
|
316
288
|
const sortDropdownClassName = twMerge(
|
|
317
|
-
'h-10 px-
|
|
289
|
+
'h-10 px-4 text-md md:text-xs bg-gray-200 hover:bg-gray-400 transition-colors duration-200 border-gray-300 focus:border-primary focus:ring-1 focus:ring-primary w-full md:w-40 min-w-[120px]',
|
|
318
290
|
settings?.customStyles?.sortDropdown
|
|
319
291
|
);
|
|
320
292
|
|
|
321
|
-
const modalSearchInputClassName = twMerge(
|
|
322
|
-
'h-10 px-3 md:px-4 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent w-48 md:w-60 min-w-[180px]',
|
|
323
|
-
settings?.customStyles?.modalSearchInput
|
|
324
|
-
);
|
|
325
|
-
|
|
326
|
-
const modalSearchContainerClassName = twMerge(
|
|
327
|
-
'relative flex items-center',
|
|
328
|
-
settings?.customStyles?.modalSearchContainer
|
|
329
|
-
);
|
|
330
|
-
|
|
331
|
-
const modalSearchButtonClassName = twMerge(
|
|
332
|
-
'absolute right-2 top-1/2 -translate-y-1/2 p-2 rounded-md hover:bg-gray-100 disabled:opacity-50 disabled:cursor-not-allowed transition-colors',
|
|
333
|
-
settings?.customStyles?.modalSearchButton
|
|
334
|
-
);
|
|
335
|
-
|
|
336
293
|
const renderItemCount = () => {
|
|
337
294
|
if (settings?.customRenderers?.render?.modal?.renderItemCount) {
|
|
338
295
|
return settings.customRenderers.render.modal.renderItemCount({
|
|
@@ -342,9 +299,7 @@ export function SimilarProductsModal({
|
|
|
342
299
|
}
|
|
343
300
|
return (
|
|
344
301
|
<div className={itemCountClassName}>
|
|
345
|
-
|
|
346
|
-
{searchResults?.pagination?.total_count || 0} items
|
|
347
|
-
</span>
|
|
302
|
+
{searchResults?.pagination?.total_count || 0} items
|
|
348
303
|
</div>
|
|
349
304
|
);
|
|
350
305
|
};
|
|
@@ -364,11 +319,7 @@ export function SimilarProductsModal({
|
|
|
364
319
|
onClick={() => setIsFilterMenuOpen(true)}
|
|
365
320
|
data-testid="similar-products-filter"
|
|
366
321
|
>
|
|
367
|
-
<Icon
|
|
368
|
-
name={filterIconName}
|
|
369
|
-
size={14}
|
|
370
|
-
className={filterIconClassName}
|
|
371
|
-
/>
|
|
322
|
+
<Icon name="filter" size={14} />
|
|
372
323
|
{t('common.product.filters')}
|
|
373
324
|
</Button>
|
|
374
325
|
);
|
|
@@ -404,134 +355,26 @@ export function SimilarProductsModal({
|
|
|
404
355
|
);
|
|
405
356
|
};
|
|
406
357
|
|
|
407
|
-
const renderModalSearchInput = () => {
|
|
408
|
-
if (
|
|
409
|
-
!settings?.enableTextSearch ||
|
|
410
|
-
searchText === undefined ||
|
|
411
|
-
!setSearchText ||
|
|
412
|
-
!searchResults?.products?.length ||
|
|
413
|
-
!searchResults?.sorters?.length
|
|
414
|
-
) {
|
|
415
|
-
return null;
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
if (settings?.customRenderers?.render?.modal?.renderModalSearchInput) {
|
|
419
|
-
return settings.customRenderers.render.modal.renderModalSearchInput({
|
|
420
|
-
searchText,
|
|
421
|
-
setSearchText,
|
|
422
|
-
isLoading,
|
|
423
|
-
placeholder: t('common.search.placeholder'),
|
|
424
|
-
onSearch: handleTextSearch
|
|
425
|
-
});
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
return (
|
|
429
|
-
<div className={modalSearchContainerClassName}>
|
|
430
|
-
<Input
|
|
431
|
-
type="text"
|
|
432
|
-
value={searchText}
|
|
433
|
-
onChange={(e) => setSearchText(e.currentTarget.value)}
|
|
434
|
-
onKeyDown={(e) => {
|
|
435
|
-
if (e.key === 'Enter' && handleTextSearch) {
|
|
436
|
-
e.preventDefault();
|
|
437
|
-
handleTextSearch();
|
|
438
|
-
}
|
|
439
|
-
}}
|
|
440
|
-
placeholder={t('common.search.placeholder')}
|
|
441
|
-
className={twMerge(modalSearchInputClassName, 'pr-20')}
|
|
442
|
-
disabled={isLoading}
|
|
443
|
-
/>
|
|
444
|
-
{searchText && (
|
|
445
|
-
<button
|
|
446
|
-
onClick={() => {
|
|
447
|
-
if (handleClearText) {
|
|
448
|
-
handleClearText();
|
|
449
|
-
}
|
|
450
|
-
}}
|
|
451
|
-
disabled={isLoading}
|
|
452
|
-
className={twMerge(
|
|
453
|
-
'absolute right-12 top-1/2 -translate-y-1/2 p-2 rounded-md hover:bg-gray-100 disabled:opacity-50 disabled:cursor-not-allowed transition-colors',
|
|
454
|
-
settings?.customStyles?.modalSearchClearButton
|
|
455
|
-
)}
|
|
456
|
-
>
|
|
457
|
-
<Icon
|
|
458
|
-
name={modalSearchClearIconName}
|
|
459
|
-
size={16}
|
|
460
|
-
className={twMerge(
|
|
461
|
-
'text-gray-400 hover:text-gray-600',
|
|
462
|
-
settings?.customStyles?.modalSearchClearIcon
|
|
463
|
-
)}
|
|
464
|
-
/>
|
|
465
|
-
</button>
|
|
466
|
-
)}
|
|
467
|
-
<button
|
|
468
|
-
onClick={() => {
|
|
469
|
-
if (handleTextSearch) {
|
|
470
|
-
handleTextSearch();
|
|
471
|
-
}
|
|
472
|
-
}}
|
|
473
|
-
disabled={isLoading}
|
|
474
|
-
className={modalSearchButtonClassName}
|
|
475
|
-
>
|
|
476
|
-
{settings?.customRenderers?.render?.modal?.renderSearchIcon ? (
|
|
477
|
-
settings.customRenderers.render.modal.renderSearchIcon({
|
|
478
|
-
disabled: isLoading,
|
|
479
|
-
onClick: handleTextSearch
|
|
480
|
-
})
|
|
481
|
-
) : (
|
|
482
|
-
<Icon
|
|
483
|
-
name={modalSearchIconName}
|
|
484
|
-
size={18}
|
|
485
|
-
className={modalSearchIconClassName}
|
|
486
|
-
/>
|
|
487
|
-
)}
|
|
488
|
-
</button>
|
|
489
|
-
</div>
|
|
490
|
-
);
|
|
491
|
-
};
|
|
492
|
-
|
|
493
358
|
return (
|
|
494
359
|
<div className={containerClassName}>
|
|
495
360
|
<div
|
|
496
361
|
className={twMerge(
|
|
497
|
-
'p-3 md:p-4',
|
|
498
|
-
settings?.enableTextSearch &&
|
|
499
|
-
searchText !== undefined &&
|
|
500
|
-
setSearchText
|
|
501
|
-
? 'flex gap-3 md:grid md:grid-cols-3 md:gap-4 md:items-center'
|
|
502
|
-
: 'flex items-center justify-between',
|
|
362
|
+
'flex items-center justify-between p-3 md:p-4',
|
|
503
363
|
settings?.customStyles?.controlsInner
|
|
504
364
|
)}
|
|
505
365
|
>
|
|
506
366
|
<div
|
|
507
367
|
className={twMerge(
|
|
508
|
-
'flex items-center gap-
|
|
368
|
+
'flex items-center gap-3',
|
|
509
369
|
settings?.customStyles?.controlsLeft
|
|
510
370
|
)}
|
|
511
371
|
>
|
|
512
372
|
{renderItemCount()}
|
|
513
373
|
{renderFilterToggle()}
|
|
514
374
|
</div>
|
|
515
|
-
{settings?.enableTextSearch &&
|
|
516
|
-
searchText !== undefined &&
|
|
517
|
-
setSearchText && (
|
|
518
|
-
<div
|
|
519
|
-
className={twMerge(
|
|
520
|
-
'flex items-center justify-center w-full',
|
|
521
|
-
settings?.customStyles?.controlsCenter
|
|
522
|
-
)}
|
|
523
|
-
>
|
|
524
|
-
{renderModalSearchInput()}
|
|
525
|
-
</div>
|
|
526
|
-
)}
|
|
527
375
|
<div
|
|
528
376
|
className={twMerge(
|
|
529
377
|
'relative',
|
|
530
|
-
settings?.enableTextSearch &&
|
|
531
|
-
searchText !== undefined &&
|
|
532
|
-
setSearchText
|
|
533
|
-
? 'flex justify-end md:mt-0'
|
|
534
|
-
: '',
|
|
535
378
|
settings?.customStyles?.controlsRight
|
|
536
379
|
)}
|
|
537
380
|
>
|
|
@@ -567,8 +410,6 @@ export function SimilarProductsModal({
|
|
|
567
410
|
isLoading={isLoading}
|
|
568
411
|
handleFacetChange={handleFacetChange}
|
|
569
412
|
removeFacetFilter={removeFacetFilter}
|
|
570
|
-
searchText={searchText}
|
|
571
|
-
setSearchText={setSearchText}
|
|
572
413
|
currentImageUrl={currentImageUrl}
|
|
573
414
|
isCropping={isCropping}
|
|
574
415
|
imageRef={imageRef}
|