@nyris/nyris-webapp 0.3.35 → 0.3.36
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 +13 -13
- package/build/index.html +1 -1
- package/build/{precache-manifest.375ac411683570ee1df5aea6de03409d.js → precache-manifest.a2657ad49c132fb9c82629d37be15547.js} +14 -14
- package/build/service-worker.js +1 -1
- package/build/static/css/main.41fb4769.chunk.css +2 -0
- package/build/static/css/main.41fb4769.chunk.css.map +1 -0
- package/build/static/js/2.ff44260e.chunk.js +3 -0
- package/build/static/js/2.ff44260e.chunk.js.map +1 -0
- package/build/static/js/main.0b3b11fc.chunk.js +3 -0
- package/build/static/js/main.0b3b11fc.chunk.js.map +1 -0
- package/build/static/media/error.48b946a9.svg +3 -0
- package/package.json +3 -3
- package/public/index.html +13 -0
- package/src/Store/search/Search.ts +4 -4
- package/src/Store/search/search.initialState.ts +1 -1
- package/src/Store/search/types.ts +1 -1
- package/src/common/assets/icons/arrow_left.svg +3 -0
- package/src/common/assets/icons/arrow_right.svg +3 -0
- package/src/common/assets/icons/error.svg +3 -0
- package/src/components/AppMobile.tsx +111 -0
- package/src/components/DragDropFile.tsx +5 -4
- package/src/components/FooterMobile.tsx +8 -2
- package/src/components/Header.tsx +2 -1
- package/src/components/HeaderMobile.tsx +5 -3
- package/src/components/ImageCaptureHelpModal.tsx +7 -1
- package/src/components/ImagePreviewMobile.tsx +87 -0
- package/src/components/Layout.tsx +32 -92
- package/src/components/ProductList/index.tsx +4 -1
- package/src/components/RfqBanner.tsx +120 -0
- package/src/components/SidePanel.tsx +147 -0
- package/src/components/appMobile.scss +147 -142
- package/src/components/drawer/cameraCustom.tsx +5 -4
- package/src/components/input/inputSearch.tsx +12 -7
- package/src/components/modal/DefaultModal.tsx +1 -1
- package/src/components/pre-filter/index.tsx +108 -51
- package/src/components/results/ItemResult.tsx +1 -1
- package/src/components/rfq/RfqModal.tsx +262 -0
- package/src/helpers/ToastHelper.ts +10 -0
- package/src/helpers/getCroppedCanvas.ts +26 -0
- package/src/hooks/useFilteredRegions.ts +37 -0
- package/src/index.css +0 -4
- package/src/page/landingPage/AppMD.tsx +0 -11
- package/src/page/landingPage/AppMobile.tsx +1 -0
- package/src/page/landingPage/common.scss +41 -58
- package/src/page/result/index.tsx +256 -309
- package/src/translations.ts +2 -2
- package/src/types.ts +1 -0
- package/src/utils.ts +0 -1
- package/build/static/css/main.f5a1c730.chunk.css +0 -2
- package/build/static/css/main.f5a1c730.chunk.css.map +0 -1
- package/build/static/js/2.724f4cba.chunk.js +0 -3
- package/build/static/js/2.724f4cba.chunk.js.map +0 -1
- package/build/static/js/main.f579fa9f.chunk.js +0 -3
- package/build/static/js/main.f579fa9f.chunk.js.map +0 -1
- package/build/static/media/support3.4a17f96e.svg +0 -3
- /package/build/static/js/{2.724f4cba.chunk.js.LICENSE.txt → 2.ff44260e.chunk.js.LICENSE.txt} +0 -0
- /package/build/static/js/{main.f579fa9f.chunk.js.LICENSE.txt → main.0b3b11fc.chunk.js.LICENSE.txt} +0 -0
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { Box, Button, Tooltip, Typography } from '@material-ui/core';
|
|
2
|
-
import React, { useEffect, useState } from 'react';
|
|
2
|
+
import React, { useEffect, useMemo, useState } from 'react';
|
|
3
3
|
import CloseIcon from '@material-ui/icons/Close';
|
|
4
4
|
import IconSearch from 'common/assets/icons/icon_search.svg';
|
|
5
5
|
import { getFilters, searchFilters } from 'services/filter';
|
|
6
6
|
import { useAppDispatch, useAppSelector } from 'Store/Store';
|
|
7
|
-
import {
|
|
7
|
+
import { setPreFilter } from 'Store/search/Search';
|
|
8
8
|
import { useMediaQuery } from 'react-responsive';
|
|
9
|
-
import { isEmpty } from 'lodash';
|
|
9
|
+
import { isEmpty, pickBy } from 'lodash';
|
|
10
10
|
import { Skeleton } from '@material-ui/lab';
|
|
11
11
|
import { truncateString } from 'helpers/truncateString';
|
|
12
12
|
|
|
@@ -14,21 +14,35 @@ interface Props {
|
|
|
14
14
|
handleClose?: any;
|
|
15
15
|
// onChangeKeyFilter?: any;
|
|
16
16
|
}
|
|
17
|
-
|
|
17
|
+
const maxFilter = 10;
|
|
18
18
|
function PreFilterComponent(props: Props) {
|
|
19
19
|
const { handleClose } = props;
|
|
20
20
|
const dispatch = useAppDispatch();
|
|
21
21
|
const stateGlobal = useAppSelector(state => state);
|
|
22
22
|
const {
|
|
23
23
|
settings,
|
|
24
|
-
search: {
|
|
24
|
+
search: { preFilter: keyFilterState },
|
|
25
25
|
} = stateGlobal;
|
|
26
26
|
const [resultFilter, setResultFilter] = useState<any>([]);
|
|
27
|
-
const [keyFilter, setKeyFilter] = useState<string
|
|
27
|
+
const [keyFilter, setKeyFilter] = useState<Record<string, boolean>>(
|
|
28
|
+
keyFilterState || {},
|
|
29
|
+
);
|
|
30
|
+
|
|
28
31
|
const [isLoading, setLoading] = useState<boolean>(false);
|
|
29
32
|
const [columns, setColumns] = useState<number>(0);
|
|
30
33
|
const isMobile = useMediaQuery({ query: '(max-width: 776px)' });
|
|
31
34
|
|
|
35
|
+
const selectedFilter = useMemo(
|
|
36
|
+
() =>
|
|
37
|
+
Object.keys(keyFilter).reduce((count, key) => {
|
|
38
|
+
if (keyFilter[key] === true) {
|
|
39
|
+
return count + 1;
|
|
40
|
+
}
|
|
41
|
+
return count;
|
|
42
|
+
}, 0),
|
|
43
|
+
[keyFilter],
|
|
44
|
+
);
|
|
45
|
+
|
|
32
46
|
const getDataFilterDesktop = async () => {
|
|
33
47
|
setLoading(true);
|
|
34
48
|
const dataResultFilter = getFilters(1000, settings)
|
|
@@ -73,7 +87,6 @@ function PreFilterComponent(props: Props) {
|
|
|
73
87
|
settings,
|
|
74
88
|
)
|
|
75
89
|
.then(res => {
|
|
76
|
-
// console.log("res", res);
|
|
77
90
|
if (res.length > 0) {
|
|
78
91
|
setResultFilter({ [res[0][0].toLocaleUpperCase()]: res });
|
|
79
92
|
if (res.length <= 20) setColumns(1);
|
|
@@ -92,7 +105,7 @@ function PreFilterComponent(props: Props) {
|
|
|
92
105
|
};
|
|
93
106
|
|
|
94
107
|
const onHandlerSubmitData = () => {
|
|
95
|
-
dispatch(
|
|
108
|
+
dispatch(setPreFilter(pickBy(keyFilter, value => !!value)));
|
|
96
109
|
handleClose();
|
|
97
110
|
};
|
|
98
111
|
|
|
@@ -119,7 +132,7 @@ function PreFilterComponent(props: Props) {
|
|
|
119
132
|
color: '#000',
|
|
120
133
|
fontSize: '24px',
|
|
121
134
|
fontWeight: 700,
|
|
122
|
-
paddingLeft:
|
|
135
|
+
paddingLeft: '14px',
|
|
123
136
|
marginBottom: isMobile ? '0px' : '-8px',
|
|
124
137
|
marginTop: isMobile ? '0px' : '24px',
|
|
125
138
|
}}
|
|
@@ -133,7 +146,6 @@ function PreFilterComponent(props: Props) {
|
|
|
133
146
|
</div>
|
|
134
147
|
<Box
|
|
135
148
|
className="box-top"
|
|
136
|
-
style={isMobile ? { padding: 0, marginTop: '16px' } : undefined}
|
|
137
149
|
display={'flex'}
|
|
138
150
|
justifyContent={'space-between'}
|
|
139
151
|
alignItems={'center'}
|
|
@@ -144,34 +156,21 @@ function PreFilterComponent(props: Props) {
|
|
|
144
156
|
justifyItems={'center'}
|
|
145
157
|
style={isMobile ? { width: '100%' } : undefined}
|
|
146
158
|
>
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
</Box>
|
|
163
|
-
)}
|
|
164
|
-
|
|
165
|
-
{keyFilter && !isMobile && (
|
|
166
|
-
<Box display={'flex'} className="box-keyFilter">
|
|
167
|
-
<Typography className="keyFilter max-line-1">
|
|
168
|
-
{keyFilter}
|
|
169
|
-
</Typography>
|
|
170
|
-
<Button style={{ padding: 0 }} onClick={() => setKeyFilter('')}>
|
|
171
|
-
<CloseIcon style={{ fontSize: 12, color: '#2B2C46' }} />
|
|
172
|
-
</Button>
|
|
173
|
-
</Box>
|
|
174
|
-
)}
|
|
159
|
+
<Box
|
|
160
|
+
className="icon-search"
|
|
161
|
+
style={{ marginRight: 11 }}
|
|
162
|
+
display={'flex'}
|
|
163
|
+
justifyContent={'center'}
|
|
164
|
+
alignItems={'center'}
|
|
165
|
+
>
|
|
166
|
+
<img
|
|
167
|
+
style={{ maxWidth: 'fit-content' }}
|
|
168
|
+
src={IconSearch}
|
|
169
|
+
alt=""
|
|
170
|
+
width={18}
|
|
171
|
+
height={18}
|
|
172
|
+
/>
|
|
173
|
+
</Box>
|
|
175
174
|
|
|
176
175
|
<input
|
|
177
176
|
className="input-search-filter"
|
|
@@ -183,19 +182,59 @@ function PreFilterComponent(props: Props) {
|
|
|
183
182
|
</Box>
|
|
184
183
|
</Box>
|
|
185
184
|
|
|
186
|
-
{keyFilter &&
|
|
187
|
-
<Box
|
|
185
|
+
{!isEmpty(keyFilter) && selectedFilter > 0 && (
|
|
186
|
+
<Box
|
|
187
|
+
style={{
|
|
188
|
+
margin: '10px 16px 10px 16px',
|
|
189
|
+
display: 'flex',
|
|
190
|
+
justifyContent: 'space-between',
|
|
191
|
+
}}
|
|
192
|
+
>
|
|
188
193
|
<Box
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
194
|
+
style={{
|
|
195
|
+
display: 'flex',
|
|
196
|
+
flexWrap: 'wrap',
|
|
197
|
+
rowGap: '8px',
|
|
198
|
+
columnGap: '8px',
|
|
199
|
+
alignItems: 'baseline',
|
|
200
|
+
fontSize: '12px',
|
|
201
|
+
marginBottom: '4px',
|
|
202
|
+
}}
|
|
192
203
|
>
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
204
|
+
{Object.keys(keyFilter).map((key, index) => {
|
|
205
|
+
if (!keyFilter[key]) return <></>;
|
|
206
|
+
return (
|
|
207
|
+
<Box
|
|
208
|
+
key={index}
|
|
209
|
+
className="box-keyFilter"
|
|
210
|
+
style={{ display: 'flex', height: 'fit-content' }}
|
|
211
|
+
>
|
|
212
|
+
<Typography className="keyFilter">{key}</Typography>
|
|
213
|
+
<Button
|
|
214
|
+
style={{ padding: 0 }}
|
|
215
|
+
onClick={() => setKeyFilter({ ...keyFilter, [key]: false })}
|
|
216
|
+
>
|
|
217
|
+
<CloseIcon style={{ fontSize: 12, color: '#2B2C46' }} />
|
|
218
|
+
</Button>
|
|
219
|
+
</Box>
|
|
220
|
+
);
|
|
221
|
+
})}
|
|
222
|
+
<p
|
|
223
|
+
style={{ fontWeight: 'bold', color: '#000' }}
|
|
224
|
+
>{`${selectedFilter}/${maxFilter}`}</p>
|
|
225
|
+
<Box
|
|
226
|
+
style={{
|
|
227
|
+
color: '#E31B5D',
|
|
228
|
+
fontSize: '12px',
|
|
229
|
+
cursor: 'pointer',
|
|
230
|
+
marginLeft: '12px',
|
|
231
|
+
}}
|
|
232
|
+
onClick={() => {
|
|
233
|
+
setKeyFilter({});
|
|
234
|
+
}}
|
|
235
|
+
>
|
|
236
|
+
Clear all
|
|
237
|
+
</Box>
|
|
199
238
|
</Box>
|
|
200
239
|
</Box>
|
|
201
240
|
)}
|
|
@@ -204,7 +243,10 @@ function PreFilterComponent(props: Props) {
|
|
|
204
243
|
height={'100%'}
|
|
205
244
|
style={
|
|
206
245
|
isMobile
|
|
207
|
-
? {
|
|
246
|
+
? {
|
|
247
|
+
columnCount: 1,
|
|
248
|
+
marginBottom: keyFilter ? '50px' : '0px',
|
|
249
|
+
}
|
|
208
250
|
: columns <= 4
|
|
209
251
|
? { columnCount: columns, height: '100%', paddingBottom: 20 }
|
|
210
252
|
: { columnCount: 4, paddingBottom: 20 }
|
|
@@ -248,12 +290,22 @@ function PreFilterComponent(props: Props) {
|
|
|
248
290
|
minHeight: '20px',
|
|
249
291
|
color: '#2B2C46',
|
|
250
292
|
width: '100%',
|
|
293
|
+
maxWidth: 'fit-content',
|
|
251
294
|
overflow: 'hidden',
|
|
252
295
|
textOverflow: 'ellipsis',
|
|
253
296
|
whiteSpace: 'nowrap',
|
|
297
|
+
backgroundColor: keyFilter[item] ? '#E9E9EC' : '',
|
|
298
|
+
borderRadius: 8,
|
|
299
|
+
paddingLeft: '8px',
|
|
300
|
+
paddingRight: '8px',
|
|
254
301
|
}}
|
|
255
302
|
onClick={() => {
|
|
256
|
-
|
|
303
|
+
if (selectedFilter < maxFilter) {
|
|
304
|
+
setKeyFilter({
|
|
305
|
+
...keyFilter,
|
|
306
|
+
[item]: !keyFilter[item],
|
|
307
|
+
});
|
|
308
|
+
}
|
|
257
309
|
}}
|
|
258
310
|
>
|
|
259
311
|
{truncateString(item, !isMobile ? 35 : 35)}
|
|
@@ -297,10 +349,13 @@ function PreFilterComponent(props: Props) {
|
|
|
297
349
|
className="button-left"
|
|
298
350
|
style={{
|
|
299
351
|
width: '50%',
|
|
352
|
+
height: '64px',
|
|
300
353
|
backgroundColor: '#000000',
|
|
301
354
|
color: '#fff',
|
|
302
355
|
borderRadius: 0,
|
|
303
356
|
justifyContent: 'flex-start',
|
|
357
|
+
textTransform: 'none',
|
|
358
|
+
paddingLeft: '16px',
|
|
304
359
|
}}
|
|
305
360
|
onClick={() => handleClose()}
|
|
306
361
|
>
|
|
@@ -314,6 +369,8 @@ function PreFilterComponent(props: Props) {
|
|
|
314
369
|
color: '#fff',
|
|
315
370
|
borderRadius: 0,
|
|
316
371
|
justifyContent: 'flex-start',
|
|
372
|
+
textTransform: 'none',
|
|
373
|
+
paddingLeft: '16px',
|
|
317
374
|
}}
|
|
318
375
|
onClick={() => onHandlerSubmitData()}
|
|
319
376
|
>
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import DefaultModal from 'components/modal/DefaultModal';
|
|
2
|
+
import React, { useEffect, useState } from 'react';
|
|
3
|
+
import CloseIcon from '@material-ui/icons/Close';
|
|
4
|
+
import { getCroppedCanvas } from 'helpers/getCroppedCanvas';
|
|
5
|
+
import emailjs from '@emailjs/browser';
|
|
6
|
+
import { ToastHelper } from 'helpers/ToastHelper';
|
|
7
|
+
import { isUndefined } from 'lodash';
|
|
8
|
+
import { TextareaAutosize } from '@material-ui/core';
|
|
9
|
+
import toast from 'react-hot-toast';
|
|
10
|
+
import { ReactComponent as ErrorIcon } from 'common/assets/icons/error.svg';
|
|
11
|
+
interface Props {
|
|
12
|
+
requestImage: any;
|
|
13
|
+
selectedRegion: any;
|
|
14
|
+
setIsRfqModalOpen: any;
|
|
15
|
+
isRfqModalOpen?: any;
|
|
16
|
+
setRfqStatus: any;
|
|
17
|
+
}
|
|
18
|
+
// eslint-disable-next-line
|
|
19
|
+
const emailRegex = /.+\@.+\..+$/;
|
|
20
|
+
|
|
21
|
+
const getErrorMessage = (error: any) => {
|
|
22
|
+
switch (error.status) {
|
|
23
|
+
case 400:
|
|
24
|
+
return 'Your email could not be sent, please try again or send an email to support@nyris.io';
|
|
25
|
+
case 421:
|
|
26
|
+
case 450:
|
|
27
|
+
case 451:
|
|
28
|
+
case 452:
|
|
29
|
+
return "Email delivery failed. Rest assured, we're continuously attempting to send it for you. Alternatively, you can forward the email to support@nyris.io";
|
|
30
|
+
default:
|
|
31
|
+
return 'Your email could not be sent, please try again or send an email to support@nyris.io';
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export default function RfqModal({
|
|
36
|
+
requestImage,
|
|
37
|
+
selectedRegion,
|
|
38
|
+
setIsRfqModalOpen,
|
|
39
|
+
isRfqModalOpen,
|
|
40
|
+
setRfqStatus,
|
|
41
|
+
}: Props) {
|
|
42
|
+
const [email, setEmail] = useState('');
|
|
43
|
+
const [emailValid, setEmailValid] = useState<boolean | undefined>(undefined);
|
|
44
|
+
|
|
45
|
+
const [information, setInformation] = useState('');
|
|
46
|
+
useEffect(() => emailjs.init('SMGihPnuEGcYLm0V4'), []);
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
if (email)
|
|
49
|
+
emailRegex.test(email) ? setEmailValid(true) : setEmailValid(false);
|
|
50
|
+
}, [email]);
|
|
51
|
+
|
|
52
|
+
const handleRfq = async (e: { preventDefault: () => void }) => {
|
|
53
|
+
e.preventDefault();
|
|
54
|
+
const { canvas }: any = requestImage;
|
|
55
|
+
const croppedImage = getCroppedCanvas(canvas, selectedRegion);
|
|
56
|
+
const serviceId = 'service_zfsxshi';
|
|
57
|
+
const templateId = 'template_jlgc9le';
|
|
58
|
+
setIsRfqModalOpen(false);
|
|
59
|
+
try {
|
|
60
|
+
setRfqStatus('loading');
|
|
61
|
+
await emailjs.send(serviceId, templateId, {
|
|
62
|
+
email_id: email.trim(),
|
|
63
|
+
information_text: information,
|
|
64
|
+
request_image: croppedImage?.toDataURL(),
|
|
65
|
+
});
|
|
66
|
+
setRfqStatus('sent');
|
|
67
|
+
ToastHelper.success('Request sent successfully');
|
|
68
|
+
} catch (error) {
|
|
69
|
+
setRfqStatus('inactive');
|
|
70
|
+
|
|
71
|
+
toast(
|
|
72
|
+
t => {
|
|
73
|
+
return (
|
|
74
|
+
<div
|
|
75
|
+
style={{
|
|
76
|
+
display: 'flex',
|
|
77
|
+
flexDirection: 'column',
|
|
78
|
+
fontSize: '14px',
|
|
79
|
+
width: '294px',
|
|
80
|
+
}}
|
|
81
|
+
>
|
|
82
|
+
<span style={{ fontWeight: 'bold' }}>Email not sent</span>
|
|
83
|
+
<span>{getErrorMessage(error)}</span>
|
|
84
|
+
<a
|
|
85
|
+
href={`mailto:support@nyris.io?subject=Request for quotation&body=${information}`}
|
|
86
|
+
style={{
|
|
87
|
+
padding: '8px 16px 8px 16px',
|
|
88
|
+
border: '1px solid #000',
|
|
89
|
+
marginTop: '16px',
|
|
90
|
+
backgroundColor: 'transparent',
|
|
91
|
+
color: '#000',
|
|
92
|
+
cursor: 'pointer',
|
|
93
|
+
width: 'fit-content',
|
|
94
|
+
}}
|
|
95
|
+
>
|
|
96
|
+
support@nyris.io
|
|
97
|
+
</a>
|
|
98
|
+
</div>
|
|
99
|
+
);
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
duration: 5000,
|
|
103
|
+
style: {
|
|
104
|
+
background: '#FFE5EF',
|
|
105
|
+
color: '#000000',
|
|
106
|
+
maxWidth: '400px',
|
|
107
|
+
},
|
|
108
|
+
icon: (
|
|
109
|
+
<div style={{ minWidth: '20px', minHeight: '20px' }}>
|
|
110
|
+
<ErrorIcon />
|
|
111
|
+
</div>
|
|
112
|
+
),
|
|
113
|
+
},
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
setIsRfqModalOpen(false);
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
return (
|
|
120
|
+
<DefaultModal
|
|
121
|
+
openModal={isRfqModalOpen}
|
|
122
|
+
handleClose={(e: any) => {
|
|
123
|
+
setIsRfqModalOpen(false);
|
|
124
|
+
}}
|
|
125
|
+
>
|
|
126
|
+
<div
|
|
127
|
+
style={{
|
|
128
|
+
display: 'flex',
|
|
129
|
+
width: '378px',
|
|
130
|
+
flexDirection: 'column',
|
|
131
|
+
backgroundColor: '#fff',
|
|
132
|
+
}}
|
|
133
|
+
>
|
|
134
|
+
<div
|
|
135
|
+
style={{
|
|
136
|
+
padding: '16px',
|
|
137
|
+
display: 'flex',
|
|
138
|
+
flexDirection: 'column',
|
|
139
|
+
}}
|
|
140
|
+
>
|
|
141
|
+
<CloseIcon
|
|
142
|
+
style={{
|
|
143
|
+
fontSize: 16,
|
|
144
|
+
color: 'black',
|
|
145
|
+
alignSelf: 'flex-end',
|
|
146
|
+
cursor: 'pointer',
|
|
147
|
+
}}
|
|
148
|
+
onClick={() => setIsRfqModalOpen(false)}
|
|
149
|
+
/>
|
|
150
|
+
<p
|
|
151
|
+
style={{
|
|
152
|
+
color: '#2B2C46',
|
|
153
|
+
fontSize: '20px',
|
|
154
|
+
fontWeight: 'bold',
|
|
155
|
+
}}
|
|
156
|
+
>
|
|
157
|
+
Submit your image for quotation
|
|
158
|
+
</p>
|
|
159
|
+
</div>
|
|
160
|
+
<div
|
|
161
|
+
style={{
|
|
162
|
+
padding: '16px',
|
|
163
|
+
backgroundColor: '#F3F3F5',
|
|
164
|
+
display: 'flex',
|
|
165
|
+
justifyContent: 'center',
|
|
166
|
+
}}
|
|
167
|
+
>
|
|
168
|
+
<img
|
|
169
|
+
src={getCroppedCanvas(
|
|
170
|
+
requestImage?.canvas,
|
|
171
|
+
selectedRegion,
|
|
172
|
+
)?.toDataURL()}
|
|
173
|
+
alt="request_image"
|
|
174
|
+
style={{ maxHeight: '200px' }}
|
|
175
|
+
/>
|
|
176
|
+
</div>
|
|
177
|
+
<div
|
|
178
|
+
style={{
|
|
179
|
+
padding: '0px 16px 16px 16px',
|
|
180
|
+
backgroundColor: '#F3F3F5',
|
|
181
|
+
display: 'flex',
|
|
182
|
+
flexDirection: 'column',
|
|
183
|
+
rowGap: '16px',
|
|
184
|
+
}}
|
|
185
|
+
>
|
|
186
|
+
<div>
|
|
187
|
+
<p style={{ fontSize: '12px', color: '#2B2C46' }}>
|
|
188
|
+
Your email (required)
|
|
189
|
+
</p>
|
|
190
|
+
<input
|
|
191
|
+
value={email}
|
|
192
|
+
onChange={e => setEmail(e.currentTarget.value.trim())}
|
|
193
|
+
style={{
|
|
194
|
+
width: '100%',
|
|
195
|
+
border: 'none',
|
|
196
|
+
height: '32px',
|
|
197
|
+
padding: '8px 16px 8px 16px',
|
|
198
|
+
}}
|
|
199
|
+
/>
|
|
200
|
+
{!emailValid && !isUndefined(emailValid) && (
|
|
201
|
+
<p style={{ color: 'red', fontSize: '12px', paddingTop: '8px' }}>
|
|
202
|
+
Please enter a valid email.
|
|
203
|
+
</p>
|
|
204
|
+
)}
|
|
205
|
+
</div>
|
|
206
|
+
<div>
|
|
207
|
+
<p style={{ fontSize: '12px', color: '#2B2C46' }}>
|
|
208
|
+
Additional information
|
|
209
|
+
</p>
|
|
210
|
+
<TextareaAutosize
|
|
211
|
+
value={information}
|
|
212
|
+
onChange={e => setInformation(e.currentTarget.value)}
|
|
213
|
+
style={{
|
|
214
|
+
width: '100%',
|
|
215
|
+
border: 'none',
|
|
216
|
+
maxWidth: '346px',
|
|
217
|
+
padding: '8px 16px 8px 16px',
|
|
218
|
+
}}
|
|
219
|
+
/>
|
|
220
|
+
</div>
|
|
221
|
+
</div>
|
|
222
|
+
<div style={{ display: 'flex' }}>
|
|
223
|
+
<button
|
|
224
|
+
style={{
|
|
225
|
+
height: '66px',
|
|
226
|
+
display: 'flex',
|
|
227
|
+
alignItems: 'center',
|
|
228
|
+
width: '50%',
|
|
229
|
+
backgroundColor: '#4B4B4A',
|
|
230
|
+
color: 'white',
|
|
231
|
+
fontSize: '14px',
|
|
232
|
+
paddingLeft: '16px',
|
|
233
|
+
border: 'none',
|
|
234
|
+
cursor: 'pointer',
|
|
235
|
+
}}
|
|
236
|
+
onClick={() => setIsRfqModalOpen(false)}
|
|
237
|
+
>
|
|
238
|
+
Cancel
|
|
239
|
+
</button>
|
|
240
|
+
<button
|
|
241
|
+
style={{
|
|
242
|
+
height: '66px',
|
|
243
|
+
display: 'flex',
|
|
244
|
+
alignItems: 'center',
|
|
245
|
+
width: '50%',
|
|
246
|
+
backgroundColor: emailValid ? '#4DBE51' : '#E9E9EC',
|
|
247
|
+
color: emailValid ? '#fff' : '#AAABB5',
|
|
248
|
+
fontSize: '14px',
|
|
249
|
+
paddingLeft: '16px',
|
|
250
|
+
border: 'none',
|
|
251
|
+
cursor: emailValid ? 'pointer' : 'normal',
|
|
252
|
+
}}
|
|
253
|
+
disabled={!emailValid}
|
|
254
|
+
onClick={handleRfq}
|
|
255
|
+
>
|
|
256
|
+
Send
|
|
257
|
+
</button>
|
|
258
|
+
</div>
|
|
259
|
+
</div>
|
|
260
|
+
</DefaultModal>
|
|
261
|
+
);
|
|
262
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import {
|
|
2
|
+
RectCoords,
|
|
3
|
+
getElementSize,
|
|
4
|
+
getRectAspectRatio,
|
|
5
|
+
getThumbSizeArea,
|
|
6
|
+
elementToCanvas,
|
|
7
|
+
} from '@nyris/nyris-api';
|
|
8
|
+
|
|
9
|
+
export const getCroppedCanvas = (
|
|
10
|
+
canvas: HTMLCanvasElement | HTMLImageElement | HTMLVideoElement,
|
|
11
|
+
cropRect?: RectCoords,
|
|
12
|
+
) => {
|
|
13
|
+
let crop = cropRect || {
|
|
14
|
+
x1: 0,
|
|
15
|
+
x2: 1,
|
|
16
|
+
y1: 0,
|
|
17
|
+
y2: 1,
|
|
18
|
+
};
|
|
19
|
+
if (!canvas) return null;
|
|
20
|
+
|
|
21
|
+
const originalSize = getElementSize(canvas);
|
|
22
|
+
const aspectRatio = getRectAspectRatio(crop, originalSize);
|
|
23
|
+
let scaledSize = getThumbSizeArea(600, 600, aspectRatio);
|
|
24
|
+
let resizedCroppedCanvas = elementToCanvas(canvas, scaledSize, crop);
|
|
25
|
+
return resizedCroppedCanvas;
|
|
26
|
+
};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { useMemo } from 'react';
|
|
2
|
+
|
|
3
|
+
const useFilteredRegions = (regions: any, imageSelection: any) => {
|
|
4
|
+
const filteredRegions = useMemo(
|
|
5
|
+
() =>
|
|
6
|
+
regions.map(
|
|
7
|
+
(region: {
|
|
8
|
+
normalizedRect: { x1: any; x2: any; y1: any; y2: any };
|
|
9
|
+
}) => {
|
|
10
|
+
if (
|
|
11
|
+
region.normalizedRect?.x1 === imageSelection?.x1 &&
|
|
12
|
+
region.normalizedRect?.x2 === imageSelection?.x2 &&
|
|
13
|
+
region.normalizedRect?.y1 === imageSelection?.y1 &&
|
|
14
|
+
region.normalizedRect?.y2 === imageSelection?.y2
|
|
15
|
+
) {
|
|
16
|
+
return { ...region, show: false };
|
|
17
|
+
}
|
|
18
|
+
if (
|
|
19
|
+
imageSelection?.x1 === 0 &&
|
|
20
|
+
imageSelection?.x2 === 1 &&
|
|
21
|
+
imageSelection?.y1 === 0 &&
|
|
22
|
+
imageSelection?.y2 === 1
|
|
23
|
+
) {
|
|
24
|
+
return { ...region, show: false };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return { ...region, show: true };
|
|
28
|
+
},
|
|
29
|
+
),
|
|
30
|
+
|
|
31
|
+
[regions, imageSelection],
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
return filteredRegions;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export default useFilteredRegions;
|
package/src/index.css
CHANGED
|
@@ -3,12 +3,9 @@ import React, { useState } from 'react';
|
|
|
3
3
|
import './common.scss';
|
|
4
4
|
import { cadExtensions } from '@nyris/nyris-api';
|
|
5
5
|
import algoliasearch from 'algoliasearch/lite';
|
|
6
|
-
import IconSupport from 'common/assets/icons/support3.svg';
|
|
7
6
|
import DragDropFile from 'components/DragDropFile';
|
|
8
7
|
import CustomSearchBox from 'components/input/inputSearch';
|
|
9
8
|
import { connectInfiniteHits } from 'react-instantsearch-dom';
|
|
10
|
-
import { useMediaQuery } from 'react-responsive';
|
|
11
|
-
import { Link } from 'react-router-dom';
|
|
12
9
|
import { useAppSelector } from 'Store/Store';
|
|
13
10
|
import { AlgoliaSettings } from '../../types';
|
|
14
11
|
|
|
@@ -18,7 +15,6 @@ function AppMD() {
|
|
|
18
15
|
const { apiKey, appId, indexName } = settings.algolia as AlgoliaSettings;
|
|
19
16
|
const searchClient = algoliasearch(appId, apiKey);
|
|
20
17
|
searchClient.initIndex(indexName);
|
|
21
|
-
const isMobile = useMediaQuery({ query: '(max-width: 776px)' });
|
|
22
18
|
|
|
23
19
|
const acceptTypes = ['image/*']
|
|
24
20
|
.concat(settings.cadSearch ? cadExtensions : [])
|
|
@@ -36,13 +32,6 @@ function AppMD() {
|
|
|
36
32
|
|
|
37
33
|
return (
|
|
38
34
|
<Box className={`box-content-main ${isLoading ? 'loading' : ''}`}>
|
|
39
|
-
{isMobile && (
|
|
40
|
-
<Box className="btn-open-support">
|
|
41
|
-
<Link to={'/support'} style={{ color: '#3E36DC' }}>
|
|
42
|
-
<img src={IconSupport} alt="" width={16} height={16} />
|
|
43
|
-
</Link>
|
|
44
|
-
</Box>
|
|
45
|
-
)}
|
|
46
35
|
<Box className="box-content_top">
|
|
47
36
|
<Box className="fw-700 text-f32 text-dark2">
|
|
48
37
|
<h1>{settings.headerText}</h1>
|