@nyris/nyris-webapp 0.3.90 → 0.3.92
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/data/related-parts.json +83 -0
- package/build/index.html +1 -1
- package/build/js/settings.example.js +3 -0
- package/build/static/css/main.5ea01690.css +4 -0
- package/build/static/css/main.5ea01690.css.map +1 -0
- package/build/static/js/main.36b77705.js +3 -0
- package/build/static/js/{main.cede3ae1.js.map → main.36b77705.js.map} +1 -1
- package/package.json +4 -3
- package/public/data/related-parts.json +83 -0
- package/public/js/settings.example.js +3 -0
- package/src/App.test.tsx +0 -1
- package/src/App.tsx +0 -1
- package/src/assets/arrow_down_expanded.svg +3 -0
- package/src/assets/arrow_enter.svg +3 -0
- package/src/assets/camera.svg +3 -0
- package/src/assets/close.svg +3 -0
- package/src/assets/enter.svg +3 -0
- package/src/assets/refresh.svg +3 -0
- package/src/assets/vizo_avatar.svg +16 -0
- package/src/components/Cadenas/CadenasWebViewer.tsx +1 -1
- package/src/components/Cart.tsx +48 -36
- package/src/components/ChatAssistant/ChatAssistant.tsx +289 -0
- package/src/components/ChatAssistant/MobileChatAssistant.tsx +291 -0
- package/src/components/ChatAssistant/OptionChip.tsx +78 -0
- package/src/components/ChatAssistant/index.ts +3 -0
- package/src/components/ChatAssistant/useChatAssistantLogic.ts +745 -0
- package/src/components/CurrentRefinements.tsx +2 -2
- package/src/components/CustomCameraDrawer.tsx +56 -13
- package/src/components/DragDropFile.tsx +5 -5
- package/src/components/ExperienceVisualSearch/ExperienceVisualSearch.tsx +1 -1
- package/src/components/Header.tsx +116 -96
- package/src/components/Hint.tsx +1 -2
- package/src/components/HitsPerPage.tsx +9 -3
- package/src/components/ImagePreview.tsx +32 -17
- package/src/components/ImageUpload.tsx +16 -8
- package/src/components/Inquiry/InquiryBanner.tsx +1 -1
- package/src/components/Inquiry/InquiryModal.tsx +35 -29
- package/src/components/ItemSpecification.tsx +58 -126
- package/src/components/LocationInfoPopup.tsx +33 -33
- package/src/components/MatchNotificationBanner.tsx +90 -36
- package/src/components/PostFilter/PostFilter.tsx +1 -1
- package/src/components/PostFilter/PostFilterComponent.tsx +0 -1
- package/src/components/PostFilter/PostFilterFindApi.tsx +0 -1
- package/src/components/PoweredBy.tsx +1 -1
- package/src/components/PreFilter/PreFilter.tsx +14 -3
- package/src/components/PreFilter/PreFilterModal.tsx +0 -1
- package/src/components/Product/Product.tsx +15 -11
- package/src/components/Product/ProductAttribute.tsx +4 -5
- package/src/components/Product/ProductDetailViewModal.tsx +2 -4
- package/src/components/Product/ProductList.tsx +26 -13
- package/src/components/Rfq/RfqModal.tsx +1 -1
- package/src/components/SidePanel.tsx +124 -91
- package/src/components/SmartFilter.tsx +320 -0
- package/src/components/TextSearch.tsx +134 -70
- package/src/components/UploadDisclaimer.tsx +1 -1
- package/src/hooks/useBadResultsRecovery.ts +407 -0
- package/src/hooks/useEffectiveGroundingResults.ts +54 -0
- package/src/hooks/useGoodResultsChat.ts +651 -0
- package/src/hooks/useGroundedSearch.ts +88 -0
- package/src/hooks/useImageSearch.ts +139 -187
- package/src/hooks/useResultEvaluator.ts +417 -0
- package/src/index.css +1 -1
- package/src/index.tsx +0 -1
- package/src/layouts/AppLayout.tsx +53 -2
- package/src/pages/Home.tsx +11 -52
- package/src/pages/Login.tsx +1 -2
- package/src/pages/Logout.tsx +1 -1
- package/src/pages/Result.tsx +198 -200
- package/src/providers/AuthProvider.tsx +0 -1
- package/src/services/Feedback.ts +1 -1
- package/src/services/visualSearch.ts +0 -21
- package/src/services/vizo.ts +192 -4
- package/src/stores/chat/chatStore.ts +150 -0
- package/src/stores/chat/conversationStore.ts +300 -0
- package/src/stores/request/Misc/misc.slice.ts +2 -2
- package/src/stores/request/filter/filter.slice.ts +8 -8
- package/src/stores/request/query/query.slice.ts +2 -2
- package/src/stores/request/requestImage/requestImage.slice.ts +6 -6
- package/src/stores/request/specifications/specifications.slice.ts +10 -7
- package/src/stores/result/detectedRegions/detectedRegions.slice.ts +1 -1
- package/src/stores/result/prodcuts/products.initialState.ts +12 -0
- package/src/stores/result/prodcuts/products.slice.ts +28 -8
- package/src/stores/result/session/session.slice.ts +2 -2
- package/src/stores/smartFilters/smartFiltersStore.ts +270 -0
- package/src/stores/types.ts +41 -0
- package/src/stores/ui/ai/ai.initialState.ts +5 -0
- package/src/stores/ui/ai/ai.slice.ts +15 -0
- package/src/stores/ui/banner/banner.initialState.ts +6 -0
- package/src/stores/ui/banner/banner.slice.ts +14 -0
- package/src/stores/ui/feedback/feedback.slice.ts +1 -1
- package/src/stores/ui/loading/loading.slice.ts +4 -4
- package/src/stores/ui/uiStore.ts +7 -1
- package/src/styles/product.scss +0 -2
- package/src/types.ts +3 -7
- package/src/utils/cropImageToBase64.ts +32 -0
- package/src/utils/fetchProductImage.ts +109 -0
- package/src/utils/imageConverters.ts +124 -0
- package/src/utils/relatedParts.ts +35 -0
- package/src/utils/specificationFilter.ts +1 -5
- package/tailwind.config.js +3 -2
- package/build/static/css/main.734b52e1.css +0 -4
- package/build/static/css/main.734b52e1.css.map +0 -1
- package/build/static/js/main.cede3ae1.js +0 -3
- package/src/utils/addAssets.ts +0 -40
- /package/build/static/js/{main.cede3ae1.js.LICENSE.txt → main.36b77705.js.LICENSE.txt} +0 -0
|
@@ -12,7 +12,13 @@ import { useCadSearch } from 'hooks/useCadSearch';
|
|
|
12
12
|
import { isCadFile } from '@nyris/nyris-api';
|
|
13
13
|
import UploadDisclaimer from './UploadDisclaimer';
|
|
14
14
|
|
|
15
|
-
function ImageUpload({
|
|
15
|
+
function ImageUpload({
|
|
16
|
+
onCameraClick,
|
|
17
|
+
disableDisclaimer,
|
|
18
|
+
}: {
|
|
19
|
+
onCameraClick?: () => void;
|
|
20
|
+
disableDisclaimer?: boolean;
|
|
21
|
+
}) {
|
|
16
22
|
const settings = window.settings;
|
|
17
23
|
const user = useAuth0().user;
|
|
18
24
|
|
|
@@ -66,12 +72,13 @@ function ImageUpload({ onCameraClick }: { onCameraClick?: () => void }) {
|
|
|
66
72
|
]);
|
|
67
73
|
|
|
68
74
|
const showDisclaimerDisabled = useMemo(() => {
|
|
75
|
+
if (disableDisclaimer) return true;
|
|
69
76
|
const disclaimer = localStorage.getItem('upload-disclaimer-suite');
|
|
70
77
|
if (requestImages.length === 0) return true;
|
|
71
78
|
if (!disclaimer) return false;
|
|
72
79
|
return disclaimer === 'dont-show';
|
|
73
80
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
74
|
-
}, [showDisclaimer, requestImages]);
|
|
81
|
+
}, [showDisclaimer, requestImages, disableDisclaimer]);
|
|
75
82
|
|
|
76
83
|
const { singleImageSearch } = useImageSearch();
|
|
77
84
|
const { cadSearch } = useCadSearch();
|
|
@@ -81,7 +88,7 @@ function ImageUpload({ onCameraClick }: { onCameraClick?: () => void }) {
|
|
|
81
88
|
setQuery('');
|
|
82
89
|
|
|
83
90
|
if (isCadFile(files[0])) {
|
|
84
|
-
cadSearch({ file: files[0], settings, newSearch: true }).then(
|
|
91
|
+
cadSearch({ file: files[0], settings, newSearch: true }).then(() => {});
|
|
85
92
|
|
|
86
93
|
return;
|
|
87
94
|
}
|
|
@@ -191,14 +198,15 @@ function ImageUpload({ onCameraClick }: { onCameraClick?: () => void }) {
|
|
|
191
198
|
<Tooltip content={t('Search with an image')}>
|
|
192
199
|
<label
|
|
193
200
|
className={twMerge(
|
|
194
|
-
'
|
|
195
|
-
'w-10 h-10 flex justify-center items-center cursor-pointer rounded-full bg-gray-100 desktop:bg-transparent hover:bg-gray-100',
|
|
201
|
+
'min-w-10 min-h-10 flex justify-center items-center cursor-pointer rounded-full bg-gray-100 desktop:bg-transparent hover:bg-gray-100',
|
|
196
202
|
location.pathname === '/result' && 'desktop:w-8 desktop:h-8',
|
|
203
|
+
location.pathname !== '/result' &&
|
|
204
|
+
'desktop:bg-[#FAFAFA] desktop:hover:bg-[#FAFAFA]',
|
|
197
205
|
)}
|
|
198
206
|
htmlFor={
|
|
199
207
|
showDisclaimerDisabled && !onCameraClick ? 'icon-button-file' : ''
|
|
200
208
|
}
|
|
201
|
-
onClick={
|
|
209
|
+
onClick={() => {
|
|
202
210
|
if (!showDisclaimerDisabled) {
|
|
203
211
|
// disclaimer
|
|
204
212
|
setShowDisclaimer(true);
|
|
@@ -207,12 +215,12 @@ function ImageUpload({ onCameraClick }: { onCameraClick?: () => void }) {
|
|
|
207
215
|
}
|
|
208
216
|
}}
|
|
209
217
|
>
|
|
210
|
-
<Icon name="
|
|
218
|
+
<Icon name="camera_ai" width={16} height={16} fill="#3B3E5F" />
|
|
211
219
|
</label>
|
|
212
220
|
</Tooltip>
|
|
213
221
|
</div>
|
|
214
222
|
|
|
215
|
-
{showDisclaimer && (
|
|
223
|
+
{showDisclaimer && !disableDisclaimer && (
|
|
216
224
|
<UploadDisclaimer
|
|
217
225
|
onClose={() => {
|
|
218
226
|
setShowDisclaimer(false);
|
|
@@ -66,20 +66,22 @@ 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
|
+
'is_nameplate',
|
|
74
|
+
'prefilter_value',
|
|
75
|
+
]);
|
|
73
76
|
setInformation(
|
|
74
|
-
Object
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
);
|
|
77
|
+
Object.entries(omittedSpecification).reduce(
|
|
78
|
+
(acc, [k, v]) =>
|
|
79
|
+
v == null || (typeof v === 'string' && v.trim() === '')
|
|
80
|
+
? acc
|
|
81
|
+
: `${acc}${k}: ${v}\n`,
|
|
82
|
+
'',
|
|
83
|
+
),
|
|
84
|
+
);
|
|
83
85
|
}, [specifications]);
|
|
84
86
|
|
|
85
87
|
useEffect(() => emailjs.init('SMGihPnuEGcYLm0V4'), []);
|
|
@@ -94,32 +96,36 @@ export default function InquiryModal({
|
|
|
94
96
|
const croppedImage = requestImage
|
|
95
97
|
? getCroppedCanvas(requestImage, selectedRegion)
|
|
96
98
|
: null;
|
|
97
|
-
const nameplateImageCanvas = nameplateImage
|
|
98
|
-
|
|
99
|
+
const nameplateImageCanvas = nameplateImage
|
|
100
|
+
? await createImage(nameplateImage)
|
|
101
|
+
: null;
|
|
102
|
+
|
|
99
103
|
const serviceId = 'service_zfsxshi';
|
|
100
104
|
setIsInquiryModalOpen(false);
|
|
101
105
|
const templateId = settings.support?.emailTemplateId;
|
|
102
106
|
if (templateId) {
|
|
103
|
-
const body = !specifications?.is_nameplate
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
107
|
+
const body = !specifications?.is_nameplate
|
|
108
|
+
? {
|
|
109
|
+
email_id: email.trim(),
|
|
110
|
+
information_text: information ? information : '<not specified>',
|
|
111
|
+
request_image: croppedImage?.toDataURL(),
|
|
112
|
+
prefilter_values: preFilterValues?.length
|
|
113
|
+
? preFilterValues.join(', ')
|
|
114
|
+
: '<not specified>',
|
|
115
|
+
}
|
|
116
|
+
: {
|
|
117
|
+
email_id: email.trim(),
|
|
118
|
+
information_text: information,
|
|
119
|
+
request_image: croppedImage?.toDataURL() || '',
|
|
120
|
+
typeplate_image: nameplateImageCanvas?.toDataURL() || '',
|
|
121
|
+
prefilter_values: specifications.prefilter_value,
|
|
122
|
+
};
|
|
117
123
|
try {
|
|
118
124
|
await emailjs.send(serviceId, templateId, body);
|
|
119
125
|
ToastHelper.success(t('Request sent successfully'));
|
|
120
126
|
} catch (error) {
|
|
121
127
|
toast(
|
|
122
|
-
|
|
128
|
+
() => {
|
|
123
129
|
return (
|
|
124
130
|
<div
|
|
125
131
|
style={{
|
|
@@ -174,7 +180,7 @@ export default function InquiryModal({
|
|
|
174
180
|
return (
|
|
175
181
|
<Dialog
|
|
176
182
|
open={isInquiryModalOpen}
|
|
177
|
-
onOpenChange={(
|
|
183
|
+
onOpenChange={() => {
|
|
178
184
|
setIsInquiryModalOpen(false);
|
|
179
185
|
}}
|
|
180
186
|
>
|
|
@@ -4,7 +4,6 @@ import { twMerge } from 'tailwind-merge';
|
|
|
4
4
|
import useRequestStore from '../stores/request/requestStore';
|
|
5
5
|
import useResultStore from '../stores/result/resultStore';
|
|
6
6
|
import { Icon } from '@nyris/nyris-react-components';
|
|
7
|
-
import { useMediaQuery } from 'react-responsive';
|
|
8
7
|
|
|
9
8
|
const ItemSpecification = ({
|
|
10
9
|
attr,
|
|
@@ -16,7 +15,6 @@ const ItemSpecification = ({
|
|
|
16
15
|
return ref.header.toLocaleLowerCase() === attr.toLocaleLowerCase();
|
|
17
16
|
});
|
|
18
17
|
const attribute = refinement?.length ? refinement[0].attribute : 'none';
|
|
19
|
-
const isMobile = useMediaQuery({ query: '(max-width: 776px)' });
|
|
20
18
|
const { refine } = useRefinementList({
|
|
21
19
|
attribute,
|
|
22
20
|
operator: 'or',
|
|
@@ -24,140 +22,74 @@ const ItemSpecification = ({
|
|
|
24
22
|
limit: 1,
|
|
25
23
|
});
|
|
26
24
|
|
|
27
|
-
return
|
|
28
|
-
|
|
25
|
+
return (
|
|
26
|
+
<Tooltip
|
|
27
|
+
content={
|
|
28
|
+
specificationFilter[attr]
|
|
29
|
+
? 'Filter applied. Clear to choose a different value.'
|
|
30
|
+
: 'Click to apply as a search filter.'
|
|
31
|
+
}
|
|
32
|
+
delayDuration={1000}
|
|
33
|
+
disabled={!value}
|
|
34
|
+
>
|
|
29
35
|
<div
|
|
30
|
-
className=
|
|
31
|
-
|
|
36
|
+
className={twMerge(
|
|
37
|
+
`px-3.5 py-2 bg-[#E7E8F1] rounded-lg flex justify-center items-center gap-1.5`,
|
|
38
|
+
'border border-solid border-transparent hover:border-[#E7E8F1]',
|
|
39
|
+
'cursor-pointer',
|
|
40
|
+
'w-fit',
|
|
41
|
+
specificationFilter[attr]
|
|
42
|
+
? ' bg-[#545987] '
|
|
43
|
+
: 'hover:shadow-[0_0_6px_0_#DDDEE7]',
|
|
44
|
+
)}
|
|
45
|
+
onClick={() => {
|
|
46
|
+
if (!value) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (attribute !== 'none') {
|
|
50
|
+
refine(value);
|
|
51
|
+
} else {
|
|
52
|
+
const setSpecificationFilter =
|
|
53
|
+
useRequestStore.getState().setSpecificationFilter;
|
|
54
|
+
|
|
55
|
+
const setSpecificationFilteredProducts =
|
|
56
|
+
useResultStore.getState().setSpecificationFilteredProducts;
|
|
57
|
+
|
|
58
|
+
if (specificationFilter[attr]) {
|
|
59
|
+
setSpecificationFilter({});
|
|
60
|
+
setSpecificationFilteredProducts([]);
|
|
61
|
+
// setProducts(results);
|
|
62
|
+
} else {
|
|
63
|
+
setSpecificationFilter({
|
|
64
|
+
[attr]: value,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}}
|
|
32
69
|
>
|
|
33
|
-
<div className="pl-1 inline-flex justify-center items-center gap-2.5">
|
|
34
|
-
<div className="justify-start text-[#3B3E5F] text-sm font-semibold">
|
|
35
|
-
{attr}
|
|
36
|
-
</div>
|
|
37
|
-
</div>
|
|
38
70
|
<div
|
|
39
71
|
className={twMerge(
|
|
40
|
-
|
|
41
|
-
'
|
|
72
|
+
'justify-start text-[#767A9F] text-sm leading-none px-0.5',
|
|
73
|
+
'font-normal',
|
|
42
74
|
specificationFilter[attr]
|
|
43
|
-
? '
|
|
44
|
-
: '',
|
|
75
|
+
? 'text-white hover:px-0.5'
|
|
76
|
+
: 'hover:font-semibold',
|
|
77
|
+
'leading-4',
|
|
78
|
+
'flex gap-2 items-center',
|
|
45
79
|
)}
|
|
46
|
-
onClick={() => {
|
|
47
|
-
if (!value) {
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
if (attribute !== 'none') {
|
|
51
|
-
refine(value);
|
|
52
|
-
} else {
|
|
53
|
-
const setSpecificationFilter =
|
|
54
|
-
useRequestStore.getState().setSpecificationFilter;
|
|
55
|
-
|
|
56
|
-
const setSpecificationFilteredProducts =
|
|
57
|
-
useResultStore.getState().setSpecificationFilteredProducts;
|
|
58
|
-
|
|
59
|
-
if (specificationFilter[attr]) {
|
|
60
|
-
setSpecificationFilter({});
|
|
61
|
-
setSpecificationFilteredProducts([]);
|
|
62
|
-
// setProducts(results);
|
|
63
|
-
} else {
|
|
64
|
-
setSpecificationFilter({
|
|
65
|
-
[attr]: value,
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
}}
|
|
70
80
|
>
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
name="close"
|
|
76
|
-
className={twMerge(
|
|
77
|
-
'w-3 h-3 text-white',
|
|
78
|
-
specificationFilter[attr] ? 'block' : 'hidden',
|
|
79
|
-
)}
|
|
80
|
-
/>
|
|
81
|
-
</div>
|
|
82
|
-
</div>
|
|
83
|
-
</div>
|
|
84
|
-
</div>
|
|
85
|
-
</>
|
|
86
|
-
) : (
|
|
87
|
-
<div key={attr} className="flex justify-between w-full gap-2 items-center">
|
|
88
|
-
<div className="self-stretch inline-flex justify-start items-center gap-1.5">
|
|
89
|
-
<div className="justify-start text-[#3B3E5F] text-xs font-semibold">
|
|
90
|
-
{attr}:
|
|
91
|
-
</div>
|
|
92
|
-
<Tooltip
|
|
93
|
-
content={
|
|
94
|
-
specificationFilter[attr]
|
|
95
|
-
? 'Filter applied. Clear to choose a different value.'
|
|
96
|
-
: 'Click to apply as a search filter.'
|
|
97
|
-
}
|
|
98
|
-
delayDuration={1000}
|
|
99
|
-
disabled={!value}
|
|
100
|
-
>
|
|
101
|
-
<div
|
|
81
|
+
{imageAnalysis?.specification[attr] || 'N/A'}
|
|
82
|
+
|
|
83
|
+
<Icon
|
|
84
|
+
name="close"
|
|
102
85
|
className={twMerge(
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
'cursor-pointer',
|
|
106
|
-
specificationFilter[attr] ? 'border-[#3E36DC] bg-[#3E36DC] ' : '',
|
|
86
|
+
'w-[10px] h-[10px] text-white',
|
|
87
|
+
specificationFilter[attr] ? 'block' : 'hidden',
|
|
107
88
|
)}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
if (attribute !== 'none') {
|
|
113
|
-
refine(value);
|
|
114
|
-
} else {
|
|
115
|
-
const setSpecificationFilter =
|
|
116
|
-
useRequestStore.getState().setSpecificationFilter;
|
|
117
|
-
|
|
118
|
-
const setSpecificationFilteredProducts =
|
|
119
|
-
useResultStore.getState().setSpecificationFilteredProducts;
|
|
120
|
-
|
|
121
|
-
if (specificationFilter[attr]) {
|
|
122
|
-
setSpecificationFilter({});
|
|
123
|
-
setSpecificationFilteredProducts([]);
|
|
124
|
-
// setProducts(results);
|
|
125
|
-
} else {
|
|
126
|
-
setSpecificationFilter({
|
|
127
|
-
[attr]: value,
|
|
128
|
-
});
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
}}
|
|
132
|
-
>
|
|
133
|
-
<div
|
|
134
|
-
className={twMerge(
|
|
135
|
-
'justify-start text-[#3e36dc] text-[10px] leading-none px-0.5',
|
|
136
|
-
'font-normal hover:font-bold hover:px-0',
|
|
137
|
-
specificationFilter[attr]
|
|
138
|
-
? 'font-bold text-white hover:px-0.5'
|
|
139
|
-
: '',
|
|
140
|
-
'max-line-1',
|
|
141
|
-
)}
|
|
142
|
-
>
|
|
143
|
-
{imageAnalysis?.specification[attr] || 'N/A'}
|
|
144
|
-
</div>
|
|
145
|
-
</div>
|
|
146
|
-
</Tooltip>
|
|
147
|
-
</div>
|
|
148
|
-
<div
|
|
149
|
-
onClick={() => {
|
|
150
|
-
navigator.clipboard.writeText(
|
|
151
|
-
imageAnalysis?.specification[attr] || '',
|
|
152
|
-
);
|
|
153
|
-
}}
|
|
154
|
-
>
|
|
155
|
-
<Icon
|
|
156
|
-
name="copy"
|
|
157
|
-
className="text-[#AAABB5] w-[12px] h-[12px] hover:text-[#3E36DC] cursor-pointer"
|
|
158
|
-
/>
|
|
89
|
+
/>
|
|
90
|
+
</div>
|
|
159
91
|
</div>
|
|
160
|
-
</
|
|
92
|
+
</Tooltip>
|
|
161
93
|
);
|
|
162
94
|
};
|
|
163
95
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
2
|
import { useTranslation } from 'react-i18next';
|
|
3
3
|
import { createPortal } from 'react-dom';
|
|
4
4
|
|
|
@@ -10,49 +10,49 @@ const LocationInfoPopup = () => {
|
|
|
10
10
|
const shown = sessionStorage.getItem('locationNoticeShown');
|
|
11
11
|
if (shown) return;
|
|
12
12
|
|
|
13
|
-
navigator.permissions
|
|
14
|
-
.
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
}
|
|
24
|
-
});
|
|
13
|
+
navigator.permissions.query({ name: 'geolocation' }).then(result => {
|
|
14
|
+
if (result.state === 'prompt') {
|
|
15
|
+
setShowPopup(true);
|
|
16
|
+
} else if (result.state === 'granted') {
|
|
17
|
+
navigator.geolocation.getCurrentPosition(console.log, console.error);
|
|
18
|
+
sessionStorage.setItem('locationNoticeShown', 'true');
|
|
19
|
+
} else {
|
|
20
|
+
sessionStorage.setItem('locationNoticeShown', 'true');
|
|
21
|
+
}
|
|
22
|
+
});
|
|
25
23
|
}, []);
|
|
26
24
|
|
|
27
25
|
const closePopup = () => {
|
|
28
26
|
navigator.geolocation.getCurrentPosition(console.log, console.error);
|
|
29
27
|
sessionStorage.setItem('locationNoticeShown', 'true');
|
|
30
28
|
setShowPopup(false);
|
|
31
|
-
}
|
|
32
|
-
|
|
29
|
+
};
|
|
30
|
+
|
|
33
31
|
return (
|
|
34
32
|
<div>
|
|
35
|
-
{showPopup &&
|
|
36
|
-
|
|
37
|
-
className="custom-modal"
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
className="custom-modal-body geolocation"
|
|
42
|
-
onClick={(e) => {
|
|
33
|
+
{showPopup &&
|
|
34
|
+
createPortal(
|
|
35
|
+
<div className="custom-modal" onClick={closePopup}>
|
|
36
|
+
<div
|
|
37
|
+
className="custom-modal-body geolocation"
|
|
38
|
+
onClick={e => {
|
|
43
39
|
e.preventDefault();
|
|
44
40
|
e.stopPropagation();
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
<div className="geolocation-title">
|
|
41
|
+
}}
|
|
42
|
+
>
|
|
43
|
+
<div className="geolocation-title">
|
|
44
|
+
{t('Please enable location services when prompted.')}
|
|
45
|
+
</div>
|
|
48
46
|
<div>{window.settings.geoLocationMessage}</div>
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
47
|
+
<button type="button" onClick={closePopup}>
|
|
48
|
+
{t('I understand')}
|
|
49
|
+
</button>
|
|
50
|
+
</div>
|
|
51
|
+
</div>,
|
|
52
|
+
document.body,
|
|
53
|
+
)}
|
|
54
54
|
</div>
|
|
55
|
-
)
|
|
56
|
-
}
|
|
55
|
+
);
|
|
56
|
+
};
|
|
57
57
|
|
|
58
58
|
export default LocationInfoPopup;
|