@nyris/nyris-webapp 0.3.97 → 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.
- package/build/asset-manifest.json +6 -6
- package/build/index.html +1 -1
- package/build/js/settings.example.js +1 -0
- package/build/static/css/main.81216c5d.css +4 -0
- package/build/static/css/{main.7c7f5447.css.map → main.81216c5d.css.map} +1 -1
- package/build/static/js/main.93fdbd6e.js +3 -0
- package/build/static/js/{main.9e7fb38a.js.map → main.93fdbd6e.js.map} +1 -1
- package/package.json +3 -3
- package/public/js/settings.example.js +1 -0
- package/src/components/AuthApiKeySync.tsx +34 -0
- package/src/components/ChatAssistant/useChatAssistantLogic.ts +3 -2
- package/src/components/ImageUpload.tsx +2 -2
- package/src/components/LogoutModal.tsx +2 -0
- package/src/components/PostFilter/PostFilterComponent.tsx +8 -22
- package/src/components/Product/ProductList.tsx +2 -0
- package/src/components/SidePanel.tsx +10 -0
- package/src/components/TextSearch.tsx +3 -3
- package/src/hooks/useImageSearch.ts +35 -8
- package/src/index.tsx +4 -0
- package/src/layouts/AppLayout.tsx +52 -6
- package/src/pages/Logout.tsx +2 -0
- package/src/pages/Result.tsx +12 -6
- package/src/providers/AuthProvider.tsx +5 -0
- package/src/services/Feedback.ts +2 -1
- package/src/services/filter.ts +3 -2
- package/src/services/visualSearch.ts +5 -4
- package/src/services/vizo.ts +13 -2
- package/src/utils/derivePostFilterFacets.ts +68 -0
- package/src/utils/nyrisApiKey.ts +115 -0
- package/build/static/css/main.7c7f5447.css +0 -4
- package/build/static/js/main.9e7fb38a.js +0 -3
- /package/build/static/js/{main.9e7fb38a.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.
|
|
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.
|
|
9
|
-
"@nyris/nyris-react-components": "^0.3.
|
|
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",
|
|
@@ -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
|
+
}
|
|
@@ -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 =
|
|
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 =
|
|
584
|
+
const nyrisKey = getEffectiveApiKey();
|
|
584
585
|
const baseUrl = settings?.baseUrl || 'https://api.nyris.io';
|
|
585
586
|
|
|
586
587
|
let searchQuery = rawText;
|
|
@@ -31,13 +31,13 @@ function ImageUpload({
|
|
|
31
31
|
|
|
32
32
|
const showPreFilter = useMemo(() => {
|
|
33
33
|
if (settings.shouldUseUserMetadata && user) {
|
|
34
|
-
if (user['/user_metadata']
|
|
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']
|
|
40
|
+
if (settings.preFilterOption && !user['/user_metadata']?.value) {
|
|
41
41
|
return true;
|
|
42
42
|
}
|
|
43
43
|
return false;
|
|
@@ -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
|
});
|
|
@@ -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
|
-
<
|
|
121
|
-
{
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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
|
)}
|
|
@@ -63,6 +63,7 @@ function ProductList({ sendFeedBackAction }: Props): JSX.Element {
|
|
|
63
63
|
}, [productsFromFindApi, query, requestImages]);
|
|
64
64
|
|
|
65
65
|
const postFilteredProducts = useMemo(() => {
|
|
66
|
+
// Find-API post-filters re-query when Algolia is off; do not also client-filter.
|
|
66
67
|
if (!window.settings.algolia?.enabled && window.settings.postFilterOption) {
|
|
67
68
|
return textFilteredProducts;
|
|
68
69
|
}
|
|
@@ -114,6 +115,7 @@ function ProductList({ sendFeedBackAction }: Props): JSX.Element {
|
|
|
114
115
|
);
|
|
115
116
|
}
|
|
116
117
|
|
|
118
|
+
// Product source follows search backend (Algolia vs Find API), not postFilterOption.
|
|
117
119
|
if (!isAlgoliaEnabled) {
|
|
118
120
|
return baseFindApiProducts;
|
|
119
121
|
}
|
|
@@ -18,6 +18,9 @@ export default function SidePanel({ className }: { className?: string }) {
|
|
|
18
18
|
const specificationFilter = useRequestStore(
|
|
19
19
|
state => state.specificationFilter,
|
|
20
20
|
);
|
|
21
|
+
const postFilterSelections = useRequestStore(
|
|
22
|
+
state => state.postFilterSelections,
|
|
23
|
+
);
|
|
21
24
|
|
|
22
25
|
const findApiProducts = useResultStore(state => state.productsFromFindApi);
|
|
23
26
|
|
|
@@ -45,13 +48,20 @@ export default function SidePanel({ className }: { className?: string }) {
|
|
|
45
48
|
|
|
46
49
|
const effectiveGroundingResults = useEffectiveGroundingResults();
|
|
47
50
|
|
|
51
|
+
const hasPostFilterSelections = Object.values(
|
|
52
|
+
postFilterSelections || {},
|
|
53
|
+
).some(values => values?.length > 0);
|
|
54
|
+
|
|
48
55
|
if (!showPostFilter && requestImages.length === 0) {
|
|
49
56
|
return <></>;
|
|
50
57
|
}
|
|
51
58
|
|
|
59
|
+
// Keep the panel when post-filters are applied so the user can unselect
|
|
60
|
+
// after a filtered find returns zero results.
|
|
52
61
|
if (
|
|
53
62
|
requestImages.length === 0 &&
|
|
54
63
|
findApiProducts.length === 0 &&
|
|
64
|
+
!hasPostFilterSelections &&
|
|
55
65
|
!window.settings.algolia.enabled
|
|
56
66
|
) {
|
|
57
67
|
return <></>;
|
|
@@ -65,13 +65,13 @@ function TextSearch({
|
|
|
65
65
|
|
|
66
66
|
const showPreFilter = useMemo(() => {
|
|
67
67
|
if (settings.shouldUseUserMetadata && user) {
|
|
68
|
-
if (user['/user_metadata']
|
|
68
|
+
if (user['/user_metadata']?.value) {
|
|
69
69
|
setMetaFilter(user['/user_metadata'].value);
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
if (settings.shouldUseUserMetadata && user) {
|
|
74
|
-
if (settings.preFilterOption && !user['/user_metadata']
|
|
74
|
+
if (settings.preFilterOption && !user['/user_metadata']?.value) {
|
|
75
75
|
return true;
|
|
76
76
|
}
|
|
77
77
|
return false;
|
|
@@ -103,7 +103,7 @@ function TextSearch({
|
|
|
103
103
|
imageRegion: regions[0],
|
|
104
104
|
text: value,
|
|
105
105
|
settings,
|
|
106
|
-
showFeedback:
|
|
106
|
+
showFeedback: true,
|
|
107
107
|
newSearch: false,
|
|
108
108
|
});
|
|
109
109
|
}
|
|
@@ -20,6 +20,7 @@ import { useGroundedSearch } from './useGroundedSearch';
|
|
|
20
20
|
import { generateSmartFilters } from '../services/vizo';
|
|
21
21
|
import { cropImage } from 'utils/cropImageToBase64';
|
|
22
22
|
import { invalidatePostFilterRequests } from 'components/PostFilter/PostFilterFindApi';
|
|
23
|
+
import { derivePostFilterFacetsFromResults } from 'utils/derivePostFilterFacets';
|
|
23
24
|
import { useAuth0 } from '@auth0/auth0-react';
|
|
24
25
|
|
|
25
26
|
export const useImageSearch = () => {
|
|
@@ -293,17 +294,20 @@ export const useImageSearch = () => {
|
|
|
293
294
|
});
|
|
294
295
|
}
|
|
295
296
|
|
|
297
|
+
const usesFindApiPostFilters =
|
|
298
|
+
!settings.algolia?.enabled && settings.postFilterOption;
|
|
299
|
+
// Crop/region finds replace results without sending postFilterSelections;
|
|
300
|
+
// clear stale selections so checkboxes match the new unfiltered grid.
|
|
296
301
|
const shouldResetPostFilters =
|
|
297
302
|
clearPostFilter ||
|
|
298
|
-
(
|
|
299
|
-
|
|
300
|
-
settings.postFilterOption);
|
|
303
|
+
(Boolean(imageRegion) && usesFindApiPostFilters) ||
|
|
304
|
+
(!requestImage && settings.postFilterOption);
|
|
301
305
|
|
|
302
306
|
if (shouldResetPostFilters) {
|
|
303
307
|
invalidatePostFilterRequests();
|
|
304
308
|
refine();
|
|
305
309
|
clearPostFilterSelections();
|
|
306
|
-
if (clearPostFilter) {
|
|
310
|
+
if (clearPostFilter || !requestImage) {
|
|
307
311
|
setPostFilterFacets([]);
|
|
308
312
|
setInitialFindSearchSnapshot(null);
|
|
309
313
|
}
|
|
@@ -335,12 +339,35 @@ export const useImageSearch = () => {
|
|
|
335
339
|
useResultStore.getState().resetGrounding();
|
|
336
340
|
|
|
337
341
|
setFindApiProducts(res?.results);
|
|
338
|
-
|
|
339
|
-
|
|
342
|
+
|
|
343
|
+
// Image: API facets if present, else derive from result.filters.
|
|
344
|
+
// Text: API facets only.
|
|
345
|
+
// Empty results with no facet payload may keep prior facet UI for unselect,
|
|
346
|
+
// but the search snapshot always updates to this response.
|
|
347
|
+
const isImageSearch = Boolean(requestImage);
|
|
348
|
+
const results = res?.results || [];
|
|
349
|
+
const apiFacets = Array.isArray(res?.facets) ? res.facets : [];
|
|
350
|
+
const nextFacets = isImageSearch
|
|
351
|
+
? apiFacets.length > 0
|
|
352
|
+
? apiFacets
|
|
353
|
+
: derivePostFilterFacetsFromResults(results)
|
|
354
|
+
: apiFacets;
|
|
355
|
+
const previousFacets = useResultStore.getState().postFilterFacets;
|
|
356
|
+
const shouldPreserveFacets =
|
|
357
|
+
results.length === 0 &&
|
|
358
|
+
nextFacets.length === 0 &&
|
|
359
|
+
Array.isArray(previousFacets) &&
|
|
360
|
+
previousFacets.length > 0;
|
|
361
|
+
|
|
362
|
+
// Facet UI may stay for recovery; snapshot always matches this search.
|
|
363
|
+
if (!shouldPreserveFacets) {
|
|
364
|
+
setPostFilterFacets(nextFacets);
|
|
365
|
+
}
|
|
340
366
|
setInitialFindSearchSnapshot({
|
|
341
|
-
results
|
|
342
|
-
postFilterFacets:
|
|
367
|
+
results,
|
|
368
|
+
postFilterFacets: nextFacets,
|
|
343
369
|
});
|
|
370
|
+
|
|
344
371
|
setImageAnalysis(res?.image_analysis);
|
|
345
372
|
setSessionId(res?.session);
|
|
346
373
|
setRequestId(res?.id);
|
package/src/index.tsx
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import ReactDOM from 'react-dom/client';
|
|
2
2
|
import './index.css';
|
|
3
3
|
import App from './App';
|
|
4
|
+
import { bootstrapAuth0ApiKeyGuard } from 'utils/nyrisApiKey';
|
|
5
|
+
|
|
6
|
+
// Clear settings.js apiKey before any React tree can issue Nyris requests (Auth0 mode).
|
|
7
|
+
bootstrapAuth0ApiKeyGuard();
|
|
4
8
|
|
|
5
9
|
const root = ReactDOM.createRoot(
|
|
6
10
|
document.getElementById('root') as HTMLElement,
|
|
@@ -22,6 +22,11 @@ import { getFilters } from 'services/filter';
|
|
|
22
22
|
import { useUploadSearch } from 'hooks/useUploadSearch';
|
|
23
23
|
import { ToastHelper } from 'helpers/ToastHelper';
|
|
24
24
|
import { useTranslation } from 'react-i18next';
|
|
25
|
+
import {
|
|
26
|
+
clearAuthApiKey,
|
|
27
|
+
isNyrisApiKeyReady,
|
|
28
|
+
useAuthApiKeyStore,
|
|
29
|
+
} from 'utils/nyrisApiKey';
|
|
25
30
|
import useScopeStore from 'stores/scope/scopeStore';
|
|
26
31
|
|
|
27
32
|
i18n.use(initReactI18next).init({
|
|
@@ -34,7 +39,7 @@ i18n.use(initReactI18next).init({
|
|
|
34
39
|
});
|
|
35
40
|
|
|
36
41
|
function AppLayout(): JSX.Element {
|
|
37
|
-
const { isAuthenticated } = useAuth0();
|
|
42
|
+
const { isAuthenticated, logout } = useAuth0();
|
|
38
43
|
const { t } = useTranslation();
|
|
39
44
|
const { auth0, alogoliaFilterField, showPoweredByNyris } = window.settings;
|
|
40
45
|
const { uploadFile } = useUploadSearch();
|
|
@@ -114,13 +119,13 @@ function AppLayout(): JSX.Element {
|
|
|
114
119
|
|
|
115
120
|
const showPreFilter = useMemo(() => {
|
|
116
121
|
if (settings.shouldUseUserMetadata && user) {
|
|
117
|
-
if (user['/user_metadata']
|
|
122
|
+
if (user['/user_metadata']?.value) {
|
|
118
123
|
setMetaFilter(user['/user_metadata'].value);
|
|
119
124
|
}
|
|
120
125
|
}
|
|
121
126
|
|
|
122
127
|
if (settings.shouldUseUserMetadata && user) {
|
|
123
|
-
if (settings.preFilterOption && !user['/user_metadata']
|
|
128
|
+
if (settings.preFilterOption && !user['/user_metadata']?.value) {
|
|
124
129
|
return true;
|
|
125
130
|
}
|
|
126
131
|
return false;
|
|
@@ -134,7 +139,15 @@ function AppLayout(): JSX.Element {
|
|
|
134
139
|
user,
|
|
135
140
|
]);
|
|
136
141
|
|
|
142
|
+
const authApiKeyVersion = useAuthApiKeyStore(state => state.version);
|
|
143
|
+
const isMissingAuthApiKey = useAuthApiKeyStore(state => state.isMissingClaim);
|
|
144
|
+
const isApiKeyReady = isNyrisApiKeyReady();
|
|
145
|
+
|
|
137
146
|
const getPreFilters = useCallback(async () => {
|
|
147
|
+
if (!isNyrisApiKeyReady()) {
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
|
|
138
151
|
setPreFilterLoading(true);
|
|
139
152
|
try {
|
|
140
153
|
const res = await getFilters(1000, settings);
|
|
@@ -147,10 +160,10 @@ function AppLayout(): JSX.Element {
|
|
|
147
160
|
}, [settings, setPreFilterList, setPreFilterLoading]);
|
|
148
161
|
|
|
149
162
|
useEffect(() => {
|
|
150
|
-
if (showPreFilter && !settings.scopeFilter) {
|
|
163
|
+
if (showPreFilter && !settings.scopeFilter && isApiKeyReady) {
|
|
151
164
|
getPreFilters();
|
|
152
165
|
}
|
|
153
|
-
}, [getPreFilters, showPreFilter, settings.scopeFilter]);
|
|
166
|
+
}, [getPreFilters, showPreFilter, settings.scopeFilter, isApiKeyReady, authApiKeyVersion]);
|
|
154
167
|
|
|
155
168
|
const getImageFromClipboard = useCallback(
|
|
156
169
|
(event: ClipboardEvent): File | null => {
|
|
@@ -208,12 +221,17 @@ function AppLayout(): JSX.Element {
|
|
|
208
221
|
return;
|
|
209
222
|
}
|
|
210
223
|
|
|
224
|
+
// Auth0: only listen once authApiKey is resolved (not while waiting / missing claim).
|
|
225
|
+
if (auth0.enabled && !isApiKeyReady) {
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
|
|
211
229
|
document.addEventListener('paste', handleClipboardPaste);
|
|
212
230
|
|
|
213
231
|
return () => {
|
|
214
232
|
document.removeEventListener('paste', handleClipboardPaste);
|
|
215
233
|
};
|
|
216
|
-
}, [handleClipboardPaste, showLayout]);
|
|
234
|
+
}, [auth0.enabled, handleClipboardPaste, isApiKeyReady, showLayout]);
|
|
217
235
|
|
|
218
236
|
const applyScopeFilter = (filters: string | undefined) => {
|
|
219
237
|
const scopeFilterClause =
|
|
@@ -238,6 +256,34 @@ function AppLayout(): JSX.Element {
|
|
|
238
256
|
return <Outlet />;
|
|
239
257
|
}
|
|
240
258
|
|
|
259
|
+
// Auth0 mode: wait for AuthApiKeySync before rendering search UI / prefilters.
|
|
260
|
+
if (auth0.enabled && !isApiKeyReady && !isMissingAuthApiKey) {
|
|
261
|
+
return <div />;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
if (isMissingAuthApiKey) {
|
|
265
|
+
return (
|
|
266
|
+
<div className="full-height flex flex-col items-center justify-center bg-[#F3F4F8] p-6 text-center">
|
|
267
|
+
<p className="mt-2 max-w-md text-sm text-gray-600">
|
|
268
|
+
Search is currently unavailable for your account. Please contact your
|
|
269
|
+
administrator.
|
|
270
|
+
</p>
|
|
271
|
+
<button
|
|
272
|
+
type="button"
|
|
273
|
+
className="mt-6 bg-[#2B2C46] px-6 py-3 text-sm text-white"
|
|
274
|
+
onClick={() => {
|
|
275
|
+
clearAuthApiKey();
|
|
276
|
+
logout({
|
|
277
|
+
logoutParams: { returnTo: window.location.origin },
|
|
278
|
+
});
|
|
279
|
+
}}
|
|
280
|
+
>
|
|
281
|
+
{t('Log out')}
|
|
282
|
+
</button>
|
|
283
|
+
</div>
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
|
|
241
287
|
return (
|
|
242
288
|
<div className="full-height flex flex-col bg-[#F3F4F8]">
|
|
243
289
|
<div>
|
package/src/pages/Logout.tsx
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { useEffect } from 'react';
|
|
2
2
|
import { useAuth0 } from '@auth0/auth0-react';
|
|
3
|
+
import { clearAuthApiKey } from 'utils/nyrisApiKey';
|
|
3
4
|
|
|
4
5
|
const Logout = () => {
|
|
5
6
|
const { user, isAuthenticated, isLoading, loginWithRedirect, logout } =
|
|
@@ -10,6 +11,7 @@ const Logout = () => {
|
|
|
10
11
|
if (!isLoading && !user && !isAuthenticated && auth0.enabled) {
|
|
11
12
|
loginWithRedirect();
|
|
12
13
|
} else if (isAuthenticated) {
|
|
14
|
+
clearAuthApiKey();
|
|
13
15
|
logout({ logoutParams: { returnTo: window.location.origin } });
|
|
14
16
|
} else if (!auth0.enabled) {
|
|
15
17
|
window.location.href = '/';
|
package/src/pages/Result.tsx
CHANGED
|
@@ -174,16 +174,21 @@ function Results() {
|
|
|
174
174
|
]);
|
|
175
175
|
|
|
176
176
|
const disablePostFilter = useMemo(() => {
|
|
177
|
-
|
|
177
|
+
// Find-API post-filters do not depend on Algolia hits.
|
|
178
|
+
if (!settings.algolia.enabled || settings.postFilterOption) {
|
|
178
179
|
return false;
|
|
179
180
|
}
|
|
180
181
|
|
|
181
|
-
return
|
|
182
|
-
? false
|
|
183
|
-
: true;
|
|
182
|
+
return productsFromAlgolia.length > 0 ? false : true;
|
|
184
183
|
}, [settings, productsFromAlgolia]);
|
|
185
184
|
|
|
186
|
-
const
|
|
185
|
+
const postFilterSelections = useRequestStore(
|
|
186
|
+
state => state.postFilterSelections,
|
|
187
|
+
);
|
|
188
|
+
const hasFindApiPostFilterSelection = Object.values(
|
|
189
|
+
postFilterSelections || {},
|
|
190
|
+
).some(values => values?.length > 0);
|
|
191
|
+
const isPostFilterApplied = items.length > 0 || hasFindApiPostFilterSelection;
|
|
187
192
|
|
|
188
193
|
return (
|
|
189
194
|
<>
|
|
@@ -364,7 +369,8 @@ function Results() {
|
|
|
364
369
|
|
|
365
370
|
{feedbackStatus === 'visible' &&
|
|
366
371
|
!showFeedbackSuccess &&
|
|
367
|
-
productsFromAlgolia.length > 0
|
|
372
|
+
(productsFromAlgolia.length > 0 ||
|
|
373
|
+
productsFromFindApi.length > 0) && (
|
|
368
374
|
<div className={'feedback-floating'}>
|
|
369
375
|
<div className="feedback-section feedback-backdrop-blur" />
|
|
370
376
|
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import { Auth0Provider } from '@auth0/auth0-react';
|
|
2
|
+
import { AuthApiKeySync } from 'components/AuthApiKeySync';
|
|
3
|
+
import { bootstrapAuth0ApiKeyGuard } from 'utils/nyrisApiKey';
|
|
4
|
+
|
|
5
|
+
bootstrapAuth0ApiKeyGuard();
|
|
2
6
|
|
|
3
7
|
const AuthProvider = ({ children }: any) => {
|
|
4
8
|
const settings = window.settings;
|
|
@@ -15,6 +19,7 @@ const AuthProvider = ({ children }: any) => {
|
|
|
15
19
|
redirect_uri: window.location.origin,
|
|
16
20
|
}}
|
|
17
21
|
>
|
|
22
|
+
<AuthApiKeySync />
|
|
18
23
|
{children}
|
|
19
24
|
</Auth0Provider>
|
|
20
25
|
);
|
package/src/services/Feedback.ts
CHANGED
|
@@ -3,6 +3,7 @@ import NyrisAPI, {
|
|
|
3
3
|
NyrisAPISettings,
|
|
4
4
|
RectCoords,
|
|
5
5
|
} from '@nyris/nyris-api';
|
|
6
|
+
import { getNyrisSettings } from 'utils/nyrisApiKey';
|
|
6
7
|
|
|
7
8
|
export const feedbackSuccessEpic = async (
|
|
8
9
|
requestId: string,
|
|
@@ -62,7 +63,7 @@ export const sendFeedbackByApi = async (
|
|
|
62
63
|
requestId: string | undefined,
|
|
63
64
|
payload: FeedbackEventPayload,
|
|
64
65
|
) => {
|
|
65
|
-
const api = new NyrisAPI(settings);
|
|
66
|
+
const api = new NyrisAPI(getNyrisSettings(settings));
|
|
66
67
|
if (requestId) {
|
|
67
68
|
try {
|
|
68
69
|
await api.sendFeedback({ sessionId, requestId, payload }).then(() => {});
|
package/src/services/filter.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import NyrisAPI, { NyrisAPISettings } from '@nyris/nyris-api';
|
|
2
|
+
import { getNyrisSettings } from 'utils/nyrisApiKey';
|
|
2
3
|
|
|
3
4
|
export interface Filter {
|
|
4
5
|
key?: string;
|
|
@@ -9,7 +10,7 @@ export const getFilters = async (
|
|
|
9
10
|
number: number = 10,
|
|
10
11
|
settings: NyrisAPISettings,
|
|
11
12
|
) => {
|
|
12
|
-
const nyrisApi = new NyrisAPI(settings);
|
|
13
|
+
const nyrisApi = new NyrisAPI(getNyrisSettings(settings));
|
|
13
14
|
return nyrisApi.getFilters(number);
|
|
14
15
|
};
|
|
15
16
|
|
|
@@ -18,7 +19,7 @@ export const searchFilters = async (
|
|
|
18
19
|
value: string,
|
|
19
20
|
settingsNyris: NyrisAPISettings,
|
|
20
21
|
) => {
|
|
21
|
-
const nyrisApi = new NyrisAPI(settingsNyris);
|
|
22
|
+
const nyrisApi = new NyrisAPI(getNyrisSettings(settingsNyris));
|
|
22
23
|
const newValue = value ? value : '';
|
|
23
24
|
return nyrisApi.searchFilters(key, newValue);
|
|
24
25
|
};
|
|
@@ -9,6 +9,7 @@ import { isEqual } from 'lodash';
|
|
|
9
9
|
|
|
10
10
|
import { DEFAULT_REGION } from '../constants';
|
|
11
11
|
import { getUserLocation } from 'helpers/getGeoLocation';
|
|
12
|
+
import { getNyrisSettings } from 'utils/nyrisApiKey';
|
|
12
13
|
|
|
13
14
|
export interface Filter {
|
|
14
15
|
key?: string;
|
|
@@ -41,7 +42,7 @@ export const findRegions = async (
|
|
|
41
42
|
image: HTMLCanvasElement,
|
|
42
43
|
settings: NyrisAPISettings,
|
|
43
44
|
) => {
|
|
44
|
-
const nyrisApi = new NyrisAPI(settings);
|
|
45
|
+
const nyrisApi = new NyrisAPI(getNyrisSettings(settings));
|
|
45
46
|
let regions = await nyrisApi.findRegions(image);
|
|
46
47
|
const selectedRegion = getRegionByMaxConfidence(regions);
|
|
47
48
|
return {
|
|
@@ -67,7 +68,7 @@ export const find = async ({
|
|
|
67
68
|
text?: string;
|
|
68
69
|
scopeIds?: string;
|
|
69
70
|
}) => {
|
|
70
|
-
const nyrisApi = new NyrisAPI(settings);
|
|
71
|
+
const nyrisApi = new NyrisAPI(getNyrisSettings(settings));
|
|
71
72
|
let options: ImageSearchOptions = text ? { text } : {};
|
|
72
73
|
|
|
73
74
|
if (window.settings?.geoLocation) {
|
|
@@ -103,7 +104,7 @@ export const findCad = ({
|
|
|
103
104
|
settings: NyrisAPISettings;
|
|
104
105
|
filters?: Filter[];
|
|
105
106
|
}) => {
|
|
106
|
-
const nyrisApi = new NyrisAPI(settings);
|
|
107
|
+
const nyrisApi = new NyrisAPI(getNyrisSettings(settings));
|
|
107
108
|
|
|
108
109
|
return nyrisApi.findByCad(file, {}, filters);
|
|
109
110
|
};
|
|
@@ -115,7 +116,7 @@ export const getRequestImage = ({
|
|
|
115
116
|
url: string;
|
|
116
117
|
settings: NyrisAPISettings;
|
|
117
118
|
}) => {
|
|
118
|
-
const nyrisApi = new NyrisAPI(settings);
|
|
119
|
+
const nyrisApi = new NyrisAPI(getNyrisSettings(settings));
|
|
119
120
|
|
|
120
121
|
return nyrisApi.getCadRequestImage(url);
|
|
121
122
|
};
|
package/src/services/vizo.ts
CHANGED
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
import { RectCoords } from '@nyris/nyris-api';
|
|
2
2
|
import axios from 'axios';
|
|
3
3
|
import { cropImage } from 'utils/cropImageToBase64';
|
|
4
|
+
import { getEffectiveApiKey } from 'utils/nyrisApiKey';
|
|
5
|
+
|
|
6
|
+
function resolveAiOrNyrisApiKey(explicitApiKey?: string): string {
|
|
7
|
+
if (explicitApiKey) {
|
|
8
|
+
return explicitApiKey;
|
|
9
|
+
}
|
|
10
|
+
if (window.settings.aiApiKey) {
|
|
11
|
+
return window.settings.aiApiKey;
|
|
12
|
+
}
|
|
13
|
+
return getEffectiveApiKey();
|
|
14
|
+
}
|
|
4
15
|
|
|
5
16
|
export interface ChatMultimodalInput {
|
|
6
17
|
metadata: string;
|
|
@@ -111,7 +122,7 @@ export async function rerankSmartFilters({
|
|
|
111
122
|
formData,
|
|
112
123
|
{
|
|
113
124
|
headers: {
|
|
114
|
-
'x-api-key': apiKey
|
|
125
|
+
'x-api-key': resolveAiOrNyrisApiKey(apiKey),
|
|
115
126
|
},
|
|
116
127
|
},
|
|
117
128
|
);
|
|
@@ -156,7 +167,7 @@ export async function generateSmartFilters({
|
|
|
156
167
|
formData,
|
|
157
168
|
{
|
|
158
169
|
headers: {
|
|
159
|
-
'x-api-key':
|
|
170
|
+
'x-api-key': resolveAiOrNyrisApiKey(),
|
|
160
171
|
},
|
|
161
172
|
},
|
|
162
173
|
);
|