@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.
- package/build/asset-manifest.json +13 -13
- package/build/index.html +1 -1
- package/build/{precache-manifest.b85c7807a93875355f9f0f6490e6dc8c.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/DetailItem.tsx +21 -18
- package/src/components/DragDropFile.tsx +5 -4
- package/src/components/FooterMobile.tsx +9 -3
- 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/carousel/ImagePreviewCarousel.tsx +1 -7
- 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 +144 -83
- package/src/components/results/ItemResult.tsx +9 -20
- 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 +44 -60
- 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.f2aa67fc.chunk.css +0 -2
- package/build/static/css/main.f2aa67fc.chunk.css.map +0 -1
- package/build/static/js/2.d1f7e826.chunk.js +0 -3
- package/build/static/js/2.d1f7e826.chunk.js.map +0 -1
- package/build/static/js/main.e9aec8a9.chunk.js +0 -3
- package/build/static/js/main.e9aec8a9.chunk.js.map +0 -1
- package/build/static/media/support3.4a17f96e.svg +0 -3
- package/src/hooks/useVisualSearch.tsx +0 -77
- /package/build/static/js/{2.d1f7e826.chunk.js.LICENSE.txt → 2.ff44260e.chunk.js.LICENSE.txt} +0 -0
- /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 {
|
|
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: {
|
|
24
|
+
search: { preFilter: keyFilterState },
|
|
26
25
|
} = stateGlobal;
|
|
27
26
|
const [resultFilter, setResultFilter] = useState<any>([]);
|
|
28
|
-
const [keyFilter, setKeyFilter] = useState<string
|
|
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
|
-
|
|
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(
|
|
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:
|
|
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
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
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 &&
|
|
193
|
-
<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
|
+
>
|
|
194
193
|
<Box
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
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
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
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
|
-
? {
|
|
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
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
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
|
-
|
|
229
|
-
|
|
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 <
|
|
283
|
+
disableHoverListener={item.length < 35}
|
|
246
284
|
>
|
|
247
|
-
<
|
|
248
|
-
value={item}
|
|
285
|
+
<Box
|
|
249
286
|
aria-label={item}
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
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 ?
|
|
256
|
-
</
|
|
311
|
+
{truncateString(item, !isMobile ? 35 : 35)}
|
|
312
|
+
</Box>
|
|
257
313
|
</Tooltip>
|
|
258
314
|
);
|
|
259
315
|
})}
|
|
260
|
-
</
|
|
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:
|
|
323
|
-
? 'pointer'
|
|
324
|
-
: 'normal',
|
|
317
|
+
cursor: ctaLink ? 'pointer' : 'normal',
|
|
325
318
|
}}
|
|
326
319
|
onClick={() => {
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
`${
|
|
330
|
-
|
|
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 &&
|
|
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
|
+
}
|