@nyris/nyris-webapp 0.3.98 → 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 +40 -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.93fdbd6e.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 +40 -0
- package/src/common/assets/images/plate_info.png +0 -0
- package/src/components/Cart.tsx +41 -0
- 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 +12 -1
- package/src/components/Inquiry/InquiryModal.tsx +3 -0
- package/src/components/Pagination.tsx +33 -6
- package/src/components/PostFilter/PostFilter.tsx +8 -1
- 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 -18
- 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 +33 -0
- package/src/components/TextSearch.tsx +77 -11
- 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 +290 -12
- package/src/hooks/useMachinePartsListingPagination.ts +95 -0
- package/src/hooks/useResultEvaluator.ts +43 -19
- package/src/index.css +1 -1
- package/src/layouts/AppLayout.tsx +43 -3
- package/src/pages/Home.tsx +60 -14
- package/src/pages/Result.tsx +194 -14
- package/src/services/Feedback.ts +223 -35
- package/src/services/feedbackSubtypes.ts +63 -0
- package/src/services/filter.ts +1 -1
- 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 +71 -3
- package/src/services/vizo.ts +4 -3
- 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/fetchProductImage.ts +1 -0
- package/src/utils/normalizeChatAssistantResponse.ts +227 -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.81216c5d.css +0 -4
- package/build/static/css/main.81216c5d.css.map +0 -1
- package/build/static/js/main.93fdbd6e.js +0 -3
- /package/build/static/js/{main.93fdbd6e.js.LICENSE.txt → main.2339271b.js.LICENSE.txt} +0 -0
|
@@ -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';
|
|
@@ -27,7 +27,9 @@ import {
|
|
|
27
27
|
isNyrisApiKeyReady,
|
|
28
28
|
useAuthApiKeyStore,
|
|
29
29
|
} from 'utils/nyrisApiKey';
|
|
30
|
-
import useScopeStore from 'stores/scope/scopeStore';
|
|
30
|
+
import useScopeStore, { hasActiveMachineScope } from 'stores/scope/scopeStore';
|
|
31
|
+
import useConversationStore from 'stores/chat/conversationStore';
|
|
32
|
+
import { useResultEvaluator } from 'hooks/useResultEvaluator';
|
|
31
33
|
|
|
32
34
|
i18n.use(initReactI18next).init({
|
|
33
35
|
resources: translations,
|
|
@@ -66,9 +68,13 @@ function AppLayout(): JSX.Element {
|
|
|
66
68
|
const requestImages = useRequestStore(state => state.requestImages);
|
|
67
69
|
const showLoading = useRequestStore(state => state.showLoading);
|
|
68
70
|
|
|
69
|
-
const { status } = useInstantSearch();
|
|
71
|
+
const { status, results } = useInstantSearch();
|
|
70
72
|
const { items } = useHits();
|
|
71
73
|
|
|
74
|
+
const isAiModeOpen = useUiStore(state => state.isAiModeOpen);
|
|
75
|
+
const { evaluateResults } = useResultEvaluator();
|
|
76
|
+
const lastAlgoliaEvaluationRef = useRef<string | null>(null);
|
|
77
|
+
|
|
72
78
|
useEffect(() => {
|
|
73
79
|
i18n.changeLanguage(window.settings.language);
|
|
74
80
|
console.log('App version:', packageJson.version);
|
|
@@ -97,6 +103,40 @@ function AppLayout(): JSX.Element {
|
|
|
97
103
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
98
104
|
}, [items, scopeItemKeys]);
|
|
99
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
|
+
|
|
100
140
|
useEffect(() => {
|
|
101
141
|
if (status === 'loading' || status === 'stalled') {
|
|
102
142
|
setIsAlgoliaLoading(true);
|
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/Result.tsx
CHANGED
|
@@ -11,7 +11,7 @@ import SidePanel from 'components/SidePanel';
|
|
|
11
11
|
import Loading from 'components/Loading';
|
|
12
12
|
|
|
13
13
|
import Feedback from 'components/Feedback';
|
|
14
|
-
import {
|
|
14
|
+
import { trackResultFeedback } from 'services/Feedback';
|
|
15
15
|
import useRequestStore from 'stores/request/requestStore';
|
|
16
16
|
import RfqBanner from 'components/Rfq/RfqBanner';
|
|
17
17
|
import InquiryBanner from 'components/Inquiry/InquiryBanner';
|
|
@@ -22,15 +22,24 @@ import ImagePreview from 'components/ImagePreview';
|
|
|
22
22
|
import PoweredBy from 'components/PoweredBy';
|
|
23
23
|
import CurrentRefinements from 'components/CurrentRefinements';
|
|
24
24
|
import { GoBackButton } from 'components/GoBackButton';
|
|
25
|
+
import { useNavigate } from 'react-router';
|
|
25
26
|
import { useCurrentRefinements } from 'react-instantsearch';
|
|
26
27
|
import CustomCamera from 'components/CustomCameraDrawer';
|
|
27
28
|
import { useTranslation } from 'react-i18next';
|
|
28
29
|
import { useImageSearch } from 'hooks/useImageSearch';
|
|
30
|
+
import { useMachinePartsListingPagination } from 'hooks/useMachinePartsListingPagination';
|
|
29
31
|
import MatchNotificationBanner from 'components/MatchNotificationBanner';
|
|
30
32
|
import ImageUpload from 'components/ImageUpload';
|
|
31
33
|
import SmartFilter from 'components/SmartFilter';
|
|
32
34
|
import { ChatAssistant, MobileChatAssistant } from 'components/ChatAssistant';
|
|
33
35
|
import IdentifiedAttributes from 'components/IdentifiedAttributes';
|
|
36
|
+
import useScopeStore from 'stores/scope/scopeStore';
|
|
37
|
+
import { TypePlateUpload } from 'components/TypePlateUpload';
|
|
38
|
+
import { ScopeSelectModal } from 'components/ScopeSelect/ScopeSelectModal';
|
|
39
|
+
|
|
40
|
+
const DEFAULT_SCOPE_RESULTS_LABEL =
|
|
41
|
+
'Searching within parts for {{scope_id}}. ' +
|
|
42
|
+
'You can search with keyword or image to find the right item';
|
|
34
43
|
|
|
35
44
|
function Results() {
|
|
36
45
|
const settings = window.settings;
|
|
@@ -55,8 +64,10 @@ function Results() {
|
|
|
55
64
|
const showFeedback = useUiStore(state => state.showFeedback);
|
|
56
65
|
const setIsFindApiLoading = useUiStore(state => state.setIsFindApiLoading);
|
|
57
66
|
const isAiModeOpen = useUiStore(state => state.isAiModeOpen);
|
|
67
|
+
const requestQuality = useResultStore(state => state.requestQuality);
|
|
68
|
+
const [isRequestQualityExpanded, setIsRequestQualityExpanded] =
|
|
69
|
+
useState(true);
|
|
58
70
|
|
|
59
|
-
const requestId = useResultStore(state => state.requestId);
|
|
60
71
|
const imageAnalysis = useResultStore(state => state.imageAnalysis);
|
|
61
72
|
|
|
62
73
|
const specificationFilter = useRequestStore(
|
|
@@ -64,13 +75,32 @@ function Results() {
|
|
|
64
75
|
);
|
|
65
76
|
const requestImages = useRequestStore(state => state.requestImages);
|
|
66
77
|
const specifications = useRequestStore(state => state.specifications);
|
|
67
|
-
const query = useRequestStore(state => state.query);
|
|
68
78
|
const regions = useRequestStore(state => state.regions);
|
|
69
|
-
|
|
79
|
+
const query = useRequestStore(state => state.query);
|
|
80
|
+
const scopeItemKeys = useScopeStore(state => state.itemKeys);
|
|
81
|
+
const activeScopeId = useScopeStore(state => state.scopeId);
|
|
82
|
+
const resetScope = useScopeStore(state => state.resetScope);
|
|
83
|
+
const navigate = useNavigate();
|
|
70
84
|
const [showPostFilter, setShowPostFilter] = useState(false);
|
|
71
85
|
const [isOpenModalCamera, setOpenModalCamera] = useState<boolean>(false);
|
|
72
86
|
|
|
73
87
|
const isShowFilter = query || requestImages[0];
|
|
88
|
+
const isScopeFeatureEnabled = Boolean(window.settings?.scopes?.scopeID);
|
|
89
|
+
const isScopeFilterActive =
|
|
90
|
+
isScopeFeatureEnabled && scopeItemKeys.length > 0 && Boolean(activeScopeId);
|
|
91
|
+
const [isTypePlateOpen, setIsTypePlateOpen] = useState(false);
|
|
92
|
+
const [isNoScopeBannerDismissed, setIsNoScopeBannerDismissed] =
|
|
93
|
+
useState<boolean>(false);
|
|
94
|
+
|
|
95
|
+
useEffect(() => {
|
|
96
|
+
if (isScopeFilterActive) {
|
|
97
|
+
setIsNoScopeBannerDismissed(false);
|
|
98
|
+
}
|
|
99
|
+
}, [isScopeFilterActive]);
|
|
100
|
+
|
|
101
|
+
useEffect(() => {
|
|
102
|
+
setIsNoScopeBannerDismissed(false);
|
|
103
|
+
}, [query, requestImages]);
|
|
74
104
|
|
|
75
105
|
const { items } = useCurrentRefinements();
|
|
76
106
|
|
|
@@ -80,6 +110,46 @@ function Results() {
|
|
|
80
110
|
document.title = 'Search results';
|
|
81
111
|
}, [cadenas?.cadenas3dWebView, cadenas?.cadenasAPIKey]);
|
|
82
112
|
const { singleImageSearch } = useImageSearch();
|
|
113
|
+
const listingPagination = useMachinePartsListingPagination();
|
|
114
|
+
// Scoped keyword/image results come from Find API, which has no paging, so
|
|
115
|
+
// the Algolia page-size control would only change hidden results.
|
|
116
|
+
const isFindApiSearchUnderAlgolia =
|
|
117
|
+
isScopeFilterActive &&
|
|
118
|
+
Boolean(settings.algolia?.enabled) &&
|
|
119
|
+
!listingPagination.isListing;
|
|
120
|
+
|
|
121
|
+
const clearActiveScopeFilter = () => {
|
|
122
|
+
const keyword = (useRequestStore.getState().query || '').trim();
|
|
123
|
+
const searchImage = useRequestStore.getState().requestImages[0];
|
|
124
|
+
const region = useRequestStore.getState().regions[0];
|
|
125
|
+
resetScope();
|
|
126
|
+
if (searchImage) {
|
|
127
|
+
void singleImageSearch({
|
|
128
|
+
image: searchImage,
|
|
129
|
+
imageRegion: region,
|
|
130
|
+
settings,
|
|
131
|
+
showFeedback: false,
|
|
132
|
+
newSearch: false,
|
|
133
|
+
});
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
if (keyword) {
|
|
137
|
+
if (window.settings.algolia?.enabled) {
|
|
138
|
+
useRequestStore.getState().setAlgoliaFilter('');
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
void singleImageSearch({
|
|
142
|
+
text: keyword,
|
|
143
|
+
settings,
|
|
144
|
+
showFeedback: false,
|
|
145
|
+
newSearch: false,
|
|
146
|
+
});
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
useResultStore.getState().reset();
|
|
150
|
+
useRequestStore.getState().reset();
|
|
151
|
+
navigate('/');
|
|
152
|
+
};
|
|
83
153
|
|
|
84
154
|
const getImageFromUrl = async (
|
|
85
155
|
url: string,
|
|
@@ -134,7 +204,7 @@ function Results() {
|
|
|
134
204
|
}, 3000);
|
|
135
205
|
setFeedbackStatus('submitted');
|
|
136
206
|
setShowFeedback(false);
|
|
137
|
-
|
|
207
|
+
trackResultFeedback(data);
|
|
138
208
|
};
|
|
139
209
|
|
|
140
210
|
useEffect(() => {
|
|
@@ -236,6 +306,33 @@ function Results() {
|
|
|
236
306
|
/>
|
|
237
307
|
</div>
|
|
238
308
|
)}
|
|
309
|
+
{window.settings.requestQuality && requestQuality && (
|
|
310
|
+
<div className="mx-4 rounded-3xl bg-[#F3F4F8] border border-[#e4e3ff] p-3.5 text-[#3B3E5F]">
|
|
311
|
+
<button
|
|
312
|
+
type="button"
|
|
313
|
+
className="w-full flex items-center justify-between gap-2"
|
|
314
|
+
onClick={() =>
|
|
315
|
+
setIsRequestQualityExpanded(prev => !prev)
|
|
316
|
+
}
|
|
317
|
+
>
|
|
318
|
+
<span className="text-[#3B3E5F] font-bold text-sm leading-4">
|
|
319
|
+
Request Image Quality
|
|
320
|
+
</span>
|
|
321
|
+
<Icon
|
|
322
|
+
name="arrow_down"
|
|
323
|
+
className={twMerge(
|
|
324
|
+
'fill-[#3B3E5F] w-3 h-3 shrink-0 transition-transform',
|
|
325
|
+
isRequestQualityExpanded && 'rotate-180',
|
|
326
|
+
)}
|
|
327
|
+
/>
|
|
328
|
+
</button>
|
|
329
|
+
{isRequestQualityExpanded && (
|
|
330
|
+
<div className="text-[#767A9F] text-sm mt-2.5">
|
|
331
|
+
{requestQuality}
|
|
332
|
+
</div>
|
|
333
|
+
)}
|
|
334
|
+
</div>
|
|
335
|
+
)}
|
|
239
336
|
{window.settings.showImageDetails &&
|
|
240
337
|
(imageAnalysis?.imageDescription ||
|
|
241
338
|
Object.keys(imageAnalysis?.specification || {}).length >
|
|
@@ -354,6 +451,69 @@ function Results() {
|
|
|
354
451
|
<GoBackButton className="col-span-full mb-2 desktop:mb-0 mt-2 desktop:mt-0" />
|
|
355
452
|
<CurrentRefinements className="col-span-full" />
|
|
356
453
|
<MatchNotificationBanner className="col-span-full" />
|
|
454
|
+
{isScopeFilterActive && (
|
|
455
|
+
<div className="col-span-full flex items-center justify-between gap-4 rounded-2xl border border-solid border-theme-primary bg-white px-4 py-[.8rem] text-sm text-primary">
|
|
456
|
+
<p className="min-w-0 flex-1 font-normal text-left">
|
|
457
|
+
{t(
|
|
458
|
+
window.settings.scopes
|
|
459
|
+
?.typePlateResultsScopeLabel ||
|
|
460
|
+
DEFAULT_SCOPE_RESULTS_LABEL,
|
|
461
|
+
{ scope_id: activeScopeId },
|
|
462
|
+
)}
|
|
463
|
+
</p>
|
|
464
|
+
<button
|
|
465
|
+
type="button"
|
|
466
|
+
className="flex h-5 w-5 shrink-0 items-center justify-center rounded-sm text-primary hover:text-black cursor-pointer"
|
|
467
|
+
onClick={clearActiveScopeFilter}
|
|
468
|
+
aria-label={t(
|
|
469
|
+
window.settings.scopes
|
|
470
|
+
?.typePlateResultsClearScopeLabel ||
|
|
471
|
+
'Search all products',
|
|
472
|
+
)}
|
|
473
|
+
title={t(
|
|
474
|
+
window.settings.scopes
|
|
475
|
+
?.typePlateResultsClearScopeLabel ||
|
|
476
|
+
'Search all products',
|
|
477
|
+
)}
|
|
478
|
+
>
|
|
479
|
+
<Icon name="close" className="h-3 w-3" />
|
|
480
|
+
</button>
|
|
481
|
+
</div>
|
|
482
|
+
)}
|
|
483
|
+
{isScopeFeatureEnabled &&
|
|
484
|
+
!isScopeFilterActive &&
|
|
485
|
+
!isNoScopeBannerDismissed && (
|
|
486
|
+
<div className="col-span-full flex items-center justify-between gap-4 rounded-2xl border border-solid border-theme-primary bg-white px-4 py-2.5 text-sm text-[#2B2C46]">
|
|
487
|
+
<div className="min-w-0 flex-1 text-left">
|
|
488
|
+
<p className="font-normal text-[#2B2C46]">
|
|
489
|
+
{t(
|
|
490
|
+
window.settings.scopes
|
|
491
|
+
?.typePlateResultsNoScopeLabel ||
|
|
492
|
+
'Scan your type plate to see only parts for your equipment.',
|
|
493
|
+
)}
|
|
494
|
+
</p>
|
|
495
|
+
</div>
|
|
496
|
+
<div className="flex shrink-0 items-center gap-2 desktop:gap-3">
|
|
497
|
+
<TypePlateUpload
|
|
498
|
+
alternativeView
|
|
499
|
+
showModal={false}
|
|
500
|
+
open={isTypePlateOpen}
|
|
501
|
+
onOpenChange={setIsTypePlateOpen}
|
|
502
|
+
/>
|
|
503
|
+
<button
|
|
504
|
+
type="button"
|
|
505
|
+
className="flex h-5 w-5 shrink-0 items-center justify-center rounded-sm text-primary hover:text-black cursor-pointer"
|
|
506
|
+
onClick={() =>
|
|
507
|
+
setIsNoScopeBannerDismissed(true)
|
|
508
|
+
}
|
|
509
|
+
aria-label={t('Close')}
|
|
510
|
+
title={t('Close')}
|
|
511
|
+
>
|
|
512
|
+
<Icon name="close" className="h-3 w-3" />
|
|
513
|
+
</button>
|
|
514
|
+
</div>
|
|
515
|
+
</div>
|
|
516
|
+
)}
|
|
357
517
|
<ProductList />
|
|
358
518
|
</div>
|
|
359
519
|
|
|
@@ -386,15 +546,26 @@ function Results() {
|
|
|
386
546
|
</div>
|
|
387
547
|
)}
|
|
388
548
|
<div>
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
549
|
+
{listingPagination.isListing ? (
|
|
550
|
+
<Pagination
|
|
551
|
+
isLoading={isFindApiLoading || isAlgoliaLoading}
|
|
552
|
+
pageCount={listingPagination.pageCount}
|
|
553
|
+
pageIndex={listingPagination.pageIndex}
|
|
554
|
+
onPageChange={listingPagination.changePage}
|
|
555
|
+
/>
|
|
556
|
+
) : (
|
|
557
|
+
!isScopeFilterActive && (
|
|
558
|
+
<Pagination
|
|
559
|
+
isLoading={isFindApiLoading || isAlgoliaLoading}
|
|
560
|
+
className={
|
|
561
|
+
productsFromAlgolia.length === 0 &&
|
|
562
|
+
!isAlgoliaLoading
|
|
563
|
+
? 'opacity-0 hidden'
|
|
564
|
+
: ''
|
|
565
|
+
}
|
|
566
|
+
/>
|
|
567
|
+
)
|
|
568
|
+
)}
|
|
398
569
|
<div
|
|
399
570
|
style={{
|
|
400
571
|
display: 'flex',
|
|
@@ -442,6 +613,9 @@ function Results() {
|
|
|
442
613
|
{ label: '40', value: 40 },
|
|
443
614
|
{ label: '50', value: 50 },
|
|
444
615
|
]}
|
|
616
|
+
onFindApiPageSizeChange={listingPagination.changePageSize}
|
|
617
|
+
useFindApiPageSize={listingPagination.isListing}
|
|
618
|
+
showPageSize={!isFindApiSearchUnderAlgolia}
|
|
445
619
|
/>
|
|
446
620
|
</div>
|
|
447
621
|
</div>
|
|
@@ -571,6 +745,12 @@ function Results() {
|
|
|
571
745
|
openModal={showPostFilter}
|
|
572
746
|
handleClose={() => setShowPostFilter(false)}
|
|
573
747
|
/>
|
|
748
|
+
{isScopeFeatureEnabled && (
|
|
749
|
+
<ScopeSelectModal
|
|
750
|
+
openModal={isTypePlateOpen}
|
|
751
|
+
handleClose={() => setIsTypePlateOpen(false)}
|
|
752
|
+
/>
|
|
753
|
+
)}
|
|
574
754
|
<CustomCamera
|
|
575
755
|
show={isOpenModalCamera}
|
|
576
756
|
onClose={() => {
|