@nyris/nyris-webapp 0.3.91 → 0.3.92
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 +6 -6
- package/build/data/related-parts.json +83 -0
- package/build/index.html +1 -1
- package/build/js/settings.example.js +3 -0
- package/build/static/css/main.5ea01690.css +4 -0
- package/build/static/css/main.5ea01690.css.map +1 -0
- package/build/static/js/main.36b77705.js +3 -0
- package/build/static/js/{main.f2255597.js.map → main.36b77705.js.map} +1 -1
- package/package.json +4 -3
- package/public/data/related-parts.json +83 -0
- package/public/js/settings.example.js +3 -0
- package/src/App.test.tsx +0 -1
- package/src/App.tsx +0 -1
- package/src/assets/arrow_down_expanded.svg +3 -0
- package/src/assets/arrow_enter.svg +3 -0
- package/src/assets/camera.svg +3 -0
- package/src/assets/close.svg +3 -0
- package/src/assets/enter.svg +3 -0
- package/src/assets/refresh.svg +3 -0
- package/src/assets/vizo_avatar.svg +16 -0
- package/src/components/Cadenas/CadenasWebViewer.tsx +1 -1
- package/src/components/Cart.tsx +48 -36
- package/src/components/ChatAssistant/ChatAssistant.tsx +289 -0
- package/src/components/ChatAssistant/MobileChatAssistant.tsx +291 -0
- package/src/components/ChatAssistant/OptionChip.tsx +78 -0
- package/src/components/ChatAssistant/index.ts +3 -0
- package/src/components/ChatAssistant/useChatAssistantLogic.ts +745 -0
- package/src/components/CurrentRefinements.tsx +2 -2
- package/src/components/CustomCameraDrawer.tsx +56 -13
- package/src/components/DragDropFile.tsx +5 -5
- package/src/components/ExperienceVisualSearch/ExperienceVisualSearch.tsx +1 -1
- package/src/components/Header.tsx +116 -96
- package/src/components/Hint.tsx +1 -2
- package/src/components/HitsPerPage.tsx +9 -3
- package/src/components/ImagePreview.tsx +32 -17
- package/src/components/ImageUpload.tsx +16 -8
- package/src/components/Inquiry/InquiryBanner.tsx +1 -1
- package/src/components/Inquiry/InquiryModal.tsx +35 -29
- package/src/components/ItemSpecification.tsx +58 -126
- package/src/components/LocationInfoPopup.tsx +33 -33
- package/src/components/MatchNotificationBanner.tsx +90 -36
- package/src/components/PostFilter/PostFilter.tsx +1 -1
- package/src/components/PostFilter/PostFilterComponent.tsx +0 -1
- package/src/components/PostFilter/PostFilterFindApi.tsx +0 -1
- package/src/components/PoweredBy.tsx +1 -1
- package/src/components/PreFilter/PreFilter.tsx +14 -3
- package/src/components/PreFilter/PreFilterModal.tsx +0 -1
- package/src/components/Product/Product.tsx +15 -11
- package/src/components/Product/ProductAttribute.tsx +4 -5
- package/src/components/Product/ProductDetailViewModal.tsx +2 -4
- package/src/components/Product/ProductList.tsx +26 -13
- package/src/components/Rfq/RfqModal.tsx +1 -1
- package/src/components/SidePanel.tsx +124 -91
- package/src/components/SmartFilter.tsx +320 -0
- package/src/components/TextSearch.tsx +134 -70
- package/src/components/UploadDisclaimer.tsx +1 -1
- package/src/hooks/useBadResultsRecovery.ts +407 -0
- package/src/hooks/useEffectiveGroundingResults.ts +54 -0
- package/src/hooks/useGoodResultsChat.ts +651 -0
- package/src/hooks/useGroundedSearch.ts +88 -0
- package/src/hooks/useImageSearch.ts +139 -187
- package/src/hooks/useResultEvaluator.ts +417 -0
- package/src/index.css +1 -1
- package/src/index.tsx +0 -1
- package/src/layouts/AppLayout.tsx +53 -2
- package/src/pages/Home.tsx +11 -52
- package/src/pages/Login.tsx +1 -2
- package/src/pages/Logout.tsx +1 -1
- package/src/pages/Result.tsx +198 -200
- package/src/providers/AuthProvider.tsx +0 -1
- package/src/services/Feedback.ts +1 -1
- package/src/services/visualSearch.ts +0 -21
- package/src/services/vizo.ts +192 -4
- package/src/stores/chat/chatStore.ts +150 -0
- package/src/stores/chat/conversationStore.ts +300 -0
- package/src/stores/request/Misc/misc.slice.ts +2 -2
- package/src/stores/request/filter/filter.slice.ts +8 -8
- package/src/stores/request/query/query.slice.ts +2 -2
- package/src/stores/request/requestImage/requestImage.slice.ts +6 -6
- package/src/stores/request/specifications/specifications.slice.ts +10 -7
- package/src/stores/result/detectedRegions/detectedRegions.slice.ts +1 -1
- package/src/stores/result/prodcuts/products.initialState.ts +12 -0
- package/src/stores/result/prodcuts/products.slice.ts +28 -8
- package/src/stores/result/session/session.slice.ts +2 -2
- package/src/stores/smartFilters/smartFiltersStore.ts +270 -0
- package/src/stores/types.ts +41 -0
- package/src/stores/ui/ai/ai.initialState.ts +5 -0
- package/src/stores/ui/ai/ai.slice.ts +15 -0
- package/src/stores/ui/banner/banner.initialState.ts +6 -0
- package/src/stores/ui/banner/banner.slice.ts +14 -0
- package/src/stores/ui/feedback/feedback.slice.ts +1 -1
- package/src/stores/ui/loading/loading.slice.ts +4 -4
- package/src/stores/ui/uiStore.ts +7 -1
- package/src/styles/product.scss +0 -2
- package/src/types.ts +3 -7
- package/src/utils/cropImageToBase64.ts +32 -0
- package/src/utils/fetchProductImage.ts +109 -0
- package/src/utils/imageConverters.ts +124 -0
- package/src/utils/relatedParts.ts +35 -0
- package/src/utils/specificationFilter.ts +1 -5
- package/tailwind.config.js +3 -2
- package/build/static/css/main.734b52e1.css +0 -4
- package/build/static/css/main.734b52e1.css.map +0 -1
- package/build/static/js/main.f2255597.js +0 -3
- package/src/utils/addAssets.ts +0 -40
- /package/build/static/js/{main.f2255597.js.LICENSE.txt → main.36b77705.js.LICENSE.txt} +0 -0
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nyris/nyris-webapp",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.92",
|
|
4
4
|
"dependencies": {
|
|
5
5
|
"@auth0/auth0-react": "^2.2.4",
|
|
6
6
|
"@emailjs/browser": "^4.3.3",
|
|
7
|
-
"@
|
|
8
|
-
"@nyris/nyris-
|
|
7
|
+
"@google/genai": "^1.46.0",
|
|
8
|
+
"@nyris/nyris-api": "^0.3.92",
|
|
9
|
+
"@nyris/nyris-react-components": "^0.3.92",
|
|
9
10
|
"@radix-ui/react-accordion": "^1.2.2",
|
|
10
11
|
"@radix-ui/react-dialog": "^1.1.4",
|
|
11
12
|
"@radix-ui/react-popover": "^1.1.4",
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
{
|
|
2
|
+
"GC Gruppe": [
|
|
3
|
+
{
|
|
4
|
+
"keywords": ["faucet", "mixer tap", "basin mixer", "Waschtischarmatur", "Armatur", "Mischer", "Einhandmischer", "Einhandmischbatterie", "Einhebelmischer", "Waschtischbatterie"],
|
|
5
|
+
"relatedParts": [
|
|
6
|
+
{ "label": "Filter Cartridge", "description": "Replacement ceramic or carbon filter cartridge for water purification" },
|
|
7
|
+
{ "label": "Sealing Tape", "description": "PTFE thread seal tape for leak-proof pipe connections" },
|
|
8
|
+
{ "label": "Aerator", "description": "Flow regulator/strainer insert for the faucet spout" },
|
|
9
|
+
{ "label": "Flexible Hose", "description": "Braided water supply connection hose" },
|
|
10
|
+
{ "label": "Angle Valve", "description": "Shut-off valve for the water supply line beneath the basin" }
|
|
11
|
+
]
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"keywords": ["shower", "Brause", "Dusche", "Duschkopf", "Duscharmatur", "Brausekopf", "shower head", "shower enclosure"],
|
|
15
|
+
"relatedParts": [
|
|
16
|
+
{ "label": "Shower Hose", "description": "Flexible connection hose between mixer and shower head" },
|
|
17
|
+
{ "label": "Wall Bracket", "description": "Mounting bracket or slide bar for the shower head" },
|
|
18
|
+
{ "label": "Sealing Set", "description": "Gaskets and O-rings for shower connections" },
|
|
19
|
+
{ "label": "Shower Filter", "description": "Inline water filter for limescale and chlorine reduction" },
|
|
20
|
+
{ "label": "Diverter Valve", "description": "Valve to switch water flow between tub spout and shower" }
|
|
21
|
+
]
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"keywords": ["radiator", "Heizkoerper", "Heizung", "Heizkörper", "Konvektor", "panel radiator"],
|
|
25
|
+
"relatedParts": [
|
|
26
|
+
{ "label": "Thermostat Valve", "description": "Temperature control valve head for the radiator" },
|
|
27
|
+
{ "label": "Bleed Valve", "description": "Air release valve for radiator maintenance" },
|
|
28
|
+
{ "label": "Wall Bracket", "description": "Mounting bracket set for radiator installation" },
|
|
29
|
+
{ "label": "Lockshield Valve", "description": "Return-side balancing valve for flow regulation" },
|
|
30
|
+
{ "label": "Radiator Plug", "description": "Blanking plug and seal for unused radiator connections" }
|
|
31
|
+
]
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"keywords": ["toilet", "WC", "Toilette", "Klosett", "wall-hung WC", "stand-WC"],
|
|
35
|
+
"relatedParts": [
|
|
36
|
+
{ "label": "Flush Valve", "description": "Internal flush mechanism and valve for the cistern" },
|
|
37
|
+
{ "label": "Toilet Seat", "description": "Replacement seat and lid with hinges" },
|
|
38
|
+
{ "label": "Flush Plate", "description": "Actuator plate for concealed cistern systems" },
|
|
39
|
+
{ "label": "Concealed Cistern", "description": "In-wall cistern frame for wall-hung installations" },
|
|
40
|
+
{ "label": "Connection Set", "description": "Waste pipe connector and seal kit for WC installation" }
|
|
41
|
+
]
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"keywords": ["pump", "Pumpe", "Umwälzpumpe", "circulation pump", "Heizungspumpe"],
|
|
45
|
+
"relatedParts": [
|
|
46
|
+
{ "label": "Check Valve", "description": "Non-return valve to prevent backflow in the system" },
|
|
47
|
+
{ "label": "Pump Flange Set", "description": "Connection flanges and gaskets for pump installation" },
|
|
48
|
+
{ "label": "Insulation Shell", "description": "Thermal insulation jacket for the pump body" },
|
|
49
|
+
{ "label": "Replacement Seal", "description": "Mechanical shaft seal for pump maintenance" },
|
|
50
|
+
{ "label": "Speed Controller", "description": "Electronic controller for variable pump speed" }
|
|
51
|
+
]
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"keywords": ["boiler", "Kessel", "Heizkessel", "Brennwertkessel", "Gaskessel", "water heater", "Warmwasserspeicher", "Durchlauferhitzer"],
|
|
55
|
+
"relatedParts": [
|
|
56
|
+
{ "label": "Expansion Vessel", "description": "Pressure compensation vessel for the heating circuit" },
|
|
57
|
+
{ "label": "Safety Valve", "description": "Pressure relief valve for overpressure protection" },
|
|
58
|
+
{ "label": "Flue Pipe", "description": "Exhaust gas duct for combustion venting" },
|
|
59
|
+
{ "label": "Condensate Trap", "description": "Siphon for condensate drainage from condensing boilers" },
|
|
60
|
+
{ "label": "Ignition Electrode", "description": "Replacement spark electrode for burner ignition" }
|
|
61
|
+
]
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
"keywords": ["washbasin", "Waschbecken", "basin", "Waschtisch", "Handwaschbecken", "vanity"],
|
|
65
|
+
"relatedParts": [
|
|
66
|
+
{ "label": "Pop-up Waste", "description": "Click-clack or pop-up drain plug for the basin" },
|
|
67
|
+
{ "label": "Siphon", "description": "Bottle trap or P-trap for basin waste drainage" },
|
|
68
|
+
{ "label": "Basin Mixer", "description": "Single-lever or two-handle mixer tap for the washbasin" },
|
|
69
|
+
{ "label": "Mounting Kit", "description": "Fixing bolts and brackets for basin installation" },
|
|
70
|
+
{ "label": "Overflow Cover", "description": "Decorative cover for the basin overflow hole" }
|
|
71
|
+
]
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
"keywords": ["valve", "Ventil", "Absperrventil", "Regelventil", "ball valve", "Kugelventil", "gate valve"],
|
|
75
|
+
"relatedParts": [
|
|
76
|
+
{ "label": "Replacement Seal", "description": "O-ring or flat gasket set for valve maintenance" },
|
|
77
|
+
{ "label": "Handle Extension", "description": "Extended lever or wheel handle for hard-to-reach valves" },
|
|
78
|
+
{ "label": "Insulation Jacket", "description": "Thermal insulation shell for exposed valve bodies" },
|
|
79
|
+
{ "label": "Fitting Adapter", "description": "Thread adapter or compression fitting for pipe connection" }
|
|
80
|
+
]
|
|
81
|
+
}
|
|
82
|
+
]
|
|
83
|
+
}
|
|
@@ -46,6 +46,8 @@ var settings = {
|
|
|
46
46
|
vizo: {
|
|
47
47
|
groundingEnabled: false,
|
|
48
48
|
fallbackToElasticSearch: false,
|
|
49
|
+
chat: true,
|
|
50
|
+
findRelatedParts: true,
|
|
49
51
|
},
|
|
50
52
|
// UI - theme
|
|
51
53
|
theme: {
|
|
@@ -103,6 +105,7 @@ var settings = {
|
|
|
103
105
|
experienceVisualSearchImages: [],
|
|
104
106
|
simpleCardView: false,
|
|
105
107
|
noSimilarSearch: false,
|
|
108
|
+
useSmartFilters: false,
|
|
106
109
|
showFeedback: false,
|
|
107
110
|
geoLocation: false,
|
|
108
111
|
geoLocationMessage: '',
|
package/src/App.test.tsx
CHANGED
package/src/App.tsx
CHANGED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg width="16" height="6" viewBox="0 0 16 6" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path d="M7.46905 5.70262C7.79378 5.90608 8.20622 5.90608 8.53095 5.70262L15.6455 1.2449C16.0406 0.997313 16.0406 0.42165 15.6455 0.174064C15.4403 0.045508 15.1797 0.0455079 14.9745 0.174064L8.53095 4.21139C8.20622 4.41486 7.79378 4.41486 7.46905 4.21139L1.02547 0.174064C0.820298 0.0455083 0.559701 0.0455079 0.354525 0.174064C-0.0406208 0.42165 -0.0406208 0.997313 0.354525 1.2449L7.46905 5.70262Z" fill="#2B2C46"/>
|
|
3
|
+
</svg>
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
2
|
+
<!-- License: CC Attribution. Made by iconhub: https://iconhub.io/ -->
|
|
3
|
+
<svg width="16px" height="16px" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" id="up" class="icon glyph"><path d="M19.71,9.29l-7-7h0a1.15,1.15,0,0,0-.33-.21.94.94,0,0,0-.76,0,1.15,1.15,0,0,0-.33.21h0l-7,7a1,1,0,0,0,1.42,1.42L11,5.41V21a1,1,0,0,0,2,0V5.41l5.29,5.3a1,1,0,0,0,1.42,0A1,1,0,0,0,19.71,9.29Z" fill="currentColor"></path></svg>
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M17.3571 16.0714H0.642857C0.287817 16.0714 0 15.7836 0 15.4286V4.50002C0 4.14498 0.287817 3.85716 0.642857 3.85716H4.79571L5.895 2.21787C6.01327 2.03827 6.21352 1.9297 6.42857 1.92859H11.5714C11.7865 1.9297 11.9867 2.03827 12.105 2.21787L13.2043 3.85716H17.3571C17.7122 3.85716 18 4.14498 18 4.50002V15.4286C18 15.7836 17.7122 16.0714 17.3571 16.0714ZM1.28597 14.7857H16.7145V5.14289H12.8574C12.6424 5.14177 12.4421 5.03321 12.3238 4.8536L11.2245 3.21431H6.77597L5.67668 4.8536C5.55841 5.03321 5.35816 5.14177 5.14311 5.14289H1.28597V14.7857ZM9.00013 13.5001C6.86989 13.5001 5.14299 11.7732 5.14299 9.64291C5.14299 7.51267 6.86989 5.78577 9.00013 5.78577C11.1304 5.78577 12.8573 7.51267 12.8573 9.64291C12.8573 11.7732 11.1304 13.5001 9.00013 13.5001ZM9.00042 7.07141C7.58026 7.07141 6.429 8.22268 6.429 9.64284C6.429 11.063 7.58026 12.2143 9.00042 12.2143C10.4206 12.2143 11.5719 11.063 11.5719 9.64284C11.5719 8.22268 10.4206 7.07141 9.00042 7.07141Z" fill="currentColor"/>
|
|
3
|
+
</svg>
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.475 3.575C13.7649 3.28505 13.7649 2.81495 13.475 2.525C13.1851 2.23505 12.715 2.23505 12.425 2.525L8.70711 6.24289C8.31658 6.63342 7.68342 6.63342 7.29289 6.24289L3.575 2.525C3.28505 2.23505 2.81495 2.23505 2.525 2.525C2.23505 2.81495 2.23505 3.28505 2.525 3.575L6.24289 7.29289C6.63342 7.68342 6.63342 8.31658 6.24289 8.70711L2.525 12.425C2.23505 12.7149 2.23505 13.1851 2.525 13.475C2.81495 13.7649 3.28505 13.7649 3.575 13.475L7.29289 9.75711C7.68342 9.36658 8.31658 9.36658 8.70711 9.75711L12.425 13.475C12.7149 13.7649 13.1851 13.7649 13.475 13.475C13.7649 13.1851 13.7649 12.715 13.475 12.425L9.75711 8.70711C9.36658 8.31658 9.36658 7.68342 9.75711 7.29289L13.475 3.575Z" fill="currentColor"/>
|
|
3
|
+
</svg>
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg width="16" height="13" viewBox="0 0 16 13" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path d="M10.6571 2.43183C10.4497 2.63925 10.4497 2.97899 10.6571 3.18641L13.5539 6.08315C13.7827 6.31202 13.6218 6.70898 13.2964 6.70898H3.50827C3.50827 6.70898 2.42468 6.73759 1.72016 6.06884C1.28744 5.654 1.06571 5.04247 1.06214 4.24497V0.532856C1.06571 0.239607 0.826106 0 0.532857 0C0.239607 0 0 0.239607 0 0.532856V4.24139C0 5.34287 0.336164 6.21547 0.987036 6.83773C1.9097 7.71748 3.17211 7.7747 3.46893 7.7747C3.50827 7.7747 3.52973 7.7747 3.5333 7.7747H13.2964C13.6218 7.7747 13.7863 8.16808 13.5539 8.40054L10.5999 11.3545C10.3925 11.5619 10.3925 11.9017 10.5999 12.1091C10.8073 12.3165 11.1471 12.3165 11.3545 12.1091L15.8426 7.62092C16.0501 7.4135 16.0501 7.07376 15.8426 6.86634L11.4117 2.4354C11.2043 2.22798 10.8646 2.22798 10.6571 2.4354V2.43183Z" fill="#AAABB5"/>
|
|
3
|
+
</svg>
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg width="14" height="16" viewBox="0 0 14 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path d="M12.8333 9.37931C12.8333 12.4264 10.2217 14.8966 7 14.8966C3.77834 14.8966 1.16667 12.4264 1.16667 9.37931C1.16667 6.33222 3.77834 3.86207 7 3.86207L10.6062 3.86207L8.51532 5.84022L9.33333 6.62069L12.8333 3.31034L9.33333 0L8.51532 0.780138L10.608 2.75862L7 2.75862C3.13401 2.75862 0 5.72281 0 9.37931C0 13.0358 3.13401 16 7 16C10.866 16 14 13.0358 14 9.37931H12.8333Z" fill="currentColor"/>
|
|
3
|
+
</svg>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<rect width="40" height="40" rx="20" fill="#55566B"/>
|
|
3
|
+
<g clip-path="url(#clip0_8518_62118)">
|
|
4
|
+
<path d="M20 36C28.8366 36 36 28.8366 36 20C36 11.1634 28.8366 4 20 4C11.1634 4 4 11.1634 4 20C4 28.8366 11.1634 36 20 36Z" fill="url(#paint0_radial_8518_62118)"/>
|
|
5
|
+
<path fill-rule="evenodd" clip-rule="evenodd" d="M19.9998 33.3024C12.6632 33.3024 6.69727 27.3365 6.69727 19.9998C6.69727 12.6632 12.6632 6.69727 19.9998 6.69727C27.3365 6.69727 33.3024 12.6676 33.3024 19.9998C33.3024 27.3321 27.3365 33.3024 19.9998 33.3024ZM19.9998 8.42379C13.6165 8.42379 8.42379 13.6165 8.42379 19.9998C8.42379 26.3832 13.6165 31.5759 19.9998 31.5759C26.3832 31.5759 31.5759 26.3832 31.5759 19.9998C31.5759 13.6165 26.3832 8.42379 19.9998 8.42379ZM19.5565 27.9213C19.3149 27.9126 19.0689 27.895 18.8273 27.8598C15.2776 27.4073 12.4615 24.6133 11.9783 21.0636C11.9431 20.8 11.9212 20.5364 11.9124 20.2728C11.9036 19.9916 11.6751 19.7676 11.3984 19.7676H10.88C10.7394 19.7676 10.6032 19.8247 10.5066 19.9257C10.4099 20.0268 10.3572 20.1586 10.3616 20.2992C10.5241 25.2766 14.5527 29.3052 19.5302 29.4677H19.5477C19.6795 29.4677 19.8069 29.415 19.9036 29.3228C20.0046 29.2261 20.0617 29.0899 20.0617 28.9493V28.431C20.0617 28.1498 19.8421 27.9257 19.5565 27.9126V27.9213ZM25.0209 12.2162C25.1615 12.1899 25.2977 12.2206 25.4119 12.3041V12.3085C27.8062 14.0438 29.234 16.7324 29.3306 19.6802C29.335 19.8208 29.2823 19.9526 29.1856 20.0537C29.0846 20.1547 28.9484 20.2118 28.8078 20.2118H28.2894C28.0127 20.2118 27.7842 19.9878 27.7754 19.7066C27.6876 17.2552 26.5014 15.0191 24.5157 13.5737C24.2872 13.4112 24.2301 13.0992 24.3883 12.8664L24.6782 12.4403C24.7573 12.3216 24.8803 12.2426 25.0209 12.2162ZM24.108 26.7081C25.7651 26.7081 27.1085 25.3647 27.1085 23.7076C27.1085 22.0504 25.7651 20.707 24.108 20.707C22.4508 20.707 21.1074 22.0504 21.1074 23.7076C21.1074 25.3647 22.4508 26.7081 24.108 26.7081ZM24.1079 19.2441C21.6433 19.2441 19.6444 21.2431 19.6444 23.7076C19.6444 24.3578 19.785 24.9729 20.0354 25.5308H20.0134C16.9602 25.5308 14.4824 23.053 14.4824 19.9998C14.4824 16.9465 16.9602 14.4688 20.0134 14.4688C22.891 14.4688 25.2501 16.6653 25.5181 19.4726C25.0744 19.3276 24.5999 19.2441 24.1079 19.2441Z" fill="white"/>
|
|
6
|
+
</g>
|
|
7
|
+
<defs>
|
|
8
|
+
<radialGradient id="paint0_radial_8518_62118" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(20 20) rotate(90) scale(16)">
|
|
9
|
+
<stop offset="0.3125" stop-color="#655EE3"/>
|
|
10
|
+
<stop offset="1" stop-color="#2B2C46"/>
|
|
11
|
+
</radialGradient>
|
|
12
|
+
<clipPath id="clip0_8518_62118">
|
|
13
|
+
<rect width="32" height="32" fill="white" transform="translate(4 4)"/>
|
|
14
|
+
</clipPath>
|
|
15
|
+
</defs>
|
|
16
|
+
</svg>
|
package/src/components/Cart.tsx
CHANGED
|
@@ -117,41 +117,50 @@ const Cart = () => {
|
|
|
117
117
|
content="The basket is currently empty"
|
|
118
118
|
disabled={!!amountOfParts}
|
|
119
119
|
>
|
|
120
|
-
<
|
|
120
|
+
<div
|
|
121
121
|
className={twMerge(
|
|
122
|
-
'
|
|
123
|
-
'h-
|
|
122
|
+
'h-[56px] min-w-[56px]',
|
|
123
|
+
'desktop:min-w-[68px] desktop:h-[68px] bg-white rounded-2xl flex items-center justify-center ',
|
|
124
|
+
'border border-solid border-[#DDDEE7]',
|
|
125
|
+
'shadow-ds-1',
|
|
124
126
|
)}
|
|
125
|
-
style={{
|
|
126
|
-
backgroundColor:
|
|
127
|
-
amountOfParts > 0
|
|
128
|
-
? window.settings.theme.primaryColor
|
|
129
|
-
: '#FAFAFA',
|
|
130
|
-
}}
|
|
131
|
-
onClick={() => onToggleCart(true)}
|
|
132
127
|
>
|
|
133
|
-
|
|
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"
|
|
128
|
+
<button
|
|
150
129
|
className={twMerge(
|
|
151
|
-
|
|
130
|
+
'relative flex items-center justify-center rounded-2xl',
|
|
131
|
+
'h-10 w-10',
|
|
152
132
|
)}
|
|
153
|
-
|
|
154
|
-
|
|
133
|
+
style={{
|
|
134
|
+
backgroundColor:
|
|
135
|
+
amountOfParts > 0
|
|
136
|
+
? window.settings.theme.primaryColor
|
|
137
|
+
: '#FAFAFA',
|
|
138
|
+
}}
|
|
139
|
+
onClick={() => onToggleCart(true)}
|
|
140
|
+
>
|
|
141
|
+
{amountOfParts ? (
|
|
142
|
+
<div
|
|
143
|
+
className={twMerge(
|
|
144
|
+
'absolute -top-1 -right-1 flex h-4 w-4 items-center justify-center rounded-full border-2 border-white border-solid text-white',
|
|
145
|
+
'text-[9px]',
|
|
146
|
+
)}
|
|
147
|
+
style={{
|
|
148
|
+
backgroundColor: window.settings.theme.primaryColor,
|
|
149
|
+
}}
|
|
150
|
+
>
|
|
151
|
+
{amountOfParts}
|
|
152
|
+
</div>
|
|
153
|
+
) : (
|
|
154
|
+
''
|
|
155
|
+
)}
|
|
156
|
+
<Icon
|
|
157
|
+
name="cart"
|
|
158
|
+
className={twMerge(
|
|
159
|
+
amountOfParts > 0 ? 'fill-white text-white' : 'text-[#BBBDCF]',
|
|
160
|
+
)}
|
|
161
|
+
/>
|
|
162
|
+
</button>
|
|
163
|
+
</div>
|
|
155
164
|
</Tooltip>
|
|
156
165
|
{isCartOpen &&
|
|
157
166
|
createPortal(
|
|
@@ -163,9 +172,10 @@ const Cart = () => {
|
|
|
163
172
|
<div
|
|
164
173
|
className={twMerge(
|
|
165
174
|
'absolute right-0 flex h-full w-[410px] max-w-full flex-col bg-transparent',
|
|
175
|
+
'p-4',
|
|
166
176
|
)}
|
|
167
177
|
>
|
|
168
|
-
<div className="flex h-full max-w-full flex-col bg-white">
|
|
178
|
+
<div className="flex h-full max-w-full flex-col bg-white rounded-2xl">
|
|
169
179
|
<div className="flex h-[52px] items-center justify-between px-4 py-2 border-b border-solid border-[#DDDEE7]">
|
|
170
180
|
<div className="text-xl font-semibold">
|
|
171
181
|
Your cart ({amountOfParts} items)
|
|
@@ -224,11 +234,13 @@ const Cart = () => {
|
|
|
224
234
|
className="mr-2 h-[50px] w-[50px] bg-white object-contain"
|
|
225
235
|
/>
|
|
226
236
|
<div>
|
|
227
|
-
<div
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
237
|
+
<div>
|
|
238
|
+
<div className="max-w-[175px] break-words text-[9px] font-normal">
|
|
239
|
+
{part.sku}
|
|
240
|
+
</div>
|
|
241
|
+
<div className="max-w-[175px] break-words text-xs font-semibold">
|
|
242
|
+
{part.title}
|
|
243
|
+
</div>
|
|
232
244
|
</div>
|
|
233
245
|
</div>
|
|
234
246
|
</div>
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
import { memo, useRef, useCallback } from 'react';
|
|
2
|
+
import { twMerge } from 'tailwind-merge';
|
|
3
|
+
import { Sparkles, Send, Loader2, Globe } from 'lucide-react';
|
|
4
|
+
|
|
5
|
+
import useConversationStore from 'stores/chat/conversationStore';
|
|
6
|
+
import { Icon } from '@nyris/nyris-react-components';
|
|
7
|
+
import useUiStore from 'stores/ui/uiStore';
|
|
8
|
+
import OptionChip, { ChatOption } from './OptionChip';
|
|
9
|
+
import { useChatAssistantLogic } from './useChatAssistantLogic';
|
|
10
|
+
|
|
11
|
+
function ChatAssistant() {
|
|
12
|
+
const inputRef = useRef<HTMLTextAreaElement>(null);
|
|
13
|
+
|
|
14
|
+
const {
|
|
15
|
+
inputValue,
|
|
16
|
+
setInputValue,
|
|
17
|
+
messages,
|
|
18
|
+
isLoading,
|
|
19
|
+
isEvaluated,
|
|
20
|
+
hasApiKey,
|
|
21
|
+
p7Running,
|
|
22
|
+
p7Done,
|
|
23
|
+
catalogSearching,
|
|
24
|
+
messagesEndRef,
|
|
25
|
+
imageInputRef,
|
|
26
|
+
handleSendMessage,
|
|
27
|
+
handleOptionClick,
|
|
28
|
+
handleImageUpload,
|
|
29
|
+
} = useChatAssistantLogic();
|
|
30
|
+
|
|
31
|
+
const setIsOpen = useConversationStore(s => s.setIsOpen);
|
|
32
|
+
|
|
33
|
+
const toggleAiMode = useUiStore(state => state.toggleAiMode);
|
|
34
|
+
|
|
35
|
+
// Focus input on text_search_prompt option click
|
|
36
|
+
const wrappedHandleOptionClick = useCallback(
|
|
37
|
+
async (option: ChatOption) => {
|
|
38
|
+
if (option.action_type === 'text_search_prompt') {
|
|
39
|
+
inputRef.current?.focus();
|
|
40
|
+
}
|
|
41
|
+
if (option.action_type === 'related_part') {
|
|
42
|
+
inputRef.current?.focus();
|
|
43
|
+
}
|
|
44
|
+
await handleOptionClick(option);
|
|
45
|
+
},
|
|
46
|
+
[handleOptionClick],
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<div
|
|
51
|
+
className={twMerge([
|
|
52
|
+
'w-full h-full rounded-[24px] relative shadow-outer overflow-hidden',
|
|
53
|
+
'flex flex-col',
|
|
54
|
+
|
|
55
|
+
'border border-solid border-[#DDDEE7]',
|
|
56
|
+
'bg-[radial-gradient(ellipse_at_70%_7%,_#F3F4F8_50%,_#E4E3FF_100%)]',
|
|
57
|
+
])}
|
|
58
|
+
>
|
|
59
|
+
<div
|
|
60
|
+
className="absolute top-3 right-4 bg-[#FAFAFA] w-7 h-7 flex justify-center items-center rounded-lg cursor-pointer"
|
|
61
|
+
onClick={() => {
|
|
62
|
+
toggleAiMode();
|
|
63
|
+
setIsOpen(false);
|
|
64
|
+
}}
|
|
65
|
+
>
|
|
66
|
+
<Icon name="close" className="fill-[#BBBDCF] text-[#BBBDCF]" />
|
|
67
|
+
</div>
|
|
68
|
+
|
|
69
|
+
{/* {error && (
|
|
70
|
+
<div className="flex-shrink-0 mx-3 mt-2 p-2.5 bg-red-50 border border-red-200 rounded-lg flex items-center gap-2">
|
|
71
|
+
<AlertCircle size={12} className="text-red-500 flex-shrink-0" />
|
|
72
|
+
<p className="text-[11px] text-red-700">{error}</p>
|
|
73
|
+
</div>
|
|
74
|
+
)} */}
|
|
75
|
+
|
|
76
|
+
{/* ── Messages ── */}
|
|
77
|
+
<div className="flex-1 min-h-0 overflow-y-auto px-5 py-3 space-y-3">
|
|
78
|
+
{messages.length === 0 && !isLoading ? (
|
|
79
|
+
<div className="flex flex-col items-center justify-center flex-grow text-center px-4 py-10">
|
|
80
|
+
<div className="w-12 h-12 rounded-full bg-[#F0EFFF] flex items-center justify-center mb-3">
|
|
81
|
+
<Sparkles size={20} className="text-[#3E36DC]" />
|
|
82
|
+
</div>
|
|
83
|
+
<p className="text-sm font-semibold text-[#2B2C46] mb-1">
|
|
84
|
+
Vizo Search Assistant
|
|
85
|
+
</p>
|
|
86
|
+
<p className="text-[11px] text-[#999] leading-relaxed max-w-[200px]">
|
|
87
|
+
Upload an image to get started, or search for a product
|
|
88
|
+
</p>
|
|
89
|
+
</div>
|
|
90
|
+
) : (
|
|
91
|
+
messages.map((msg, i) => {
|
|
92
|
+
return (
|
|
93
|
+
<div
|
|
94
|
+
key={msg.id || i}
|
|
95
|
+
className={twMerge(
|
|
96
|
+
'flex',
|
|
97
|
+
msg.role === 'user' ? 'justify-end' : 'justify-start',
|
|
98
|
+
)}
|
|
99
|
+
>
|
|
100
|
+
<div className="">
|
|
101
|
+
{/* Bubble */}
|
|
102
|
+
<div
|
|
103
|
+
className={twMerge(
|
|
104
|
+
'py-3 pr-4 text-sm leading-relaxed',
|
|
105
|
+
msg.role === 'user'
|
|
106
|
+
? 'bg-[#E7E8F1] text-[#3B3E5F] rounded-tl-[16px] rounded-tr-[8px] rounded-br-[16px] rounded-bl-[16px] px-4 py-3 font-medium'
|
|
107
|
+
: msg?.content?.startsWith('[P7')
|
|
108
|
+
? 'bg-[#EEF2FF] border border-[#C7D2FE] text-[#2B2C46] rounded-2xl rounded-bl-sm'
|
|
109
|
+
: ' text-[#2B2C46] rounded-2xl rounded-bl-sm',
|
|
110
|
+
i === 0 && 'pr-6',
|
|
111
|
+
)}
|
|
112
|
+
>
|
|
113
|
+
{msg.imageUrl && (
|
|
114
|
+
<img
|
|
115
|
+
src={msg.imageUrl}
|
|
116
|
+
alt="Uploaded"
|
|
117
|
+
className="w-[48px] h-[48px] rounded-lg object-cover mb-2"
|
|
118
|
+
/>
|
|
119
|
+
)}
|
|
120
|
+
{msg?.content?.startsWith('[P7') && (
|
|
121
|
+
<div className="flex items-center gap-1 mb-1.5 text-indigo-600 font-semibold text-[10px]">
|
|
122
|
+
<Globe size={10} /> Google Grounding
|
|
123
|
+
</div>
|
|
124
|
+
)}
|
|
125
|
+
<pre className="whitespace-pre-wrap font-sans text-sm leading-4 tracking-[0.16px]">
|
|
126
|
+
{msg?.content?.startsWith('[P7')
|
|
127
|
+
? msg.content.replace(/^\[P7[^\]]*\]\s*/, '')
|
|
128
|
+
: msg.content}
|
|
129
|
+
</pre>
|
|
130
|
+
</div>
|
|
131
|
+
|
|
132
|
+
{/* Quick reply options */}
|
|
133
|
+
{msg.role === 'assistant' &&
|
|
134
|
+
msg.options &&
|
|
135
|
+
msg.options.length > 0 && (
|
|
136
|
+
<div className="mt-2 space-y-2.5">
|
|
137
|
+
{/* Option chips */}
|
|
138
|
+
<div className="flex flex-wrap gap-1.5">
|
|
139
|
+
{msg.options
|
|
140
|
+
.filter(o => o.action_type !== 'text_search_prompt')
|
|
141
|
+
.map((option, optIdx) => (
|
|
142
|
+
<OptionChip
|
|
143
|
+
key={optIdx}
|
|
144
|
+
option={option}
|
|
145
|
+
onClick={wrappedHandleOptionClick}
|
|
146
|
+
disabled={
|
|
147
|
+
isLoading || i !== messages.length - 1
|
|
148
|
+
}
|
|
149
|
+
/>
|
|
150
|
+
))}
|
|
151
|
+
</div>
|
|
152
|
+
</div>
|
|
153
|
+
)}
|
|
154
|
+
</div>
|
|
155
|
+
</div>
|
|
156
|
+
);
|
|
157
|
+
})
|
|
158
|
+
)}
|
|
159
|
+
|
|
160
|
+
{/* Loading indicator */}
|
|
161
|
+
{isLoading && (
|
|
162
|
+
<div className="flex justify-start">
|
|
163
|
+
<div className="flex items-center gap-2 px-3 py-2.5 bg-[#F3F3F5] rounded-2xl rounded-bl-sm">
|
|
164
|
+
<Loader2 size={12} className="animate-spin text-[#3E36DC]" />
|
|
165
|
+
<span className="text-xs text-[#666]">
|
|
166
|
+
{!isEvaluated
|
|
167
|
+
? 'Analyzing results...'
|
|
168
|
+
: catalogSearching
|
|
169
|
+
? 'Refining your search…'
|
|
170
|
+
: 'Thinking...'}
|
|
171
|
+
</span>
|
|
172
|
+
</div>
|
|
173
|
+
</div>
|
|
174
|
+
)}
|
|
175
|
+
|
|
176
|
+
<div ref={messagesEndRef} />
|
|
177
|
+
</div>
|
|
178
|
+
|
|
179
|
+
{/* ── P7 Grounding status strip ── */}
|
|
180
|
+
{(p7Running || p7Done) && (
|
|
181
|
+
<div className="flex-shrink-0 px-3 py-1.5 border-t border-[#E0E0E0] bg-[#F8F8FF]">
|
|
182
|
+
{p7Running ? (
|
|
183
|
+
<span className="flex items-center gap-1.5 text-[10px] text-blue-600">
|
|
184
|
+
<Globe size={10} className="animate-pulse" /> Running Google
|
|
185
|
+
Grounding...
|
|
186
|
+
</span>
|
|
187
|
+
) : (
|
|
188
|
+
<span className="flex items-center gap-1.5 text-[10px] text-blue-500">
|
|
189
|
+
<Globe size={10} /> Google Grounding complete
|
|
190
|
+
</span>
|
|
191
|
+
)}
|
|
192
|
+
</div>
|
|
193
|
+
)}
|
|
194
|
+
|
|
195
|
+
{/* ── Upload image — fixed above input ── */}
|
|
196
|
+
{isEvaluated && hasApiKey && (
|
|
197
|
+
<div className="px-2">
|
|
198
|
+
<button
|
|
199
|
+
onClick={() => imageInputRef.current?.click()}
|
|
200
|
+
disabled={isLoading}
|
|
201
|
+
className={twMerge(
|
|
202
|
+
'w-full h-12 rounded-[32px] bg-[#3B3E5F] mb-2',
|
|
203
|
+
'flex items-center justify-center gap-2 text-white cursor-pointer',
|
|
204
|
+
'hover:bg-[#333653] transition-colors',
|
|
205
|
+
isLoading && 'opacity-50 cursor-not-allowed',
|
|
206
|
+
)}
|
|
207
|
+
>
|
|
208
|
+
<span>Upload image</span>
|
|
209
|
+
<Icon name="camera_ai" />
|
|
210
|
+
</button>
|
|
211
|
+
</div>
|
|
212
|
+
)}
|
|
213
|
+
|
|
214
|
+
{/* ── Input ── */}
|
|
215
|
+
{/* <div className="flex-shrink-0 px-3 py-3 border-t border-[#E0E0E0] bg-white rounded-3xl mt-2">
|
|
216
|
+
</div> */}
|
|
217
|
+
<form
|
|
218
|
+
onSubmit={e => {
|
|
219
|
+
e.preventDefault();
|
|
220
|
+
handleSendMessage();
|
|
221
|
+
}}
|
|
222
|
+
className="flex gap-2 items-end px-2"
|
|
223
|
+
>
|
|
224
|
+
<textarea
|
|
225
|
+
ref={inputRef}
|
|
226
|
+
value={inputValue}
|
|
227
|
+
onChange={e => setInputValue(e.target.value)}
|
|
228
|
+
onKeyDown={e => {
|
|
229
|
+
if (e.key === 'Enter' && !e.shiftKey && !isLoading) {
|
|
230
|
+
e.preventDefault();
|
|
231
|
+
handleSendMessage();
|
|
232
|
+
}
|
|
233
|
+
}}
|
|
234
|
+
placeholder={
|
|
235
|
+
isLoading ? 'Please wait...' : 'Ask about the results...'
|
|
236
|
+
}
|
|
237
|
+
disabled={isLoading}
|
|
238
|
+
rows={1}
|
|
239
|
+
className={twMerge(
|
|
240
|
+
'flex-1 resize-none rounded-xl px-3 py-2.5 text-sm pr-14',
|
|
241
|
+
'border-2 border-[#3E36DC] outline-none',
|
|
242
|
+
'bg-[#F3F4F8] disabled:opacity-60',
|
|
243
|
+
'min-h-[56px] max-h-[100px]',
|
|
244
|
+
'rounded-[32px]',
|
|
245
|
+
'overflow-y-auto scrollbar-hide',
|
|
246
|
+
)}
|
|
247
|
+
style={{
|
|
248
|
+
fontFamily: 'inherit',
|
|
249
|
+
lineHeight: '1.4',
|
|
250
|
+
scrollbarWidth: 'none',
|
|
251
|
+
msOverflowStyle: 'none',
|
|
252
|
+
}}
|
|
253
|
+
/* Hide scrollbar for Webkit browsers */
|
|
254
|
+
/* Add this style in a global CSS if not already present: .scrollbar-hide::-webkit-scrollbar { display: none; } */
|
|
255
|
+
/>
|
|
256
|
+
|
|
257
|
+
<button
|
|
258
|
+
type="submit"
|
|
259
|
+
disabled={isLoading || !inputValue.trim() || !hasApiKey}
|
|
260
|
+
className={twMerge(
|
|
261
|
+
'p-2.5 rounded-[32px] transition-colors flex-shrink-0 w-10 h-10 flex items-center justify-center',
|
|
262
|
+
inputValue.trim() && !isLoading && hasApiKey
|
|
263
|
+
? 'bg-[#FAFAFA] text-black hover:bg-blue-100/40'
|
|
264
|
+
: 'bg-[#E0E0E0] text-[#999] cursor-not-allowed',
|
|
265
|
+
'absolute right-4 bottom-2',
|
|
266
|
+
)}
|
|
267
|
+
>
|
|
268
|
+
<Send size={16} />
|
|
269
|
+
</button>
|
|
270
|
+
{/* Hidden file input — triggered by Upload image button above */}
|
|
271
|
+
<input
|
|
272
|
+
ref={imageInputRef}
|
|
273
|
+
type="file"
|
|
274
|
+
accept="image/*"
|
|
275
|
+
className="hidden"
|
|
276
|
+
onChange={e => {
|
|
277
|
+
const file = e.target.files?.[0];
|
|
278
|
+
if (file) {
|
|
279
|
+
handleImageUpload(file);
|
|
280
|
+
e.target.value = '';
|
|
281
|
+
}
|
|
282
|
+
}}
|
|
283
|
+
/>
|
|
284
|
+
</form>
|
|
285
|
+
</div>
|
|
286
|
+
);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
export default memo(ChatAssistant);
|