@nyris/nyris-webapp 0.3.86 → 0.3.88

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/build/asset-manifest.json +6 -6
  2. package/build/index.html +1 -1
  3. package/build/js/settings.example.js +1 -0
  4. package/build/static/css/main.ba1c7479.css +4 -0
  5. package/build/static/css/main.ba1c7479.css.map +1 -0
  6. package/build/static/js/main.e861b336.js +3 -0
  7. package/build/static/js/{main.1e617139.js.map → main.e861b336.js.map} +1 -1
  8. package/package.json +3 -3
  9. package/public/js/settings.example.js +1 -0
  10. package/src/components/Carousel/ImagePreviewCarousel.tsx +1 -1
  11. package/src/components/CustomCameraDrawer.tsx +73 -3
  12. package/src/components/DragDropFile.tsx +121 -46
  13. package/src/components/Header.tsx +17 -12
  14. package/src/components/Hint.tsx +98 -0
  15. package/src/components/ImagePreview.tsx +79 -10
  16. package/src/components/Inquiry/InquiryModal.tsx +40 -9
  17. package/src/components/PreFilter/PreFilter.tsx +14 -0
  18. package/src/components/Product/Product.tsx +3 -3
  19. package/src/components/Product/ProductDetailView.tsx +2 -4
  20. package/src/components/Product/ProductList.tsx +0 -5
  21. package/src/components/SidePanel.tsx +77 -84
  22. package/src/components/TextSearch.tsx +161 -9
  23. package/src/layouts/AppLayout.tsx +14 -10
  24. package/src/pages/Home.tsx +20 -2
  25. package/src/pages/Result.tsx +84 -65
  26. package/src/stores/request/requestStore.ts +5 -1
  27. package/src/stores/request/specifications/specifications.initialState.ts +9 -0
  28. package/src/stores/request/specifications/specifications.slice.ts +15 -0
  29. package/src/stores/types.ts +16 -0
  30. package/src/styles/common.scss +15 -0
  31. package/src/styles/product.scss +13 -1
  32. package/src/translations.ts +27 -0
  33. package/src/types.ts +1 -0
  34. package/build/static/css/main.a0a44a64.css +0 -4
  35. package/build/static/css/main.a0a44a64.css.map +0 -1
  36. package/build/static/js/main.1e617139.js +0 -3
  37. /package/build/static/js/{main.1e617139.js.LICENSE.txt → main.e861b336.js.LICENSE.txt} +0 -0
@@ -13,6 +13,7 @@ import { AutosizeTextarea } from 'components/AutosizeTextArea';
13
13
  import Tooltip from 'components/Tooltip/TooltipComponent';
14
14
 
15
15
  import { useTranslation } from 'react-i18next';
16
+ import { createImage } from '../../services/visualSearch';
16
17
  interface Props {
17
18
  requestImage: any;
18
19
  selectedRegion: any;
@@ -44,6 +45,8 @@ export default function InquiryModal({
44
45
  }: Props) {
45
46
  const settings = window.settings;
46
47
  const preFilter = useRequestStore(state => state.preFilter);
48
+ const specifications = useRequestStore(state => state.specifications);
49
+ const nameplateImage = useRequestStore(state => state.nameplateImage);
47
50
 
48
51
  const preFilterValues = Object.keys(preFilter) as string[];
49
52
 
@@ -60,6 +63,25 @@ export default function InquiryModal({
60
63
  );
61
64
  const { t } = useTranslation();
62
65
 
66
+ useEffect(() => {
67
+ function omitKeys(obj: any, keys: string[]) {
68
+ return Object.fromEntries(
69
+ Object.entries(obj).filter(([key]) => !keys.includes(key))
70
+ );
71
+ }
72
+ const omittedSpecification = omitKeys(specifications, ['is_nameplate', 'prefilter_value']);
73
+ setInformation(
74
+ Object
75
+ .entries(omittedSpecification)
76
+ .reduce((acc, [k, v]) =>
77
+ v == null || (typeof v === 'string' && v.trim() === '')
78
+ ? acc
79
+ : `${acc}${k}: ${v}\n`,
80
+ ''
81
+ )
82
+ );
83
+ }, [specifications]);
84
+
63
85
  useEffect(() => emailjs.init('SMGihPnuEGcYLm0V4'), []);
64
86
  useEffect(() => {
65
87
  if (email)
@@ -72,19 +94,28 @@ export default function InquiryModal({
72
94
  const croppedImage = requestImage
73
95
  ? getCroppedCanvas(requestImage, selectedRegion)
74
96
  : null;
97
+ const nameplateImageCanvas = nameplateImage ? await createImage(nameplateImage) : null;
98
+
75
99
  const serviceId = 'service_zfsxshi';
76
100
  setIsInquiryModalOpen(false);
77
101
  const templateId = settings.support?.emailTemplateId;
78
102
  if (templateId) {
103
+ const body = !specifications?.is_nameplate ? {
104
+ email_id: email.trim(),
105
+ information_text: information ? information : '<not specified>',
106
+ request_image: croppedImage?.toDataURL(),
107
+ prefilter_values: preFilterValues?.length
108
+ ? preFilterValues.join(', ')
109
+ : '<not specified>',
110
+ } : {
111
+ email_id: email.trim(),
112
+ information_text: information,
113
+ request_image: croppedImage?.toDataURL() || '',
114
+ typeplate_image: nameplateImageCanvas?.toDataURL() || '',
115
+ prefilter_values: specifications.prefilter_value,
116
+ };
79
117
  try {
80
- await emailjs.send(serviceId, templateId, {
81
- email_id: email.trim(),
82
- information_text: information ? information : '<not specified>',
83
- request_image: croppedImage?.toDataURL(),
84
- prefilter_values: preFilterValues?.length
85
- ? preFilterValues.join(', ')
86
- : '<not specified>',
87
- });
118
+ await emailjs.send(serviceId, templateId, body);
88
119
  ToastHelper.success(t('Request sent successfully'));
89
120
  } catch (error) {
90
121
  toast(
@@ -260,7 +291,7 @@ export default function InquiryModal({
260
291
  color: '#2B2C46',
261
292
  }}
262
293
  >
263
- {settings.support.prefilterFieldName}
294
+ {settings.preFilterTitle}
264
295
  </p>
265
296
  <Tooltip
266
297
  content={t(
@@ -10,6 +10,8 @@ import { truncateString } from 'utils/truncateString';
10
10
  import { twMerge } from 'tailwind-merge';
11
11
  import Tooltip from 'components/Tooltip/TooltipComponent';
12
12
  import { Skeleton } from 'components/Skeleton';
13
+ import { useNavigate } from 'react-router';
14
+ import useResultStore from "../../stores/result/resultStore";
13
15
 
14
16
  interface Props {
15
17
  handleClose?: any;
@@ -34,10 +36,14 @@ const PreFilterComponent = (props: Props) => {
34
36
 
35
37
  const setPreFilter = useRequestStore(state => state.setPreFilter);
36
38
  const setAlgoliaFilter = useRequestStore(state => state.setAlgoliaFilter);
39
+ const specification = useRequestStore(state => state.specifications);
40
+ const setSpecifications = useRequestStore(state => state.setSpecifications);
41
+ const setImageAnalysis = useResultStore(state => state.setImageAnalysis);
37
42
 
38
43
  const [keyFilter, setKeyFilter] = useState<Record<string, boolean>>(
39
44
  keyFilterState || {},
40
45
  );
46
+ const navigate = useNavigate();
41
47
 
42
48
  const selectedFilter = useMemo(
43
49
  () =>
@@ -72,6 +78,7 @@ const PreFilterComponent = (props: Props) => {
72
78
  }, {});
73
79
  setResultFilter(newResult);
74
80
  setColumns(Object.keys(newResult).length);
81
+
75
82
  })
76
83
  .catch((e: any) => {
77
84
  console.log('err getDataFilterDesktop', e);
@@ -124,6 +131,13 @@ const PreFilterComponent = (props: Props) => {
124
131
  : '';
125
132
  setAlgoliaFilter(filter);
126
133
 
134
+ if (preFilterValues?.length && preFilterValues[0] !== specification?.prefilter_value) {
135
+ setSpecifications({ prefilter_value: preFilterValues?.join(', ') || ''});
136
+ }
137
+ if (specification?.is_nameplate) {
138
+ setImageAnalysis({});
139
+ }
140
+
127
141
  handleClose();
128
142
 
129
143
  if (requestImages.length === 0) {
@@ -280,7 +280,7 @@ function Product(props: Props) {
280
280
  'justify-between',
281
281
  'flex',
282
282
  'flex-col',
283
- 'bg-[#FAFAFA]',
283
+ 'bg-[#fff]',
284
284
  'flex-grow',
285
285
  'z-10',
286
286
  'pt-2',
@@ -416,7 +416,7 @@ function Product(props: Props) {
416
416
  background:
417
417
  settings.secondaryCTAButton?.secondaryCTAButtonColor ||
418
418
  '#2B2C46',
419
- borderRadius: 2,
419
+ borderRadius: 4,
420
420
  padding: '0px 8px',
421
421
  marginBottom: settings.CTAButton?.CTAButton ? 8 : 0,
422
422
  display: 'flex',
@@ -493,7 +493,7 @@ function Product(props: Props) {
493
493
  background:
494
494
  settings.CTAButton?.CTAButtonColor ||
495
495
  settings.theme?.primaryColor,
496
- borderRadius: 2,
496
+ borderRadius: 4,
497
497
  padding: '0px 8px',
498
498
  display: 'flex',
499
499
  justifyItems: 'center',
@@ -169,9 +169,7 @@ function ProductDetailView(props: Props) {
169
169
  </div>
170
170
 
171
171
  <div
172
- className={`overflow-y-auto max-h-[90svh] px-4 pb-4 ${
173
- settings.simpleCardView ? 'bg-[#FaFafa]' : 'bg-[#f3f3f5]'
174
- } mt-1`}
172
+ className={`overflow-y-auto max-h-[90svh] px-4 pb-4 'bg-[#fff] mt-1`}
175
173
  >
176
174
  {settings.simpleCardView ? (
177
175
  <div className="info-container">
@@ -186,7 +184,7 @@ function ProductDetailView(props: Props) {
186
184
  ) : (
187
185
  <div className="box-content flex flex-col bg-[#F3F3F5] mt-4">
188
186
  <div className="box-top">
189
- <div className="bg-[#F3F3F5] flex flex-col justify-between">
187
+ <div className="bg-[#fff] flex flex-col justify-between">
190
188
  <div className="gap-1.5 flex flex-wrap w-full">
191
189
  <div className="w-full">
192
190
  {settings.mainTitle && (
@@ -6,7 +6,6 @@ import useRequestStore from 'stores/request/requestStore';
6
6
  import useUiStore from 'stores/ui/uiStore';
7
7
  import { useTranslation } from 'react-i18next';
8
8
  import { twMerge } from 'tailwind-merge';
9
- import { useCurrentRefinements } from 'react-instantsearch';
10
9
  import { filterProducts } from 'utils/specificationFilter';
11
10
 
12
11
  interface Props {
@@ -36,10 +35,6 @@ function ProductList({ sendFeedBackAction }: Props): JSX.Element {
36
35
  state => state.specificationFilter,
37
36
  );
38
37
 
39
- const specificationFilteredProducts = useResultStore(
40
- state => state.specificationFilteredProducts,
41
- );
42
-
43
38
  const getUrlToCanvasFile = async (url: string) => {
44
39
  setQuery('');
45
40
  setValueInput('');
@@ -6,18 +6,9 @@ import PostFilterComponent from './PostFilter/PostFilterComponent';
6
6
  import useResultStore from 'stores/result/resultStore';
7
7
  import { Icon } from '@nyris/nyris-react-components';
8
8
  import Tooltip from './Tooltip/TooltipComponent';
9
- import { filterProducts } from 'utils/specificationFilter';
10
- import { useEffect } from 'react';
11
9
 
12
10
  export default function SidePanel({ className }: { className?: string }) {
13
11
  const requestImages = useRequestStore(state => state.requestImages);
14
- const setSpecificationFilteredProducts = useResultStore(
15
- state => state.setSpecificationFilteredProducts,
16
- );
17
-
18
- const productsFromAlgolia = useResultStore(
19
- state => state.productsFromAlgolia,
20
- );
21
12
 
22
13
  const specificationFilter = useRequestStore(
23
14
  state => state.specificationFilter,
@@ -66,7 +57,7 @@ export default function SidePanel({ className }: { className?: string }) {
66
57
  {requestImages[0] && <ImagePreview />}
67
58
  </div>
68
59
 
69
- {(imageAnalysis?.imageDescription ||
60
+ {window.settings.showImageDetails && (imageAnalysis?.imageDescription ||
70
61
  Object.keys(imageAnalysis?.specification || {}).length > 0) && (
71
62
  <div className="self-stretch p-4 bg-[#f3f3f5] rounded inline-flex flex-col justify-start items-start gap-1.5 mt-4 mx-4 ">
72
63
  {imageAnalysis?.imageDescription !== 'No description available' && (
@@ -84,90 +75,92 @@ export default function SidePanel({ className }: { className?: string }) {
84
75
  Identified Attributes
85
76
  </div>
86
77
 
87
- {Object.keys(imageAnalysis?.specification || {}).map(key => {
88
- const value = imageAnalysis?.specification[key];
89
- if (!value) {
90
- return null;
91
- }
92
- return (
93
- <div
94
- key={key}
95
- className="flex justify-between w-full gap-2 items-center"
96
- >
97
- <div className="self-stretch inline-flex justify-start items-center gap-1.5">
98
- <div className="justify-start text-black text-xs font-semibold">
99
- {key}:
100
- </div>
101
- <Tooltip
102
- content={
103
- specificationFilter[key]
104
- ? 'Filter applied. Clear to choose a different value.'
105
- : 'Click to apply as a search filter.'
106
- }
107
- delayDuration={1000}
108
- disabled={!value}
109
- >
110
- <div
111
- className={twMerge(
112
- `px-1 py-1 bg-[#e4e3ff] rounded-[1px] flex justify-center items-center gap-1.5`,
113
- 'border border-solid border-transparent hover:border-[#3E36DC]',
114
- 'cursor-pointer',
78
+ {Object.keys(imageAnalysis?.specification || {})
79
+ .filter((key) => (key !== 'is_nameplate' && key !== 'prefilter_value'))
80
+ .map(key => {
81
+ const value = imageAnalysis?.specification[key];
82
+ if (!value) {
83
+ return null;
84
+ }
85
+ return (
86
+ <div
87
+ key={key}
88
+ className="flex justify-between w-full gap-2 items-center"
89
+ >
90
+ <div className="self-stretch inline-flex justify-start items-center gap-1.5">
91
+ <div className="justify-start text-black text-xs font-semibold">
92
+ {key}:
93
+ </div>
94
+ <Tooltip
95
+ content={
115
96
  specificationFilter[key]
116
- ? 'border-[#3E36DC] bg-[#3E36DC] '
117
- : '',
118
- )}
119
- onClick={() => {
120
- if (!value) {
121
- return;
122
- }
123
- const setSpecificationFilter =
124
- useRequestStore.getState().setSpecificationFilter;
125
-
126
- const setSpecificationFilteredProducts =
127
- useResultStore.getState()
128
- .setSpecificationFilteredProducts;
129
-
130
- if (specificationFilter[key]) {
131
- setSpecificationFilter({});
132
- setSpecificationFilteredProducts([]);
133
- // setProducts(results);
134
- } else {
135
- setSpecificationFilter({
136
- [key]: value,
137
- });
138
- }
139
- }}
97
+ ? 'Filter applied. Clear to choose a different value.'
98
+ : 'Click to apply as a search filter.'
99
+ }
100
+ delayDuration={1000}
101
+ disabled={!value}
140
102
  >
141
103
  <div
142
104
  className={twMerge(
143
- 'justify-start text-[#3e36dc] text-[10px] leading-none px-0.5',
144
- 'font-normal hover:font-bold hover:px-0',
105
+ `px-1 py-1 bg-[#e4e3ff] rounded-[1px] flex justify-center items-center gap-1.5`,
106
+ 'border border-solid border-transparent hover:border-[#3E36DC]',
107
+ 'cursor-pointer',
145
108
  specificationFilter[key]
146
- ? 'font-bold text-white hover:px-0.5'
109
+ ? 'border-[#3E36DC] bg-[#3E36DC] '
147
110
  : '',
148
- 'max-line-1',
149
111
  )}
112
+ onClick={() => {
113
+ if (!value) {
114
+ return;
115
+ }
116
+ const setSpecificationFilter =
117
+ useRequestStore.getState().setSpecificationFilter;
118
+
119
+ const setSpecificationFilteredProducts =
120
+ useResultStore.getState()
121
+ .setSpecificationFilteredProducts;
122
+
123
+ if (specificationFilter[key]) {
124
+ setSpecificationFilter({});
125
+ setSpecificationFilteredProducts([]);
126
+ // setProducts(results);
127
+ } else {
128
+ setSpecificationFilter({
129
+ [key]: value,
130
+ });
131
+ }
132
+ }}
150
133
  >
151
- {imageAnalysis?.specification[key] || 'N/A'}
134
+ <div
135
+ className={twMerge(
136
+ 'justify-start text-[#3e36dc] text-[10px] leading-none px-0.5',
137
+ 'font-normal hover:font-bold hover:px-0',
138
+ specificationFilter[key]
139
+ ? 'font-bold text-white hover:px-0.5'
140
+ : '',
141
+ 'max-line-1',
142
+ )}
143
+ >
144
+ {imageAnalysis?.specification[key] || 'N/A'}
145
+ </div>
152
146
  </div>
153
- </div>
154
- </Tooltip>
155
- </div>
156
- <div
157
- onClick={() => {
158
- navigator.clipboard.writeText(
159
- imageAnalysis?.specification[key] || '',
160
- );
161
- }}
162
- >
163
- <Icon
164
- name="copy"
165
- className="text-[#AAABB5] w-[12px] h-[12px] hover:text-[#3E36DC] cursor-pointer"
166
- />
147
+ </Tooltip>
148
+ </div>
149
+ <div
150
+ onClick={() => {
151
+ navigator.clipboard.writeText(
152
+ imageAnalysis?.specification[key] || '',
153
+ );
154
+ }}
155
+ >
156
+ <Icon
157
+ name="copy"
158
+ className="text-[#AAABB5] w-[12px] h-[12px] hover:text-[#3E36DC] cursor-pointer"
159
+ />
160
+ </div>
167
161
  </div>
168
- </div>
169
- );
170
- })}
162
+ );
163
+ })}
171
164
  </div>
172
165
  )}
173
166
  {showPostFilter && (
@@ -1,6 +1,5 @@
1
- import { useCallback, useMemo, useRef, useState } from 'react';
2
-
3
- import { isEmpty, debounce } from 'lodash';
1
+ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
2
+ import { isEmpty, debounce, clone } from 'lodash';
4
3
  import { twMerge } from 'tailwind-merge';
5
4
  import { useAuth0 } from '@auth0/auth0-react';
6
5
  import { useLocation, useNavigate } from 'react-router';
@@ -15,6 +14,8 @@ import PreFilterModal from './PreFilter/PreFilterModal';
15
14
  import useRequestStore from 'stores/request/requestStore';
16
15
  import Tooltip from './Tooltip/TooltipComponent';
17
16
  import UploadDisclaimer from './UploadDisclaimer';
17
+ import { getFilters } from '../services/filter';
18
+ import { useMediaQuery } from 'react-responsive';
18
19
 
19
20
  function TextSearch({
20
21
  className,
@@ -27,12 +28,15 @@ function TextSearch({
27
28
  const user = useAuth0().user;
28
29
 
29
30
  const focusInp: any = useRef<HTMLDivElement | null>(null);
31
+ const iconRef = useRef<HTMLDivElement>(null);
30
32
 
31
33
  const { t } = useTranslation();
32
34
 
33
35
  const location = useLocation();
34
36
  const navigate = useNavigate();
35
37
 
38
+ const [resultFilter, setResultFilter] = useState<any>([]);
39
+
36
40
  const preFilter = useRequestStore(state => state.preFilter);
37
41
  const requestImages = useRequestStore(state => state.requestImages);
38
42
  const setQuery = useRequestStore(state => state.setQuery);
@@ -40,8 +44,19 @@ function TextSearch({
40
44
  const valueInput = useRequestStore(state => state.valueInput);
41
45
  const setValueInput = useRequestStore(state => state.setValueInput);
42
46
  const setMetaFilter = useRequestStore(state => state.setMetaFilter);
47
+ const specifications = useRequestStore(state => state.specifications);
48
+ const nameplateNotificationText = useRequestStore(state => state.nameplateNotificationText);
49
+ const isMobile = useMediaQuery({ query: '(max-width: 776px)' });
43
50
 
44
51
  const regions = useRequestStore(state => state.regions);
52
+ const setRequestImages = useRequestStore(state => state.setRequestImages);
53
+ const setSpecifications = useRequestStore(state => state.setSpecifications);
54
+ const setShowLoading = useRequestStore(state => state.setShowLoading);
55
+ const setNameplateNotificationText = useRequestStore(state => state.setNameplateNotificationText);
56
+ const setShowNotMatchedError = useRequestStore(state => state.setShowNotMatchedError);
57
+ const setAlgoliaFilter = useRequestStore(state => state.setAlgoliaFilter);
58
+ const setPreFilter = useRequestStore(state => state.setPreFilter);
59
+ const setNameplateImage = useRequestStore(state => state.setNameplateImage);
45
60
 
46
61
  const [isOpenModalFilterDesktop, setToggleModalFilterDesktop] =
47
62
  useState<boolean>(false);
@@ -84,7 +99,7 @@ function TextSearch({
84
99
  const searchOrRedirect = useCallback(
85
100
  debounce((value: any) => {
86
101
  setQuery(value);
87
- if (requestImages.length === 0 && value === '') {
102
+ if (requestImages.length === 0 && value === '' && !specifications) {
88
103
  navigate('/');
89
104
  return;
90
105
  }
@@ -109,32 +124,91 @@ function TextSearch({
109
124
  setValueInput(event.currentTarget.value);
110
125
  searchOrRedirect(event.currentTarget.value);
111
126
 
112
- if (event.currentTarget.value === '') {
127
+ if (event.currentTarget.value === '' && !specifications) {
113
128
  setValueInput('');
114
129
  setQuery('');
115
130
  }
116
131
  };
117
132
 
118
133
  const { cadSearch } = useCadSearch();
134
+
135
+ const getPreFilters = async () => {
136
+ getFilters(1000, settings)
137
+ .then(res => {
138
+ setResultFilter(res);
139
+ })
140
+ .catch((e: any) => {
141
+ console.log('err getDataFilterDesktop', e);
142
+ });
143
+ }
144
+
145
+ useEffect(() => {
146
+ getPreFilters()
147
+ }, []);
119
148
 
120
149
  const handleUpload = (files: File[]) => {
121
150
  setValueInput('');
122
151
  setQuery('');
123
152
 
124
- navigate('/result');
125
-
126
153
  if (isCadFile(files[0])) {
127
154
  cadSearch({ file: files[0], settings, newSearch: true }).then(res => {});
128
155
 
129
156
  return;
130
157
  }
131
158
 
159
+ setShowLoading(true);
160
+
132
161
  singleImageSearch({
133
162
  image: files[0],
134
163
  settings: window.settings,
135
164
  showFeedback: true,
136
165
  newSearch: true,
137
- }).then(() => {});
166
+ }).then((singleImageResp) => {
167
+ const specificationPrefilter = singleImageResp.image_analysis?.specification?.prefilter_value || null;
168
+ const hasPrefilter = resultFilter.filter((filter: any) => filter.values.includes(specificationPrefilter));
169
+ if (specificationPrefilter) {
170
+ setRequestImages([]);
171
+ setShowNotMatchedError(false);
172
+ if (hasPrefilter.length) {
173
+ setSpecifications(clone(singleImageResp.image_analysis.specification));
174
+ setNameplateImage(files[0]);
175
+ setPreFilter({[singleImageResp.image_analysis?.specification?.prefilter_value]: true});
176
+ setAlgoliaFilter(`${settings.alogoliaFilterField}:'${singleImageResp.image_analysis?.specification?.prefilter_value}'`);
177
+
178
+ setShowLoading(false);
179
+ navigate('/result');
180
+
181
+ setTimeout(() => {
182
+ setNameplateNotificationText(t('We have successfully defined the search criteria', { prefilter_value: specificationPrefilter, preFilterTitle: window.settings.preFilterTitle?.toLocaleLowerCase() }));
183
+ }, 1000);
184
+ setTimeout(() => {
185
+ setNameplateNotificationText('');
186
+ }, 6000);
187
+ }
188
+ if (!hasPrefilter.length && showPreFilter) {
189
+ setSpecifications(clone({...singleImageResp.image_analysis.specification, prefilter_value: '', specificationPrefilter}));
190
+ navigate('/result');
191
+ setPreFilter({});
192
+ setAlgoliaFilter('');
193
+ setShowLoading(false);
194
+ setShowNotMatchedError(true);
195
+ setTimeout(() => {
196
+ setNameplateNotificationText(t('Extracted details from the nameplate could not be matched', { preFilterTitle: window.settings.preFilterTitle?.toLocaleLowerCase() }));
197
+ }, 1000);
198
+ setTimeout(() => {
199
+ setNameplateNotificationText('');
200
+ }, 6000);
201
+ }
202
+ } else {
203
+ if (specifications?.is_nameplate) {
204
+ setSpecifications({...specifications, prefilter_value: '', specificationPrefilter: ''});
205
+ } else {
206
+ setSpecifications({...specifications, is_nameplate: false});
207
+ }
208
+ setShowLoading(false);
209
+ navigate('/result');
210
+ }
211
+ });
138
212
  };
139
213
 
140
214
  return (
@@ -159,7 +233,6 @@ function TextSearch({
159
233
  'flex',
160
234
  'h-full',
161
235
  'justify-between',
162
- 'overflow-hidden',
163
236
  'p-0',
164
237
  'rounded-3xl',
165
238
  'w-full',
@@ -207,6 +280,7 @@ function TextSearch({
207
280
  }
208
281
  >
209
282
  <div
283
+ ref={iconRef}
210
284
  className={twMerge(
211
285
  'p-2 desktop:p-3',
212
286
  location.pathname === '/result' && 'desktop:p-2',
@@ -228,6 +302,82 @@ function TextSearch({
228
302
  </div>
229
303
  </Tooltip>
230
304
  )}
305
+ {nameplateNotificationText && (
306
+ <div
307
+ style={{
308
+ position: 'fixed',
309
+ backgroundColor:
310
+ nameplateNotificationText !== t('Extracted details from the nameplate could not be matched', { preFilterTitle: window.settings.preFilterTitle?.toLocaleLowerCase() })
311
+ ? '#E4E3FF' : '#FFDBB3',
312
+ border: '1px solid',
313
+ borderColor: nameplateNotificationText !== t('Extracted details from the nameplate could not be matched', { preFilterTitle: window.settings.preFilterTitle?.toLocaleLowerCase() }) ? '#3E36DC' : '#FF8800',
314
+ fontSize: 13,
315
+ borderRadius: 24,
316
+ color: '#545987',
317
+ padding: '8px 16px',
318
+ margin: 'auto',
319
+ display: 'flex',
320
+ alignItems: 'center',
321
+ justifyContent: 'center',
322
+ marginLeft: 8,
323
+ zIndex: 999999,
324
+ top: !isMobile ? 60 : 'unset',
325
+ bottom: isMobile ? 76 : 'unset',
326
+ maxWidth: 510,
327
+ width: !isMobile ? 'unset' : '90%',
328
+ left: !isMobile ? 'unset' : 0,
329
+ }}
330
+ >
331
+ <div
332
+ style={{
333
+ position: 'absolute',
334
+ top: !isMobile ? -7 : 'unset',
335
+ bottom: isMobile ? -7 : 'unset',
336
+ left: !isMobile ? '50%' : 30,
337
+ transform: 'translateX(-50%)',
338
+ width: 0,
339
+ height: 0,
340
+ borderLeft: '7px solid transparent',
341
+ borderRight: '7px solid transparent',
342
+ borderTop: isMobile
343
+ ? `7px solid ${
344
+ nameplateNotificationText !== t('Extracted details from the nameplate could not be matched',
345
+ { preFilterTitle: window.settings.preFilterTitle?.toLocaleLowerCase() }) ? '#3E36DC' : '#FF8800'
346
+ }`
347
+ : 'unset',
348
+ borderBottom: !isMobile
349
+ ? `7px solid ${
350
+ nameplateNotificationText !== t('Extracted details from the nameplate could not be matched',
351
+ { preFilterTitle: window.settings.preFilterTitle?.toLocaleLowerCase() }) ? '#3E36DC' : '#FF8800'
352
+ }`
353
+ : 'unset',
354
+ }}
355
+ />
356
+
357
+ <div
358
+ style={{
359
+ position: 'absolute',
360
+ top: !isMobile ? -6 : 'unset',
361
+ bottom: isMobile ? -6 : 'unset',
362
+ left: !isMobile ? '50%' : 30,
363
+ transform: 'translateX(-50%)',
364
+ width: 0,
365
+ height: 0,
366
+ borderLeft: '6px solid transparent',
367
+ borderRight: '6px solid transparent',
368
+ borderTop: isMobile
369
+ ? `6px solid ${nameplateNotificationText !== t('Extracted details from the nameplate could not be matched',
370
+ { preFilterTitle: window.settings.preFilterTitle?.toLocaleLowerCase() }) ? '#E4E3FF' : '#FFDBB3'}`
371
+ : 'unset',
372
+ borderBottom: !isMobile
373
+ ? `6px solid ${nameplateNotificationText !== t('Extracted details from the nameplate could not be matched',
374
+ { preFilterTitle: window.settings.preFilterTitle?.toLocaleLowerCase() }) ? '#E4E3FF' : '#FFDBB3'}`
375
+ : 'unset',
376
+ }}
377
+ />
378
+ {nameplateNotificationText}
379
+ </div>
380
+ )}
231
381
  {!showPreFilter && (
232
382
  <div className="p-2 hidden desktop:block">
233
383
  <Icon name="search" width={16} height={16} />
@@ -311,6 +461,7 @@ function TextSearch({
311
461
  }
312
462
  return;
313
463
  }
464
+ setSpecifications(null);
314
465
  setQuery('');
315
466
  setValueInput('');
316
467
  navigate('/');
@@ -356,6 +507,7 @@ function TextSearch({
356
507
  }
357
508
  onClick={e => {
358
509
  if (!showDisclaimerDisabled) {
510
+ // disclaimer
359
511
  setShowDisclaimer(true);
360
512
  } else if (onCameraClick) {
361
513
  onCameraClick();