@nyris/nyris-webapp 0.3.96 → 0.3.98

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/build/asset-manifest.json +6 -6
  2. package/build/index.html +1 -1
  3. package/build/js/settings.example.js +7 -1
  4. package/build/static/css/main.81216c5d.css +4 -0
  5. package/build/static/css/main.81216c5d.css.map +1 -0
  6. package/build/static/js/main.93fdbd6e.js +3 -0
  7. package/build/static/js/{main.fd12cce3.js.map → main.93fdbd6e.js.map} +1 -1
  8. package/package.json +3 -3
  9. package/public/js/settings.example.js +7 -1
  10. package/src/components/AuthApiKeySync.tsx +34 -0
  11. package/src/components/Cart.tsx +200 -85
  12. package/src/components/ChatAssistant/useChatAssistantLogic.ts +3 -2
  13. package/src/components/Header.tsx +1 -1
  14. package/src/components/ImageUpload.tsx +2 -2
  15. package/src/components/Inquiry/InquiryModal.tsx +2 -2
  16. package/src/components/LogoutModal.tsx +2 -0
  17. package/src/components/PostFilter/PostFilter.tsx +36 -3
  18. package/src/components/PostFilter/PostFilterComponent.tsx +8 -22
  19. package/src/components/PostFilter/PostFilterFindApi.tsx +41 -3
  20. package/src/components/PreFilter/PreFilter.tsx +30 -2
  21. package/src/components/Product/Product.tsx +2 -2
  22. package/src/components/Product/ProductDetailView.tsx +2 -2
  23. package/src/components/Product/ProductList.tsx +2 -0
  24. package/src/components/ScopeMissing.tsx +50 -0
  25. package/src/components/SidePanel.tsx +10 -0
  26. package/src/components/SmartFilter.tsx +59 -69
  27. package/src/components/TextSearch.tsx +6 -4
  28. package/src/hooks/useImageSearch.ts +44 -9
  29. package/src/hooks/useResultEvaluator.ts +8 -8
  30. package/src/index.tsx +4 -0
  31. package/src/layouts/AppLayout.tsx +77 -10
  32. package/src/pages/Logout.tsx +2 -0
  33. package/src/pages/Result.tsx +14 -7
  34. package/src/providers/AuthProvider.tsx +5 -0
  35. package/src/routes/ProctedRoute.tsx +52 -9
  36. package/src/services/Feedback.ts +2 -1
  37. package/src/services/filter.ts +16 -2
  38. package/src/services/visualSearch.ts +15 -5
  39. package/src/services/vizo.ts +13 -2
  40. package/src/stores/scope/scopeStore.ts +35 -0
  41. package/src/styles/Cart.scss +22 -0
  42. package/src/types.ts +6 -1
  43. package/src/utils/derivePostFilterFacets.ts +68 -0
  44. package/src/utils/nyrisApiKey.ts +115 -0
  45. package/build/static/css/main.5fff4ca0.css +0 -4
  46. package/build/static/css/main.5fff4ca0.css.map +0 -1
  47. package/build/static/js/main.fd12cce3.js +0 -3
  48. /package/build/static/js/{main.fd12cce3.js.LICENSE.txt → main.93fdbd6e.js.LICENSE.txt} +0 -0
package/package.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@nyris/nyris-webapp",
3
- "version": "0.3.96",
3
+ "version": "0.3.98",
4
4
  "dependencies": {
5
5
  "@auth0/auth0-react": "^2.2.4",
6
6
  "@emailjs/browser": "^4.3.3",
7
7
  "@google/genai": "^1.46.0",
8
- "@nyris/nyris-api": "^0.3.96",
9
- "@nyris/nyris-react-components": "^0.3.96",
8
+ "@nyris/nyris-api": "^0.3.98",
9
+ "@nyris/nyris-react-components": "^0.3.98",
10
10
  "@radix-ui/react-accordion": "^1.2.2",
11
11
  "@radix-ui/react-dialog": "^1.1.4",
12
12
  "@radix-ui/react-popover": "^1.1.4",
@@ -23,6 +23,7 @@ var settings = {
23
23
  },
24
24
  ],
25
25
  // Nyris - visual search
26
+ // When auth0.enabled is true, leave apiKey empty — the app uses Auth0 claim
26
27
  apiKey: '',
27
28
  aiApiKey: '',
28
29
  baseUrl: 'https://api.nyris.io',
@@ -97,7 +98,11 @@ var settings = {
97
98
  },
98
99
  ],
99
100
  // features
100
- cart: false,
101
+ cart: {
102
+ enabled: true,
103
+ buttonText: 'Add to cart',
104
+ emailTemplateId: 'template_h3rt2pi',
105
+ },
101
106
  showPoweredByNyris: '',
102
107
  postFilterOption: '',
103
108
  preFilterOption: '',
@@ -115,6 +120,7 @@ var settings = {
115
120
  enabled: '',
116
121
  emailTemplateId: '',
117
122
  },
123
+ scopeFilter: true,
118
124
  support: {
119
125
  enabled: '',
120
126
  emailInquiry: '',
@@ -0,0 +1,34 @@
1
+ import { useAuth0 } from '@auth0/auth0-react';
2
+ import { useEffect } from 'react';
3
+ import {
4
+ applyAuthApiKey,
5
+ clearAuthApiKey,
6
+ readAuthApiKeyFromUser,
7
+ } from 'utils/nyrisApiKey';
8
+
9
+ /**
10
+ * Keeps window.settings.apiKey in sync with Auth0 claim
11
+ * `https://nyris.io/authApiKey`.
12
+ * Must only render inside Auth0Provider when auth0.enabled is true.
13
+ */
14
+ export function AuthApiKeySync() {
15
+ const { user, isAuthenticated, isLoading } = useAuth0();
16
+
17
+ useEffect(() => {
18
+ if (isLoading) {
19
+ return;
20
+ }
21
+
22
+ if (!isAuthenticated || !user) {
23
+ clearAuthApiKey();
24
+ return;
25
+ }
26
+
27
+ const authApiKey = readAuthApiKeyFromUser(
28
+ user as Record<string, unknown>,
29
+ );
30
+ applyAuthApiKey(authApiKey);
31
+ }, [user, isAuthenticated, isLoading]);
32
+
33
+ return null;
34
+ }
@@ -4,6 +4,14 @@ import Tooltip from './Tooltip/TooltipComponent';
4
4
  import { twMerge } from 'tailwind-merge';
5
5
  import toast from 'react-hot-toast';
6
6
  import { Icon } from '@nyris/nyris-react-components';
7
+ import { Send } from 'lucide-react';
8
+ import emailjs from '@emailjs/browser';
9
+ import { useAuth0 } from '@auth0/auth0-react';
10
+ import NoImage from '../common/assets/images/no-image.svg';
11
+
12
+ const EMAILJS_SERVICE_ID = 'service_zfsxshi';
13
+ const EMAILJS_TEMPLATE_ID = window.settings.cart?.emailTemplateId || '';
14
+ const EMAILJS_PUBLIC_KEY = 'SMGihPnuEGcYLm0V4';
7
15
 
8
16
  const Cart = () => {
9
17
  const [isCartOpen, setIsCartOpen] = useState(false);
@@ -13,6 +21,13 @@ const Cart = () => {
13
21
  {},
14
22
  );
15
23
 
24
+ const { user } = useAuth0();
25
+ const auth0Email = window.settings.auth0?.enabled ? user?.email : undefined;
26
+
27
+ const [isSendingEmail, setIsSendingEmail] = useState(false);
28
+ const [isEmailInputVisible, setIsEmailInputVisible] = useState(false);
29
+ const [userEmail, setUserEmail] = useState('');
30
+
16
31
  const updateCount = () => {
17
32
  const cart = sessionStorage.getItem('cart');
18
33
  if (cart) {
@@ -111,6 +126,48 @@ const Cart = () => {
111
126
  toast.success('Your data has been downloaded.');
112
127
  };
113
128
 
129
+ const sendCSVByEmail = async (
130
+ e?: React.FormEvent,
131
+ emailOverride?: string,
132
+ ) => {
133
+ if (e) {
134
+ e.preventDefault();
135
+ }
136
+ const emailToSend = emailOverride ?? userEmail;
137
+ if (!emailToSend || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(emailToSend)) {
138
+ toast.error('Please enter a valid email address.');
139
+ return;
140
+ }
141
+
142
+ setIsSendingEmail(true);
143
+ try {
144
+ const csvContent = generateCSVContent();
145
+ // Use btoa to encode the CSV string to base64 directly as recommended by EmailJS
146
+ const base64Content = btoa(unescape(encodeURIComponent(csvContent)));
147
+
148
+ await emailjs.send(
149
+ EMAILJS_SERVICE_ID,
150
+ EMAILJS_TEMPLATE_ID,
151
+ {
152
+ email_id: emailToSend,
153
+ // machine_name: getMachineName(),
154
+ date: new Date().toLocaleDateString(),
155
+ item_count: amountOfParts,
156
+ content: base64Content,
157
+ },
158
+ EMAILJS_PUBLIC_KEY,
159
+ );
160
+
161
+ toast.success('Parts list has been sent by email.');
162
+ setIsEmailInputVisible(false);
163
+ setUserEmail('');
164
+ } catch (error) {
165
+ toast.error('Failed to send the parts list by email.');
166
+ } finally {
167
+ setIsSendingEmail(false);
168
+ }
169
+ };
170
+
114
171
  return (
115
172
  <>
116
173
  <Tooltip
@@ -211,103 +268,113 @@ const Cart = () => {
211
268
  ) : (
212
269
  ''
213
270
  )}
214
- {cartParts.map((part: any, i: number) => (
215
- <div
216
- key={part.sku + i}
217
- className={twMerge(
218
- 'flex h-fit w-full items-center justify-between rounded-lg border border-solid border-[#DDDEE7] bg-[#F3F4F8]',
219
- 'p-2',
220
- 'gap-2',
221
- )}
222
- >
223
- <div className="flex flex-1 items-center">
224
- <img
225
- src={
226
- !window.settings.algolia.enabled
227
- ? part['image']
228
- : part['image(main_similarity)']
229
- ? part['image(main_similarity)']
230
- : part['main_image_link']
231
- }
232
- key={part.sku + i}
233
- alt={`product ${i}`}
234
- className="mr-2 h-[50px] w-[50px] bg-white object-contain"
235
- />
236
- <div>
271
+ {cartParts.map((part: any, i: number) => {
272
+ const imageSrc = !window.settings.algolia.enabled
273
+ ? part['image']
274
+ : part['image(main_similarity)'] ||
275
+ part['main_image_link'];
276
+
277
+ return (
278
+ <div
279
+ key={part.sku + i}
280
+ className={twMerge(
281
+ 'flex h-fit w-full items-center justify-between rounded-lg border border-solid border-[#DDDEE7] bg-[#F3F4F8]',
282
+ 'p-2',
283
+ 'gap-2',
284
+ )}
285
+ >
286
+ <div className="flex flex-1 items-center">
287
+ {!imageSrc ? (
288
+ <img
289
+ src={NoImage}
290
+ alt="image_item"
291
+ className="mr-2 h-[50px] w-[50px] object-contain"
292
+ />
293
+ ) : (
294
+ <img
295
+ src={imageSrc}
296
+ key={part.sku + i}
297
+ alt={`product ${i}`}
298
+ className="mr-2 h-[50px] w-[50px] bg-white object-contain"
299
+ />
300
+ )}
301
+
237
302
  <div>
238
- <div className="max-w-[175px] break-words text-[9px] font-normal">
239
- {part.sku}
240
- </div>
241
- <div className="max-w-[175px] break-words text-xs font-semibold">
242
- {part.title}
303
+ <div>
304
+ <div className="max-w-[175px] break-words text-[9px] font-normal">
305
+ {part.sku}
306
+ </div>
307
+ <div className="max-w-[175px] break-words text-xs font-semibold">
308
+ {part.title}
309
+ </div>
243
310
  </div>
244
311
  </div>
245
312
  </div>
246
- </div>
247
- <div className="flex items-center">
248
- <div className="ml-auto flex items-center gap-4 pr-4">
249
- <button
250
- className="h-4 w-4 rounded-full bg-[#FAFAFA]"
251
- onClick={() => updateQuantity(part.sku, -1)}
252
- >
253
- <Icon name="minus" width="16" height="16" />
254
- </button>
255
- <input
256
- className="flex h-[22px] w-[22px] items-center justify-center rounded border border-solid border-[#DDDEE7] bg-white text-center"
257
- value={getQuantityDisplayValue(
258
- part.sku,
259
- part.quantity,
260
- )}
261
- onChange={e => {
262
- const rawValue = e.target.value;
263
- if (rawValue === '') {
264
- setQuantityEdits(prev => ({
265
- ...prev,
266
- [part.sku]: '',
267
- }));
268
- return;
269
- }
313
+ <div className="flex items-center">
314
+ <div className="ml-auto flex items-center gap-4 pr-4">
315
+ <button
316
+ className="h-4 w-4 rounded-full bg-[#FAFAFA]"
317
+ onClick={() => updateQuantity(part.sku, -1)}
318
+ >
319
+ <Icon name="minus" width="16" height="16" />
320
+ </button>
321
+ <input
322
+ className="flex h-[22px] w-[22px] items-center justify-center rounded border border-solid border-[#DDDEE7] bg-white text-center"
323
+ value={getQuantityDisplayValue(
324
+ part.sku,
325
+ part.quantity,
326
+ )}
327
+ onChange={e => {
328
+ const rawValue = e.target.value;
329
+ if (rawValue === '') {
330
+ setQuantityEdits(prev => ({
331
+ ...prev,
332
+ [part.sku]: '',
333
+ }));
334
+ return;
335
+ }
270
336
 
271
- if (!/^[0-9]+$/.test(rawValue)) return;
337
+ if (!/^[0-9]+$/.test(rawValue)) return;
272
338
 
273
- const newValue = Number(rawValue);
274
- if (newValue < 1) return;
339
+ const newValue = Number(rawValue);
340
+ if (newValue < 1) return;
275
341
 
276
- setQuantityEdits(prev => ({
277
- ...prev,
278
- [part.sku]: rawValue,
279
- }));
280
- updateQuantity(
281
- part.sku,
282
- newValue - (part.quantity || 0),
283
- );
284
- }}
285
- onBlur={() => {
286
- if (quantityEdits[part.sku] === '') {
287
- setQuantityEdits(prev => {
288
- const updated = { ...prev };
289
- delete updated[part.sku];
290
- return updated;
291
- });
292
- }
293
- }}
294
- />
342
+ setQuantityEdits(prev => ({
343
+ ...prev,
344
+ [part.sku]: rawValue,
345
+ }));
346
+ updateQuantity(
347
+ part.sku,
348
+ newValue - (part.quantity || 0),
349
+ );
350
+ }}
351
+ onBlur={() => {
352
+ if (quantityEdits[part.sku] === '') {
353
+ setQuantityEdits(prev => {
354
+ const updated = { ...prev };
355
+ delete updated[part.sku];
356
+ return updated;
357
+ });
358
+ }
359
+ }}
360
+ />
361
+ <button
362
+ className="h-4 w-4 rounded-full bg-[#FAFAFA] flex items-center justify-center"
363
+ onClick={() => updateQuantity(part.sku, 1)}
364
+ >
365
+ <Icon name="plus" width="10" height="10" />
366
+ </button>
367
+ </div>
295
368
  <button
296
- className="h-4 w-4 rounded-full bg-[#FAFAFA] flex items-center justify-center"
297
- onClick={() => updateQuantity(part.sku, 1)}
369
+ className="h-7 w-7 rounded bg-[#FFE5EF4D] p-1.5"
370
+ onClick={() => removePartBySku(part.sku)}
298
371
  >
299
- <Icon name="plus" width="10" height="10" />
372
+ <Icon name="trash" color="#E31B5D" />
300
373
  </button>
301
374
  </div>
302
- <button
303
- className="h-7 w-7 rounded bg-[#FFE5EF4D] p-1.5"
304
- onClick={() => removePartBySku(part.sku)}
305
- >
306
- <Icon name="trash" color="#E31B5D" />
307
- </button>
308
375
  </div>
309
- </div>
310
- ))}
376
+ );
377
+ })}
311
378
  {amountOfParts > 0 ? (
312
379
  <button
313
380
  className="flex h-12 w-full items-center justify-center rounded-lg bg-[#3E36DC] text-base font-semibold text-white"
@@ -320,6 +387,54 @@ const Cart = () => {
320
387
  ) : (
321
388
  ''
322
389
  )}
390
+ {amountOfParts > 0 &&
391
+ window.settings.cart?.emailTemplateId ? (
392
+ auth0Email ? (
393
+ <button
394
+ className="mt-2 flex h-12 w-full items-center justify-center gap-2 rounded-lg border border-solid border-[#3E36DC] bg-white text-base font-semibold text-[#3E36DC] disabled:cursor-not-allowed disabled:opacity-60"
395
+ type="button"
396
+ onClick={() => sendCSVByEmail(undefined, auth0Email)}
397
+ disabled={isSendingEmail}
398
+ >
399
+ <Icon name="email" />
400
+ Send by email
401
+ </button>
402
+ ) : isEmailInputVisible ? (
403
+ <form
404
+ className="cart-email-form flex items-center relative mt-2 h-12 w-full"
405
+ onSubmit={sendCSVByEmail}
406
+ >
407
+ <input
408
+ type="email"
409
+ className="w-full h-full px-4 border border-[#CACAD1] rounded-lg pr-12 focus:outline-none focus:border-[#3e36dc]"
410
+ placeholder="Enter your email"
411
+ value={userEmail}
412
+ onChange={e => setUserEmail(e.target.value)}
413
+ disabled={isSendingEmail}
414
+ autoFocus
415
+ />
416
+ <button
417
+ type="submit"
418
+ className="absolute right-2 h-8 w-8 flex items-center justify-center rounded disabled:opacity-50"
419
+ disabled={isSendingEmail || !userEmail}
420
+ >
421
+ <Send className="w-5 h-5 " />
422
+ </button>
423
+ </form>
424
+ ) : (
425
+ <button
426
+ className="mt-2 flex h-12 w-full items-center justify-center gap-2 rounded-lg border border-solid border-[#3E36DC] bg-white text-base font-semibold text-[#3E36DC] disabled:cursor-not-allowed disabled:opacity-60"
427
+ type="button"
428
+ onClick={() => setIsEmailInputVisible(true)}
429
+ disabled={isSendingEmail}
430
+ >
431
+ <Icon name="email" />
432
+ Send by email
433
+ </button>
434
+ )
435
+ ) : (
436
+ ''
437
+ )}
323
438
  </div>
324
439
  </div>
325
440
  </div>
@@ -12,6 +12,7 @@ import {
12
12
  fetchProductImageBase64,
13
13
  getProductImageUrl,
14
14
  } from 'utils/fetchProductImage';
15
+ import { getEffectiveApiKey } from 'utils/nyrisApiKey';
15
16
 
16
17
  export function useChatAssistantLogic() {
17
18
  const [inputValue, setInputValue] = useState('');
@@ -482,7 +483,7 @@ Each result includes a unique SKU in brackets. Use the result thumbnails and met
482
483
  setCatalogSearching(true);
483
484
  try {
484
485
  const settings = (window as any).settings;
485
- const nyrisKey = settings?.apiKey || settings?.nyrisApiKey;
486
+ const nyrisKey = getEffectiveApiKey();
486
487
  const baseUrl = settings?.baseUrl || 'https://api.nyris.io';
487
488
 
488
489
  store.setPreCatalogResults([...activeProducts]);
@@ -580,7 +581,7 @@ Each result includes a unique SKU in brackets. Use the result thumbnails and met
580
581
 
581
582
  try {
582
583
  const settings = (window as any).settings;
583
- const nyrisKey = settings?.apiKey || settings?.nyrisApiKey;
584
+ const nyrisKey = getEffectiveApiKey();
584
585
  const baseUrl = settings?.baseUrl || 'https://api.nyris.io';
585
586
 
586
587
  let searchQuery = rawText;
@@ -164,7 +164,7 @@ function Header() {
164
164
  </div>
165
165
  </>
166
166
  )}
167
- {window.settings.cart && <Cart />}
167
+ {window.settings.cart?.enabled && <Cart />}
168
168
  </div>
169
169
 
170
170
  <LogoutModal
@@ -31,13 +31,13 @@ function ImageUpload({
31
31
 
32
32
  const showPreFilter = useMemo(() => {
33
33
  if (settings.shouldUseUserMetadata && user) {
34
- if (user['/user_metadata'].value) {
34
+ if (user['/user_metadata']?.value) {
35
35
  setMetaFilter(user['/user_metadata'].value);
36
36
  }
37
37
  }
38
38
 
39
39
  if (settings.shouldUseUserMetadata && user) {
40
- if (settings.preFilterOption && !user['/user_metadata'].value) {
40
+ if (settings.preFilterOption && !user['/user_metadata']?.value) {
41
41
  return true;
42
42
  }
43
43
  return false;
@@ -66,10 +66,10 @@ export default function InquiryModal({
66
66
  useEffect(() => {
67
67
  function omitKeys(obj: any, keys: string[]) {
68
68
  return Object.fromEntries(
69
- Object.entries(obj).filter(([key]) => !keys.includes(key)),
69
+ Object.entries(obj)?.filter(([key]) => !keys.includes(key)),
70
70
  );
71
71
  }
72
- const omittedSpecification = omitKeys(specifications, [
72
+ const omittedSpecification = omitKeys(specifications || {}, [
73
73
  'is_nameplate',
74
74
  'prefilter_value',
75
75
  ]);
@@ -1,5 +1,6 @@
1
1
  import { useAuth0 } from '@auth0/auth0-react';
2
2
  import { Dialog, DialogContent } from './Modal/Dialog';
3
+ import { clearAuthApiKey } from 'utils/nyrisApiKey';
3
4
 
4
5
  function LogoutModal({
5
6
  setShowLogoutModal,
@@ -41,6 +42,7 @@ function LogoutModal({
41
42
  cursor: 'pointer',
42
43
  }}
43
44
  onClick={() => {
45
+ clearAuthApiKey();
44
46
  logout({
45
47
  logoutParams: { returnTo: window.location.origin },
46
48
  });
@@ -1,4 +1,4 @@
1
- import { useEffect, useState } from 'react';
1
+ import { useEffect, useMemo, useState } from 'react';
2
2
  import {
3
3
  AccordionContent,
4
4
  AccordionItem,
@@ -7,6 +7,7 @@ import {
7
7
  import { useRefinementList } from 'react-instantsearch';
8
8
  import { Icon } from '@nyris/nyris-react-components';
9
9
  import { useTranslation } from 'react-i18next';
10
+ import useScopeStore from 'stores/scope/scopeStore';
10
11
 
11
12
  function PostFilter({
12
13
  attribute,
@@ -23,7 +24,11 @@ function PostFilter({
23
24
  }) {
24
25
  const [itemsLimit, setItemsLimit] = useState(10);
25
26
  const [searchInput, setSearchInput] = useState<string>('');
26
- const { items, refine, searchForItems } = useRefinementList({
27
+ const {
28
+ items: allItems,
29
+ refine,
30
+ searchForItems,
31
+ } = useRefinementList({
27
32
  attribute: attribute,
28
33
  operator: 'or',
29
34
  showMore: false,
@@ -32,6 +37,32 @@ function PostFilter({
32
37
 
33
38
  const { t } = useTranslation();
34
39
 
40
+ const scopeFilterEnabled = window.settings.scopeFilter;
41
+ const scopeFilterAttributes = useScopeStore(state => state.filterAttributes);
42
+
43
+ const scopedAllowedValues = useMemo(() => {
44
+ if (!scopeFilterEnabled || !window.settings.auth0.enabled) {
45
+ return null;
46
+ }
47
+ // todo: support multiple attributes in the future
48
+ const scopeAttribute = scopeFilterAttributes[0];
49
+ if (!scopeAttribute) {
50
+ return new Set<string>();
51
+ }
52
+ return new Set(
53
+ scopeAttribute.values.map(value => value.toLocaleLowerCase()),
54
+ );
55
+ }, [scopeFilterEnabled, scopeFilterAttributes]);
56
+
57
+ const items = useMemo(() => {
58
+ if (!scopedAllowedValues) {
59
+ return allItems;
60
+ }
61
+ return allItems.filter(item =>
62
+ scopedAllowedValues.has(item.value.toLocaleLowerCase()),
63
+ );
64
+ }, [allItems, scopedAllowedValues]);
65
+
35
66
  useEffect(() => {
36
67
  searchForItems(searchInput);
37
68
  // eslint-disable-next-line react-hooks/exhaustive-deps
@@ -39,7 +70,9 @@ function PostFilter({
39
70
 
40
71
  const hasSelection = items.some(item => item.isRefined);
41
72
  const hasAvailableValues = items.length > 0 || searchInput.length > 0;
42
- const isVisible = hasAvailableValues || hasSelection;
73
+ const isVisible =
74
+ (!scopedAllowedValues || scopedAllowedValues.size > 0) &&
75
+ (hasAvailableValues || hasSelection);
43
76
 
44
77
  useEffect(() => {
45
78
  onVisibilityChange?.(attribute, isVisible);
@@ -6,7 +6,6 @@ import {
6
6
  import React, { useCallback, useMemo, useState } from 'react';
7
7
  import { useTranslation } from 'react-i18next';
8
8
  import { twMerge } from 'tailwind-merge';
9
- import PostFilter from './PostFilter';
10
9
  import PostFilterFindApi from './PostFilterFindApi';
11
10
 
12
11
  function PostFilterComponent({ className }: { className?: string }) {
@@ -117,27 +116,14 @@ function PostFilterComponent({ className }: { className?: string }) {
117
116
  refinement.attribute;
118
117
 
119
118
  return (
120
- <React.Fragment key={index}>
121
- {window.settings.algolia.enabled && (
122
- <PostFilter
123
- attribute={refinement.attribute}
124
- label={refinement.header}
125
- searchable={refinement.searchable}
126
- onVisibilityChange={handleVisibilityChange}
127
- isLastVisible={isLastVisible}
128
- />
129
- )}
130
-
131
- {!window.settings.algolia.enabled && (
132
- <PostFilterFindApi
133
- attribute={refinement.attribute}
134
- label={refinement.header}
135
- searchable={false}
136
- onVisibilityChange={handleVisibilityChange}
137
- isLastVisible={isLastVisible}
138
- />
139
- )}
140
- </React.Fragment>
119
+ <PostFilterFindApi
120
+ key={index}
121
+ attribute={refinement.attribute}
122
+ label={refinement.header}
123
+ searchable={false}
124
+ onVisibilityChange={handleVisibilityChange}
125
+ isLastVisible={isLastVisible}
126
+ />
141
127
  );
142
128
  },
143
129
  )}