@akinon/pz-similar-products 1.114.0-snapshot-ZERO-3890-20251209002502 → 1.115.0-rc.22
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 +33 -31
- package/README.md +0 -202
- package/package.json +1 -1
- package/src/data/endpoints.ts +5 -14
- package/src/hooks/index.ts +1 -0
- package/src/hooks/use-image-search-feature.ts +32 -0
- package/src/hooks/use-similar-products.ts +23 -148
- 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 +34 -28
- package/src/views/image-search-button.tsx +23 -19
- package/src/views/main.tsx +5 -82
- package/src/views/product-image-search-feature.tsx +22 -22
- package/src/views/search-button.tsx +2 -12
- package/src/views/search-modal.tsx +10 -169
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useState, useEffect, useCallback
|
|
1
|
+
import { useState, useEffect, useCallback } from 'react';
|
|
2
2
|
import { Product } from '@akinon/next/types';
|
|
3
3
|
import {
|
|
4
4
|
useLazyGetSimilarProductsByUrlQuery,
|
|
@@ -13,7 +13,6 @@ import {
|
|
|
13
13
|
validateImageFromDataUrl,
|
|
14
14
|
type ImageValidationResult
|
|
15
15
|
} from '../utils/image-validation';
|
|
16
|
-
import { debounce } from '../utils';
|
|
17
16
|
|
|
18
17
|
type SearchResults = SimilarProductsListResponse;
|
|
19
18
|
|
|
@@ -25,7 +24,6 @@ export function useSimilarProducts(product: Product) {
|
|
|
25
24
|
const { t } = useLocalization();
|
|
26
25
|
const [currentImageUrl, setCurrentImageUrl] = useState('');
|
|
27
26
|
const [fileError, setFileError] = useState('');
|
|
28
|
-
const [searchText, setSearchText] = useState('');
|
|
29
27
|
const [searchResults, setSearchResults] = useState<SearchResults | null>(
|
|
30
28
|
null
|
|
31
29
|
);
|
|
@@ -35,16 +33,6 @@ export function useSimilarProducts(product: Product) {
|
|
|
35
33
|
const [isCropProcessing, setIsCropProcessing] = useState(false);
|
|
36
34
|
const [loadedPages, setLoadedPages] = useState<Set<number>>(new Set([1]));
|
|
37
35
|
const [allLoadedProducts, setAllLoadedProducts] = useState<Product[]>([]);
|
|
38
|
-
const [currentImageBase64, setCurrentImageBase64] = useState<string>('');
|
|
39
|
-
const [hasCroppedImage, setHasCroppedImage] = useState(false);
|
|
40
|
-
|
|
41
|
-
const searchTextRef = useRef<string>('');
|
|
42
|
-
const hasUploadedImageRef = useRef<boolean>(false);
|
|
43
|
-
|
|
44
|
-
const setSearchTextWithRef = useCallback((text: string) => {
|
|
45
|
-
setSearchText(text);
|
|
46
|
-
searchTextRef.current = text;
|
|
47
|
-
}, []);
|
|
48
36
|
|
|
49
37
|
const [fetchSimilarProductsByUrl, { isLoading: isUrlSearchLoading }] =
|
|
50
38
|
useLazyGetSimilarProductsByUrlQuery();
|
|
@@ -438,24 +426,17 @@ export function useSimilarProducts(product: Product) {
|
|
|
438
426
|
);
|
|
439
427
|
|
|
440
428
|
const fetchSimilarProductsByImageUrl = useCallback(
|
|
441
|
-
async (imageUrl: string
|
|
429
|
+
async (imageUrl: string) => {
|
|
442
430
|
setFileError('');
|
|
443
|
-
|
|
444
431
|
try {
|
|
445
432
|
const productPk = product?.pk;
|
|
446
433
|
const excludedIds = productPk ? [productPk] : undefined;
|
|
447
434
|
|
|
448
|
-
const
|
|
449
|
-
overrideText !== undefined ? overrideText : searchTextRef.current;
|
|
450
|
-
|
|
451
|
-
const requestParams = {
|
|
435
|
+
const result = await fetchSimilarProductsByUrl({
|
|
452
436
|
url: imageUrl,
|
|
453
437
|
limit: 20,
|
|
454
|
-
excluded_product_ids: excludedIds
|
|
455
|
-
|
|
456
|
-
};
|
|
457
|
-
|
|
458
|
-
const result = await fetchSimilarProductsByUrl(requestParams).unwrap();
|
|
438
|
+
excluded_product_ids: excludedIds
|
|
439
|
+
}).unwrap();
|
|
459
440
|
|
|
460
441
|
await handleSearchResults(result);
|
|
461
442
|
return result;
|
|
@@ -469,8 +450,6 @@ export function useSimilarProducts(product: Product) {
|
|
|
469
450
|
[
|
|
470
451
|
fetchSimilarProductsByUrl,
|
|
471
452
|
product?.pk,
|
|
472
|
-
searchText,
|
|
473
|
-
searchTextRef,
|
|
474
453
|
handleSearchResults,
|
|
475
454
|
updateResultsAndKey,
|
|
476
455
|
createEmptySearchResults,
|
|
@@ -483,62 +462,31 @@ export function useSimilarProducts(product: Product) {
|
|
|
483
462
|
if (product?.productimage_set?.length > 0) {
|
|
484
463
|
const initialImageUrl = product.productimage_set[0].image;
|
|
485
464
|
setCurrentImageUrl(initialImageUrl);
|
|
486
|
-
setHasCroppedImage(false);
|
|
487
|
-
setCurrentImageBase64('');
|
|
488
|
-
setHasUploadedImage(false);
|
|
489
|
-
hasUploadedImageRef.current = false;
|
|
490
465
|
}
|
|
491
466
|
}, [product]);
|
|
492
467
|
|
|
493
468
|
const fetchSimilarProductsByBase64 = useCallback(
|
|
494
|
-
async (
|
|
495
|
-
base64Image: string,
|
|
496
|
-
overrideText?: string,
|
|
497
|
-
forceExclude?: boolean
|
|
498
|
-
) => {
|
|
469
|
+
async (base64Image: string) => {
|
|
499
470
|
const base64Data = base64Image.startsWith('data:')
|
|
500
471
|
? base64Image.split(',')[1]
|
|
501
472
|
: base64Image;
|
|
502
473
|
|
|
503
|
-
const textToUse =
|
|
504
|
-
overrideText !== undefined ? overrideText : searchTextRef.current;
|
|
505
|
-
|
|
506
|
-
const shouldExclude =
|
|
507
|
-
forceExclude !== undefined ? forceExclude : !hasUploadedImage;
|
|
508
|
-
|
|
509
|
-
const requestData = {
|
|
510
|
-
image: base64Data,
|
|
511
|
-
limit: 20,
|
|
512
|
-
excluded_product_ids: shouldExclude
|
|
513
|
-
? product?.pk
|
|
514
|
-
? [product.pk]
|
|
515
|
-
: undefined
|
|
516
|
-
: undefined,
|
|
517
|
-
text: textToUse || undefined
|
|
518
|
-
};
|
|
519
|
-
|
|
520
474
|
return handleImageSearch(
|
|
521
|
-
async () =>
|
|
475
|
+
async () =>
|
|
476
|
+
getSimilarProductsByImage({
|
|
477
|
+
image: base64Data,
|
|
478
|
+
limit: 20
|
|
479
|
+
}).unwrap(),
|
|
522
480
|
base64Image,
|
|
523
481
|
'Image search'
|
|
524
482
|
);
|
|
525
483
|
},
|
|
526
|
-
[
|
|
527
|
-
getSimilarProductsByImage,
|
|
528
|
-
searchText,
|
|
529
|
-
searchTextRef,
|
|
530
|
-
handleImageSearch,
|
|
531
|
-
hasUploadedImage,
|
|
532
|
-
product?.pk
|
|
533
|
-
]
|
|
484
|
+
[getSimilarProductsByImage, handleImageSearch]
|
|
534
485
|
);
|
|
535
486
|
|
|
536
487
|
const fetchSimilarProductsByImageCrop = useCallback(
|
|
537
|
-
async (dataString: string
|
|
488
|
+
async (dataString: string) => {
|
|
538
489
|
setFileError('');
|
|
539
|
-
|
|
540
|
-
setCurrentImageBase64(dataString);
|
|
541
|
-
setHasCroppedImage(true);
|
|
542
490
|
if (dataString.startsWith('data:application/x-cors-fallback;base64,')) {
|
|
543
491
|
try {
|
|
544
492
|
const fallbackDataEncoded = dataString.replace(
|
|
@@ -601,12 +549,9 @@ export function useSimilarProducts(product: Product) {
|
|
|
601
549
|
getSimilarProductsByImage({
|
|
602
550
|
image: base64Data,
|
|
603
551
|
limit: 20,
|
|
604
|
-
excluded_product_ids:
|
|
605
|
-
? product
|
|
606
|
-
|
|
607
|
-
: undefined
|
|
608
|
-
: undefined,
|
|
609
|
-
text: searchTextRef.current || undefined
|
|
552
|
+
excluded_product_ids: product?.pk
|
|
553
|
+
? [product.pk]
|
|
554
|
+
: undefined
|
|
610
555
|
}).unwrap(),
|
|
611
556
|
croppedBase64,
|
|
612
557
|
'Proxy crop search'
|
|
@@ -656,12 +601,7 @@ export function useSimilarProducts(product: Product) {
|
|
|
656
601
|
getSimilarProductsByImage({
|
|
657
602
|
image: base64Data,
|
|
658
603
|
limit: 20,
|
|
659
|
-
excluded_product_ids:
|
|
660
|
-
? product?.pk
|
|
661
|
-
? [product.pk]
|
|
662
|
-
: undefined
|
|
663
|
-
: undefined,
|
|
664
|
-
text: searchTextRef.current || undefined
|
|
604
|
+
excluded_product_ids: product?.pk ? [product.pk] : undefined
|
|
665
605
|
}).unwrap(),
|
|
666
606
|
dataString,
|
|
667
607
|
'Image crop search'
|
|
@@ -669,14 +609,11 @@ export function useSimilarProducts(product: Product) {
|
|
|
669
609
|
},
|
|
670
610
|
[
|
|
671
611
|
getSimilarProductsByImage,
|
|
672
|
-
searchText,
|
|
673
|
-
searchTextRef,
|
|
674
612
|
handleImageSearch,
|
|
675
613
|
product,
|
|
676
614
|
fetchSimilarProductsByImageUrl,
|
|
677
615
|
setFileError,
|
|
678
|
-
t
|
|
679
|
-
hasUploadedImage
|
|
616
|
+
t
|
|
680
617
|
]
|
|
681
618
|
);
|
|
682
619
|
|
|
@@ -687,8 +624,6 @@ export function useSimilarProducts(product: Product) {
|
|
|
687
624
|
const file = event.target.files?.[0];
|
|
688
625
|
if (!file) return;
|
|
689
626
|
|
|
690
|
-
setSearchTextWithRef('');
|
|
691
|
-
|
|
692
627
|
try {
|
|
693
628
|
let processedFile = file;
|
|
694
629
|
|
|
@@ -715,9 +650,6 @@ export function useSimilarProducts(product: Product) {
|
|
|
715
650
|
const dataUrl = e.target?.result as string;
|
|
716
651
|
setCurrentImageUrl(dataUrl);
|
|
717
652
|
setHasUploadedImage(true);
|
|
718
|
-
hasUploadedImageRef.current = true;
|
|
719
|
-
setHasCroppedImage(false);
|
|
720
|
-
setCurrentImageBase64('');
|
|
721
653
|
|
|
722
654
|
const metadataValidation = await validateImageFromDataUrl(dataUrl);
|
|
723
655
|
if (!metadataValidation.isValid) {
|
|
@@ -726,7 +658,7 @@ export function useSimilarProducts(product: Product) {
|
|
|
726
658
|
);
|
|
727
659
|
return;
|
|
728
660
|
}
|
|
729
|
-
fetchSimilarProductsByBase64(dataUrl
|
|
661
|
+
fetchSimilarProductsByBase64(dataUrl);
|
|
730
662
|
};
|
|
731
663
|
|
|
732
664
|
reader.onerror = () =>
|
|
@@ -747,8 +679,6 @@ export function useSimilarProducts(product: Product) {
|
|
|
747
679
|
|
|
748
680
|
try {
|
|
749
681
|
setCurrentImageUrl(base64Image);
|
|
750
|
-
setCurrentImageBase64(base64Image);
|
|
751
|
-
setHasCroppedImage(true);
|
|
752
682
|
|
|
753
683
|
const metadataValidation = await validateImageFromDataUrl(base64Image);
|
|
754
684
|
if (!metadataValidation.isValid) {
|
|
@@ -758,14 +688,10 @@ export function useSimilarProducts(product: Product) {
|
|
|
758
688
|
return;
|
|
759
689
|
}
|
|
760
690
|
|
|
761
|
-
if (
|
|
762
|
-
await fetchSimilarProductsByBase64(
|
|
763
|
-
base64Image,
|
|
764
|
-
searchTextRef.current,
|
|
765
|
-
false
|
|
766
|
-
);
|
|
691
|
+
if (hasUploadedImage) {
|
|
692
|
+
await fetchSimilarProductsByBase64(base64Image);
|
|
767
693
|
} else {
|
|
768
|
-
await fetchSimilarProductsByImageCrop(base64Image
|
|
694
|
+
await fetchSimilarProductsByImageCrop(base64Image);
|
|
769
695
|
}
|
|
770
696
|
} catch (error) {
|
|
771
697
|
setFileError(t('common.similar_products.errors.crop_processing_error'));
|
|
@@ -956,11 +882,6 @@ export function useSimilarProducts(product: Product) {
|
|
|
956
882
|
setLoadedPages(new Set([1]));
|
|
957
883
|
}, []);
|
|
958
884
|
|
|
959
|
-
const resetCropState = useCallback(() => {
|
|
960
|
-
setHasCroppedImage(false);
|
|
961
|
-
setCurrentImageBase64('');
|
|
962
|
-
}, []);
|
|
963
|
-
|
|
964
885
|
const clearFileInput = useCallback(
|
|
965
886
|
(fileInputRef: React.RefObject<HTMLInputElement>) => {
|
|
966
887
|
if (fileInputRef.current) {
|
|
@@ -970,47 +891,6 @@ export function useSimilarProducts(product: Product) {
|
|
|
970
891
|
[]
|
|
971
892
|
);
|
|
972
893
|
|
|
973
|
-
const handleTextSearch = useCallback(async () => {
|
|
974
|
-
const textToUse = searchTextRef.current || searchText;
|
|
975
|
-
|
|
976
|
-
if (hasCroppedImage && currentImageBase64) {
|
|
977
|
-
await fetchSimilarProductsByBase64(currentImageBase64, textToUse, !hasUploadedImage);
|
|
978
|
-
} else if (hasUploadedImage && currentImageUrl) {
|
|
979
|
-
await fetchSimilarProductsByBase64(currentImageUrl, textToUse, false);
|
|
980
|
-
} else if (currentImageUrl) {
|
|
981
|
-
await fetchSimilarProductsByImageUrl(currentImageUrl, textToUse);
|
|
982
|
-
}
|
|
983
|
-
}, [
|
|
984
|
-
searchText,
|
|
985
|
-
searchTextRef,
|
|
986
|
-
currentImageUrl,
|
|
987
|
-
currentImageBase64,
|
|
988
|
-
hasCroppedImage,
|
|
989
|
-
hasUploadedImage,
|
|
990
|
-
fetchSimilarProductsByImageUrl,
|
|
991
|
-
fetchSimilarProductsByBase64
|
|
992
|
-
]);
|
|
993
|
-
|
|
994
|
-
const handleClearText = useCallback(async () => {
|
|
995
|
-
setSearchTextWithRef('');
|
|
996
|
-
|
|
997
|
-
if (hasCroppedImage && currentImageBase64) {
|
|
998
|
-
await fetchSimilarProductsByBase64(currentImageBase64, '', !hasUploadedImage);
|
|
999
|
-
} else if (hasUploadedImage && currentImageUrl) {
|
|
1000
|
-
await fetchSimilarProductsByBase64(currentImageUrl, '', false);
|
|
1001
|
-
} else if (currentImageUrl) {
|
|
1002
|
-
await fetchSimilarProductsByImageUrl(currentImageUrl, '');
|
|
1003
|
-
}
|
|
1004
|
-
}, [
|
|
1005
|
-
hasCroppedImage,
|
|
1006
|
-
currentImageBase64,
|
|
1007
|
-
currentImageUrl,
|
|
1008
|
-
hasUploadedImage,
|
|
1009
|
-
fetchSimilarProductsByBase64,
|
|
1010
|
-
fetchSimilarProductsByImageUrl,
|
|
1011
|
-
setSearchTextWithRef
|
|
1012
|
-
]);
|
|
1013
|
-
|
|
1014
894
|
const handleLoadMore = async () => {
|
|
1015
895
|
if (!searchResults?.pagination || isLoading) return;
|
|
1016
896
|
|
|
@@ -1051,8 +931,6 @@ export function useSimilarProducts(product: Product) {
|
|
|
1051
931
|
setCurrentImageUrl,
|
|
1052
932
|
isLoading,
|
|
1053
933
|
fileError,
|
|
1054
|
-
searchText,
|
|
1055
|
-
setSearchText: setSearchTextWithRef,
|
|
1056
934
|
searchResults,
|
|
1057
935
|
resultsKey,
|
|
1058
936
|
hasUploadedImage,
|
|
@@ -1071,9 +949,6 @@ export function useSimilarProducts(product: Product) {
|
|
|
1071
949
|
allLoadedProducts,
|
|
1072
950
|
clearError,
|
|
1073
951
|
clearResults,
|
|
1074
|
-
clearFileInput
|
|
1075
|
-
handleTextSearch,
|
|
1076
|
-
handleClearText,
|
|
1077
|
-
resetCropState
|
|
952
|
+
clearFileInput
|
|
1078
953
|
};
|
|
1079
954
|
}
|
package/src/types/index.ts
CHANGED
|
@@ -58,10 +58,6 @@ export interface SimilarProductsModalProps {
|
|
|
58
58
|
showResetButton?: boolean;
|
|
59
59
|
settings?: any;
|
|
60
60
|
className?: string;
|
|
61
|
-
searchText?: string;
|
|
62
|
-
setSearchText?: (text: string) => void;
|
|
63
|
-
handleTextSearch?: () => void;
|
|
64
|
-
handleClearText?: () => void;
|
|
65
61
|
}
|
|
66
62
|
|
|
67
63
|
export interface FilterSidebarProps {
|
|
@@ -71,8 +67,6 @@ export interface FilterSidebarProps {
|
|
|
71
67
|
isLoading: boolean;
|
|
72
68
|
handleFacetChange: (facetKey: string, choiceValue: string | number) => void;
|
|
73
69
|
removeFacetFilter: (facetKey: string, choiceValue: string | number) => void;
|
|
74
|
-
searchText?: string;
|
|
75
|
-
setSearchText?: (text: string) => void;
|
|
76
70
|
currentImageUrl: string;
|
|
77
71
|
isCropping: boolean;
|
|
78
72
|
imageRef: React.RefObject<HTMLImageElement>;
|
|
@@ -132,8 +126,6 @@ export interface CustomRendererProps {
|
|
|
132
126
|
onSortChange: (value: string) => void;
|
|
133
127
|
onFilterMenuToggle: () => void;
|
|
134
128
|
isLoading: boolean;
|
|
135
|
-
searchText?: string;
|
|
136
|
-
setSearchText?: (text: string) => void;
|
|
137
129
|
}) => React.ReactNode;
|
|
138
130
|
renderSortDropdown?: (props: {
|
|
139
131
|
sorters: SortOption[];
|
|
@@ -149,17 +141,6 @@ export interface CustomRendererProps {
|
|
|
149
141
|
onClick: () => void;
|
|
150
142
|
isLoading: boolean;
|
|
151
143
|
}) => React.ReactNode;
|
|
152
|
-
renderModalSearchInput?: (props: {
|
|
153
|
-
searchText: string;
|
|
154
|
-
setSearchText: (text: string) => void;
|
|
155
|
-
isLoading: boolean;
|
|
156
|
-
placeholder: string;
|
|
157
|
-
onSearch?: () => void;
|
|
158
|
-
}) => React.ReactNode;
|
|
159
|
-
renderSearchIcon?: (props: {
|
|
160
|
-
disabled: boolean;
|
|
161
|
-
onClick?: () => void;
|
|
162
|
-
}) => React.ReactNode;
|
|
163
144
|
renderEmptyState?: () => React.ReactNode;
|
|
164
145
|
};
|
|
165
146
|
filterSidebar?: {
|
|
@@ -238,12 +219,6 @@ export interface CustomRendererProps {
|
|
|
238
219
|
onClearAll: () => void;
|
|
239
220
|
isLoading: boolean;
|
|
240
221
|
}) => React.ReactNode;
|
|
241
|
-
renderTextSearch?: (props: {
|
|
242
|
-
searchText: string;
|
|
243
|
-
setSearchText: (text: string) => void;
|
|
244
|
-
isLoading: boolean;
|
|
245
|
-
placeholder: string;
|
|
246
|
-
}) => React.ReactNode;
|
|
247
222
|
};
|
|
248
223
|
resultsGrid?: {
|
|
249
224
|
renderGrid?: (props: ResultsGridProps) => React.ReactNode;
|
|
@@ -334,7 +309,6 @@ export interface SimilarProductsSettings {
|
|
|
334
309
|
resultsPerPage?: number;
|
|
335
310
|
enableCropping?: boolean;
|
|
336
311
|
enableFileUpload?: boolean;
|
|
337
|
-
enableTextSearch?: boolean;
|
|
338
312
|
paginationType?: 'pagination' | 'load-more' | 'infinite-scroll';
|
|
339
313
|
loadMoreText?: string;
|
|
340
314
|
loadMoreStyle?: 'button' | 'auto';
|
|
@@ -355,22 +329,10 @@ export interface SimilarProductsSettings {
|
|
|
355
329
|
controlsContainer?: string;
|
|
356
330
|
controlsInner?: string;
|
|
357
331
|
controlsLeft?: string;
|
|
358
|
-
controlsCenter?: string;
|
|
359
332
|
controlsRight?: string;
|
|
360
333
|
itemCount?: string;
|
|
361
334
|
sortDropdown?: string;
|
|
362
335
|
filterToggleButton?: string;
|
|
363
|
-
modalSearchInput?: string;
|
|
364
|
-
modalSearchContainer?: string;
|
|
365
|
-
modalSearchButton?: string;
|
|
366
|
-
modalSearchIcon?: string;
|
|
367
|
-
modalSearchClearButton?: string;
|
|
368
|
-
modalSearchClearIcon?: string;
|
|
369
|
-
searchButtonIcon?: string;
|
|
370
|
-
modalCloseIcon?: string;
|
|
371
|
-
filterIcon?: string;
|
|
372
|
-
filterRemoveIcon?: string;
|
|
373
|
-
imageSearchButtonIcon?: string;
|
|
374
336
|
|
|
375
337
|
filterSidebarMobileHeader?: string;
|
|
376
338
|
filterSidebarMobileTitle?: string;
|
|
@@ -452,19 +414,6 @@ export interface SimilarProductsSettings {
|
|
|
452
414
|
mobileActiveFilterTag?: string;
|
|
453
415
|
mobileClearAllButton?: string;
|
|
454
416
|
filterSidebarMobileOverlay?: string;
|
|
455
|
-
|
|
456
|
-
textSearchContainer?: string;
|
|
457
|
-
textSearchLabel?: string;
|
|
458
|
-
textSearchInput?: string;
|
|
459
|
-
};
|
|
460
|
-
iconNames?: {
|
|
461
|
-
searchButton?: string;
|
|
462
|
-
modalClose?: string;
|
|
463
|
-
filter?: string;
|
|
464
|
-
filterRemove?: string;
|
|
465
|
-
modalSearch?: string;
|
|
466
|
-
modalSearchClear?: string;
|
|
467
|
-
imageSearchButton?: string;
|
|
468
417
|
};
|
|
469
418
|
customRenderers?: {
|
|
470
419
|
Modal?: React.ComponentType<SimilarProductsModalProps>;
|
package/src/utils/index.ts
CHANGED
|
@@ -7,7 +7,6 @@ export const defaultSettings: Required<SimilarProductsSettings> = {
|
|
|
7
7
|
resultsPerPage: 20,
|
|
8
8
|
enableCropping: true,
|
|
9
9
|
enableFileUpload: true,
|
|
10
|
-
enableTextSearch: false,
|
|
11
10
|
customStyles: {
|
|
12
11
|
modal: '',
|
|
13
12
|
filterSidebar: '',
|
|
@@ -16,31 +15,7 @@ export const defaultSettings: Required<SimilarProductsSettings> = {
|
|
|
16
15
|
productItem: '',
|
|
17
16
|
pagination: '',
|
|
18
17
|
filterGroup: '',
|
|
19
|
-
imageSection: ''
|
|
20
|
-
textSearchContainer: '',
|
|
21
|
-
textSearchLabel: '',
|
|
22
|
-
textSearchInput: '',
|
|
23
|
-
controlsCenter: '',
|
|
24
|
-
modalSearchInput: '',
|
|
25
|
-
modalSearchContainer: '',
|
|
26
|
-
modalSearchButton: '',
|
|
27
|
-
modalSearchIcon: '',
|
|
28
|
-
modalSearchClearButton: '',
|
|
29
|
-
modalSearchClearIcon: '',
|
|
30
|
-
searchButtonIcon: '',
|
|
31
|
-
modalCloseIcon: '',
|
|
32
|
-
filterIcon: '',
|
|
33
|
-
filterRemoveIcon: '',
|
|
34
|
-
imageSearchButtonIcon: ''
|
|
35
|
-
},
|
|
36
|
-
iconNames: {
|
|
37
|
-
searchButton: 'search',
|
|
38
|
-
modalClose: 'close',
|
|
39
|
-
filter: 'filter',
|
|
40
|
-
filterRemove: 'close',
|
|
41
|
-
modalSearch: 'search',
|
|
42
|
-
modalSearchClear: 'close',
|
|
43
|
-
imageSearchButton: 'search'
|
|
18
|
+
imageSection: ''
|
|
44
19
|
},
|
|
45
20
|
customRenderers: {
|
|
46
21
|
render: {}
|
|
@@ -68,10 +43,6 @@ export function mergeSettings(
|
|
|
68
43
|
...defaultSettings.customStyles,
|
|
69
44
|
...userSettings.customStyles
|
|
70
45
|
},
|
|
71
|
-
iconNames: {
|
|
72
|
-
...defaultSettings.iconNames,
|
|
73
|
-
...userSettings.iconNames
|
|
74
|
-
},
|
|
75
46
|
customRenderers: {
|
|
76
47
|
...defaultSettings.customRenderers,
|
|
77
48
|
...userSettings.customRenderers,
|
|
@@ -129,19 +100,13 @@ export function validateImageFile(
|
|
|
129
100
|
export function debounce<T extends (...args: any[]) => any>(
|
|
130
101
|
func: T,
|
|
131
102
|
wait: number
|
|
132
|
-
): (
|
|
103
|
+
): (...args: Parameters<T>) => void {
|
|
133
104
|
let timeout: NodeJS.Timeout;
|
|
134
105
|
|
|
135
|
-
|
|
106
|
+
return (...args: Parameters<T>) => {
|
|
136
107
|
clearTimeout(timeout);
|
|
137
108
|
timeout = setTimeout(() => func(...args), wait);
|
|
138
109
|
};
|
|
139
|
-
|
|
140
|
-
debounced.cancel = () => {
|
|
141
|
-
clearTimeout(timeout);
|
|
142
|
-
};
|
|
143
|
-
|
|
144
|
-
return debounced as ((...args: Parameters<T>) => void) & { cancel: () => void };
|
|
145
110
|
}
|
|
146
111
|
|
|
147
112
|
export function dataURLToBlob(dataURL: string): Blob {
|
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
|
))}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
|
-
import React, { useState } from 'react';
|
|
3
|
+
import React, { useState, useRef } from 'react';
|
|
4
4
|
import { Product } from '@akinon/next/types';
|
|
5
|
+
import { useImageSearchFeature } from '../hooks/use-image-search-feature';
|
|
5
6
|
import { ImageSearchButton } from './image-search-button';
|
|
6
7
|
import { SimilarProductsPlugin } from './main';
|
|
7
8
|
|
|
@@ -9,25 +10,27 @@ interface HeaderImageSearchFeatureProps {
|
|
|
9
10
|
className?: string;
|
|
10
11
|
isEnabled?: boolean;
|
|
11
12
|
settings?: any;
|
|
12
|
-
enableTextSearch?: boolean;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
export function HeaderImageSearchFeature({
|
|
16
16
|
className,
|
|
17
|
-
isEnabled
|
|
18
|
-
settings
|
|
19
|
-
enableTextSearch = false
|
|
17
|
+
isEnabled: isEnabledProp,
|
|
18
|
+
settings
|
|
20
19
|
}: HeaderImageSearchFeatureProps) {
|
|
20
|
+
const { isEnabled: hookIsEnabled, isLoading } = useImageSearchFeature();
|
|
21
|
+
|
|
22
|
+
const envEnabled = process.env.NEXT_PUBLIC_ENABLE_IMAGE_SEARCH === 'true';
|
|
23
|
+
const finalIsEnabled = envEnabled
|
|
24
|
+
? true
|
|
25
|
+
: isEnabledProp !== undefined
|
|
26
|
+
? isEnabledProp
|
|
27
|
+
: hookIsEnabled;
|
|
28
|
+
|
|
21
29
|
const [isImageSearchModalOpen, setIsImageSearchModalOpen] = useState(false);
|
|
22
30
|
const [isResultsModalOpen, setIsResultsModalOpen] = useState(false);
|
|
23
31
|
const [uploadedImageFile, setUploadedImageFile] = useState<File | null>(null);
|
|
24
32
|
|
|
25
|
-
|
|
26
|
-
...userSettings,
|
|
27
|
-
enableTextSearch
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
if (!isEnabled) {
|
|
33
|
+
if (isLoading || !finalIsEnabled) {
|
|
31
34
|
return null;
|
|
32
35
|
}
|
|
33
36
|
|
|
@@ -52,23 +55,26 @@ export function HeaderImageSearchFeature({
|
|
|
52
55
|
|
|
53
56
|
return (
|
|
54
57
|
<>
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
58
|
+
{finalIsEnabled && (
|
|
59
|
+
<>
|
|
60
|
+
<ImageSearchButton
|
|
61
|
+
onClick={handleOpenImageSearch}
|
|
62
|
+
className={className}
|
|
63
|
+
/>
|
|
64
|
+
<SimilarProductsPlugin
|
|
65
|
+
product={{} as Product}
|
|
66
|
+
isOpen={isResultsModalOpen}
|
|
67
|
+
onClose={handleResultsModalClose}
|
|
68
|
+
activeIndex={0}
|
|
69
|
+
showImageSearchModal={isImageSearchModalOpen}
|
|
70
|
+
onImageSearchModalClose={handleImageSearchModalClose}
|
|
71
|
+
uploadedImageFile={uploadedImageFile}
|
|
72
|
+
onImageUpload={handleImageUpload}
|
|
73
|
+
showResetButton={false}
|
|
74
|
+
settings={settings}
|
|
75
|
+
/>
|
|
76
|
+
</>
|
|
77
|
+
)}
|
|
72
78
|
</>
|
|
73
79
|
);
|
|
74
80
|
}
|