@akinon/pz-similar-products 1.113.0-rc.19 → 1.113.0-snapshot-ZERO-3878-20251203130841

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.
@@ -1,4 +1,4 @@
1
- import { useState, useEffect, useCallback } from 'react';
1
+ import { useState, useEffect, useCallback, useRef } from 'react';
2
2
  import { Product } from '@akinon/next/types';
3
3
  import {
4
4
  useLazyGetSimilarProductsByUrlQuery,
@@ -13,6 +13,7 @@ import {
13
13
  validateImageFromDataUrl,
14
14
  type ImageValidationResult
15
15
  } from '../utils/image-validation';
16
+ import { debounce } from '../utils';
16
17
 
17
18
  type SearchResults = SimilarProductsListResponse;
18
19
 
@@ -24,6 +25,7 @@ export function useSimilarProducts(product: Product) {
24
25
  const { t } = useLocalization();
25
26
  const [currentImageUrl, setCurrentImageUrl] = useState('');
26
27
  const [fileError, setFileError] = useState('');
28
+ const [searchText, setSearchText] = useState('');
27
29
  const [searchResults, setSearchResults] = useState<SearchResults | null>(
28
30
  null
29
31
  );
@@ -33,6 +35,16 @@ export function useSimilarProducts(product: Product) {
33
35
  const [isCropProcessing, setIsCropProcessing] = useState(false);
34
36
  const [loadedPages, setLoadedPages] = useState<Set<number>>(new Set([1]));
35
37
  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
+ }, []);
36
48
 
37
49
  const [fetchSimilarProductsByUrl, { isLoading: isUrlSearchLoading }] =
38
50
  useLazyGetSimilarProductsByUrlQuery();
@@ -426,17 +438,24 @@ export function useSimilarProducts(product: Product) {
426
438
  );
427
439
 
428
440
  const fetchSimilarProductsByImageUrl = useCallback(
429
- async (imageUrl: string) => {
441
+ async (imageUrl: string, overrideText?: string) => {
430
442
  setFileError('');
443
+
431
444
  try {
432
445
  const productPk = product?.pk;
433
446
  const excludedIds = productPk ? [productPk] : undefined;
434
447
 
435
- const result = await fetchSimilarProductsByUrl({
448
+ const textToUse =
449
+ overrideText !== undefined ? overrideText : searchTextRef.current;
450
+
451
+ const requestParams = {
436
452
  url: imageUrl,
437
453
  limit: 20,
438
- excluded_product_ids: excludedIds
439
- }).unwrap();
454
+ excluded_product_ids: excludedIds,
455
+ text: textToUse || undefined
456
+ };
457
+
458
+ const result = await fetchSimilarProductsByUrl(requestParams).unwrap();
440
459
 
441
460
  await handleSearchResults(result);
442
461
  return result;
@@ -450,6 +469,8 @@ export function useSimilarProducts(product: Product) {
450
469
  [
451
470
  fetchSimilarProductsByUrl,
452
471
  product?.pk,
472
+ searchText,
473
+ searchTextRef,
453
474
  handleSearchResults,
454
475
  updateResultsAndKey,
455
476
  createEmptySearchResults,
@@ -462,31 +483,62 @@ export function useSimilarProducts(product: Product) {
462
483
  if (product?.productimage_set?.length > 0) {
463
484
  const initialImageUrl = product.productimage_set[0].image;
464
485
  setCurrentImageUrl(initialImageUrl);
486
+ setHasCroppedImage(false);
487
+ setCurrentImageBase64('');
488
+ setHasUploadedImage(false);
489
+ hasUploadedImageRef.current = false;
465
490
  }
466
491
  }, [product]);
467
492
 
468
493
  const fetchSimilarProductsByBase64 = useCallback(
469
- async (base64Image: string) => {
494
+ async (
495
+ base64Image: string,
496
+ overrideText?: string,
497
+ forceExclude?: boolean
498
+ ) => {
470
499
  const base64Data = base64Image.startsWith('data:')
471
500
  ? base64Image.split(',')[1]
472
501
  : base64Image;
473
502
 
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
+
474
520
  return handleImageSearch(
475
- async () =>
476
- getSimilarProductsByImage({
477
- image: base64Data,
478
- limit: 20
479
- }).unwrap(),
521
+ async () => getSimilarProductsByImage(requestData).unwrap(),
480
522
  base64Image,
481
523
  'Image search'
482
524
  );
483
525
  },
484
- [getSimilarProductsByImage, handleImageSearch]
526
+ [
527
+ getSimilarProductsByImage,
528
+ searchText,
529
+ searchTextRef,
530
+ handleImageSearch,
531
+ hasUploadedImage,
532
+ product?.pk
533
+ ]
485
534
  );
486
535
 
487
536
  const fetchSimilarProductsByImageCrop = useCallback(
488
- async (dataString: string) => {
537
+ async (dataString: string, excludeCurrentProduct: boolean = true) => {
489
538
  setFileError('');
539
+
540
+ setCurrentImageBase64(dataString);
541
+ setHasCroppedImage(true);
490
542
  if (dataString.startsWith('data:application/x-cors-fallback;base64,')) {
491
543
  try {
492
544
  const fallbackDataEncoded = dataString.replace(
@@ -549,9 +601,12 @@ export function useSimilarProducts(product: Product) {
549
601
  getSimilarProductsByImage({
550
602
  image: base64Data,
551
603
  limit: 20,
552
- excluded_product_ids: product?.pk
553
- ? [product.pk]
554
- : undefined
604
+ excluded_product_ids: excludeCurrentProduct
605
+ ? product?.pk
606
+ ? [product.pk]
607
+ : undefined
608
+ : undefined,
609
+ text: searchTextRef.current || undefined
555
610
  }).unwrap(),
556
611
  croppedBase64,
557
612
  'Proxy crop search'
@@ -601,7 +656,12 @@ export function useSimilarProducts(product: Product) {
601
656
  getSimilarProductsByImage({
602
657
  image: base64Data,
603
658
  limit: 20,
604
- excluded_product_ids: product?.pk ? [product.pk] : undefined
659
+ excluded_product_ids: excludeCurrentProduct
660
+ ? product?.pk
661
+ ? [product.pk]
662
+ : undefined
663
+ : undefined,
664
+ text: searchTextRef.current || undefined
605
665
  }).unwrap(),
606
666
  dataString,
607
667
  'Image crop search'
@@ -609,11 +669,14 @@ export function useSimilarProducts(product: Product) {
609
669
  },
610
670
  [
611
671
  getSimilarProductsByImage,
672
+ searchText,
673
+ searchTextRef,
612
674
  handleImageSearch,
613
675
  product,
614
676
  fetchSimilarProductsByImageUrl,
615
677
  setFileError,
616
- t
678
+ t,
679
+ hasUploadedImage
617
680
  ]
618
681
  );
619
682
 
@@ -624,6 +687,8 @@ export function useSimilarProducts(product: Product) {
624
687
  const file = event.target.files?.[0];
625
688
  if (!file) return;
626
689
 
690
+ setSearchTextWithRef('');
691
+
627
692
  try {
628
693
  let processedFile = file;
629
694
 
@@ -650,6 +715,9 @@ export function useSimilarProducts(product: Product) {
650
715
  const dataUrl = e.target?.result as string;
651
716
  setCurrentImageUrl(dataUrl);
652
717
  setHasUploadedImage(true);
718
+ hasUploadedImageRef.current = true;
719
+ setHasCroppedImage(false);
720
+ setCurrentImageBase64('');
653
721
 
654
722
  const metadataValidation = await validateImageFromDataUrl(dataUrl);
655
723
  if (!metadataValidation.isValid) {
@@ -658,7 +726,7 @@ export function useSimilarProducts(product: Product) {
658
726
  );
659
727
  return;
660
728
  }
661
- fetchSimilarProductsByBase64(dataUrl);
729
+ fetchSimilarProductsByBase64(dataUrl, undefined, false);
662
730
  };
663
731
 
664
732
  reader.onerror = () =>
@@ -679,6 +747,8 @@ export function useSimilarProducts(product: Product) {
679
747
 
680
748
  try {
681
749
  setCurrentImageUrl(base64Image);
750
+ setCurrentImageBase64(base64Image);
751
+ setHasCroppedImage(true);
682
752
 
683
753
  const metadataValidation = await validateImageFromDataUrl(base64Image);
684
754
  if (!metadataValidation.isValid) {
@@ -688,10 +758,14 @@ export function useSimilarProducts(product: Product) {
688
758
  return;
689
759
  }
690
760
 
691
- if (hasUploadedImage) {
692
- await fetchSimilarProductsByBase64(base64Image);
761
+ if (hasUploadedImageRef.current) {
762
+ await fetchSimilarProductsByBase64(
763
+ base64Image,
764
+ searchTextRef.current,
765
+ false
766
+ );
693
767
  } else {
694
- await fetchSimilarProductsByImageCrop(base64Image);
768
+ await fetchSimilarProductsByImageCrop(base64Image, true);
695
769
  }
696
770
  } catch (error) {
697
771
  setFileError(t('common.similar_products.errors.crop_processing_error'));
@@ -882,6 +956,11 @@ export function useSimilarProducts(product: Product) {
882
956
  setLoadedPages(new Set([1]));
883
957
  }, []);
884
958
 
959
+ const resetCropState = useCallback(() => {
960
+ setHasCroppedImage(false);
961
+ setCurrentImageBase64('');
962
+ }, []);
963
+
885
964
  const clearFileInput = useCallback(
886
965
  (fileInputRef: React.RefObject<HTMLInputElement>) => {
887
966
  if (fileInputRef.current) {
@@ -891,6 +970,47 @@ export function useSimilarProducts(product: Product) {
891
970
  []
892
971
  );
893
972
 
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
+
894
1014
  const handleLoadMore = async () => {
895
1015
  if (!searchResults?.pagination || isLoading) return;
896
1016
 
@@ -931,6 +1051,8 @@ export function useSimilarProducts(product: Product) {
931
1051
  setCurrentImageUrl,
932
1052
  isLoading,
933
1053
  fileError,
1054
+ searchText,
1055
+ setSearchText: setSearchTextWithRef,
934
1056
  searchResults,
935
1057
  resultsKey,
936
1058
  hasUploadedImage,
@@ -949,6 +1071,9 @@ export function useSimilarProducts(product: Product) {
949
1071
  allLoadedProducts,
950
1072
  clearError,
951
1073
  clearResults,
952
- clearFileInput
1074
+ clearFileInput,
1075
+ handleTextSearch,
1076
+ handleClearText,
1077
+ resetCropState
953
1078
  };
954
1079
  }
@@ -58,6 +58,10 @@ 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;
61
65
  }
62
66
 
63
67
  export interface FilterSidebarProps {
@@ -67,6 +71,8 @@ export interface FilterSidebarProps {
67
71
  isLoading: boolean;
68
72
  handleFacetChange: (facetKey: string, choiceValue: string | number) => void;
69
73
  removeFacetFilter: (facetKey: string, choiceValue: string | number) => void;
74
+ searchText?: string;
75
+ setSearchText?: (text: string) => void;
70
76
  currentImageUrl: string;
71
77
  isCropping: boolean;
72
78
  imageRef: React.RefObject<HTMLImageElement>;
@@ -126,6 +132,8 @@ export interface CustomRendererProps {
126
132
  onSortChange: (value: string) => void;
127
133
  onFilterMenuToggle: () => void;
128
134
  isLoading: boolean;
135
+ searchText?: string;
136
+ setSearchText?: (text: string) => void;
129
137
  }) => React.ReactNode;
130
138
  renderSortDropdown?: (props: {
131
139
  sorters: SortOption[];
@@ -141,6 +149,17 @@ export interface CustomRendererProps {
141
149
  onClick: () => void;
142
150
  isLoading: boolean;
143
151
  }) => 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;
144
163
  renderEmptyState?: () => React.ReactNode;
145
164
  };
146
165
  filterSidebar?: {
@@ -219,6 +238,12 @@ export interface CustomRendererProps {
219
238
  onClearAll: () => void;
220
239
  isLoading: boolean;
221
240
  }) => React.ReactNode;
241
+ renderTextSearch?: (props: {
242
+ searchText: string;
243
+ setSearchText: (text: string) => void;
244
+ isLoading: boolean;
245
+ placeholder: string;
246
+ }) => React.ReactNode;
222
247
  };
223
248
  resultsGrid?: {
224
249
  renderGrid?: (props: ResultsGridProps) => React.ReactNode;
@@ -309,6 +334,7 @@ export interface SimilarProductsSettings {
309
334
  resultsPerPage?: number;
310
335
  enableCropping?: boolean;
311
336
  enableFileUpload?: boolean;
337
+ enableTextSearch?: boolean;
312
338
  paginationType?: 'pagination' | 'load-more' | 'infinite-scroll';
313
339
  loadMoreText?: string;
314
340
  loadMoreStyle?: 'button' | 'auto';
@@ -329,10 +355,22 @@ export interface SimilarProductsSettings {
329
355
  controlsContainer?: string;
330
356
  controlsInner?: string;
331
357
  controlsLeft?: string;
358
+ controlsCenter?: string;
332
359
  controlsRight?: string;
333
360
  itemCount?: string;
334
361
  sortDropdown?: string;
335
362
  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;
336
374
 
337
375
  filterSidebarMobileHeader?: string;
338
376
  filterSidebarMobileTitle?: string;
@@ -414,6 +452,19 @@ export interface SimilarProductsSettings {
414
452
  mobileActiveFilterTag?: string;
415
453
  mobileClearAllButton?: string;
416
454
  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;
417
468
  };
418
469
  customRenderers?: {
419
470
  Modal?: React.ComponentType<SimilarProductsModalProps>;
@@ -7,6 +7,7 @@ export const defaultSettings: Required<SimilarProductsSettings> = {
7
7
  resultsPerPage: 20,
8
8
  enableCropping: true,
9
9
  enableFileUpload: true,
10
+ enableTextSearch: false,
10
11
  customStyles: {
11
12
  modal: '',
12
13
  filterSidebar: '',
@@ -15,7 +16,31 @@ export const defaultSettings: Required<SimilarProductsSettings> = {
15
16
  productItem: '',
16
17
  pagination: '',
17
18
  filterGroup: '',
18
- imageSection: ''
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'
19
44
  },
20
45
  customRenderers: {
21
46
  render: {}
@@ -43,6 +68,10 @@ export function mergeSettings(
43
68
  ...defaultSettings.customStyles,
44
69
  ...userSettings.customStyles
45
70
  },
71
+ iconNames: {
72
+ ...defaultSettings.iconNames,
73
+ ...userSettings.iconNames
74
+ },
46
75
  customRenderers: {
47
76
  ...defaultSettings.customRenderers,
48
77
  ...userSettings.customRenderers,
@@ -100,13 +129,19 @@ export function validateImageFile(
100
129
  export function debounce<T extends (...args: any[]) => any>(
101
130
  func: T,
102
131
  wait: number
103
- ): (...args: Parameters<T>) => void {
132
+ ): ((...args: Parameters<T>) => void) & { cancel: () => void } {
104
133
  let timeout: NodeJS.Timeout;
105
134
 
106
- return (...args: Parameters<T>) => {
135
+ const debounced = (...args: Parameters<T>) => {
107
136
  clearTimeout(timeout);
108
137
  timeout = setTimeout(() => func(...args), wait);
109
138
  };
139
+
140
+ debounced.cancel = () => {
141
+ clearTimeout(timeout);
142
+ };
143
+
144
+ return debounced as ((...args: Parameters<T>) => void) & { cancel: () => void };
110
145
  }
111
146
 
112
147
  export function dataURLToBlob(dataURL: string): Blob {
@@ -5,7 +5,8 @@ import {
5
5
  Button,
6
6
  Icon,
7
7
  Accordion,
8
- LoaderSpinner
8
+ LoaderSpinner,
9
+ Input
9
10
  } from '@akinon/next/components';
10
11
  import { useLocalization } from '@akinon/next/hooks';
11
12
  import { FilterSidebarProps } from '../types';
@@ -55,6 +56,8 @@ export function SimilarProductsFilterSidebar({
55
56
  isLoading,
56
57
  handleFacetChange,
57
58
  removeFacetFilter,
59
+ searchText,
60
+ setSearchText,
58
61
  currentImageUrl,
59
62
  isCropping,
60
63
  imageRef,
@@ -166,6 +169,19 @@ export function SimilarProductsFilterSidebar({
166
169
  className
167
170
  );
168
171
 
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
+
169
185
  return (
170
186
  <>
171
187
  {isFilterMenuOpen && (
@@ -222,7 +238,11 @@ export function SimilarProductsFilterSidebar({
222
238
  settings?.customStyles?.filterSidebarMobileCloseButton
223
239
  )}
224
240
  >
225
- <Icon name="close" size={16} />
241
+ <Icon
242
+ name={modalCloseIconName}
243
+ size={16}
244
+ className={modalCloseIconClassName}
245
+ />
226
246
  </Button>
227
247
  </div>
228
248
  <div
@@ -928,7 +948,11 @@ export function SimilarProductsFilterSidebar({
928
948
  disabled={isLoading}
929
949
  className="hover:bg-gray-200 rounded-full p-0 w-5 h-5 disabled:opacity-50 disabled:cursor-not-allowed ml-1"
930
950
  >
931
- <Icon name="close" size={10} />
951
+ <Icon
952
+ name={filterRemoveIconName}
953
+ size={10}
954
+ className={filterRemoveIconClassName}
955
+ />
932
956
  </Button>
933
957
  </div>
934
958
  ))}
@@ -1,8 +1,7 @@
1
1
  'use client';
2
2
 
3
- import React, { useState, useRef } from 'react';
3
+ import React, { useState } from 'react';
4
4
  import { Product } from '@akinon/next/types';
5
- import { useImageSearchFeature } from '../hooks/use-image-search-feature';
6
5
  import { ImageSearchButton } from './image-search-button';
7
6
  import { SimilarProductsPlugin } from './main';
8
7
 
@@ -10,27 +9,25 @@ interface HeaderImageSearchFeatureProps {
10
9
  className?: string;
11
10
  isEnabled?: boolean;
12
11
  settings?: any;
12
+ enableTextSearch?: boolean;
13
13
  }
14
14
 
15
15
  export function HeaderImageSearchFeature({
16
16
  className,
17
- isEnabled: isEnabledProp,
18
- settings
17
+ isEnabled = false,
18
+ settings: userSettings,
19
+ enableTextSearch = false
19
20
  }: 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
-
29
21
  const [isImageSearchModalOpen, setIsImageSearchModalOpen] = useState(false);
30
22
  const [isResultsModalOpen, setIsResultsModalOpen] = useState(false);
31
23
  const [uploadedImageFile, setUploadedImageFile] = useState<File | null>(null);
32
24
 
33
- if (isLoading || !finalIsEnabled) {
25
+ const settings = {
26
+ ...userSettings,
27
+ enableTextSearch
28
+ };
29
+
30
+ if (!isEnabled) {
34
31
  return null;
35
32
  }
36
33
 
@@ -55,26 +52,23 @@ export function HeaderImageSearchFeature({
55
52
 
56
53
  return (
57
54
  <>
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
- )}
55
+ <ImageSearchButton
56
+ onClick={handleOpenImageSearch}
57
+ className={className}
58
+ settings={settings}
59
+ />
60
+ <SimilarProductsPlugin
61
+ product={{} as Product}
62
+ isOpen={isResultsModalOpen}
63
+ onClose={handleResultsModalClose}
64
+ activeIndex={0}
65
+ showImageSearchModal={isImageSearchModalOpen}
66
+ onImageSearchModalClose={handleImageSearchModalClose}
67
+ uploadedImageFile={uploadedImageFile}
68
+ onImageUpload={handleImageUpload}
69
+ showResetButton={false}
70
+ settings={settings}
71
+ />
78
72
  </>
79
73
  );
80
74
  }