@nyris/nyris-webapp 0.3.97 → 0.3.99
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 +7 -6
- package/build/index.html +1 -1
- package/build/js/settings.example.js +41 -0
- package/build/static/css/main.5cb72c09.css +4 -0
- package/build/static/css/main.5cb72c09.css.map +1 -0
- package/build/static/js/main.2339271b.js +3 -0
- package/build/static/js/{main.9e7fb38a.js.map → main.2339271b.js.map} +1 -1
- package/build/static/media/plate_info.6beaea36d35053be3a45.png +0 -0
- package/package.json +3 -3
- package/public/js/settings.example.js +41 -0
- package/src/common/assets/images/plate_info.png +0 -0
- package/src/components/AuthApiKeySync.tsx +34 -0
- package/src/components/Cart.tsx +41 -0
- package/src/components/ChatAssistant/useChatAssistantLogic.ts +3 -2
- package/src/components/GoBackButton.tsx +29 -2
- package/src/components/Header.tsx +3 -0
- package/src/components/HitsPerPage.tsx +55 -18
- package/src/components/ImagePreview.tsx +31 -1
- package/src/components/ImageUpload.tsx +14 -3
- package/src/components/Inquiry/InquiryModal.tsx +3 -0
- package/src/components/LogoutModal.tsx +2 -0
- package/src/components/Pagination.tsx +33 -6
- package/src/components/PostFilter/PostFilter.tsx +8 -1
- package/src/components/PostFilter/PostFilterComponent.tsx +8 -22
- package/src/components/PostFilter/PostFilterFindApi.test.tsx +249 -0
- package/src/components/PostFilter/PostFilterFindApi.tsx +128 -52
- package/src/components/PreFilter/PreFilter.tsx +10 -2
- package/src/components/Product/Product.tsx +60 -10
- package/src/components/Product/ProductDetailView.tsx +23 -1
- package/src/components/Product/ProductDetailViewModal.tsx +5 -1
- package/src/components/Product/ProductList.tsx +51 -16
- package/src/components/Rfq/RfqModal.tsx +3 -0
- package/src/components/ScopeSelect/ScopeSelectForm.tsx +514 -0
- package/src/components/ScopeSelect/ScopeSelectImageActions.tsx +62 -0
- package/src/components/ScopeSelect/ScopeSelectImagePreview.tsx +141 -0
- package/src/components/ScopeSelect/ScopeSelectInfo.tsx +68 -0
- package/src/components/ScopeSelect/ScopeSelectInput.tsx +123 -0
- package/src/components/ScopeSelect/ScopeSelectModal.tsx +310 -0
- package/src/components/ScopeSelect/identifyTypePlateImage.test.ts +129 -0
- package/src/components/ScopeSelect/identifyTypePlateImage.ts +83 -0
- package/src/components/ScopeSelect/parseScopeIdResponse.test.ts +103 -0
- package/src/components/ScopeSelect/parseScopeIdResponse.ts +72 -0
- package/src/components/ScopeSelect/scopeSelectHelpers.ts +54 -0
- package/src/components/SidePanel.tsx +43 -0
- package/src/components/TextSearch.tsx +80 -14
- package/src/components/Tooltip/TooltipComponent.tsx +12 -4
- package/src/components/TypePlateUpload.tsx +132 -0
- package/src/hooks/useEffectiveGroundingResults.ts +8 -6
- package/src/hooks/useGoodResultsChat.ts +8 -22
- package/src/hooks/useImageSearch.ts +325 -20
- package/src/hooks/useMachinePartsListingPagination.ts +95 -0
- package/src/hooks/useResultEvaluator.ts +43 -19
- package/src/index.css +1 -1
- package/src/index.tsx +4 -0
- package/src/layouts/AppLayout.tsx +95 -9
- package/src/pages/Home.tsx +60 -14
- package/src/pages/Logout.tsx +2 -0
- package/src/pages/Result.tsx +206 -20
- package/src/providers/AuthProvider.tsx +5 -0
- package/src/services/Feedback.ts +224 -35
- package/src/services/feedbackSubtypes.ts +63 -0
- package/src/services/filter.ts +4 -3
- package/src/services/initialMachinePartsText.payload.test.ts +61 -0
- package/src/services/initialMachinePartsText.test.ts +33 -0
- package/src/services/initialMachinePartsText.ts +11 -0
- package/src/services/machinePartsPage.test.ts +74 -0
- package/src/services/machinePartsPage.ts +120 -0
- package/src/services/normalizeChatResponse.ts +26 -0
- package/src/services/scopeLookup.test.ts +67 -0
- package/src/services/scopeLookup.ts +39 -0
- package/src/services/visualSearch.listingOptions.test.ts +79 -0
- package/src/services/visualSearch.ts +76 -7
- package/src/services/vizo.ts +17 -5
- package/src/stores/machinePartsListing/machinePartsListingStore.ts +35 -0
- package/src/stores/request/filter/filter.initialState.ts +1 -0
- package/src/stores/request/filter/filter.slice.ts +2 -0
- package/src/stores/result/prodcuts/products.initialState.ts +1 -0
- package/src/stores/result/prodcuts/products.slice.ts +4 -0
- package/src/stores/scope/scopeStore.ts +67 -4
- package/src/stores/types.ts +19 -14
- package/src/translations.ts +213 -0
- package/src/types.ts +40 -0
- package/src/utils/derivePostFilterFacets.ts +68 -0
- package/src/utils/fetchProductImage.ts +1 -0
- package/src/utils/normalizeChatAssistantResponse.ts +227 -0
- package/src/utils/nyrisApiKey.ts +115 -0
- package/src/utils/productCardImageUrl.test.ts +41 -0
- package/src/utils/productCardImageUrl.ts +49 -0
- package/src/utils/textSearchFilter.test.ts +41 -0
- package/src/utils/textSearchFilter.ts +13 -6
- package/build/static/css/main.7c7f5447.css +0 -4
- package/build/static/css/main.7c7f5447.css.map +0 -1
- package/build/static/js/main.9e7fb38a.js +0 -3
- /package/build/static/js/{main.9e7fb38a.js.LICENSE.txt → main.2339271b.js.LICENSE.txt} +0 -0
|
@@ -47,10 +47,18 @@ export function useResultEvaluator() {
|
|
|
47
47
|
async ({
|
|
48
48
|
imageAnalysis,
|
|
49
49
|
activeProducts,
|
|
50
|
+
query,
|
|
50
51
|
}: {
|
|
51
52
|
imageAnalysis: any;
|
|
52
53
|
activeProducts: any[];
|
|
54
|
+
query: string;
|
|
53
55
|
}) => {
|
|
56
|
+
// Bind this run to the current session; a newer search calls
|
|
57
|
+
// startNewSession, which makes any in-flight evaluation stale.
|
|
58
|
+
const runSessionId = useConversationStore.getState().sessionId;
|
|
59
|
+
const isStale = () =>
|
|
60
|
+
useConversationStore.getState().sessionId !== runSessionId;
|
|
61
|
+
|
|
54
62
|
setIsLoading(true);
|
|
55
63
|
setError(null);
|
|
56
64
|
setRelatedPartsTableSuggestions([]);
|
|
@@ -207,12 +215,22 @@ export function useResultEvaluator() {
|
|
|
207
215
|
}
|
|
208
216
|
});
|
|
209
217
|
|
|
218
|
+
const requestImage = useRequestStore.getState().requestImages;
|
|
219
|
+
const regions = useRequestStore.getState().regions;
|
|
220
|
+
const hasRequestImage = requestImage && requestImage[0];
|
|
221
|
+
|
|
222
|
+
const evaluationTarget = hasRequestImage
|
|
223
|
+
? 'the image'
|
|
224
|
+
: 'the user query (keyword input)';
|
|
225
|
+
|
|
210
226
|
const userPrompt = `Evaluate these search results:
|
|
211
227
|
|
|
212
|
-
|
|
228
|
+
user query: ${query || 'N/A'}
|
|
229
|
+
|
|
230
|
+
IMAGE DESCRIPTION: ${hasRequestImage ? imageDescription : 'N/A'}
|
|
213
231
|
|
|
214
232
|
SPECIFICATION (from image analysis):
|
|
215
|
-
${JSON.stringify(p4Specification, null, 2)}
|
|
233
|
+
${hasRequestImage ? JSON.stringify(p4Specification, null, 2) : 'N/A'}
|
|
216
234
|
|
|
217
235
|
TOP 20 RESULTS WITH METADATA:
|
|
218
236
|
${resultsSummary
|
|
@@ -227,7 +245,7 @@ ${JSON.stringify(attributeFrequency, null, 2)}
|
|
|
227
245
|
|
|
228
246
|
TOTAL RESULTS: ${resultsSummary.length}
|
|
229
247
|
|
|
230
|
-
Evaluate if these results match
|
|
248
|
+
Evaluate if these results match ${evaluationTarget}. If they do (same type of product), route to P3 and suggest filter options based on the COMMON ATTRIBUTES found above (like voltage, size, brand). Do NOT suggest the obvious category match.`;
|
|
231
249
|
|
|
232
250
|
const productsForImages = activeProducts.slice(0, 30);
|
|
233
251
|
const imageResults = await Promise.all(
|
|
@@ -268,24 +286,27 @@ Evaluate if these results match the image. If they do (same type of product), ro
|
|
|
268
286
|
}
|
|
269
287
|
});
|
|
270
288
|
|
|
271
|
-
const
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
289
|
+
const multimodalInputs: any[] = [
|
|
290
|
+
{
|
|
291
|
+
metadata: userPrompt,
|
|
292
|
+
},
|
|
293
|
+
];
|
|
294
|
+
|
|
295
|
+
if (hasRequestImage) {
|
|
296
|
+
const croppedCanvas = cropImage(requestImage[0], regions[0]);
|
|
297
|
+
const base64Image = canvasToBase64(croppedCanvas, 'image/jpeg', 1);
|
|
298
|
+
multimodalInputs.push({
|
|
299
|
+
metadata: 'request_image',
|
|
300
|
+
mimeType: 'image/jpeg',
|
|
301
|
+
image: base64Image,
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
multimodalInputs.push(...productImageParts);
|
|
275
306
|
|
|
276
307
|
const response = await chatAnalysis({
|
|
277
308
|
promptKey: 'result_evaluator_prompt',
|
|
278
|
-
multimodalInputs
|
|
279
|
-
{
|
|
280
|
-
metadata: userPrompt,
|
|
281
|
-
},
|
|
282
|
-
{
|
|
283
|
-
metadata: 'request_image',
|
|
284
|
-
mimeType: 'image/jpeg',
|
|
285
|
-
image: base64Image,
|
|
286
|
-
},
|
|
287
|
-
...productImageParts,
|
|
288
|
-
],
|
|
309
|
+
multimodalInputs,
|
|
289
310
|
});
|
|
290
311
|
|
|
291
312
|
const evaluation: P2EvaluationResult = response;
|
|
@@ -315,6 +336,8 @@ Evaluate if these results match the image. If they do (same type of product), ro
|
|
|
315
336
|
// Check lookup table for related parts on fresh image searches and store globally.
|
|
316
337
|
const table = await loadRelatedPartsTable();
|
|
317
338
|
|
|
339
|
+
if (isStale()) return;
|
|
340
|
+
|
|
318
341
|
const tableSuggestions = lookupRelatedParts(
|
|
319
342
|
table,
|
|
320
343
|
imageAnalysis?.specification?.product_category || '',
|
|
@@ -371,6 +394,7 @@ Evaluate if these results match the image. If they do (same type of product), ro
|
|
|
371
394
|
);
|
|
372
395
|
} catch (err: any) {
|
|
373
396
|
console.error('result_evaluator_prompt', err);
|
|
397
|
+
if (isStale()) return;
|
|
374
398
|
setRelatedPartsTableSuggestions([]);
|
|
375
399
|
setError(err.message || 'Failed to evaluate results');
|
|
376
400
|
|
|
@@ -390,7 +414,7 @@ Evaluate if these results match the image. If they do (same type of product), ro
|
|
|
390
414
|
],
|
|
391
415
|
);
|
|
392
416
|
} finally {
|
|
393
|
-
setIsLoading(false);
|
|
417
|
+
if (!isStale()) setIsLoading(false);
|
|
394
418
|
}
|
|
395
419
|
},
|
|
396
420
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
package/src/index.css
CHANGED
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,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { memo, useCallback, useEffect, useMemo } from 'react';
|
|
1
|
+
import { memo, useCallback, useEffect, useMemo, useRef } from 'react';
|
|
2
2
|
import { Outlet, useLocation } from 'react-router';
|
|
3
3
|
|
|
4
4
|
import { useAuth0 } from '@auth0/auth0-react';
|
|
@@ -22,7 +22,14 @@ 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
|
|
25
|
+
import {
|
|
26
|
+
clearAuthApiKey,
|
|
27
|
+
isNyrisApiKeyReady,
|
|
28
|
+
useAuthApiKeyStore,
|
|
29
|
+
} from 'utils/nyrisApiKey';
|
|
30
|
+
import useScopeStore, { hasActiveMachineScope } from 'stores/scope/scopeStore';
|
|
31
|
+
import useConversationStore from 'stores/chat/conversationStore';
|
|
32
|
+
import { useResultEvaluator } from 'hooks/useResultEvaluator';
|
|
26
33
|
|
|
27
34
|
i18n.use(initReactI18next).init({
|
|
28
35
|
resources: translations,
|
|
@@ -34,7 +41,7 @@ i18n.use(initReactI18next).init({
|
|
|
34
41
|
});
|
|
35
42
|
|
|
36
43
|
function AppLayout(): JSX.Element {
|
|
37
|
-
const { isAuthenticated } = useAuth0();
|
|
44
|
+
const { isAuthenticated, logout } = useAuth0();
|
|
38
45
|
const { t } = useTranslation();
|
|
39
46
|
const { auth0, alogoliaFilterField, showPoweredByNyris } = window.settings;
|
|
40
47
|
const { uploadFile } = useUploadSearch();
|
|
@@ -61,9 +68,13 @@ function AppLayout(): JSX.Element {
|
|
|
61
68
|
const requestImages = useRequestStore(state => state.requestImages);
|
|
62
69
|
const showLoading = useRequestStore(state => state.showLoading);
|
|
63
70
|
|
|
64
|
-
const { status } = useInstantSearch();
|
|
71
|
+
const { status, results } = useInstantSearch();
|
|
65
72
|
const { items } = useHits();
|
|
66
73
|
|
|
74
|
+
const isAiModeOpen = useUiStore(state => state.isAiModeOpen);
|
|
75
|
+
const { evaluateResults } = useResultEvaluator();
|
|
76
|
+
const lastAlgoliaEvaluationRef = useRef<string | null>(null);
|
|
77
|
+
|
|
67
78
|
useEffect(() => {
|
|
68
79
|
i18n.changeLanguage(window.settings.language);
|
|
69
80
|
console.log('App version:', packageJson.version);
|
|
@@ -92,6 +103,40 @@ function AppLayout(): JSX.Element {
|
|
|
92
103
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
93
104
|
}, [items, scopeItemKeys]);
|
|
94
105
|
|
|
106
|
+
// Algolia text search never goes through singleImageSearch, so run P2 here
|
|
107
|
+
// on the Algolia hits once AI mode is open for the current query.
|
|
108
|
+
// Deliberately not keyed on the conversation session: singleImageSearch
|
|
109
|
+
// calls startNewSession before requestImages is set, and re-running on that
|
|
110
|
+
// session change would start a competing evaluation of stale Algolia hits.
|
|
111
|
+
useEffect(() => {
|
|
112
|
+
if (requestImages.length > 0 || hasActiveMachineScope()) {
|
|
113
|
+
// An image search or machine scope owns P2. Algolia hits are unscoped.
|
|
114
|
+
lastAlgoliaEvaluationRef.current = null;
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (
|
|
119
|
+
!window.settings.algolia?.enabled ||
|
|
120
|
+
!window.settings.vizo?.chat ||
|
|
121
|
+
!isAiModeOpen ||
|
|
122
|
+
isFindApiLoading ||
|
|
123
|
+
!query ||
|
|
124
|
+
status !== 'idle' ||
|
|
125
|
+
results?.query !== query
|
|
126
|
+
) {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (lastAlgoliaEvaluationRef.current === query) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
useConversationStore.getState().startNewSession();
|
|
135
|
+
lastAlgoliaEvaluationRef.current = query;
|
|
136
|
+
evaluateResults({ activeProducts: items, imageAnalysis: undefined, query });
|
|
137
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
138
|
+
}, [isAiModeOpen, isFindApiLoading, query, status, results, requestImages]);
|
|
139
|
+
|
|
95
140
|
useEffect(() => {
|
|
96
141
|
if (status === 'loading' || status === 'stalled') {
|
|
97
142
|
setIsAlgoliaLoading(true);
|
|
@@ -114,13 +159,13 @@ function AppLayout(): JSX.Element {
|
|
|
114
159
|
|
|
115
160
|
const showPreFilter = useMemo(() => {
|
|
116
161
|
if (settings.shouldUseUserMetadata && user) {
|
|
117
|
-
if (user['/user_metadata']
|
|
162
|
+
if (user['/user_metadata']?.value) {
|
|
118
163
|
setMetaFilter(user['/user_metadata'].value);
|
|
119
164
|
}
|
|
120
165
|
}
|
|
121
166
|
|
|
122
167
|
if (settings.shouldUseUserMetadata && user) {
|
|
123
|
-
if (settings.preFilterOption && !user['/user_metadata']
|
|
168
|
+
if (settings.preFilterOption && !user['/user_metadata']?.value) {
|
|
124
169
|
return true;
|
|
125
170
|
}
|
|
126
171
|
return false;
|
|
@@ -134,7 +179,15 @@ function AppLayout(): JSX.Element {
|
|
|
134
179
|
user,
|
|
135
180
|
]);
|
|
136
181
|
|
|
182
|
+
const authApiKeyVersion = useAuthApiKeyStore(state => state.version);
|
|
183
|
+
const isMissingAuthApiKey = useAuthApiKeyStore(state => state.isMissingClaim);
|
|
184
|
+
const isApiKeyReady = isNyrisApiKeyReady();
|
|
185
|
+
|
|
137
186
|
const getPreFilters = useCallback(async () => {
|
|
187
|
+
if (!isNyrisApiKeyReady()) {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
|
|
138
191
|
setPreFilterLoading(true);
|
|
139
192
|
try {
|
|
140
193
|
const res = await getFilters(1000, settings);
|
|
@@ -147,10 +200,10 @@ function AppLayout(): JSX.Element {
|
|
|
147
200
|
}, [settings, setPreFilterList, setPreFilterLoading]);
|
|
148
201
|
|
|
149
202
|
useEffect(() => {
|
|
150
|
-
if (showPreFilter && !settings.scopeFilter) {
|
|
203
|
+
if (showPreFilter && !settings.scopeFilter && isApiKeyReady) {
|
|
151
204
|
getPreFilters();
|
|
152
205
|
}
|
|
153
|
-
}, [getPreFilters, showPreFilter, settings.scopeFilter]);
|
|
206
|
+
}, [getPreFilters, showPreFilter, settings.scopeFilter, isApiKeyReady, authApiKeyVersion]);
|
|
154
207
|
|
|
155
208
|
const getImageFromClipboard = useCallback(
|
|
156
209
|
(event: ClipboardEvent): File | null => {
|
|
@@ -208,12 +261,17 @@ function AppLayout(): JSX.Element {
|
|
|
208
261
|
return;
|
|
209
262
|
}
|
|
210
263
|
|
|
264
|
+
// Auth0: only listen once authApiKey is resolved (not while waiting / missing claim).
|
|
265
|
+
if (auth0.enabled && !isApiKeyReady) {
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
|
|
211
269
|
document.addEventListener('paste', handleClipboardPaste);
|
|
212
270
|
|
|
213
271
|
return () => {
|
|
214
272
|
document.removeEventListener('paste', handleClipboardPaste);
|
|
215
273
|
};
|
|
216
|
-
}, [handleClipboardPaste, showLayout]);
|
|
274
|
+
}, [auth0.enabled, handleClipboardPaste, isApiKeyReady, showLayout]);
|
|
217
275
|
|
|
218
276
|
const applyScopeFilter = (filters: string | undefined) => {
|
|
219
277
|
const scopeFilterClause =
|
|
@@ -238,6 +296,34 @@ function AppLayout(): JSX.Element {
|
|
|
238
296
|
return <Outlet />;
|
|
239
297
|
}
|
|
240
298
|
|
|
299
|
+
// Auth0 mode: wait for AuthApiKeySync before rendering search UI / prefilters.
|
|
300
|
+
if (auth0.enabled && !isApiKeyReady && !isMissingAuthApiKey) {
|
|
301
|
+
return <div />;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
if (isMissingAuthApiKey) {
|
|
305
|
+
return (
|
|
306
|
+
<div className="full-height flex flex-col items-center justify-center bg-[#F3F4F8] p-6 text-center">
|
|
307
|
+
<p className="mt-2 max-w-md text-sm text-gray-600">
|
|
308
|
+
Search is currently unavailable for your account. Please contact your
|
|
309
|
+
administrator.
|
|
310
|
+
</p>
|
|
311
|
+
<button
|
|
312
|
+
type="button"
|
|
313
|
+
className="mt-6 bg-[#2B2C46] px-6 py-3 text-sm text-white"
|
|
314
|
+
onClick={() => {
|
|
315
|
+
clearAuthApiKey();
|
|
316
|
+
logout({
|
|
317
|
+
logoutParams: { returnTo: window.location.origin },
|
|
318
|
+
});
|
|
319
|
+
}}
|
|
320
|
+
>
|
|
321
|
+
{t('Log out')}
|
|
322
|
+
</button>
|
|
323
|
+
</div>
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
|
|
241
327
|
return (
|
|
242
328
|
<div className="full-height flex flex-col bg-[#F3F4F8]">
|
|
243
329
|
<div>
|
package/src/pages/Home.tsx
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { useEffect, useState } from 'react';
|
|
2
2
|
import { twMerge } from 'tailwind-merge';
|
|
3
|
+
import { useTranslation } from 'react-i18next';
|
|
4
|
+
import { ArrowRight } from 'lucide-react';
|
|
3
5
|
|
|
4
6
|
import DragDropFile from 'components/DragDropFile';
|
|
5
7
|
import MobileCameraCTA from 'components/MobileCameraCTA';
|
|
@@ -15,6 +17,7 @@ import ImageUpload from 'components/ImageUpload';
|
|
|
15
17
|
|
|
16
18
|
function Home() {
|
|
17
19
|
const settings = window.settings;
|
|
20
|
+
const { t } = useTranslation();
|
|
18
21
|
const { experienceVisualSearch, experienceVisualSearchImages, geoLocation } =
|
|
19
22
|
settings;
|
|
20
23
|
const navigate = useNavigate();
|
|
@@ -88,32 +91,75 @@ function Home() {
|
|
|
88
91
|
])}
|
|
89
92
|
>
|
|
90
93
|
{geoLocation && <LocationInfoPopup />}
|
|
91
|
-
<div className="relative flex
|
|
94
|
+
<div className="relative flex w-full flex-col items-center gap-6">
|
|
92
95
|
{settings.headerText && (
|
|
93
96
|
<div
|
|
94
97
|
className={twMerge([
|
|
95
98
|
'absolute',
|
|
96
|
-
'bottom-
|
|
99
|
+
'bottom-full',
|
|
100
|
+
'mb-[25px]',
|
|
101
|
+
'flex',
|
|
102
|
+
'flex-col',
|
|
103
|
+
'items-center',
|
|
104
|
+
'gap-3',
|
|
97
105
|
'text-primary',
|
|
98
106
|
'font-bold',
|
|
99
107
|
])}
|
|
100
108
|
>
|
|
101
|
-
<h1 className="text-[25px]">{settings.headerText}</h1>
|
|
109
|
+
<h1 className="text-[25px] m-0 mb-0">{settings.headerText}</h1>
|
|
110
|
+
{settings.scopes?.scopeID &&
|
|
111
|
+
Boolean(settings.scopes?.typePlateStepOne?.trim()) &&
|
|
112
|
+
Boolean(settings.scopes?.typePlateStepTwo?.trim()) && (
|
|
113
|
+
<div
|
|
114
|
+
className="flex w-full items-center justify-center"
|
|
115
|
+
aria-label="Search flow"
|
|
116
|
+
>
|
|
117
|
+
<div className="flex items-center gap-1.5 shrink-0">
|
|
118
|
+
<span className="flex h-5 w-5 shrink-0 items-center justify-center rounded-full bg-[#F3F4F8] border border-solid border-[#2B2C46] text-[#2B2C46] font-bold text-xs leading-none">
|
|
119
|
+
1
|
|
120
|
+
</span>
|
|
121
|
+
<span className="font-semibold text-[14px] leading-none text-[#2B2C46] whitespace-nowrap">
|
|
122
|
+
{t(settings.scopes!.typePlateStepOne!)}
|
|
123
|
+
</span>
|
|
124
|
+
</div>
|
|
125
|
+
|
|
126
|
+
<div
|
|
127
|
+
className="flex items-center flex-1 min-w-[20px] max-w-[60px] mx-2.5"
|
|
128
|
+
aria-hidden="true"
|
|
129
|
+
>
|
|
130
|
+
<div className="h-[1.5px] flex-1 bg-[#2B2C46]" />
|
|
131
|
+
<ArrowRight
|
|
132
|
+
className="h-3.5 w-3.5 -ml-1 text-[#2B2C46] shrink-0"
|
|
133
|
+
strokeWidth={2}
|
|
134
|
+
/>
|
|
135
|
+
</div>
|
|
136
|
+
|
|
137
|
+
<div className="flex items-center gap-1.5 shrink-0">
|
|
138
|
+
<span className="flex h-5 w-5 shrink-0 items-center justify-center rounded-full bg-[#2B2C46] text-white font-bold text-xs leading-none">
|
|
139
|
+
2
|
|
140
|
+
</span>
|
|
141
|
+
<span className="font-semibold text-[14px] leading-none text-[#2B2C46] whitespace-nowrap">
|
|
142
|
+
{t(settings.scopes!.typePlateStepTwo!)}
|
|
143
|
+
</span>
|
|
144
|
+
</div>
|
|
145
|
+
</div>
|
|
146
|
+
)}
|
|
102
147
|
</div>
|
|
103
148
|
)}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
149
|
+
<div className="flex items-center justify-center w-full gap-2">
|
|
150
|
+
<TextSearch className="h-[56px]" showImageUpload={false} />
|
|
151
|
+
<div
|
|
152
|
+
className={twMerge(
|
|
153
|
+
'h-14 w-14 flex items-center justify-center rounded-full bg-[#FFFFFF] hover:bg-[#F3F4F8] border border-solid border-[#DDDEE7]',
|
|
154
|
+
'shadow-ds-2',
|
|
155
|
+
)}
|
|
156
|
+
>
|
|
157
|
+
<ImageUpload />
|
|
158
|
+
</div>
|
|
113
159
|
</div>
|
|
114
160
|
</div>
|
|
115
161
|
<div className="max-w-[532px] relative w-full">
|
|
116
|
-
<DragDropFile />
|
|
162
|
+
{!settings.scopes?.scopeID && <DragDropFile />}
|
|
117
163
|
{settings.experienceVisualSearch && (
|
|
118
164
|
<ExperienceVisualSearchTrigger
|
|
119
165
|
experienceVisualSearchBlobs={experienceVisualSearchBlobs}
|
|
@@ -148,7 +194,7 @@ function Home() {
|
|
|
148
194
|
/>
|
|
149
195
|
)}
|
|
150
196
|
|
|
151
|
-
<div className="flex desktop:hidden w-full fixed bottom-12 px-2 gap-
|
|
197
|
+
<div className="flex desktop:hidden w-full fixed bottom-12 px-2 flex-col gap-4">
|
|
152
198
|
<TextSearch className="flex md:hidden w-full " />
|
|
153
199
|
</div>
|
|
154
200
|
</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 = '/';
|