@nyris/nyris-webapp 0.3.34 → 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.
Files changed (60) hide show
  1. package/build/asset-manifest.json +13 -13
  2. package/build/index.html +1 -1
  3. package/build/{precache-manifest.b85c7807a93875355f9f0f6490e6dc8c.js → precache-manifest.a2657ad49c132fb9c82629d37be15547.js} +14 -14
  4. package/build/service-worker.js +1 -1
  5. package/build/static/css/main.41fb4769.chunk.css +2 -0
  6. package/build/static/css/main.41fb4769.chunk.css.map +1 -0
  7. package/build/static/js/2.ff44260e.chunk.js +3 -0
  8. package/build/static/js/2.ff44260e.chunk.js.map +1 -0
  9. package/build/static/js/main.0b3b11fc.chunk.js +3 -0
  10. package/build/static/js/main.0b3b11fc.chunk.js.map +1 -0
  11. package/build/static/media/error.48b946a9.svg +3 -0
  12. package/package.json +3 -3
  13. package/public/index.html +13 -0
  14. package/src/Store/search/Search.ts +4 -4
  15. package/src/Store/search/search.initialState.ts +1 -1
  16. package/src/Store/search/types.ts +1 -1
  17. package/src/common/assets/icons/arrow_left.svg +3 -0
  18. package/src/common/assets/icons/arrow_right.svg +3 -0
  19. package/src/common/assets/icons/error.svg +3 -0
  20. package/src/components/AppMobile.tsx +111 -0
  21. package/src/components/DetailItem.tsx +21 -18
  22. package/src/components/DragDropFile.tsx +5 -4
  23. package/src/components/FooterMobile.tsx +9 -3
  24. package/src/components/Header.tsx +2 -1
  25. package/src/components/HeaderMobile.tsx +5 -3
  26. package/src/components/ImageCaptureHelpModal.tsx +7 -1
  27. package/src/components/ImagePreviewMobile.tsx +87 -0
  28. package/src/components/Layout.tsx +32 -92
  29. package/src/components/ProductList/index.tsx +4 -1
  30. package/src/components/RfqBanner.tsx +120 -0
  31. package/src/components/SidePanel.tsx +147 -0
  32. package/src/components/appMobile.scss +147 -142
  33. package/src/components/carousel/ImagePreviewCarousel.tsx +1 -7
  34. package/src/components/drawer/cameraCustom.tsx +5 -4
  35. package/src/components/input/inputSearch.tsx +12 -7
  36. package/src/components/modal/DefaultModal.tsx +1 -1
  37. package/src/components/pre-filter/index.tsx +144 -83
  38. package/src/components/results/ItemResult.tsx +9 -20
  39. package/src/components/rfq/RfqModal.tsx +262 -0
  40. package/src/helpers/ToastHelper.ts +10 -0
  41. package/src/helpers/getCroppedCanvas.ts +26 -0
  42. package/src/hooks/useFilteredRegions.ts +37 -0
  43. package/src/index.css +0 -4
  44. package/src/page/landingPage/AppMD.tsx +0 -11
  45. package/src/page/landingPage/AppMobile.tsx +1 -0
  46. package/src/page/landingPage/common.scss +44 -60
  47. package/src/page/result/index.tsx +256 -309
  48. package/src/translations.ts +2 -2
  49. package/src/types.ts +1 -0
  50. package/src/utils.ts +0 -1
  51. package/build/static/css/main.f2aa67fc.chunk.css +0 -2
  52. package/build/static/css/main.f2aa67fc.chunk.css.map +0 -1
  53. package/build/static/js/2.d1f7e826.chunk.js +0 -3
  54. package/build/static/js/2.d1f7e826.chunk.js.map +0 -1
  55. package/build/static/js/main.e9aec8a9.chunk.js +0 -3
  56. package/build/static/js/main.e9aec8a9.chunk.js.map +0 -1
  57. package/build/static/media/support3.4a17f96e.svg +0 -3
  58. package/src/hooks/useVisualSearch.tsx +0 -77
  59. /package/build/static/js/{2.d1f7e826.chunk.js.LICENSE.txt → 2.ff44260e.chunk.js.LICENSE.txt} +0 -0
  60. /package/build/static/js/{main.e9aec8a9.chunk.js.LICENSE.txt → main.0b3b11fc.chunk.js.LICENSE.txt} +0 -0
@@ -1,13 +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 { setUpdateKeyFilterDesktop } from 'Store/search/Search';
8
- import { ToggleButton, ToggleButtonGroup } from '@material-ui/lab';
7
+ import { setPreFilter } from 'Store/search/Search';
9
8
  import { useMediaQuery } from 'react-responsive';
10
- import { isEmpty } from 'lodash';
9
+ import { isEmpty, pickBy } from 'lodash';
11
10
  import { Skeleton } from '@material-ui/lab';
12
11
  import { truncateString } from 'helpers/truncateString';
13
12
 
@@ -15,21 +14,35 @@ interface Props {
15
14
  handleClose?: any;
16
15
  // onChangeKeyFilter?: any;
17
16
  }
18
-
17
+ const maxFilter = 10;
19
18
  function PreFilterComponent(props: Props) {
20
19
  const { handleClose } = props;
21
20
  const dispatch = useAppDispatch();
22
21
  const stateGlobal = useAppSelector(state => state);
23
22
  const {
24
23
  settings,
25
- search: { keyFilter: keyFilterState },
24
+ search: { preFilter: keyFilterState },
26
25
  } = stateGlobal;
27
26
  const [resultFilter, setResultFilter] = useState<any>([]);
28
- const [keyFilter, setKeyFilter] = useState<string>(keyFilterState || '');
27
+ const [keyFilter, setKeyFilter] = useState<Record<string, boolean>>(
28
+ keyFilterState || {},
29
+ );
30
+
29
31
  const [isLoading, setLoading] = useState<boolean>(false);
30
32
  const [columns, setColumns] = useState<number>(0);
31
33
  const isMobile = useMediaQuery({ query: '(max-width: 776px)' });
32
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
+
33
46
  const getDataFilterDesktop = async () => {
34
47
  setLoading(true);
35
48
  const dataResultFilter = getFilters(1000, settings)
@@ -37,8 +50,10 @@ function PreFilterComponent(props: Props) {
37
50
  const arrResult =
38
51
  res.find(value => value.key === settings.visualSearchFilterKey)
39
52
  ?.values || [];
53
+
40
54
  const newResult = arrResult.sort().reduce((a: any, c: any) => {
41
- let k = c[0].toLocaleUpperCase();
55
+ if (!c[0]) return a;
56
+ let k = c[0]?.toLocaleUpperCase();
42
57
  if (a[k]) a[k].push(c);
43
58
  else a[k] = [c];
44
59
  return a;
@@ -61,13 +76,6 @@ function PreFilterComponent(props: Props) {
61
76
  // eslint-disable-next-line react-hooks/exhaustive-deps
62
77
  }, []);
63
78
 
64
- const handleAlignment = (
65
- event: React.MouseEvent<HTMLElement>,
66
- value: any,
67
- ) => {
68
- setKeyFilter(value);
69
- };
70
-
71
79
  const filterSearchHandler = async (value: any) => {
72
80
  if (!value) {
73
81
  getDataFilterDesktop();
@@ -79,7 +87,6 @@ function PreFilterComponent(props: Props) {
79
87
  settings,
80
88
  )
81
89
  .then(res => {
82
- // console.log("res", res);
83
90
  if (res.length > 0) {
84
91
  setResultFilter({ [res[0][0].toLocaleUpperCase()]: res });
85
92
  if (res.length <= 20) setColumns(1);
@@ -98,7 +105,7 @@ function PreFilterComponent(props: Props) {
98
105
  };
99
106
 
100
107
  const onHandlerSubmitData = () => {
101
- dispatch(setUpdateKeyFilterDesktop(keyFilter));
108
+ dispatch(setPreFilter(pickBy(keyFilter, value => !!value)));
102
109
  handleClose();
103
110
  };
104
111
 
@@ -125,7 +132,7 @@ function PreFilterComponent(props: Props) {
125
132
  color: '#000',
126
133
  fontSize: '24px',
127
134
  fontWeight: 700,
128
- paddingLeft: isMobile ? '0px' : '14px',
135
+ paddingLeft: '14px',
129
136
  marginBottom: isMobile ? '0px' : '-8px',
130
137
  marginTop: isMobile ? '0px' : '24px',
131
138
  }}
@@ -139,7 +146,6 @@ function PreFilterComponent(props: Props) {
139
146
  </div>
140
147
  <Box
141
148
  className="box-top"
142
- style={isMobile ? { padding: 0, marginTop: '16px' } : undefined}
143
149
  display={'flex'}
144
150
  justifyContent={'space-between'}
145
151
  alignItems={'center'}
@@ -150,34 +156,21 @@ function PreFilterComponent(props: Props) {
150
156
  justifyItems={'center'}
151
157
  style={isMobile ? { width: '100%' } : undefined}
152
158
  >
153
- {(!keyFilter || isMobile) && (
154
- <Box
155
- className="icon-search"
156
- style={{ marginRight: 11 }}
157
- display={'flex'}
158
- justifyContent={'center'}
159
- alignItems={'center'}
160
- >
161
- <img
162
- style={{ maxWidth: 'fit-content' }}
163
- src={IconSearch}
164
- alt=""
165
- width={18}
166
- height={18}
167
- />
168
- </Box>
169
- )}
170
-
171
- {keyFilter && !isMobile && (
172
- <Box display={'flex'} className="box-keyFilter">
173
- <Typography className="keyFilter max-line-1">
174
- {keyFilter}
175
- </Typography>
176
- <Button style={{ padding: 0 }} onClick={() => setKeyFilter('')}>
177
- <CloseIcon style={{ fontSize: 12, color: '#2B2C46' }} />
178
- </Button>
179
- </Box>
180
- )}
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>
181
174
 
182
175
  <input
183
176
  className="input-search-filter"
@@ -189,17 +182,59 @@ function PreFilterComponent(props: Props) {
189
182
  </Box>
190
183
  </Box>
191
184
 
192
- {keyFilter && isMobile && (
193
- <Box style={{ margin: '10px 16px' }}>
185
+ {!isEmpty(keyFilter) && selectedFilter > 0 && (
186
+ <Box
187
+ style={{
188
+ margin: '10px 16px 10px 16px',
189
+ display: 'flex',
190
+ justifyContent: 'space-between',
191
+ }}
192
+ >
194
193
  <Box
195
- display={'flex'}
196
- className="box-keyFilter"
197
- style={{ display: 'inline-flex' }}
194
+ style={{
195
+ display: 'flex',
196
+ flexWrap: 'wrap',
197
+ rowGap: '8px',
198
+ columnGap: '8px',
199
+ alignItems: 'baseline',
200
+ fontSize: '12px',
201
+ marginBottom: '4px',
202
+ }}
198
203
  >
199
- <Typography className="keyFilter">{keyFilter}</Typography>
200
- <Button style={{ padding: 0 }} onClick={() => setKeyFilter('')}>
201
- <CloseIcon style={{ fontSize: 12, color: '#2B2C46' }} />
202
- </Button>
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>
203
238
  </Box>
204
239
  </Box>
205
240
  )}
@@ -208,7 +243,10 @@ function PreFilterComponent(props: Props) {
208
243
  height={'100%'}
209
244
  style={
210
245
  isMobile
211
- ? { columnCount: 1, marginBottom: keyFilter ? '50px' : '0px' }
246
+ ? {
247
+ columnCount: 1,
248
+ marginBottom: keyFilter ? '50px' : '0px',
249
+ }
212
250
  : columns <= 4
213
251
  ? { columnCount: columns, height: '100%', paddingBottom: 20 }
214
252
  : { columnCount: 4, paddingBottom: 20 }
@@ -216,25 +254,25 @@ function PreFilterComponent(props: Props) {
216
254
  >
217
255
  {Object.entries(resultFilter).map(([key, value]: any, i: any) => {
218
256
  return (
219
- <Box
220
- className="box-group-items"
221
- style={
222
- columns <= 4 ? { width: 'fit-content' } : { width: '100%' }
223
- }
224
- >
225
- <Typography
226
- style={{ fontWeight: 'bold', color: '#000', paddingLeft: 11 }}
257
+ <Box className="box-group-items" key={key}>
258
+ <Box
259
+ style={{
260
+ display: 'flex',
261
+ flexDirection: 'column',
262
+ rowGap: '12px',
263
+ width: '100%',
264
+ }}
227
265
  >
228
- {key}
229
- </Typography>
266
+ <Typography
267
+ style={{
268
+ fontWeight: 'bold',
269
+ color: '#000',
270
+ fontSize: '12px',
271
+ }}
272
+ >
273
+ {key}
274
+ </Typography>
230
275
 
231
- <ToggleButtonGroup
232
- value={keyFilter}
233
- exclusive
234
- onChange={handleAlignment}
235
- aria-label=""
236
- className="box-btn-group"
237
- >
238
276
  {value.map((item: any, index: any) => {
239
277
  return (
240
278
  <Tooltip
@@ -242,22 +280,40 @@ function PreFilterComponent(props: Props) {
242
280
  title={item}
243
281
  placement="top"
244
282
  arrow={true}
245
- disableHoverListener={item.length < 20}
283
+ disableHoverListener={item.length < 35}
246
284
  >
247
- <ToggleButton
248
- value={item}
285
+ <Box
249
286
  aria-label={item}
250
- className="item-btn"
251
- onChange={() => {
252
- setKeyFilter(item);
287
+ style={{
288
+ cursor: 'pointer',
289
+ fontSize: '12px',
290
+ minHeight: '20px',
291
+ color: '#2B2C46',
292
+ width: '100%',
293
+ maxWidth: 'fit-content',
294
+ overflow: 'hidden',
295
+ textOverflow: 'ellipsis',
296
+ whiteSpace: 'nowrap',
297
+ backgroundColor: keyFilter[item] ? '#E9E9EC' : '',
298
+ borderRadius: 8,
299
+ paddingLeft: '8px',
300
+ paddingRight: '8px',
301
+ }}
302
+ onClick={() => {
303
+ if (selectedFilter < maxFilter) {
304
+ setKeyFilter({
305
+ ...keyFilter,
306
+ [item]: !keyFilter[item],
307
+ });
308
+ }
253
309
  }}
254
310
  >
255
- {truncateString(item, !isMobile ? 20 : 35)}
256
- </ToggleButton>
311
+ {truncateString(item, !isMobile ? 35 : 35)}
312
+ </Box>
257
313
  </Tooltip>
258
314
  );
259
315
  })}
260
- </ToggleButtonGroup>
316
+ </Box>
261
317
  </Box>
262
318
  );
263
319
  })}
@@ -293,10 +349,13 @@ function PreFilterComponent(props: Props) {
293
349
  className="button-left"
294
350
  style={{
295
351
  width: '50%',
352
+ height: '64px',
296
353
  backgroundColor: '#000000',
297
354
  color: '#fff',
298
355
  borderRadius: 0,
299
356
  justifyContent: 'flex-start',
357
+ textTransform: 'none',
358
+ paddingLeft: '16px',
300
359
  }}
301
360
  onClick={() => handleClose()}
302
361
  >
@@ -310,6 +369,8 @@ function PreFilterComponent(props: Props) {
310
369
  color: '#fff',
311
370
  borderRadius: 0,
312
371
  justifyContent: 'flex-start',
372
+ textTransform: 'none',
373
+ paddingLeft: '16px',
313
374
  }}
314
375
  onClick={() => onHandlerSubmitData()}
315
376
  >
@@ -103,7 +103,7 @@ function ItemResult(props: Props) {
103
103
  dispatch(updateStatusLoading(false));
104
104
  }, 400);
105
105
  };
106
-
106
+ const ctaLink = dataItem[settings.field?.ctaLinkField];
107
107
  return (
108
108
  <Box className="wrap-main-item-result">
109
109
  <DefaultModal
@@ -185,11 +185,6 @@ function ItemResult(props: Props) {
185
185
  />
186
186
  )}
187
187
  </Button>
188
- {isHover && (
189
- <Box className="box-hover">
190
- <Button>View item</Button>
191
- </Box>
192
- )}
193
188
  </Box>
194
189
  </Box>
195
190
 
@@ -222,7 +217,7 @@ function ItemResult(props: Props) {
222
217
  <span style={{ marginRight: 3 }}>
223
218
  {settings.itemIdLabel || 'SKU'}:
224
219
  </span>
225
- {truncateString(sku, 20)}
220
+ {truncateString(sku, isMobile ? 17 : 20)}
226
221
  </Typography>
227
222
  </Tooltip>
228
223
 
@@ -319,16 +314,13 @@ function ItemResult(props: Props) {
319
314
  justifyContent: 'space-between',
320
315
  width: '100%',
321
316
  padding: 0,
322
- cursor: dataItem[settings.field?.ctaLinkField]
323
- ? 'pointer'
324
- : 'normal',
317
+ cursor: ctaLink ? 'pointer' : 'normal',
325
318
  }}
326
319
  onClick={() => {
327
- feedbackConversionEpic(state, indexItem, dataItem.sku);
328
- window.open(
329
- `${dataItem[settings.field.ctaLinkField]}`,
330
- '_blank',
331
- );
320
+ if (ctaLink) {
321
+ feedbackConversionEpic(state, indexItem, dataItem.sku);
322
+ window.open(`${ctaLink}`, '_blank');
323
+ }
332
324
  }}
333
325
  >
334
326
  <Typography
@@ -340,17 +332,14 @@ function ItemResult(props: Props) {
340
332
  fontSize: '11px',
341
333
  letterSpacing: '0.27px',
342
334
  wordBreak: 'break-all',
343
- maxWidth:
344
- !isMobile && dataItem[settings.field?.ctaLinkField]
345
- ? '136px'
346
- : '164x',
335
+ maxWidth: !isMobile && ctaLink ? '136px' : '164x',
347
336
  paddingRight: '8px',
348
337
  }}
349
338
  align="left"
350
339
  >
351
340
  {truncateString(dataItem[settings.field.productName], 45)}
352
341
  </Typography>
353
- {!isMobile && dataItem[settings.field?.ctaLinkField] && (
342
+ {!isMobile && ctaLink && (
354
343
  <img src={IconOpenLink} alt="more-info" width={20} />
355
344
  )}
356
345
  </Box>
@@ -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
+ }