wineater-bot 3.0.4 → 3.0.6
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/dist/wineater-chatbot.mjs +2239 -2012
- package/dist/wineater-chatbot.umd.js +167 -48
- package/index.html +3 -3
- package/package.json +1 -1
- package/src/js/barConfig.js +54 -0
- package/src/js/i18n.js +34 -9
- package/src/js/widget-styles.js +82 -0
- package/src/js/widget.js +155 -11
package/index.html
CHANGED
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title>Chatbot</title>
|
|
7
|
-
<!--
|
|
8
|
-
<script>window.wineaterData = {CLIENT_TOKEN: 'HeUw6IXuhvz6tRuXdPEGEbj3SGQ3Q2YtEc1Z8jd41Kg9LtyPb4Epu0to31SYIadG', position: 'right-center', TYPE: 'widget'};</script>
|
|
9
|
-
<script type="module" src="./
|
|
7
|
+
<!-- <script type="module" src="./dist/wineater-chatbot.umd.js"></script> -->
|
|
8
|
+
<script>window.wineaterData = {CLIENT_TOKEN: 'HeUw6IXuhvz6tRuXdPEGEbj3SGQ3Q2YtEc1Z8jd41Kg9LtyPb4Epu0to31SYIadG', position: 'right-center', TYPE: 'widget', WIDGET_TYPE: 'bar'};</script>
|
|
9
|
+
<script type="module" src="./src/index.js"></script>
|
|
10
10
|
</head>
|
|
11
11
|
<body style="margin: 0;">
|
|
12
12
|
<div id="wineater-widget-conteiner"></div> <!-- Контейнер для виджета -->
|
package/package.json
CHANGED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// barConfig.js
|
|
2
|
+
|
|
3
|
+
export const barConfig = {
|
|
4
|
+
barName: "LENEZ",
|
|
5
|
+
dishGroups: [
|
|
6
|
+
{
|
|
7
|
+
groupTitle: "Entradas",
|
|
8
|
+
dishes: [
|
|
9
|
+
"Pimientos rojo asado y aceite de oliva con ajo confitado",
|
|
10
|
+
"Ensalada de arúgula y parmesano",
|
|
11
|
+
"Tomate hierloom aliñado",
|
|
12
|
+
"Aceitunas LENEZ",
|
|
13
|
+
"PapaBnaz",
|
|
14
|
+
"Croquetas | hongos de temporada | jamón serrano",
|
|
15
|
+
"Carpaccio Cesar",
|
|
16
|
+
"Torreznos",
|
|
17
|
+
"Calamares fritos"
|
|
18
|
+
]
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
groupTitle: "Bocadillos",
|
|
22
|
+
dishes: [
|
|
23
|
+
"Brioche de pollo frito, col y salsa macha",
|
|
24
|
+
"Baguette de calamares rebozados en salsa brava",
|
|
25
|
+
"Hogaza de cola de res en recado negro y cebollas encurtidas"
|
|
26
|
+
]
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
groupTitle: "Embutidos",
|
|
30
|
+
dishes: [
|
|
31
|
+
"Jamón Ibérico de bellota",
|
|
32
|
+
"Lomo ibérico",
|
|
33
|
+
"Chorizo ibérico",
|
|
34
|
+
"Plato mixto"
|
|
35
|
+
]
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
groupTitle: "Quesos",
|
|
39
|
+
dishes: [
|
|
40
|
+
"Selección de quesos"
|
|
41
|
+
]
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
groupTitle: "Postres",
|
|
45
|
+
dishes: [
|
|
46
|
+
"Ate con queso manchego semi curado",
|
|
47
|
+
"Gelato de nata con balsámico",
|
|
48
|
+
"Gelato de ron con pasas y “El candado”, Valdespino PX",
|
|
49
|
+
"Gelato Dua Lipa",
|
|
50
|
+
"Créme Brûlée al Orange"
|
|
51
|
+
]
|
|
52
|
+
}
|
|
53
|
+
]
|
|
54
|
+
};
|
package/src/js/i18n.js
CHANGED
|
@@ -37,7 +37,14 @@ class Translator {
|
|
|
37
37
|
rose: "Rosé",
|
|
38
38
|
light: "Léger",
|
|
39
39
|
medium: "Moyen",
|
|
40
|
-
full: "Puissant"
|
|
40
|
+
full: "Puissant",
|
|
41
|
+
|
|
42
|
+
/* -- Новые ключи для bar-режима -- */
|
|
43
|
+
barTitle: "Sélection du bar",
|
|
44
|
+
barSubtitle: "Choisissez vos plats ci-dessous et cliquez sur «Envoyer»",
|
|
45
|
+
barSendButton: "Envoyer",
|
|
46
|
+
barResetButton: "Réinitialiser",
|
|
47
|
+
barName: "Nom du bar",
|
|
41
48
|
},
|
|
42
49
|
es: {
|
|
43
50
|
welcomeMessage: "¿Qué vas a comer? Cuéntame tu plato y te recomendaré el maridaje perfecto.",
|
|
@@ -71,7 +78,14 @@ class Translator {
|
|
|
71
78
|
rose: "Rosado",
|
|
72
79
|
light: "Ligero",
|
|
73
80
|
medium: "Medio",
|
|
74
|
-
full: "Intenso"
|
|
81
|
+
full: "Intenso",
|
|
82
|
+
|
|
83
|
+
/* -- Новые ключи bar -- */
|
|
84
|
+
barTitle: "Platos de Bar",
|
|
85
|
+
barSubtitle: "Selecciona de la lista y presiona «Enviar»",
|
|
86
|
+
barSendButton: "Enviar",
|
|
87
|
+
barResetButton: "Reiniciar",
|
|
88
|
+
barName: "Nombre del bar",
|
|
75
89
|
},
|
|
76
90
|
en: {
|
|
77
91
|
welcomeMessage: "What are you planning to eat? Share your dish and I'll recommend the perfect wine pairing for your meal.",
|
|
@@ -105,7 +119,14 @@ class Translator {
|
|
|
105
119
|
rose: "Rosé",
|
|
106
120
|
light: "Light",
|
|
107
121
|
medium: "Medium",
|
|
108
|
-
full: "Full"
|
|
122
|
+
full: "Full",
|
|
123
|
+
|
|
124
|
+
/* -- Новые ключи bar -- */
|
|
125
|
+
barTitle: "Bar Dishes",
|
|
126
|
+
barSubtitle: "Select dishes below and click ‘Send’",
|
|
127
|
+
barSendButton: "Send",
|
|
128
|
+
barResetButton: "Reset",
|
|
129
|
+
barName: "Bar Name",
|
|
109
130
|
},
|
|
110
131
|
de: {
|
|
111
132
|
welcomeMessage: "Was möchten Sie essen? Beschreiben Sie Ihr Gericht und ich empfehle Ihnen den perfekten Wein dazu.",
|
|
@@ -139,21 +160,25 @@ class Translator {
|
|
|
139
160
|
rose: "Rosé",
|
|
140
161
|
light: "Leicht",
|
|
141
162
|
medium: "Mittel",
|
|
142
|
-
full: "Voll"
|
|
143
|
-
|
|
163
|
+
full: "Voll",
|
|
164
|
+
|
|
165
|
+
/* -- Новые ключи bar -- */
|
|
166
|
+
barTitle: "Bar-Auswahl",
|
|
167
|
+
barSubtitle: "Wählen Sie Ihre Gerichte und klicken Sie auf „Senden“",
|
|
168
|
+
barSendButton: "Senden",
|
|
169
|
+
barResetButton: "Zurücksetzen",
|
|
170
|
+
barName: "Name der Bar",
|
|
171
|
+
}
|
|
144
172
|
};
|
|
145
173
|
}
|
|
146
174
|
|
|
147
175
|
async fetchConfig(storeName) {
|
|
148
|
-
console.log(storeName);
|
|
149
176
|
try {
|
|
150
177
|
const config = await this.api.getStoreConfig(storeName);
|
|
151
|
-
console.log(config);
|
|
152
178
|
this.language = config.lang;
|
|
153
179
|
} catch (e) {
|
|
154
180
|
this.language = 'en'; // Fallback to English
|
|
155
181
|
}
|
|
156
|
-
console.log(this.language);
|
|
157
182
|
}
|
|
158
183
|
|
|
159
184
|
translate(key) {
|
|
@@ -161,4 +186,4 @@ class Translator {
|
|
|
161
186
|
}
|
|
162
187
|
}
|
|
163
188
|
|
|
164
|
-
export default new Translator();
|
|
189
|
+
export default new Translator();
|
package/src/js/widget-styles.js
CHANGED
|
@@ -12,6 +12,8 @@ const widgetStyles = `
|
|
|
12
12
|
box-sizing: border-box;
|
|
13
13
|
z-index: 10000;
|
|
14
14
|
max-width: 1200px;
|
|
15
|
+
min-width: 1200px;
|
|
16
|
+
min-height: 500px;
|
|
15
17
|
margin: 40px auto;
|
|
16
18
|
}
|
|
17
19
|
|
|
@@ -312,6 +314,86 @@ const widgetStyles = `
|
|
|
312
314
|
max-width: 100px;
|
|
313
315
|
}
|
|
314
316
|
}
|
|
317
|
+
|
|
318
|
+
/* --- Стили для бар-версии --- */
|
|
319
|
+
|
|
320
|
+
/* Сетка для блюд бара */
|
|
321
|
+
.dish-grid {
|
|
322
|
+
display: grid;
|
|
323
|
+
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
|
|
324
|
+
gap: 12px;
|
|
325
|
+
margin: 16px 0;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/* Отдельная плитка блюда */
|
|
329
|
+
.dish-tile {
|
|
330
|
+
display: flex;
|
|
331
|
+
justify-content: center;
|
|
332
|
+
align-items: center;
|
|
333
|
+
padding: 16px;
|
|
334
|
+
font-size: 14px;
|
|
335
|
+
font-weight: 500;
|
|
336
|
+
border: 1px solid #ccc;
|
|
337
|
+
border-radius: 8px;
|
|
338
|
+
background-color: #fff;
|
|
339
|
+
cursor: pointer;
|
|
340
|
+
user-select: none;
|
|
341
|
+
transition: all 0.2s ease;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
.dish-tile:hover {
|
|
345
|
+
transform: scale(1.05);
|
|
346
|
+
box-shadow: 0 4px 8px rgba(123, 43, 235, 0.15);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
.dish-tile.selected {
|
|
350
|
+
background: linear-gradient(90deg, #7B2BEB, #0044FF);
|
|
351
|
+
color: #fff;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
/* Кнопка "Отправить" для бар-версии */
|
|
355
|
+
#bar-send-button {
|
|
356
|
+
padding: 12px 20px;
|
|
357
|
+
background: linear-gradient(90deg, #7B2BEB, #0044FF);
|
|
358
|
+
color: white;
|
|
359
|
+
border: none;
|
|
360
|
+
border-radius: 8px;
|
|
361
|
+
cursor: pointer;
|
|
362
|
+
font-size: 16px;
|
|
363
|
+
transition: all 0.2s ease;
|
|
364
|
+
text-align:center;
|
|
365
|
+
margin-top: 16px;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
#bar-send-button:hover {
|
|
369
|
+
transform: scale(1.05);
|
|
370
|
+
box-shadow: 0 4px 8px rgba(123, 43, 235, 0.15);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/* Кнопка "Сбросить" (bar-reset-button), стили как у bar-send-button */
|
|
374
|
+
#bar-reset-button {
|
|
375
|
+
padding: 12px 20px;
|
|
376
|
+
background: linear-gradient(90deg, #7B2BEB, #0044FF);
|
|
377
|
+
color: white;
|
|
378
|
+
border: none;
|
|
379
|
+
border-radius: 8px;
|
|
380
|
+
cursor: pointer;
|
|
381
|
+
font-size: 16px;
|
|
382
|
+
transition: all 0.2s ease;
|
|
383
|
+
margin-top: 16px; /* или подстройте под свой макет */
|
|
384
|
+
margin-left: 12px; /* чтобы отступить от кнопки "Отправить" */
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
#bar-reset-button
|
|
388
|
+
|
|
389
|
+
#bar-reset-button:hover {
|
|
390
|
+
transform: scale(1.05);
|
|
391
|
+
box-shadow: 0 4px 8px rgba(123, 43, 235, 0.15);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
.hidden {
|
|
395
|
+
display: none;
|
|
396
|
+
}
|
|
315
397
|
`;
|
|
316
398
|
|
|
317
399
|
const style = document.createElement('style');
|
package/src/js/widget.js
CHANGED
|
@@ -2,21 +2,42 @@
|
|
|
2
2
|
import './widget-styles.js';
|
|
3
3
|
import { Api } from './api.js';
|
|
4
4
|
import translator from './i18n.js';
|
|
5
|
+
import { barConfig } from './barConfig.js';
|
|
5
6
|
|
|
6
7
|
export class WineWidget {
|
|
7
8
|
constructor() {
|
|
8
|
-
|
|
9
|
+
// Определяем тип виджета: 'store' (старый) или 'bar' (новый)
|
|
10
|
+
this.widgetType = window?.wineaterData?.WIDGET_TYPE || 'bar';
|
|
11
|
+
this.api = new Api();
|
|
12
|
+
|
|
13
|
+
// Фильтры для store
|
|
9
14
|
this.selectedFilters = {
|
|
10
15
|
price: null,
|
|
11
16
|
color: null,
|
|
12
17
|
intensity: null,
|
|
13
18
|
};
|
|
19
|
+
|
|
20
|
+
// Если bar — берем название и список блюд из barConfig
|
|
21
|
+
if (this.widgetType === 'bar') {
|
|
22
|
+
this.barName = barConfig.barName; // "LENEZ"
|
|
23
|
+
this.barDishes = barConfig.dishes; // Список блюд из barConfig
|
|
24
|
+
} else {
|
|
25
|
+
this.barName = '';
|
|
26
|
+
this.barDishes = [];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Инициализируем виджет
|
|
14
30
|
this.initializeWidget();
|
|
15
|
-
|
|
31
|
+
|
|
32
|
+
// Если нужна мгновенная загрузка рекомендаций только в store
|
|
33
|
+
if (this.widgetType === 'store') {
|
|
34
|
+
this.loadInstantRecommendations();
|
|
35
|
+
}
|
|
16
36
|
}
|
|
17
37
|
|
|
18
38
|
initializeWidget() {
|
|
19
39
|
const widgetContainer = document.getElementById('wineater-widget-conteiner');
|
|
40
|
+
if (this.widgetType === 'store') {
|
|
20
41
|
const widgetHTML = `
|
|
21
42
|
<div id="wine-widget-container">
|
|
22
43
|
<div id="wine-widget-left">
|
|
@@ -75,20 +96,71 @@ export class WineWidget {
|
|
|
75
96
|
</div>
|
|
76
97
|
`;
|
|
77
98
|
widgetContainer.insertAdjacentHTML('beforeend', widgetHTML);
|
|
78
|
-
this.
|
|
99
|
+
this.addEventListenersStoreVersion();
|
|
100
|
+
} else {
|
|
101
|
+
const barHTML = `
|
|
102
|
+
<div id="wine-widget-container">
|
|
103
|
+
<div id="wine-widget-right">
|
|
104
|
+
${barConfig.dishGroups.map(group => `
|
|
105
|
+
<!-- Обёртка группы -->
|
|
106
|
+
<div class="dish-group">
|
|
107
|
+
<!-- Заголовок группы, если нужен -->
|
|
108
|
+
<!-- Сетка блюд конкретной группы -->
|
|
109
|
+
<div class="dish-grid">
|
|
110
|
+
${group.dishes.map(dish => `
|
|
111
|
+
<div class="dish-tile" data-dish="${dish}">
|
|
112
|
+
${dish}
|
|
113
|
+
</div>
|
|
114
|
+
`).join('')}
|
|
115
|
+
</div>
|
|
116
|
+
</div>
|
|
117
|
+
`).join('')}
|
|
118
|
+
|
|
119
|
+
<div id="wine-placeholder"></div>
|
|
120
|
+
|
|
121
|
+
<!-- Кнопки для отправки и сброса -->
|
|
122
|
+
<div id="bar-buttons">
|
|
123
|
+
<button id="bar-send-button">
|
|
124
|
+
${translator.translate('barSendButton')}
|
|
125
|
+
</button>
|
|
126
|
+
<button id="bar-reset-button" class="hidden">
|
|
127
|
+
${translator.translate('barResetButton')}
|
|
128
|
+
</button>
|
|
129
|
+
</div>
|
|
130
|
+
|
|
131
|
+
<div id="loading-indicator" class="hidden">
|
|
132
|
+
<div id="typing-indicator" class="typing-indicator">
|
|
133
|
+
<span></span><span></span><span></span>
|
|
134
|
+
</div>
|
|
135
|
+
</div>
|
|
136
|
+
</div>
|
|
137
|
+
</div>
|
|
138
|
+
`;
|
|
139
|
+
widgetContainer.insertAdjacentHTML('beforeend', barHTML);
|
|
140
|
+
|
|
141
|
+
// Привязка обработчиков "плиток" и кнопки
|
|
142
|
+
this.addEventListenersBarVersion();
|
|
143
|
+
}
|
|
79
144
|
}
|
|
80
145
|
|
|
81
|
-
|
|
82
|
-
document
|
|
146
|
+
addEventListenersStoreVersion() {
|
|
147
|
+
document
|
|
148
|
+
.getElementById('wine-search-button')
|
|
149
|
+
.addEventListener('click', () => this.handleSearch());
|
|
150
|
+
|
|
151
|
+
// Привязка фильтров
|
|
83
152
|
this.setupFilterButtons('price-buttons', 'price');
|
|
84
153
|
this.setupFilterButtons('color-buttons', 'color');
|
|
85
154
|
this.setupFilterButtons('intensity-buttons', 'intensity');
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
155
|
+
|
|
156
|
+
// Обработчик Enter в поле ввода
|
|
157
|
+
document
|
|
158
|
+
.getElementById('wine-search-input')
|
|
159
|
+
.addEventListener('keydown', (event) => {
|
|
160
|
+
if (event.key === 'Enter') {
|
|
161
|
+
this.handleSearch();
|
|
162
|
+
}
|
|
163
|
+
});
|
|
92
164
|
}
|
|
93
165
|
|
|
94
166
|
setupFilterButtons(containerId, filterKey) {
|
|
@@ -236,4 +308,76 @@ export class WineWidget {
|
|
|
236
308
|
console.error('Error loading instant recommendations:', error.message);
|
|
237
309
|
}
|
|
238
310
|
}
|
|
311
|
+
addEventListenersBarVersion() {
|
|
312
|
+
// Клик по плиткам
|
|
313
|
+
const tileElements = document.querySelectorAll('.dish-tile');
|
|
314
|
+
tileElements.forEach((tile) => {
|
|
315
|
+
tile.addEventListener('click', () => {
|
|
316
|
+
tile.classList.toggle('selected');
|
|
317
|
+
});
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
// Собираем все .dish-grid
|
|
321
|
+
const dishGrids = document.querySelectorAll('.dish-grid');
|
|
322
|
+
|
|
323
|
+
// Кнопки
|
|
324
|
+
const sendButton = document.getElementById('bar-send-button');
|
|
325
|
+
const resetButton = document.getElementById('bar-reset-button');
|
|
326
|
+
const winePlaceholder = document.getElementById('wine-placeholder');
|
|
327
|
+
|
|
328
|
+
if (!sendButton || !resetButton || !winePlaceholder) return;
|
|
329
|
+
|
|
330
|
+
// Нажатие «Отправить»
|
|
331
|
+
sendButton.addEventListener('click', async () => {
|
|
332
|
+
this.showLoadingIndicator();
|
|
333
|
+
|
|
334
|
+
// Скрываем все группы/гриды блюд
|
|
335
|
+
dishGrids.forEach((dg) => dg.classList.add('hidden'));
|
|
336
|
+
// Прячем кнопку «Отправить»
|
|
337
|
+
sendButton.classList.add('hidden');
|
|
338
|
+
|
|
339
|
+
// Собираем выбранные блюда
|
|
340
|
+
const selectedDishes = [];
|
|
341
|
+
document.querySelectorAll('.dish-tile.selected').forEach(selectedTile => {
|
|
342
|
+
selectedDishes.push(selectedTile.dataset.dish);
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
const query = selectedDishes.join(', ');
|
|
346
|
+
console.log('Selected bar dishes =>', query);
|
|
347
|
+
|
|
348
|
+
try {
|
|
349
|
+
const results = await this.api.getWineRecommendationsSearch(query);
|
|
350
|
+
if (results && results.length > 0) {
|
|
351
|
+
this.displayResults(results, true);
|
|
352
|
+
} else {
|
|
353
|
+
this.displayNoResultsMessage();
|
|
354
|
+
}
|
|
355
|
+
// Показываем кнопку «Сбросить»
|
|
356
|
+
resetButton.classList.remove('hidden');
|
|
357
|
+
} catch (error) {
|
|
358
|
+
console.error('Error while fetching bar recommendations:', error);
|
|
359
|
+
this.displayErrorMessage();
|
|
360
|
+
} finally {
|
|
361
|
+
this.hideLoadingIndicator();
|
|
362
|
+
}
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
// Нажатие «Сбросить»
|
|
366
|
+
resetButton.addEventListener('click', () => {
|
|
367
|
+
// Показываем снова все гриды
|
|
368
|
+
dishGrids.forEach((dg) => dg.classList.remove('hidden'));
|
|
369
|
+
|
|
370
|
+
// Снимаем выделение со всех плиток
|
|
371
|
+
tileElements.forEach(tile => tile.classList.remove('selected'));
|
|
372
|
+
|
|
373
|
+
// Очищаем контейнер с результатами
|
|
374
|
+
winePlaceholder.innerHTML = '';
|
|
375
|
+
|
|
376
|
+
// Прячем кнопку «Сбросить»
|
|
377
|
+
resetButton.classList.add('hidden');
|
|
378
|
+
|
|
379
|
+
// Снова показываем кнопку «Отправить»
|
|
380
|
+
sendButton.classList.remove('hidden');
|
|
381
|
+
});
|
|
382
|
+
}
|
|
239
383
|
}
|