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/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: 'OjvAprylAugk5pKTHRIzZxcPPb6HMPr9qHhrazG0sF3uHjkrvBzufPZ65igfRJJa', TYPE: 'widget', WIDGET_TYPE: 'bar', WIDGET_WRAPPER: 'wineater-widget-conteiner'};</script>
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wineater-bot",
3
- "version": "3.5.1",
3
+ "version": "3.6.0",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "module": "src/index.js",
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, store: this.store ? (this.store.toString().charAt(0).toUpperCase() + this.store.slice(1)) : undefined})
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 Object.keys(this.translations);
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
- <div class="card-popup-header">
277
- <h2>${title}</h2>
278
- <span class="card-popup-close">&times;</span>
279
- </div>
280
- ${shortDescription ? `<p>${shortDescription}</p>` : ''}
281
- ${wine.reason ? `<div class="popup-reason">${wine.reason}</div>` : pairing ? `<p>${pairing}</p>` : ''}
282
- ${tagsHTML ? `<div class="popup-tags">${tagsHTML}</div>` : ''}
283
- </div>
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">&times;</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');