wineater-bot 3.5.1 → 3.6.0
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 +715 -575
- package/dist/wineater-chatbot.umd.js +46 -44
- package/index.html +1 -1
- package/package.json +1 -1
- package/src/js/api.js +2 -2
- package/src/js/i18n.js +125 -3
- package/src/js/widget.js +72 -10
package/index.html
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
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
|
-
<script>window.wineaterData = {CLIENT_TOKEN: '
|
|
7
|
+
<script>window.wineaterData = {CLIENT_TOKEN: 'dRBwg6wB6LeEKFUiLHdlXa84TgSVDuR8HhE0hhq1l4vVeq3cE12DJkPDwXvuhT01', TYPE: 'widget', WIDGET_TYPE: 'store', WIDGET_WRAPPER: 'wineater-widget-conteiner'};</script>
|
|
8
8
|
<!-- <script type="module" src="./dist/wineater-chatbot.umd.js"></script> -->
|
|
9
9
|
<script type="module" src="./src/index.js"></script>
|
|
10
10
|
</head>
|
package/package.json
CHANGED
package/src/js/api.js
CHANGED
|
@@ -68,7 +68,7 @@ export class Api {
|
|
|
68
68
|
'Content-Type': 'application/json',
|
|
69
69
|
'Authorization': `Bearer ${this.token}`
|
|
70
70
|
},
|
|
71
|
-
body: JSON.stringify({ text: text
|
|
71
|
+
body: JSON.stringify({ text: text})
|
|
72
72
|
});
|
|
73
73
|
|
|
74
74
|
const data = await response.json();
|
|
@@ -119,7 +119,7 @@ export class Api {
|
|
|
119
119
|
'Content-Type': 'application/json',
|
|
120
120
|
'Authorization': `Bearer ${this.token}`
|
|
121
121
|
},
|
|
122
|
-
body: JSON.stringify({ store: this.store
|
|
122
|
+
body: JSON.stringify({ store: this.store})
|
|
123
123
|
});
|
|
124
124
|
|
|
125
125
|
const data = await response.json();
|
package/src/js/i18n.js
CHANGED
|
@@ -469,14 +469,80 @@ class Translator {
|
|
|
469
469
|
headerBudget: "бюджет",
|
|
470
470
|
headerOccasion: "случай",
|
|
471
471
|
headerOr: "или",
|
|
472
|
-
}
|
|
472
|
+
},
|
|
473
|
+
kz: {
|
|
474
|
+
lang: 'kz',
|
|
475
|
+
welcomeMessage: "Сіз не жегіңіз келеді? Тағамыңызды сипаттаңыз, мен оған керемет шарап ұсынамын.",
|
|
476
|
+
noResultsFound: "Нәтижелер табылмады.",
|
|
477
|
+
somethingWentWrong: "Кешіріңіз, бірдеңе дұрыс болмады.",
|
|
478
|
+
chatButton1: "Сәйкестік табу",
|
|
479
|
+
chatButton2: "Шарап таңдау",
|
|
480
|
+
chatButton3: "Кеңес алу",
|
|
481
|
+
chatTitle: "Шарап таңдаушы көмекші 🍷",
|
|
482
|
+
chatSubtitle: "Тағамыңызды сипаттаңыз — 30 секундта ұсыныс алыңыз",
|
|
483
|
+
inputPlaceholder: "мысалы, тунец салатымен",
|
|
484
|
+
sendButton: "Таңдау",
|
|
485
|
+
poweredBy: "Powered by",
|
|
486
|
+
price: "Бағасы",
|
|
487
|
+
region: "Өңір",
|
|
488
|
+
type: "Түрі",
|
|
489
|
+
reason: "Ұсыныс",
|
|
490
|
+
moreInfo: "Толығырақ",
|
|
491
|
+
winePairingIntro: "Бұл шарапқа келесі тағамдар сәйкес келеді:",
|
|
492
|
+
askForDinner: "Кешкі асқа не қалайсыз?",
|
|
493
|
+
clearChatButton: "Чатты тазалау",
|
|
494
|
+
widgetTitle: "КЕРЕМЕТ ШАРАПТЫ ТАБЫҢЫЗ",
|
|
495
|
+
widgetSubtitle: "Таңдауларыңызды бөлісіңіз — тағам, көңіл-күй, бюджет немесе жағдай — және біздің AI-сомелье сізге бірнеше секундта шарап ұсынады",
|
|
496
|
+
searchPlaceholder: "Кілт сөзді немесе сипаттаманы енгізіңіз",
|
|
497
|
+
wineDetails: {
|
|
498
|
+
price: "Бағасы",
|
|
499
|
+
region: "Өңір",
|
|
500
|
+
type: "Түрі",
|
|
501
|
+
moreInfo: "Толығырақ",
|
|
502
|
+
reason: "Себебі",
|
|
503
|
+
sommSelection: "Сомелье таңдауы",
|
|
504
|
+
code: "Код",
|
|
505
|
+
buy: "Сатып алу"
|
|
506
|
+
},
|
|
507
|
+
searchButton: "Іздеу",
|
|
508
|
+
priceRange: "Баға диапазоны",
|
|
509
|
+
wineColor: "Шарап түсі",
|
|
510
|
+
intensity: "Қанықтылық",
|
|
511
|
+
red: "Қызыл",
|
|
512
|
+
white: "Ақ",
|
|
513
|
+
rose: "Розалық",
|
|
514
|
+
light: "Жеңіл",
|
|
515
|
+
medium: "Орташа",
|
|
516
|
+
full: "Толық",
|
|
517
|
+
|
|
518
|
+
/* -- Барға арналған жаңа кілттер -- */
|
|
519
|
+
barTitle: "Шарапқа арналған тіскебасарлар",
|
|
520
|
+
barSubtitle: "Төмендегі тағамдарды таңдаңыз және 'Жіберу' батырмасын басыңыз",
|
|
521
|
+
barSendButton: "Жіберу",
|
|
522
|
+
barResetButton: "Қалпына келтіру",
|
|
523
|
+
barName: "Бар атауы",
|
|
524
|
+
barNotesPlaceholder: "Мұнда өз тілектеріңізді жазыңыз...",
|
|
525
|
+
placeholderSuggestions: [
|
|
526
|
+
"Пастаға арналған шарап",
|
|
527
|
+
"Балыққа тамаша үйлеседі",
|
|
528
|
+
"Кешке ұсынуға болады"
|
|
529
|
+
],
|
|
530
|
+
headerLine1: "Ойларыңызбен бөлісіңіз —",
|
|
531
|
+
headerLine2: "— және бірнеше секундта мінсіз шарап ұсынысын алыңыз",
|
|
532
|
+
headerMeal: "тағам",
|
|
533
|
+
headerMood: "көңіл-күй",
|
|
534
|
+
headerBudget: "бюджет",
|
|
535
|
+
headerOccasion: "жағдай",
|
|
536
|
+
headerOr: "немесе",
|
|
537
|
+
}
|
|
473
538
|
};
|
|
474
539
|
}
|
|
475
540
|
|
|
476
541
|
async fetchConfig(storeName) {
|
|
477
542
|
try {
|
|
478
543
|
const config = await this.api.getStoreConfig(storeName);
|
|
479
|
-
|
|
544
|
+
this.storeLanguage = config.lang;
|
|
545
|
+
this.supportedLanguages = config.available_languages;
|
|
480
546
|
// ✅ Если язык уже сохранён в localStorage — использовать его
|
|
481
547
|
const storedLang = localStorage.getItem('selectedLanguage');
|
|
482
548
|
if (storedLang && this.translations[storedLang]) {
|
|
@@ -528,8 +594,64 @@ class Translator {
|
|
|
528
594
|
}
|
|
529
595
|
|
|
530
596
|
getSupportedLanguages() {
|
|
531
|
-
return
|
|
597
|
+
return this.supportedLanguages;
|
|
532
598
|
}
|
|
599
|
+
|
|
600
|
+
async translateTexts(textObject) {
|
|
601
|
+
if (this.storeLanguage === this.language || !this.supportedLanguages) {
|
|
602
|
+
return textObject;
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
const apiKey = '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'; // ✅ Заменить на твой реальный ключ
|
|
606
|
+
const url = 'https://api.openai.com/v1/chat/completions';
|
|
607
|
+
|
|
608
|
+
const headers = {
|
|
609
|
+
'Content-Type': 'application/json',
|
|
610
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
611
|
+
};
|
|
612
|
+
|
|
613
|
+
const texts = Object.values(textObject);
|
|
614
|
+
const joinedText = texts.join(' ||| ');
|
|
615
|
+
|
|
616
|
+
const prompt = `Translate the following texts to ${this.language}. Return only the translated texts separated by " ||| " in the same order:
|
|
617
|
+
|
|
618
|
+
${joinedText}`;
|
|
619
|
+
|
|
620
|
+
const body = JSON.stringify({
|
|
621
|
+
model: 'gpt-3.5-turbo',
|
|
622
|
+
messages: [
|
|
623
|
+
{ role: 'system', content: 'You are a professional translator.' },
|
|
624
|
+
{ role: 'user', content: prompt },
|
|
625
|
+
],
|
|
626
|
+
temperature: 0.2,
|
|
627
|
+
max_tokens: 1000,
|
|
628
|
+
});
|
|
629
|
+
|
|
630
|
+
try {
|
|
631
|
+
const response = await fetch(url, {
|
|
632
|
+
method: 'POST',
|
|
633
|
+
headers,
|
|
634
|
+
body,
|
|
635
|
+
});
|
|
636
|
+
|
|
637
|
+
const data = await response.json();
|
|
638
|
+
const translatedRaw = data.choices?.[0]?.message?.content?.trim();
|
|
639
|
+
|
|
640
|
+
if (!translatedRaw) throw new Error('No response from OpenAI');
|
|
641
|
+
|
|
642
|
+
const translatedTexts = translatedRaw.split(' ||| ').map(t => t.trim());
|
|
643
|
+
|
|
644
|
+
const translatedObject = {};
|
|
645
|
+
Object.keys(textObject).forEach((key, index) => {
|
|
646
|
+
translatedObject[key] = translatedTexts[index] || textObject[key];
|
|
647
|
+
});
|
|
648
|
+
|
|
649
|
+
return translatedObject;
|
|
650
|
+
} catch (error) {
|
|
651
|
+
console.error('Error translating texts:', error);
|
|
652
|
+
return textObject;
|
|
653
|
+
}
|
|
654
|
+
}
|
|
533
655
|
}
|
|
534
656
|
|
|
535
657
|
export default new Translator();
|
package/src/js/widget.js
CHANGED
|
@@ -240,7 +240,7 @@ export class WineWidget {
|
|
|
240
240
|
return price.toLocaleString('en-US'); // e.g., 1000 -> "1,000"
|
|
241
241
|
}
|
|
242
242
|
|
|
243
|
-
wines.forEach((wine, index) => {
|
|
243
|
+
wines.forEach(async (wine, index) => {
|
|
244
244
|
const title = getValue(wine, 'title');
|
|
245
245
|
const image = getValue(wine, 'image');
|
|
246
246
|
const price = getValue(wine, 'total');
|
|
@@ -272,15 +272,16 @@ export class WineWidget {
|
|
|
272
272
|
}
|
|
273
273
|
</div>
|
|
274
274
|
|
|
275
|
-
<div class="card-popup hidden">
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
275
|
+
<div class="card-popup hidden" data-wine-index="${index}">
|
|
276
|
+
<div class="card-popup-header">
|
|
277
|
+
<h2>${title}</h2>
|
|
278
|
+
<span class="card-popup-close">×</span>
|
|
279
|
+
</div>
|
|
280
|
+
${shortDescription ? `<p class="popup-short-desc" data-orig="${shortDescription}">${shortDescription}</p>` : ''}
|
|
281
|
+
${wine.reason ? `<div class="popup-reason" data-orig="${wine.reason}">${wine.reason}</div>`
|
|
282
|
+
: pairing ? `<p class="popup-pairing" data-orig="${pairing}">${pairing}</p>` : ''}
|
|
283
|
+
${tagsHTML ? `<div class="popup-tags">${tagsHTML}</div>` : ''}
|
|
284
|
+
</div>
|
|
284
285
|
|
|
285
286
|
${reasonPopupHTML}
|
|
286
287
|
</div>
|
|
@@ -342,6 +343,8 @@ export class WineWidget {
|
|
|
342
343
|
currentCard.style.transform = 'translateY(0)';
|
|
343
344
|
}, isAnimated ? index * 200 : 0);
|
|
344
345
|
});
|
|
346
|
+
|
|
347
|
+
this.translatePopupContent(wines); // переводы подгружаются после отрисовки
|
|
345
348
|
|
|
346
349
|
if (this.widgetType === 'store') {
|
|
347
350
|
this.enableHoverPopups();
|
|
@@ -391,6 +394,62 @@ export class WineWidget {
|
|
|
391
394
|
`;
|
|
392
395
|
}
|
|
393
396
|
|
|
397
|
+
async translatePopupContent(wines) {
|
|
398
|
+
console.log(translator.language, translator.storeLanguage);
|
|
399
|
+
if (translator.language === translator.storeLanguage || this.widgetType !== 'bar') {
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
const fieldsToTranslate = ['Short Description', 'reason', 'pairing_recommendation', 'Tags'];
|
|
403
|
+
const language = translator.language;
|
|
404
|
+
|
|
405
|
+
const batchSize = 4;
|
|
406
|
+
for (let i = 0; i < wines.length; i += batchSize) {
|
|
407
|
+
const batch = wines.slice(i, i + batchSize);
|
|
408
|
+
|
|
409
|
+
const translationPromises = batch.map(async (wine, index) => {
|
|
410
|
+
const globalIndex = i + index;
|
|
411
|
+
const textObject = {};
|
|
412
|
+
|
|
413
|
+
fieldsToTranslate.forEach(field => {
|
|
414
|
+
const val = wine[field] || wine[field.toLowerCase()];
|
|
415
|
+
if (val) textObject[field] = Array.isArray(val) ? val.join(', ') : val;
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
if (Object.keys(textObject).length === 0) return;
|
|
419
|
+
|
|
420
|
+
const translated = await translator.translateTexts(textObject);
|
|
421
|
+
|
|
422
|
+
const popup = document.querySelector(`.card-popup[data-wine-index="${globalIndex}"]`);
|
|
423
|
+
if (!popup) return;
|
|
424
|
+
|
|
425
|
+
if (translated['Short Description']) {
|
|
426
|
+
const el = popup.querySelector('.popup-short-desc');
|
|
427
|
+
if (el) el.textContent = translated['Short Description'];
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
if (translated.reason) {
|
|
431
|
+
const el = popup.querySelector('.popup-reason');
|
|
432
|
+
if (el) el.textContent = translated.reason;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
if (translated.pairing_recommendation) {
|
|
436
|
+
const el = popup.querySelector('.popup-pairing');
|
|
437
|
+
if (el) el.textContent = translated.pairing_recommendation;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
if (translated.Tags) {
|
|
441
|
+
const tagsArray = translated.Tags.split(',').map(tag => tag.trim());
|
|
442
|
+
const tagHTML = tagsArray.map(tag => `<span class="popup-tag">${tag}</span>`).join('');
|
|
443
|
+
const tagsContainer = popup.querySelector('.popup-tags');
|
|
444
|
+
if (tagsContainer) tagsContainer.innerHTML = tagHTML;
|
|
445
|
+
}
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
// Ждём выполнения всей пачки
|
|
449
|
+
await Promise.all(translationPromises);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
394
453
|
async loadInstantRecommendations() {
|
|
395
454
|
this.showLoadingIndicator();
|
|
396
455
|
try {
|
|
@@ -451,6 +510,9 @@ export class WineWidget {
|
|
|
451
510
|
createLanguageSwitcher() {
|
|
452
511
|
const langContainer = document.querySelector('#powered-by-widget .powered-by-lang');
|
|
453
512
|
const languages = translator.getSupportedLanguages();
|
|
513
|
+
if (!languages) {
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
454
516
|
const currentLanguage = translator.language;
|
|
455
517
|
|
|
456
518
|
const dropdown = document.createElement('select');
|