@nyris/nyris-webapp 0.3.89 → 0.3.90
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/_headers +2 -0
- package/build/asset-manifest.json +6 -6
- package/build/index.html +1 -1
- package/build/js/settings.example.js +17 -0
- package/build/static/css/main.734b52e1.css +4 -0
- package/build/static/css/main.734b52e1.css.map +1 -0
- package/build/static/js/main.cede3ae1.js +3 -0
- package/build/static/js/{main.ca8b95bc.js.map → main.cede3ae1.js.map} +1 -1
- package/package.json +3 -3
- package/public/_headers +2 -0
- package/public/index.html +1 -1
- package/public/js/settings.example.js +17 -0
- package/src/App.tsx +5 -3
- package/src/components/Cart.tsx +321 -0
- package/src/components/CustomCameraDrawer.tsx +4 -22
- package/src/components/DragDropFile.tsx +57 -38
- package/src/components/ExperienceVisualSearch/ExperienceVisualSearch.tsx +6 -1
- package/src/components/ExperienceVisualSearch/ExperienceVisualSearchTrigger.tsx +2 -2
- package/src/components/GroundingSpecs.tsx +47 -0
- package/src/components/Header.tsx +94 -93
- package/src/components/HitsPerPage.tsx +4 -2
- package/src/components/ImagePreview.tsx +64 -31
- package/src/components/ImageUpload.tsx +247 -0
- package/src/components/ItemSpecification.tsx +164 -0
- package/src/components/MatchNotificationBanner.tsx +165 -0
- package/src/components/PostFilter/PostFilter.tsx +22 -1
- package/src/components/PostFilter/PostFilterComponent.tsx +59 -26
- package/src/components/PostFilter/PostFilterFindApi.tsx +242 -0
- package/src/components/PoweredBy.tsx +16 -0
- package/src/components/PreFilter/PreFilter.tsx +77 -54
- package/src/components/Product/Product.tsx +186 -28
- package/src/components/Product/ProductAttribute.tsx +2 -2
- package/src/components/Product/ProductDetailView.tsx +123 -18
- package/src/components/Product/ProductDetailViewModal.tsx +3 -0
- package/src/components/Product/ProductList.tsx +78 -8
- package/src/components/SidePanel.tsx +212 -120
- package/src/components/TextSearch.tsx +82 -203
- package/src/components/Toaster.tsx +34 -15
- package/src/helpers/ToastHelper.ts +6 -2
- package/src/hooks/useCadSearch.ts +5 -0
- package/src/hooks/useImageSearch.ts +102 -13
- package/src/index.css +59 -0
- package/src/layouts/AppLayout.tsx +16 -14
- package/src/pages/Home.tsx +61 -13
- package/src/pages/Result.tsx +287 -295
- package/src/services/vizo.ts +161 -0
- package/src/stores/request/Misc/misc.initialstate.ts +1 -0
- package/src/stores/request/Misc/misc.slice.ts +1 -0
- package/src/stores/request/filter/filter.initialState.ts +3 -0
- package/src/stores/request/filter/filter.slice.ts +23 -0
- package/src/stores/result/prodcuts/products.initialState.ts +4 -0
- package/src/stores/result/prodcuts/products.slice.ts +15 -0
- package/src/stores/types.ts +27 -1
- package/src/stores/ui/loading/loading.initialState.ts +1 -0
- package/src/stores/ui/loading/loading.slice.ts +4 -0
- package/src/stores/ui/sidePanel/sidePanel.initialState.ts +5 -0
- package/src/stores/ui/sidePanel/sidePanel.slice.ts +11 -0
- package/src/stores/ui/uiStore.ts +4 -1
- package/src/styles/Cart.scss +210 -0
- package/src/styles/common.scss +10 -0
- package/src/translations.ts +4 -4
- package/src/types.ts +11 -3
- package/src/utils/prepareImageList.ts +6 -5
- package/src/utils/textSearchFilter.ts +203 -0
- package/tailwind.config.js +1 -0
- package/build/static/css/main.ba1c7479.css +0 -4
- package/build/static/css/main.ba1c7479.css.map +0 -1
- package/build/static/js/main.ca8b95bc.js +0 -3
- package/src/components/Footer.tsx +0 -21
- /package/build/static/js/{main.ca8b95bc.js.LICENSE.txt → main.cede3ae1.js.LICENSE.txt} +0 -0
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nyris/nyris-webapp",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.90",
|
|
4
4
|
"dependencies": {
|
|
5
5
|
"@auth0/auth0-react": "^2.2.4",
|
|
6
6
|
"@emailjs/browser": "^4.3.3",
|
|
7
|
-
"@nyris/nyris-api": "^0.3.
|
|
8
|
-
"@nyris/nyris-react-components": "^0.3.
|
|
7
|
+
"@nyris/nyris-api": "^0.3.90",
|
|
8
|
+
"@nyris/nyris-react-components": "^0.3.90",
|
|
9
9
|
"@radix-ui/react-accordion": "^1.2.2",
|
|
10
10
|
"@radix-ui/react-dialog": "^1.1.4",
|
|
11
11
|
"@radix-ui/react-popover": "^1.1.4",
|
package/public/_headers
ADDED
package/public/index.html
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
<!DOCTYPE html>
|
|
2
2
|
<html lang="en">
|
|
3
3
|
<head>
|
|
4
|
+
<meta name="robots" content="noindex, nofollow">
|
|
4
5
|
<meta charset="utf-8" />
|
|
5
6
|
<meta
|
|
6
7
|
name="viewport"
|
|
@@ -22,7 +23,6 @@
|
|
|
22
23
|
document.documentElement.style.setProperty('--secondary-color', window.settings.theme.secondaryColor);
|
|
23
24
|
</script>
|
|
24
25
|
<script>
|
|
25
|
-
window.settings.algolia.enabled = true;
|
|
26
26
|
let vh = window.innerHeight * 0.01;
|
|
27
27
|
document.documentElement.style.setProperty("--vh", `${vh}px`);
|
|
28
28
|
|
|
@@ -6,6 +6,7 @@ var settings = {
|
|
|
6
6
|
supportEmail: '',
|
|
7
7
|
},
|
|
8
8
|
algolia: {
|
|
9
|
+
enabled: false,
|
|
9
10
|
apiKey: '',
|
|
10
11
|
appId: '',
|
|
11
12
|
indexName: '',
|
|
@@ -23,6 +24,7 @@ var settings = {
|
|
|
23
24
|
],
|
|
24
25
|
// Nyris - visual search
|
|
25
26
|
apiKey: '',
|
|
27
|
+
aiApiKey: '',
|
|
26
28
|
baseUrl: 'https://api.nyris.io',
|
|
27
29
|
jpegQuality: 0.9,
|
|
28
30
|
maxHeight: 1024,
|
|
@@ -31,6 +33,20 @@ var settings = {
|
|
|
31
33
|
responseFormat: 'application/offers.complete+json',
|
|
32
34
|
visualSearchFilterKey: '',
|
|
33
35
|
shouldUseUserMetadata: '',
|
|
36
|
+
requestContentType: 'multipart/form-data', // use 'image/jpeg' for elastic search
|
|
37
|
+
textSearchScoring: {
|
|
38
|
+
sku: { exactMatch: 50, startsWith: 30 },
|
|
39
|
+
customIds: { exactMatch: 40, startsWith: 6, partial: 2 },
|
|
40
|
+
title: { exactMatch: 25, partial: 10 },
|
|
41
|
+
brand: { exactMatch: 15, startsWith: 6, partial: 2 },
|
|
42
|
+
links: { searchable: false },
|
|
43
|
+
'customIds.gtin': { searchable: false }, // for nested fields use dot notation
|
|
44
|
+
otherFields: { exactMatch: 8, startsWith: 6, partial: 2 },
|
|
45
|
+
},
|
|
46
|
+
vizo: {
|
|
47
|
+
groundingEnabled: false,
|
|
48
|
+
fallbackToElasticSearch: false,
|
|
49
|
+
},
|
|
34
50
|
// UI - theme
|
|
35
51
|
theme: {
|
|
36
52
|
appBarLogoUrl: '',
|
|
@@ -79,6 +95,7 @@ var settings = {
|
|
|
79
95
|
},
|
|
80
96
|
],
|
|
81
97
|
// features
|
|
98
|
+
cart: false,
|
|
82
99
|
showPoweredByNyris: '',
|
|
83
100
|
postFilterOption: '',
|
|
84
101
|
preFilterOption: '',
|
package/src/App.tsx
CHANGED
|
@@ -7,8 +7,8 @@ import { InstantSearch } from 'react-instantsearch';
|
|
|
7
7
|
|
|
8
8
|
function App() {
|
|
9
9
|
const algoliaClient = algoliasearch(
|
|
10
|
-
window.settings.algolia.appId,
|
|
11
|
-
window.settings.algolia.apiKey,
|
|
10
|
+
window.settings.algolia.appId || '0',
|
|
11
|
+
window.settings.algolia.apiKey || '0',
|
|
12
12
|
);
|
|
13
13
|
|
|
14
14
|
return (
|
|
@@ -20,7 +20,9 @@ function App() {
|
|
|
20
20
|
...algoliaClient,
|
|
21
21
|
search(requests: any) {
|
|
22
22
|
if (
|
|
23
|
-
requests?.every(
|
|
23
|
+
requests?.every(
|
|
24
|
+
({ params }: any) => !params.query && !params.filters,
|
|
25
|
+
)
|
|
24
26
|
) {
|
|
25
27
|
return Promise.resolve({
|
|
26
28
|
results: requests.map(() => ({
|
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
import { useEffect, useState } from 'react';
|
|
2
|
+
import { createPortal } from 'react-dom';
|
|
3
|
+
import Tooltip from './Tooltip/TooltipComponent';
|
|
4
|
+
import { twMerge } from 'tailwind-merge';
|
|
5
|
+
import toast from 'react-hot-toast';
|
|
6
|
+
import { Icon } from '@nyris/nyris-react-components';
|
|
7
|
+
|
|
8
|
+
const Cart = () => {
|
|
9
|
+
const [isCartOpen, setIsCartOpen] = useState(false);
|
|
10
|
+
const [cartParts, setCartParts] = useState<any[]>([]);
|
|
11
|
+
const [amountOfParts, setAmountOfParts] = useState(0);
|
|
12
|
+
const [quantityEdits, setQuantityEdits] = useState<Record<string, string>>(
|
|
13
|
+
{},
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
const updateCount = () => {
|
|
17
|
+
const cart = sessionStorage.getItem('cart');
|
|
18
|
+
if (cart) {
|
|
19
|
+
const parsedCart = JSON.parse(cart);
|
|
20
|
+
setCartParts(parsedCart);
|
|
21
|
+
const amount = parsedCart.reduce(
|
|
22
|
+
(total: number, part: any) => total + (part.quantity || 1),
|
|
23
|
+
0,
|
|
24
|
+
);
|
|
25
|
+
setAmountOfParts(amount);
|
|
26
|
+
} else {
|
|
27
|
+
setCartParts([]);
|
|
28
|
+
setAmountOfParts(0);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
updateCount();
|
|
34
|
+
|
|
35
|
+
window.addEventListener('cart-updated', updateCount);
|
|
36
|
+
|
|
37
|
+
return () => {
|
|
38
|
+
window.removeEventListener('cart-updated', updateCount);
|
|
39
|
+
};
|
|
40
|
+
}, []);
|
|
41
|
+
|
|
42
|
+
const onToggleCart = (isOpen: boolean) => {
|
|
43
|
+
setIsCartOpen(isOpen);
|
|
44
|
+
if (isOpen) {
|
|
45
|
+
document.body.classList.add('overflow-hidden');
|
|
46
|
+
} else {
|
|
47
|
+
document.body.classList.remove('overflow-hidden');
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const clearSessionProducts = () => {
|
|
52
|
+
sessionStorage.removeItem('cart');
|
|
53
|
+
window.dispatchEvent(new Event('cart-updated'));
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const removePartBySku = (skuToRemove: string): void => {
|
|
57
|
+
const updatedCart = cartParts.filter((p: any) => p.sku !== skuToRemove);
|
|
58
|
+
if (!updatedCart.length) {
|
|
59
|
+
setIsCartOpen(false);
|
|
60
|
+
}
|
|
61
|
+
sessionStorage.setItem('cart', JSON.stringify(updatedCart));
|
|
62
|
+
window.dispatchEvent(new Event('cart-updated'));
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const updateQuantity = (sku: string, delta: number) => {
|
|
66
|
+
const updated = cartParts
|
|
67
|
+
.map((part: any) => {
|
|
68
|
+
if (part.sku !== sku) return part;
|
|
69
|
+
const newQuantity = (part.quantity || 1) + delta;
|
|
70
|
+
return newQuantity > 0 ? { ...part, quantity: newQuantity } : null;
|
|
71
|
+
})
|
|
72
|
+
.filter(Boolean);
|
|
73
|
+
setQuantityEdits(prev => {
|
|
74
|
+
if (!Object.prototype.hasOwnProperty.call(prev, sku)) return prev;
|
|
75
|
+
const next = { ...prev };
|
|
76
|
+
delete next[sku];
|
|
77
|
+
return next;
|
|
78
|
+
});
|
|
79
|
+
setCartParts(updated);
|
|
80
|
+
if (!updated.length) {
|
|
81
|
+
setIsCartOpen(false);
|
|
82
|
+
}
|
|
83
|
+
sessionStorage.setItem('cart', JSON.stringify(updated));
|
|
84
|
+
window.dispatchEvent(new Event('cart-updated'));
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const getQuantityDisplayValue = (sku: string, quantity: number) =>
|
|
88
|
+
quantityEdits[sku] ?? String(quantity || 1);
|
|
89
|
+
|
|
90
|
+
const generateCSVContent = (): string => {
|
|
91
|
+
const rows = [['Product Sku', 'Product Name', 'Quantity']];
|
|
92
|
+
cartParts.forEach((part: any) => {
|
|
93
|
+
rows.push([part.sku, part.title, (part.quantity || 1).toString()]);
|
|
94
|
+
});
|
|
95
|
+
return rows.map(row => row.map(cell => `"${cell}"`).join(',')).join('\n');
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const downloadCSV = () => {
|
|
99
|
+
const csvContent = generateCSVContent();
|
|
100
|
+
const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
|
|
101
|
+
const url = URL.createObjectURL(blob);
|
|
102
|
+
|
|
103
|
+
const link = document.createElement('a');
|
|
104
|
+
link.href = url;
|
|
105
|
+
link.setAttribute('download', 'products.csv');
|
|
106
|
+
document.body.appendChild(link);
|
|
107
|
+
link.click();
|
|
108
|
+
document.body.removeChild(link);
|
|
109
|
+
// clearSessionProducts();
|
|
110
|
+
|
|
111
|
+
toast.success('Your data has been downloaded.');
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
return (
|
|
115
|
+
<>
|
|
116
|
+
<Tooltip
|
|
117
|
+
content="The basket is currently empty"
|
|
118
|
+
disabled={!!amountOfParts}
|
|
119
|
+
>
|
|
120
|
+
<button
|
|
121
|
+
className={twMerge(
|
|
122
|
+
'relative flex items-center justify-center rounded-2xl border border-solid border-[#DDDEE7]',
|
|
123
|
+
'h-8 w-8',
|
|
124
|
+
)}
|
|
125
|
+
style={{
|
|
126
|
+
backgroundColor:
|
|
127
|
+
amountOfParts > 0
|
|
128
|
+
? window.settings.theme.primaryColor
|
|
129
|
+
: '#FAFAFA',
|
|
130
|
+
}}
|
|
131
|
+
onClick={() => onToggleCart(true)}
|
|
132
|
+
>
|
|
133
|
+
{amountOfParts ? (
|
|
134
|
+
<div
|
|
135
|
+
className={twMerge(
|
|
136
|
+
'absolute -top-1 -right-1 flex h-4 w-4 items-center justify-center rounded-full border-2 border-white border-solid text-white',
|
|
137
|
+
'text-[9px]',
|
|
138
|
+
)}
|
|
139
|
+
style={{
|
|
140
|
+
backgroundColor: window.settings.theme.primaryColor,
|
|
141
|
+
}}
|
|
142
|
+
>
|
|
143
|
+
{amountOfParts}
|
|
144
|
+
</div>
|
|
145
|
+
) : (
|
|
146
|
+
''
|
|
147
|
+
)}
|
|
148
|
+
<Icon
|
|
149
|
+
name="cart"
|
|
150
|
+
className={twMerge(
|
|
151
|
+
amountOfParts > 0 ? 'fill-white text-white' : 'text-black',
|
|
152
|
+
)}
|
|
153
|
+
/>
|
|
154
|
+
</button>
|
|
155
|
+
</Tooltip>
|
|
156
|
+
{isCartOpen &&
|
|
157
|
+
createPortal(
|
|
158
|
+
<div className={twMerge('fixed inset-0 z-[51]')}>
|
|
159
|
+
<div
|
|
160
|
+
className="absolute inset-0 bg-black/20"
|
|
161
|
+
onClick={() => onToggleCart(false)}
|
|
162
|
+
/>
|
|
163
|
+
<div
|
|
164
|
+
className={twMerge(
|
|
165
|
+
'absolute right-0 flex h-full w-[410px] max-w-full flex-col bg-transparent',
|
|
166
|
+
)}
|
|
167
|
+
>
|
|
168
|
+
<div className="flex h-full max-w-full flex-col bg-white">
|
|
169
|
+
<div className="flex h-[52px] items-center justify-between px-4 py-2 border-b border-solid border-[#DDDEE7]">
|
|
170
|
+
<div className="text-xl font-semibold">
|
|
171
|
+
Your cart ({amountOfParts} items)
|
|
172
|
+
</div>
|
|
173
|
+
<div className="flex items-center">
|
|
174
|
+
{amountOfParts > 0 ? (
|
|
175
|
+
<button
|
|
176
|
+
type="button"
|
|
177
|
+
className="mr-4 flex items-center gap-1 rounded border border-[#E31B5D] px-2 py-1 text-[9px] font-normal text-[#E31B5D]"
|
|
178
|
+
onClick={() => {
|
|
179
|
+
clearSessionProducts();
|
|
180
|
+
setIsCartOpen(false);
|
|
181
|
+
}}
|
|
182
|
+
>
|
|
183
|
+
<Icon name="trash" />
|
|
184
|
+
Clear All
|
|
185
|
+
</button>
|
|
186
|
+
) : (
|
|
187
|
+
''
|
|
188
|
+
)}
|
|
189
|
+
<button className="p-0" onClick={() => onToggleCart(false)}>
|
|
190
|
+
<Icon name="close" className="h-5 w-5 fill-[#55566B]" />
|
|
191
|
+
</button>
|
|
192
|
+
</div>
|
|
193
|
+
</div>
|
|
194
|
+
<div className="overflow-x-auto p-4 flex flex-col gap-2.5">
|
|
195
|
+
{amountOfParts === 0 ? (
|
|
196
|
+
<div className="flex h-full w-full flex-col items-center justify-center">
|
|
197
|
+
<div className="text-base text-[#CACAD1]">
|
|
198
|
+
No items have been added
|
|
199
|
+
</div>
|
|
200
|
+
</div>
|
|
201
|
+
) : (
|
|
202
|
+
''
|
|
203
|
+
)}
|
|
204
|
+
{cartParts.map((part: any, i: number) => (
|
|
205
|
+
<div
|
|
206
|
+
key={part.sku + i}
|
|
207
|
+
className={twMerge(
|
|
208
|
+
'flex h-fit w-full items-center justify-between rounded-lg border border-solid border-[#DDDEE7] bg-[#F3F4F8]',
|
|
209
|
+
'p-2',
|
|
210
|
+
'gap-2',
|
|
211
|
+
)}
|
|
212
|
+
>
|
|
213
|
+
<div className="flex flex-1 items-center">
|
|
214
|
+
<img
|
|
215
|
+
src={
|
|
216
|
+
!window.settings.algolia.enabled
|
|
217
|
+
? part['image']
|
|
218
|
+
: part['image(main_similarity)']
|
|
219
|
+
? part['image(main_similarity)']
|
|
220
|
+
: part['main_image_link']
|
|
221
|
+
}
|
|
222
|
+
key={part.sku + i}
|
|
223
|
+
alt={`product ${i}`}
|
|
224
|
+
className="mr-2 h-[50px] w-[50px] bg-white object-contain"
|
|
225
|
+
/>
|
|
226
|
+
<div>
|
|
227
|
+
<div className="max-w-[175px] break-words text-[9px] font-normal">
|
|
228
|
+
{part.sku}
|
|
229
|
+
</div>
|
|
230
|
+
<div className="max-w-[175px] break-words text-xs font-semibold">
|
|
231
|
+
{part.title}
|
|
232
|
+
</div>
|
|
233
|
+
</div>
|
|
234
|
+
</div>
|
|
235
|
+
<div className="flex items-center">
|
|
236
|
+
<div className="ml-auto flex items-center gap-4 pr-4">
|
|
237
|
+
<button
|
|
238
|
+
className="h-4 w-4 rounded-full bg-[#FAFAFA]"
|
|
239
|
+
onClick={() => updateQuantity(part.sku, -1)}
|
|
240
|
+
>
|
|
241
|
+
<Icon name="minus" width="16" height="16" />
|
|
242
|
+
</button>
|
|
243
|
+
<input
|
|
244
|
+
className="flex h-[22px] w-[22px] items-center justify-center rounded border border-solid border-[#DDDEE7] bg-white text-center"
|
|
245
|
+
value={getQuantityDisplayValue(
|
|
246
|
+
part.sku,
|
|
247
|
+
part.quantity,
|
|
248
|
+
)}
|
|
249
|
+
onChange={e => {
|
|
250
|
+
const rawValue = e.target.value;
|
|
251
|
+
if (rawValue === '') {
|
|
252
|
+
setQuantityEdits(prev => ({
|
|
253
|
+
...prev,
|
|
254
|
+
[part.sku]: '',
|
|
255
|
+
}));
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (!/^[0-9]+$/.test(rawValue)) return;
|
|
260
|
+
|
|
261
|
+
const newValue = Number(rawValue);
|
|
262
|
+
if (newValue < 1) return;
|
|
263
|
+
|
|
264
|
+
setQuantityEdits(prev => ({
|
|
265
|
+
...prev,
|
|
266
|
+
[part.sku]: rawValue,
|
|
267
|
+
}));
|
|
268
|
+
updateQuantity(
|
|
269
|
+
part.sku,
|
|
270
|
+
newValue - (part.quantity || 0),
|
|
271
|
+
);
|
|
272
|
+
}}
|
|
273
|
+
onBlur={() => {
|
|
274
|
+
if (quantityEdits[part.sku] === '') {
|
|
275
|
+
setQuantityEdits(prev => {
|
|
276
|
+
const updated = { ...prev };
|
|
277
|
+
delete updated[part.sku];
|
|
278
|
+
return updated;
|
|
279
|
+
});
|
|
280
|
+
}
|
|
281
|
+
}}
|
|
282
|
+
/>
|
|
283
|
+
<button
|
|
284
|
+
className="h-4 w-4 rounded-full bg-[#FAFAFA] flex items-center justify-center"
|
|
285
|
+
onClick={() => updateQuantity(part.sku, 1)}
|
|
286
|
+
>
|
|
287
|
+
<Icon name="plus" width="10" height="10" />
|
|
288
|
+
</button>
|
|
289
|
+
</div>
|
|
290
|
+
<button
|
|
291
|
+
className="h-7 w-7 rounded bg-[#FFE5EF4D] p-1.5"
|
|
292
|
+
onClick={() => removePartBySku(part.sku)}
|
|
293
|
+
>
|
|
294
|
+
<Icon name="trash" color="#E31B5D" />
|
|
295
|
+
</button>
|
|
296
|
+
</div>
|
|
297
|
+
</div>
|
|
298
|
+
))}
|
|
299
|
+
{amountOfParts > 0 ? (
|
|
300
|
+
<button
|
|
301
|
+
className="flex h-12 w-full items-center justify-center rounded-lg bg-[#3E36DC] text-base font-semibold text-white"
|
|
302
|
+
type="button"
|
|
303
|
+
onClick={downloadCSV}
|
|
304
|
+
>
|
|
305
|
+
<Icon name="download" />
|
|
306
|
+
<span className="ml-2">Download all data</span>
|
|
307
|
+
</button>
|
|
308
|
+
) : (
|
|
309
|
+
''
|
|
310
|
+
)}
|
|
311
|
+
</div>
|
|
312
|
+
</div>
|
|
313
|
+
</div>
|
|
314
|
+
</div>,
|
|
315
|
+
document.body,
|
|
316
|
+
)}
|
|
317
|
+
</>
|
|
318
|
+
);
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
export default Cart;
|
|
@@ -19,7 +19,6 @@ import {
|
|
|
19
19
|
import { useCadSearch } from 'hooks/useCadSearch';
|
|
20
20
|
import { isCadFile } from '@nyris/nyris-api';
|
|
21
21
|
import { clone } from 'lodash';
|
|
22
|
-
import { getFilters } from '../services/filter';
|
|
23
22
|
import { useTranslation } from 'react-i18next';
|
|
24
23
|
|
|
25
24
|
interface Props {
|
|
@@ -40,6 +39,7 @@ function CustomCamera(props: Props) {
|
|
|
40
39
|
const { cadSearch } = useCadSearch();
|
|
41
40
|
const { t } = useTranslation();
|
|
42
41
|
|
|
42
|
+
const preFilterList = useRequestStore(state => state.preFilterList);
|
|
43
43
|
const requestImages = useRequestStore(state => state.requestImages);
|
|
44
44
|
const specifications = useRequestStore(state => state.specifications);
|
|
45
45
|
const setSpecifications = useRequestStore(state => state.setSpecifications);
|
|
@@ -53,27 +53,12 @@ function CustomCamera(props: Props) {
|
|
|
53
53
|
const [capturedImages, setCapturedImages] = useState<HTMLCanvasElement[]>([]);
|
|
54
54
|
const [currentIndex, setCurrentIndex] = useState(0);
|
|
55
55
|
const [imageCaptureHelpModal, setImageCaptureHelpModal] = useState(false);
|
|
56
|
-
const [resultFilter, setResultFilter] = useState<any>([]);
|
|
57
56
|
|
|
58
57
|
const videoConstraints = {
|
|
59
58
|
width: 1080,
|
|
60
59
|
aspectRatio: 1.11111,
|
|
61
60
|
};
|
|
62
61
|
|
|
63
|
-
const getPreFilters = async () => {
|
|
64
|
-
getFilters(1000, settings)
|
|
65
|
-
.then(res => {
|
|
66
|
-
setResultFilter(res);
|
|
67
|
-
})
|
|
68
|
-
.catch((e: any) => {
|
|
69
|
-
console.log('err getDataFilterDesktop', e);
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
useEffect(() => {
|
|
74
|
-
getPreFilters()
|
|
75
|
-
}, []);
|
|
76
|
-
|
|
77
62
|
const handlerFindImage = async (image: any) => {
|
|
78
63
|
if (location.pathname !== '/result') {
|
|
79
64
|
navigate('/result');
|
|
@@ -94,7 +79,7 @@ function CustomCamera(props: Props) {
|
|
|
94
79
|
showFeedback: true,
|
|
95
80
|
}).then((singleImageResp) => {
|
|
96
81
|
const specificationPrefilter = singleImageResp.image_analysis?.specification?.prefilter_value || null;
|
|
97
|
-
const hasPrefilter =
|
|
82
|
+
const hasPrefilter = preFilterList.filter((filter: any) => filter.values.includes(specificationPrefilter));
|
|
98
83
|
if (specificationPrefilter) {
|
|
99
84
|
setRequestImages([]);
|
|
100
85
|
setShowNotMatchedError(false);
|
|
@@ -113,7 +98,7 @@ function CustomCamera(props: Props) {
|
|
|
113
98
|
}, 5000);
|
|
114
99
|
}
|
|
115
100
|
if (!hasPrefilter.length && window.settings.preFilterOption) {
|
|
116
|
-
setSpecifications(clone({...singleImageResp.image_analysis.specification,
|
|
101
|
+
setSpecifications(clone({...singleImageResp.image_analysis.specification, specificationPrefilter}));
|
|
117
102
|
setPreFilter({});
|
|
118
103
|
setAlgoliaFilter('');
|
|
119
104
|
setShowLoading(false);
|
|
@@ -127,16 +112,13 @@ function CustomCamera(props: Props) {
|
|
|
127
112
|
}, 6000);
|
|
128
113
|
}
|
|
129
114
|
} else {
|
|
130
|
-
if (specifications?.is_nameplate) {
|
|
131
|
-
setSpecifications({...specifications, prefilter_value: '', specificationPrefilter: ''});
|
|
132
|
-
} else {
|
|
115
|
+
if (!specifications?.is_nameplate) {
|
|
133
116
|
setSpecifications({...specifications, is_nameplate: false});
|
|
134
117
|
}
|
|
135
118
|
setShowLoading(false);
|
|
136
119
|
handleClose();
|
|
137
120
|
}
|
|
138
121
|
});
|
|
139
|
-
|
|
140
122
|
};
|
|
141
123
|
|
|
142
124
|
const handleClose = () => {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { memo
|
|
1
|
+
import { memo } from 'react';
|
|
2
2
|
import { twMerge } from 'tailwind-merge';
|
|
3
3
|
import { useTranslation } from 'react-i18next';
|
|
4
4
|
|
|
@@ -11,7 +11,6 @@ import { isCadFile } from '@nyris/nyris-api';
|
|
|
11
11
|
import Hint from './Hint';
|
|
12
12
|
import { clone } from 'lodash';
|
|
13
13
|
import useRequestStore from '../stores/request/requestStore';
|
|
14
|
-
import { getFilters } from '../services/filter';
|
|
15
14
|
|
|
16
15
|
interface Props {
|
|
17
16
|
onChangeLoading?: any;
|
|
@@ -26,34 +25,22 @@ function DragDropFile(props: Props) {
|
|
|
26
25
|
const { singleImageSearch } = useImageSearch();
|
|
27
26
|
const { cadSearch } = useCadSearch();
|
|
28
27
|
|
|
29
|
-
const
|
|
30
|
-
|
|
28
|
+
const preFilterList = useRequestStore(state => state.preFilterList);
|
|
31
29
|
const specifications = useRequestStore(state => state.specifications);
|
|
32
30
|
const setRequestImages = useRequestStore(state => state.setRequestImages);
|
|
33
31
|
const setSpecifications = useRequestStore(state => state.setSpecifications);
|
|
34
|
-
const setNameplateNotificationText = useRequestStore(
|
|
35
|
-
|
|
32
|
+
const setNameplateNotificationText = useRequestStore(
|
|
33
|
+
state => state.setNameplateNotificationText,
|
|
34
|
+
);
|
|
35
|
+
const setShowNotMatchedError = useRequestStore(
|
|
36
|
+
state => state.setShowNotMatchedError,
|
|
37
|
+
);
|
|
36
38
|
const setAlgoliaFilter = useRequestStore(state => state.setAlgoliaFilter);
|
|
37
39
|
const setPreFilter = useRequestStore(state => state.setPreFilter);
|
|
38
40
|
const setShowLoading = useRequestStore(state => state.setShowLoading);
|
|
39
41
|
const setNameplateImage = useRequestStore(state => state.setNameplateImage);
|
|
40
42
|
|
|
41
|
-
const getPreFilters = async () => {
|
|
42
|
-
getFilters(1000, window.settings)
|
|
43
|
-
.then(res => {
|
|
44
|
-
setResultFilter(res);
|
|
45
|
-
})
|
|
46
|
-
.catch((e: any) => {
|
|
47
|
-
console.log('err getDataFilterDesktop', e);
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
useEffect(() => {
|
|
52
|
-
getPreFilters()
|
|
53
|
-
}, []);
|
|
54
|
-
|
|
55
43
|
const handleUpload = (file: File) => {
|
|
56
|
-
|
|
57
44
|
if (isCadFile(file)) {
|
|
58
45
|
cadSearch({
|
|
59
46
|
file: file,
|
|
@@ -70,47 +57,73 @@ function DragDropFile(props: Props) {
|
|
|
70
57
|
image: file,
|
|
71
58
|
settings: window.settings,
|
|
72
59
|
showFeedback: true,
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
60
|
+
clearPostFilter: true,
|
|
61
|
+
newSearch: true,
|
|
62
|
+
}).then(singleImageResp => {
|
|
63
|
+
const specificationPrefilter =
|
|
64
|
+
singleImageResp.image_analysis?.specification?.prefilter_value || null;
|
|
65
|
+
const hasPrefilter = preFilterList.filter((filter: any) =>
|
|
66
|
+
filter.values.includes(specificationPrefilter),
|
|
67
|
+
);
|
|
76
68
|
if (specificationPrefilter) {
|
|
77
69
|
setRequestImages([]);
|
|
78
70
|
setShowNotMatchedError(false);
|
|
79
71
|
if (hasPrefilter.length) {
|
|
80
|
-
setSpecifications(
|
|
72
|
+
setSpecifications(
|
|
73
|
+
clone(singleImageResp.image_analysis.specification),
|
|
74
|
+
);
|
|
81
75
|
setNameplateImage(file);
|
|
82
|
-
setPreFilter({
|
|
83
|
-
|
|
76
|
+
setPreFilter({
|
|
77
|
+
[singleImageResp.image_analysis?.specification?.prefilter_value]:
|
|
78
|
+
true,
|
|
79
|
+
});
|
|
80
|
+
setAlgoliaFilter(
|
|
81
|
+
`${window.settings.alogoliaFilterField}:'${singleImageResp.image_analysis?.specification?.prefilter_value}'`,
|
|
82
|
+
);
|
|
84
83
|
|
|
85
84
|
setShowLoading(false);
|
|
86
85
|
navigate('/result');
|
|
87
86
|
|
|
88
87
|
setTimeout(() => {
|
|
89
|
-
setNameplateNotificationText(
|
|
88
|
+
setNameplateNotificationText(
|
|
89
|
+
t('We have successfully defined the search criteria', {
|
|
90
|
+
prefilter_value: specificationPrefilter,
|
|
91
|
+
preFilterTitle:
|
|
92
|
+
window.settings.preFilterTitle?.toLocaleLowerCase(),
|
|
93
|
+
}),
|
|
94
|
+
);
|
|
90
95
|
}, 1000);
|
|
91
96
|
setTimeout(() => {
|
|
92
97
|
setNameplateNotificationText('');
|
|
93
98
|
}, 6000);
|
|
94
99
|
}
|
|
95
100
|
if (!hasPrefilter.length && window.settings.preFilterOption) {
|
|
96
|
-
setSpecifications(
|
|
101
|
+
setSpecifications(
|
|
102
|
+
clone({
|
|
103
|
+
...singleImageResp.image_analysis.specification,
|
|
104
|
+
specificationPrefilter,
|
|
105
|
+
}),
|
|
106
|
+
);
|
|
97
107
|
navigate('/result');
|
|
98
108
|
setPreFilter({});
|
|
99
109
|
setAlgoliaFilter('');
|
|
100
110
|
setShowLoading(false);
|
|
101
111
|
setShowNotMatchedError(true);
|
|
102
112
|
setTimeout(() => {
|
|
103
|
-
setNameplateNotificationText(
|
|
113
|
+
setNameplateNotificationText(
|
|
114
|
+
t('Extracted details from the nameplate could not be matched', {
|
|
115
|
+
preFilterTitle:
|
|
116
|
+
window.settings.preFilterTitle?.toLocaleLowerCase(),
|
|
117
|
+
}),
|
|
118
|
+
);
|
|
104
119
|
}, 1000);
|
|
105
120
|
setTimeout(() => {
|
|
106
121
|
setNameplateNotificationText('');
|
|
107
122
|
}, 6000);
|
|
108
123
|
}
|
|
109
124
|
} else {
|
|
110
|
-
if (specifications?.is_nameplate) {
|
|
111
|
-
setSpecifications({...specifications,
|
|
112
|
-
} else {
|
|
113
|
-
setSpecifications({...specifications, is_nameplate: false});
|
|
125
|
+
if (!specifications?.is_nameplate) {
|
|
126
|
+
setSpecifications({ ...specifications, is_nameplate: false });
|
|
114
127
|
}
|
|
115
128
|
setShowLoading(false);
|
|
116
129
|
navigate('/result');
|
|
@@ -131,9 +144,11 @@ function DragDropFile(props: Props) {
|
|
|
131
144
|
}
|
|
132
145
|
>
|
|
133
146
|
{isLoading && <Loading />}
|
|
134
|
-
|
|
147
|
+
|
|
135
148
|
<div
|
|
136
|
-
className={
|
|
149
|
+
className={
|
|
150
|
+
'relative flex flex-col items-center justify-center w-full'
|
|
151
|
+
}
|
|
137
152
|
{...dragProps}
|
|
138
153
|
>
|
|
139
154
|
<div
|
|
@@ -146,7 +161,9 @@ function DragDropFile(props: Props) {
|
|
|
146
161
|
])}
|
|
147
162
|
>
|
|
148
163
|
<div className="" style={{ fontSize: 14 }}>
|
|
149
|
-
<span className="font-bold text-sm pr-1">
|
|
164
|
+
<span className="font-bold text-sm pr-1">
|
|
165
|
+
{t('Drag and drop')}
|
|
166
|
+
</span>
|
|
150
167
|
{t('an image here')}
|
|
151
168
|
</div>
|
|
152
169
|
<Hint />
|
|
@@ -164,7 +181,9 @@ function DragDropFile(props: Props) {
|
|
|
164
181
|
id="select_file"
|
|
165
182
|
className="absolute z-[-1] opacity-0"
|
|
166
183
|
placeholder="Choose photo"
|
|
167
|
-
accept={
|
|
184
|
+
accept={
|
|
185
|
+
'.stp,.step,.stl,.obj,.glb,.gltf,.heic,.heif,.pdf,image/*'
|
|
186
|
+
}
|
|
168
187
|
/>
|
|
169
188
|
</div>
|
|
170
189
|
</div>
|