wineater-bot 2.0.2 → 2.1.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
@@ -5,7 +5,7 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
6
  <title>Chatbot</title>
7
7
  <!-- <script type="module" src="https://unpkg.com/wineater-bot@1.0.0/dist/wineater-chatbot.umd.js"></script> -->
8
- <script>window.wineaterData = {CLIENT_TOKEN: 'K0ZzlrfT0z00J6dSAt8ZEIKWiUfypG1Tyb1ZGtgwuJaF8WZuRKlHfvEMCF9DyytZ'};</script><script type="module" src="./src/index.js"></script>
8
+ <script>window.wineaterData = {CLIENT_TOKEN: 'K0ZzlrfT0z00J6dSAt8ZEIKWiUfypG1Tyb1ZGtgwuJaF8WZuRKlHfvEMCF9DyytZ', position: 'right-center'};</script><script type="module" src="./src/index.js"></script>
9
9
  </head>
10
10
  <body style="margin: 0;">
11
11
  <img src="./src/assets/biobio.png" style="width: 100vw; height: 100vh; margin: 0;">
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wineater-bot",
3
- "version": "2.0.2",
3
+ "version": "2.1.0",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "module": "src/index.js",
@@ -16,6 +16,7 @@
16
16
  "license": "ISC",
17
17
  "dependencies": {
18
18
  "@openreplay/tracker": "^14.0.4",
19
+ "flagsmith": "^7.0.2",
19
20
  "vite": "^5.2.9"
20
21
  },
21
22
  "devDependencies": {
package/src/index.js CHANGED
@@ -3,13 +3,23 @@
3
3
  // Import your CSS here
4
4
  import './js/styles.js';
5
5
  import { setCookie, setUniqueUserCookie, getCookie } from './js/analytics.js';
6
+ import flagsmith from 'flagsmith'; //Add this line if you're using flagsmith via npm
7
+
6
8
 
7
9
  import Tracker from '@openreplay/tracker';
8
10
 
9
11
  const tracker = new Tracker({
10
- projectKey: "PA8j9fAi49i6g2QnvpNs",
12
+ projectKey: "PA8j9fAi49i6g2QnvpNs",
13
+ '__DISABLE_SECURE_MODE': true
11
14
  });
12
15
 
16
+ flagsmith.init({
17
+ environmentID: '5RfiDoiqb4dHrNdQvzbkfM',
18
+ // api:"http://localhost:8000/api/v1/" set this if you are self hosting, and point it to your API
19
+ cacheFlags: true, // stores flags in localStorage cache
20
+ enableAnalytics: true, // See https://docs.flagsmith.com/flag-analytics/ for more info.
21
+ });
22
+
13
23
  // Import your JavaScript modules here
14
24
  import { toggleChatWindow, closeChatWindow, openChatWindow } from './js/ui.js';
15
25
  import { Chatbot } from './js/chatbot.js';
@@ -17,7 +27,7 @@ import './js/api.js';
17
27
  import translator from './js/i18n.js';
18
28
  import {shouldOpenChatAutomatically} from './js/auto-open.js'
19
29
 
20
- const DEMO_TOKEN='OuhXJRleF6sROjsQZPWx1yqjrL6APeJxmHswqyOBWbgIqtXcht3MDadvI9DtOOoL';
30
+ const DEMO_TOKEN='tbMTFzLbWcXFgXK8SzyFHA6kIjelFcz5VQABVPbXdXh5XZJCDQH9BcVlrv3DF3kA';
21
31
 
22
32
 
23
33
  document.addEventListener('DOMContentLoaded', () => {
@@ -36,7 +46,8 @@ export {
36
46
 
37
47
  function createChatbotUI() {
38
48
  tracker.start();
39
- const buttonText = window?.wineaterData?.BUTTON_TEXT || translator.translate('chatButton');
49
+ const btnTextOption = flagsmith.getValue('chat-btn-txt');
50
+ const buttonText = window?.wineaterData?.BUTTON_TEXT || translator.translate(`chatButton${btnTextOption}`);
40
51
  const chatbotHTML = `
41
52
  <div id="chatbot-container">
42
53
  <div id="chat-icon" onclick="toggleChatWindow()">
@@ -45,25 +56,27 @@ function createChatbotUI() {
45
56
  </div>
46
57
 
47
58
  <div id="chat-window" class="hidden">
48
- <div id="chat-header">
49
- <div id="powered-by">
50
- <a href="https://wineater.com" target="_blank" style="text-decoration: none; color: inherit;">
51
- <span>Powered by</span>
52
- <img src="https://landing-static.s3.eu-central-1.amazonaws.com/images/logo.svg" alt="Wineater Logo" id="chatbot-logo">
53
- </a>
59
+ <div id="chat-header">
60
+ <div id="chat-title">
61
+ <h1>${translator.translate('chatTitle')}</h1>
54
62
  </div>
55
63
  <div id="close-btn">&times;</div>
56
- </div>
57
-
58
- <div id="message-container"></div>
59
- <div id="typing-indicator" class="typing-indicator hidden">
60
- <span></span><span></span><span></span>
61
- </div>
62
- <div id="input-container">
63
- <input type="text" id="user-input" placeholder="${translator.translate('inputPlaceholder')}">
64
- <button id="send-button">${translator.translate('sendButton')}</button>
65
- </div>
66
- </div>
64
+ </div>
65
+ <div id="message-container"></div>
66
+ <div id="typing-indicator" class="typing-indicator hidden">
67
+ <span></span><span></span><span></span>
68
+ </div>
69
+ <div id="input-container">
70
+ <input type="text" id="user-input" placeholder="${translator.translate('inputPlaceholder')}">
71
+ <button id="send-button">${translator.translate('sendButton')}</button>
72
+ </div>
73
+ <div id="powered-by">
74
+ <a href="https://wineater.com" target="_blank">
75
+ <span>${translator.translate('poweredBy')}</span>
76
+ <img src="https://landing-static.s3.eu-central-1.amazonaws.com/images/logo.svg" alt="Wineater Logo" id="chatbot-logo">
77
+ </a>
78
+ </div>
79
+ </div>
67
80
  </div>
68
81
  `;
69
82
 
@@ -121,18 +134,18 @@ document.addEventListener('DOMContentLoaded', () => {
121
134
 
122
135
  // If you want to initialize your chatbot automatically when the script is loaded
123
136
  if (typeof window !== 'undefined') {
124
- let position = window?.wineaterData?.POSITION || 'bottom-right';
137
+ let position = window?.wineaterData?.POSITION || 'right-center';
125
138
  window.addEventListener('DOMContentLoaded', async () => {
126
139
  const stores = ['climats', 'telckel', 'biobio', 'weinpoint', 'vinigrandi'];
127
140
  const store = stores.find(value => window.location.href.includes(value)) || null;
128
141
  let capitalizeStore;
129
142
  if (store) {
130
143
  capitalizeStore = store.charAt(0).toUpperCase() + store.slice(1);
131
- if (capitalizeStore === 'Biobio') {
144
+ if (capitalizeStore === 'Climats') {
132
145
  position = 'right-center';
133
146
  }
134
147
  }
135
- await translator.fetchConfig(capitalizeStore || 'Demo');
148
+ await translator.fetchConfig(capitalizeStore || 'Climats');
136
149
  createChatbotUI(); // Create the UI
137
150
  setupCloseButton();
138
151
  setupOutsideClick();
package/src/js/api.js CHANGED
@@ -1,13 +1,13 @@
1
1
  // api.js
2
2
 
3
3
  const stores = ['climats', 'telckel', 'biobio', 'weinpoint', 'vinigrandi'];
4
- const DEMO_TOKEN='OuhXJRleF6sROjsQZPWx1yqjrL6APeJxmHswqyOBWbgIqtXcht3MDadvI9DtOOoL';
4
+ const DEMO_TOKEN='bMTFzLbWcXFgXK8SzyFHA6kIjelFcz5VQABVPbXdXh5XZJCDQH9BcVlrv3DF3kA';
5
5
 
6
6
  export class Api {
7
7
  constructor(apiEndpoint) {
8
8
  // Allow users of the library to specify their own endpoint if needed
9
- // this.baseURL = 'https://api.wineater.com/';
10
- this.baseURL = 'https://wineater-backend.vercel.app/';
9
+ this.baseURL = 'http://localhost:3000/';
10
+ // this.baseURL = 'https://wineater-backend.vercel.app/';
11
11
  this.store = stores.find(value => window.location.href.includes(value));
12
12
  this.token = window?.wineaterData?.CLIENT_TOKEN || DEMO_TOKEN;
13
13
  }
@@ -23,6 +23,7 @@ export class Api {
23
23
  },
24
24
  body: JSON.stringify({ store: storeName })
25
25
  });
26
+ console.log(response);
26
27
  if (!response.ok) throw new Error(`HTTP error! Status: ${response.status}`);
27
28
  return await response.json();
28
29
  } catch (error) {
package/src/js/chatbot.js CHANGED
@@ -89,16 +89,24 @@ export class Chatbot {
89
89
  }
90
90
 
91
91
  displayResults(wines) {
92
+ console.log(wines);
92
93
  const messageContainer = document.getElementById('message-container');
93
94
  trackRecommendationsGet(wines.map(value => value.Title));
94
95
  wines.forEach(wine => {
96
+
97
+ const recDiv = document.createElement('div');
98
+ recDiv.className = 'wine-recommendation';
99
+
95
100
  const wineDiv = document.createElement('div');
96
101
  wineDiv.className = 'wine-item';
102
+ recDiv.appendChild(wineDiv);
97
103
 
98
104
  const image = document.createElement('img');
99
105
  image.src = wine.Image;
100
106
  wineDiv.appendChild(image);
101
107
 
108
+ console.log(wineDiv, 'wine div');
109
+
102
110
  const contentDiv = document.createElement('div');
103
111
  contentDiv.innerHTML = `
104
112
  <strong>${wine.Title}</strong><br>
@@ -116,11 +124,16 @@ export class Chatbot {
116
124
  window.open(wine.Url, '_blank'); // Open the link in a new tab
117
125
  });
118
126
  wineDiv.appendChild(contentDiv);
127
+
128
+ const reasonDiv = document.createElement('div');
129
+ reasonDiv.className = 'wine-description';
130
+ reasonDiv.innerHTML = wine.Reason;
131
+ recDiv.appendChild(reasonDiv);
119
132
  // const suggestionDiv = document.createElement('div');
120
133
  // suggestionDiv.innerHTML = `${translator.translate('reason')}: ${wine.Reason}<br>`;
121
134
  // wineDiv.appendChild(suggestionDiv);
122
- messageContainer.appendChild(wineDiv);
123
- this.displayFirstMessage(wine.Reason);
135
+ console.log(recDiv);
136
+ messageContainer.appendChild(recDiv);
124
137
  });
125
138
  messageContainer.scrollTop = messageContainer.scrollHeight;
126
139
  }
package/src/js/i18n.js CHANGED
@@ -6,12 +6,17 @@ class Translator {
6
6
  this.language = 'en'; // Default to English
7
7
  this.translations = {
8
8
  fr: {
9
- welcomeMessage: "Sublimez votre expérience gastronomique. Il vous suffit de donner les détails de votre plat, et obtenez la recommandation de compagnon de vin idéale. Que voulez-vous manger?",
9
+ welcomeMessage: "Quel plat allez-vous déguster ? Décrivez-le-moi et je vous proposerai le vin qui l'accompagnera parfaitement.",
10
10
  noResultsFound: "Aucun résultat trouvé.",
11
11
  somethingWentWrong: "Désolé, quelque chose a mal tourné.",
12
- chatButton: "Accorder le vin au dîner",
12
+ chatButton1: "Trouvez l'accord parfait",
13
+ chatButton2: "Associez le vin",
14
+ chatButton3: "Obtenez des conseils",
15
+ chatTitle: "Accord Mets & Vins 🍷",
16
+ chatSubtitle: "Décrivez votre plat et obtenez une recommandation en 30 secondes",
13
17
  inputPlaceholder: "ex., Salade de thon",
14
18
  sendButton: "Accorder",
19
+ poweredBy: "Propulsé par",
15
20
  price: "Prix",
16
21
  region: "Région",
17
22
  type: "Type",
@@ -22,12 +27,17 @@ class Translator {
22
27
  clearChatButton: "Effacer le chat"
23
28
  },
24
29
  es: {
25
- welcomeMessage: "Mejore su experiencia gastronómica. Solo proporcione los detalles de su plato y obtenga la recomendación de vino ideal. ¿Qué le gustaría comer?",
30
+ welcomeMessage: "¿Qué vas a comer? Cuéntame tu plato y te recomendaré el maridaje perfecto.",
26
31
  noResultsFound: "No se encontraron resultados.",
27
32
  somethingWentWrong: "Lo sentimos, algo salió mal.",
28
- chatButton: "Maridaje ideal",
33
+ chatButton1: "Encuentra tu maridaje",
34
+ chatButton2: "Combina vino ya",
35
+ chatButton3: "Recibe asesoría ahora",
36
+ chatTitle: "Maridaje de Vinos 🍷",
37
+ chatSubtitle: "Describe tu comida y obtén una recomendación en 30 segundos",
29
38
  inputPlaceholder: "ej., Ensalada de atún",
30
- sendButton: "Maridar",
39
+ sendButton: "Combinar",
40
+ poweredBy: "Desarrollado por",
31
41
  price: "Precio",
32
42
  region: "Región",
33
43
  type: "Tipo",
@@ -38,12 +48,17 @@ class Translator {
38
48
  clearChatButton: "Limpiar el chat"
39
49
  },
40
50
  en: {
41
- welcomeMessage: "Enhance your dining experience. Just provide your dish details and get the ideal wine pairing. What would you like to eat?",
51
+ welcomeMessage: "What are you planning to eat? Share your dish and I'll recommend the perfect wine pairing for your meal.",
42
52
  noResultsFound: "No results found.",
43
53
  somethingWentWrong: "Sorry, something went wrong.",
44
- chatButton: "Pair wine with dinner",
45
- inputPlaceholder: "e.g., Tuna Salad",
46
- sendButton: "Pair",
54
+ chatButton1: "Find Your Pairing",
55
+ chatButton2: "Pair Wine Now",
56
+ chatButton3: "Get Wine Advice",
57
+ chatTitle: "Wine Pairing Assistant 🍷",
58
+ chatSubtitle: "Describe your meal and get a recommendation in 30 seconds",
59
+ inputPlaceholder: "e.g., Tuna salad",
60
+ sendButton: "Match",
61
+ poweredBy: "Powered by",
47
62
  price: "Price",
48
63
  region: "Region",
49
64
  type: "Type",
@@ -54,12 +69,17 @@ class Translator {
54
69
  clearChatButton: "Clear Chat"
55
70
  },
56
71
  de: {
57
- welcomeMessage: "Verbessern Sie Ihr kulinarisches Erlebnis. Geben Sie einfach die Details Ihres Gerichts an und erhalten Sie die ideale Weinempfehlung. Was möchten Sie essen?",
72
+ welcomeMessage: "Was möchten Sie essen? Beschreiben Sie Ihr Gericht und ich empfehle Ihnen den perfekten Wein dazu.",
58
73
  noResultsFound: "Keine Ergebnisse gefunden.",
59
74
  somethingWentWrong: "Entschuldigung, etwas ist schief gelaufen.",
60
- chatButton: "Wein zum Abendessen",
75
+ chatButton1: "Finde Dein Pairing",
76
+ chatButton2: "Wein jetzt wählen",
77
+ chatButton3: "Erhalte Weinberatung",
78
+ chatTitle: "Wein & Speisen 🍷",
79
+ chatSubtitle: "Beschreiben Sie Ihr Gericht und erhalten Sie in 30 Sekunden eine Empfehlung",
61
80
  inputPlaceholder: "z.B., Thunfischsalat",
62
81
  sendButton: "Kombinieren",
82
+ poweredBy: "Unterstützt von",
63
83
  price: "Preis",
64
84
  region: "Region",
65
85
  type: "Typ",
@@ -72,13 +92,16 @@ class Translator {
72
92
  };
73
93
  }
74
94
 
75
- async fetchConfig(storeName = 'Demo') {
95
+ async fetchConfig(storeName) {
96
+ console.log(storeName);
76
97
  try {
77
98
  const config = await this.api.getStoreConfig(storeName);
99
+ console.log(config);
78
100
  this.language = config.lang;
79
101
  } catch (e) {
80
102
  this.language = 'en'; // Fallback to English
81
103
  }
104
+ console.log(this.language);
82
105
  }
83
106
 
84
107
  translate(key) {
package/src/js/styles.js CHANGED
@@ -15,17 +15,46 @@ const css = `
15
15
 
16
16
  /* Styling the chat icon */
17
17
  #chat-icon {
18
- height: 40px;
19
- display: flex; /* Use flexbox for alignment */
20
- align-items: center; /* Center items vertically */
21
- justify-content: center; /* Center items horizontally */
22
- padding: 8px 16px; /* Add some padding around the text and image */
23
- border-radius: 20px; /* Rounded corners for the button */
24
- background-color: #fff; /* Background color from your style guide */
25
- cursor: pointer;
26
- box-sizing: content-box;
27
- box-shadow: 0 2px 10px rgba(0,0,0,0.2); /* Optional: add shadow for depth */
28
- }
18
+ height: 50px;
19
+ display: flex;
20
+ align-items: center;
21
+ justify-content: center;
22
+ padding: 10px 20px;
23
+ border-radius: 25px;
24
+ background: linear-gradient(90deg, #7B2BEB, #0044FF); /* Фиолетовый к синему */
25
+ cursor: pointer;
26
+ box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3); /* Тень */
27
+ color: white; /* Белый текст */
28
+ font-family: 'Poppins', sans-serif;
29
+ font-size: 16px; /* Увеличенный размер текста */
30
+ font-weight: bold;
31
+ transition: transform 0.2s ease, box-shadow 0.2s ease; /* Анимация при наведении */
32
+ }
33
+
34
+ #chat-icon:hover {
35
+ transform: scale(1.1) translateY(-50%) rotate(90deg); /* Увеличение при наведении */
36
+ box-shadow: 0 6px 20px rgba(0, 0, 0, 0.4); /* Более глубокая тень */
37
+
38
+ }
39
+
40
+ @keyframes pulse {
41
+ 0%, 100% {
42
+ transform: scale(1);
43
+ }
44
+ 50% {
45
+ transform: scale(1.1);
46
+ }
47
+ }
48
+
49
+ /* Подёргивание раз в 10 секунд */
50
+ .wiggle {
51
+ animation: wiggle 0.3s ease-in-out 10s infinite;
52
+ }
53
+
54
+ /* Плавное пульсирование каждые 10 секунд */
55
+ .pulse {
56
+ animation: pulse 1.5s ease-in-out 10s infinite;
57
+ }
29
58
 
30
59
  /* Position class for bottom-right */
31
60
  .chat-icon-bottom-right {
@@ -48,127 +77,224 @@ const css = `
48
77
  }
49
78
 
50
79
  #chat-icon span {
51
- color: black; /* Text color */
80
+ color: white; /* Text color */
52
81
  font-family: 'Poppins', sans-serif;
53
82
  }
54
83
 
55
84
  /* Chat window styles */
56
85
  #chat-window {
57
- position: fixed;
58
- bottom: 20px;
59
- right: 20px;
60
- width: 350px;
61
- background-color: white;
62
- border-radius: 8px;
63
- box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
64
- display: none;
86
+ position: fixed;
87
+ bottom: 20px;
88
+ right: 20px;
89
+ width: 380px;
90
+ background-color: white;
91
+ border-radius: 24px;
92
+ box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
93
+ display: none;
94
+ overflow: hidden;
65
95
  }
66
96
 
67
97
  #chat-header {
68
- background-color: #fff;
69
- color: white;
70
- padding: 16px;
71
- border-top-left-radius: 8px;
72
- border-top-right-radius: 8px;
73
- display: flex;
74
- justify-content: space-between;
75
- align-items: center;
98
+ background-color: #FAFAFA;
99
+ padding: 24px 24px 20px;
100
+ border-bottom: 1px solid rgba(0, 0, 0, 0.08);
101
+ display: flex;
102
+ justify-content: space-between;
103
+ align-items: flex-start;
76
104
  }
77
105
 
78
- #powered-by {
79
- display: flex;
80
- align-items: center;
81
- background-color: #fff;
82
- width: 100%;
83
- border-radius: 16px;
106
+ #chat-title h1 {
107
+ font-family: 'Poppins', sans-serif;
108
+ font-size: 20px;
109
+ font-weight: 600;
110
+ color: #1F1F1F;
111
+ margin: 0;
112
+ line-height: 1.3;
84
113
  }
85
114
 
86
- #powered-by a span {
87
- font-family: 'Poppins', sans-serif;
88
- color: #7E27ED;
89
- margin-right: 8px;
115
+ #chat-title p {
116
+ font-family: 'Poppins', sans-serif;
117
+ font-size: 14px;
118
+ color: #666;
119
+ margin: 0;
120
+ line-height: 1.4;
90
121
  }
91
122
 
92
- #chatbot-logo {
93
- height: 20px;
94
- width: auto;
123
+ #close-btn {
124
+ font-size: 20px;
125
+ color: #666;
126
+ cursor: pointer;
127
+ padding: 4px 8px;
128
+ margin: -4px -4px 0 16px;
129
+ line-height: 1;
130
+ border-radius: 50%;
131
+ transition: all 0.2s ease;
95
132
  }
96
133
 
97
- #close-btn {
98
- font-size: 24px;
99
- color: #7E27ED;
100
- cursor: pointer;
101
- padding: 0 10px;
102
- line-height: 24px;
134
+ #close-btn:hover {
135
+ background-color: rgba(0, 0, 0, 0.05);
136
+ color: #333;
103
137
  }
104
138
 
105
- /* Container for messages */
106
139
  #message-container {
107
- max-height: 500px;
108
- height: 400px;
109
- overflow-y: auto;
110
- padding: 15px;
111
- background: #f7f7f7;
140
+ padding: 20px 24px;
141
+ max-height: 400px;
142
+ overflow-y: auto;
143
+ }
144
+
145
+ .message {
146
+ margin-bottom: 16px;
147
+ max-width: 85%;
148
+ }
149
+
150
+ .bot-message {
151
+ background-color: #F6F6F6;
152
+ padding: 12px 16px;
153
+ border-radius: 16px;
154
+ border-top-left-radius: 4px;
155
+ margin-right: auto;
156
+ font-size: 14px;
157
+ line-height: 1.5;
158
+ color: #1F1F1F;
159
+ }
160
+
161
+ .user-message {
162
+ background-color: #7E27ED;
163
+ color: white;
164
+ padding: 12px 16px;
165
+ border-radius: 16px;
166
+ border-top-right-radius: 4px;
167
+ margin-left: auto;
168
+ font-size: 14px;
169
+ line-height: 1.5;
112
170
  }
113
171
 
114
- /* Input area */
115
172
  #input-container {
116
- border-top: 2px solid #ececec;
117
- background-color: white;
118
- padding: 12px;
119
- display: flex;
120
- align-items: center;
121
- gap: 10px;
122
- }
123
-
124
- #input-container input {
125
- flex-grow: 1;
126
- border: 2px solid #B928EB;
127
- padding: 10px;
128
- border-radius: 4px;
129
- font-family: 'Poppins', sans-serif;
173
+ padding: 16px 16px; /* Уменьшенный паддинг по бокам */
174
+ border-top: 1px solid rgba(0, 0, 0, 0.08);
175
+ display: flex;
176
+ gap: 0; /* Убрали отступ между элементами */
177
+ background-color: white;
130
178
  }
131
179
 
132
- #input-container button {
133
- padding: 10px 20px;
134
- background-color: #7E27ED;
135
- border: none;
136
- color: white;
137
- border-radius: 4px;
138
- font-family: 'Poppins', sans-serif;
139
- font-size: 1rem;
140
- cursor: pointer;
141
- box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
142
- transition: background-color 0.3s ease;
180
+ /* Стили для поля ввода */
181
+ #user-input {
182
+ flex: 1;
183
+ padding: 12px 16px;
184
+ border: 2px solid transparent; /* Прозрачная граница для эффекта градиента */
185
+ border-radius: 12px 0 0 12px; /* Скругление только слева */
186
+ font-size: 14px;
187
+ line-height: 1.5;
188
+ color: #2C2C2C;
189
+ transition: all 0.2s ease;
190
+ background:
191
+ linear-gradient(white, white) padding-box,
192
+ linear-gradient(90deg, #0044FF, #7B2BEB) border-box;
193
+ }
194
+
195
+ #user-input:focus {
196
+ outline: none;
197
+ box-shadow: 0 0 0 3px rgba(123, 43, 235, 0.1);
198
+ }
199
+
200
+ /* Кнопка отправки */
201
+ #send-button {
202
+ background: linear-gradient(90deg, #7B2BEB, #0044FF);
203
+ color: white;
204
+ border: none;
205
+ padding: 12px 24px;
206
+ border-radius: 0 12px 12px 0; /* Скругление только справа */
207
+ font-size: 14px;
208
+ font-weight: 500;
209
+ cursor: pointer;
210
+ transition: transform 0.2s ease, box-shadow 0.2s ease;
211
+ box-shadow: 0 2px 8px rgba(123, 43, 235, 0.15);
212
+ }
213
+
214
+ #send-button:hover {
215
+ transform: translateY(-1px);
216
+ box-shadow: 0 4px 12px rgba(123, 43, 235, 0.2);
217
+ }
218
+
219
+ #send-button:active {
220
+ transform: translateY(0);
221
+ box-shadow: 0 2px 8px rgba(123, 43, 235, 0.15);
222
+ }
223
+
224
+ #powered-by {
225
+ padding: 12px 24px;
226
+ border-top: 1px solid rgba(0, 0, 0, 0.08);
227
+ display: flex;
228
+ align-items: center;
229
+ justify-content: center;
230
+ background-color: #FAFAFA;
231
+ }
232
+
233
+ #powered-by a {
234
+ display: flex;
235
+ align-items: center;
236
+ text-decoration: none;
237
+ opacity: 0.7;
238
+ transition: opacity 0.2s ease;
143
239
  }
144
240
 
145
- #input-container button:hover {
146
- background-color: #2FC0BF;
147
- box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
241
+ #powered-by a:hover {
242
+ opacity: 1;
243
+ }
244
+
245
+ #powered-by span {
246
+ font-family: 'Poppins', sans-serif;
247
+ color: #666;
248
+ margin-right: 8px;
249
+ font-size: 12px;
250
+ }
251
+
252
+ #chatbot-logo {
253
+ height: 16px;
254
+ width: auto;
255
+ }
256
+
257
+ /* Container for messages */
258
+ #message-container {
259
+ padding: 24px;
260
+ display: flex;
261
+ max-height: 500px;
262
+ height: 400px;
263
+ flex-direction: column;
264
+ gap: 16px;
148
265
  }
149
266
 
150
267
  /* Message bubble styling */
151
268
  .message {
152
- margin: 8px 0;
153
- padding: 12px;
154
- background-color: #e0e0e0;
155
- border-radius: 16px;
156
- font-size: 14px;
157
- line-height: 1.4;
269
+ padding: 16px 20px;
270
+ border-radius: 20px;
271
+ margin-bottom: 8px;
272
+ font-size: 15px;
273
+ line-height: 1.5;
274
+ font-family: 'Poppins', sans-serif;
275
+ width: fit-content;
276
+ max-width: 85%;
158
277
  }
159
278
 
160
- .message.user {
161
- background-color: #B928EB;
162
- color: white;
163
- align-self: flex-end;
279
+ /* Стили для сообщений API (бота) */
280
+ .message.api {
281
+ background: linear-gradient(90deg, #7B2BEB, #0044FF);
282
+ color: white;
283
+ border-top-left-radius: 4px;
284
+ margin-right: auto;
285
+ box-shadow: 0 2px 8px rgba(123, 43, 235, 0.15);
164
286
  }
165
287
 
166
- .message.api {
167
- background-color: #e0e0e0;
168
- color: black;
169
- align-self: flex-start;
288
+ /* Стили для пользовательских сообщений */
289
+ .message.user {
290
+ background-color: #F0F0F0;
291
+ color: #2C2C2C;
292
+ border-top-right-radius: 4px;
293
+ margin-left: auto;
294
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
170
295
  }
171
296
 
297
+
172
298
  /* Dish item styling */
173
299
  .dish-message {
174
300
  border: 2px solid #7E27ED; /* Bold yellow border */
@@ -183,29 +309,75 @@ const css = `
183
309
  }
184
310
 
185
311
  /* Wine item styling */
186
- .wine-item {
187
- display: flex;
188
- align-items: center;
189
- border: 1px solid #7E27ED;
190
- border-radius: 8px;
191
- padding: 10px;
192
- margin-top: 10px;
193
- background: white;
312
+
313
+ .wine-recommendation {
314
+ display: flex;
315
+ flex-direction: column;
316
+ background: white;
317
+ border: 2px solid transparent; /* Прозрачная граница для эффекта градиента */
318
+ border-radius: 20px;
319
+ border-top-left-radius: 4px;
320
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
321
+ background:
322
+ linear-gradient(white, white) padding-box,
323
+ linear-gradient(90deg, #7B2BEB, #0044FF) border-box;
194
324
  }
195
325
 
196
- .wine-item a {
197
- color: #7E27ED !important;
326
+ .wine-item {
327
+ display: flex;
328
+ padding: 16px;
329
+ gap: 16px;
198
330
  }
199
331
 
200
332
  .wine-item img {
201
- width: 80px;
202
- height: auto;
203
- border-radius: 4px;
204
- margin-right: 12px;
333
+ width: 100px;
334
+ height: 100px;
335
+ object-fit: cover;
336
+ border-radius: 8px;
337
+ }
338
+
339
+ .wine-item-details {
340
+ flex: 1;
341
+ }
342
+
343
+ .wine-item-details strong {
344
+ font-size: 16px;
345
+ color: #2C2C2C;
346
+ display: block;
347
+ margin-bottom: 4px;
348
+ }
349
+
350
+ .wine-item-price {
351
+ color: #7B2BEB;
352
+ font-weight: 500;
353
+ margin-bottom: 4px;
354
+ }
355
+
356
+ .wine-item-region {
357
+ color: #666;
358
+ font-size: 14px;
359
+ margin-bottom: 4px;
205
360
  }
206
361
 
207
- .wine-item div {
208
- font-size: 0.9rem;
362
+ .wine-item a {
363
+ color: #0044FF;
364
+ text-decoration: none;
365
+ font-size: 14px;
366
+ font-weight: 500;
367
+ }
368
+
369
+ .wine-description {
370
+ padding: 12px 16px;
371
+ color: white;
372
+
373
+ border-radius: 0px;
374
+ border-bottom-left-radius: 15px;
375
+ border-bottom-right-radius: 15px;
376
+
377
+ font-size: 14px;
378
+ line-height: 1.5;
379
+ background: linear-gradient(90deg, #7B2BEB, #0044FF);
380
+ box-shadow: 0 2px 8px rgba(123, 43, 235, 0.15);
209
381
  }
210
382
 
211
383
  /* Ensure the chat window is visible when the 'visible' class is added */
@@ -215,71 +387,244 @@ const css = `
215
387
 
216
388
  /* Loader Styles */
217
389
  .loader {
218
- display: none;
219
- border: 4px solid #f3f3f3;
220
- border-top: 4px solid #3498db;
221
- border-radius: 50%;
222
- width: 40px;
223
- height: 40px;
224
- animation: spin 2s linear infinite;
225
- margin: 20px auto;
390
+ display: none;
391
+ width: 40px;
392
+ height: 40px;
393
+ margin: 20px auto;
394
+ background: conic-gradient(from 0deg, #7B2BEB, #0044FF, #7B2BEB);
395
+ border-radius: 50%;
396
+ animation: spin 1s linear infinite;
397
+ position: relative;
398
+ }
399
+
400
+ .loader::after {
401
+ content: '';
402
+ position: absolute;
403
+ top: 4px;
404
+ left: 4px;
405
+ right: 4px;
406
+ bottom: 4px;
407
+ background: white;
408
+ border-radius: 50%;
226
409
  }
227
410
 
228
411
  .loader.visible {
229
- display: block;
412
+ display: block;
230
413
  }
231
414
 
232
415
  @keyframes spin {
233
- 0% { transform: rotate(0deg); }
234
- 100% { transform: rotate(360deg); }
416
+ from { transform: rotate(0deg); }
417
+ to { transform: rotate(360deg); }
235
418
  }
236
419
 
237
420
  /* Typing indicator styles */
238
421
  .typing-indicator {
239
- display: flex;
240
- justify-content: center;
241
- align-items: center;
242
- height: 0;
243
- visibility: hidden;
244
- background-color: #f7f7f7;
422
+ display: flex;
423
+ justify-content: center;
424
+ align-items: center;
425
+ height: 0;
426
+ visibility: hidden;
427
+ background-color: white;
245
428
  }
246
429
 
247
430
  .typing-indicator span {
248
- display: block;
249
- width: 8px;
250
- height: 8px;
251
- margin: 0 4px;
252
- background-color: #3498db;
253
- border-radius: 50%;
254
- opacity: 0.4;
255
- animation: typing 1.5s infinite;
431
+ display: block;
432
+ width: 8px;
433
+ height: 8px;
434
+ margin: 0 3px;
435
+ border-radius: 50%;
436
+ background: linear-gradient(90deg, #7B2BEB, #0044FF);
437
+ opacity: 0.7;
438
+ animation: typing 1s infinite;
256
439
  }
257
440
 
258
441
  @keyframes typing {
259
- 0% { transform: translateY(0); }
260
- 20% { transform: translateY(-10px); }
261
- 40% { transform: translateY(0); }
262
- 60% { transform: translateY(-10px); }
263
- 80% { transform: translateY(0); }
264
- 100% { transform: translateY(0); }
442
+ 0%, 100% {
443
+ transform: translateY(0);
444
+ opacity: 0.7;
445
+ }
446
+ 50% {
447
+ transform: translateY(-8px);
448
+ opacity: 1;
449
+ }
265
450
  }
266
451
 
267
452
  /* Staggered animation for typing dots */
268
453
  .typing-indicator span:nth-child(1) {
269
- animation-delay: 0s;
454
+ animation-delay: 0s;
270
455
  }
271
456
  .typing-indicator span:nth-child(2) {
272
- animation-delay: 0.2s;
457
+ animation-delay: 0.15s;
273
458
  }
274
459
  .typing-indicator span:nth-child(3) {
275
- animation-delay: 0.4s;
460
+ animation-delay: 0.3s;
276
461
  }
277
462
 
278
463
  /* Show the typing indicator */
279
464
  .typing-indicator.visible {
280
- visibility: visible;
281
- height: 50px;
282
- }`;
465
+ visibility: visible;
466
+ height: 40px;
467
+ }
468
+
469
+ @media screen and (max-width: 768px) {
470
+ #chat-window {
471
+ width: 100%;
472
+ height: 100%;
473
+ bottom: 0;
474
+ right: 0;
475
+ border-radius: 0;
476
+ position: fixed;
477
+ }
478
+
479
+ #message-container {
480
+ height: calc(100vh - 180px); /* Учитываем высоту header и input container */
481
+ max-height: none;
482
+ }
483
+
484
+ #chat-icon {
485
+ height: 45px;
486
+ padding: 8px 16px;
487
+ font-size: 14px;
488
+ }
489
+
490
+ #chat-icon {
491
+ height: 36px; /* Ещё меньше для мобильных */
492
+ padding: 6px 12px;
493
+ font-size: 13px;
494
+ }
495
+
496
+ #chat-icon img {
497
+ width: 20px;
498
+ }
499
+
500
+ #input-container {
501
+ padding: 12px;
502
+ position: fixed;
503
+ bottom: 30px;
504
+ width: 100%;
505
+ box-sizing: border-box;
506
+
507
+ }
508
+
509
+ #user-input {
510
+ font-size: 16px; /* Оптимальный размер для мобильных устройств */
511
+ padding: 10px 14px;
512
+ }
513
+
514
+ #send-button {
515
+ padding: 10px 20px;
516
+ }
517
+
518
+ .wine-item {
519
+ flex-direction: column;
520
+ padding: 12px;
521
+ }
522
+
523
+ .wine-item img {
524
+ width: 100%;
525
+ height: 200px;
526
+ margin-bottom: 12px;
527
+ }
528
+ }
529
+
530
+ @media screen and (max-width: 480px) {
531
+ #chat-header {
532
+ padding: 16px;
533
+ }
534
+
535
+ #chat-icon {
536
+ padding: 3px 10px;
537
+ font-size: 12px;
538
+ border-radius: 10px;
539
+ border-top-right-radius: 0;
540
+ border-top-left-radius: 0;
541
+ right: -77px;
542
+ }
543
+
544
+ #chat-icon img {
545
+ width: 18px;
546
+ }
547
+
548
+ #chat-title h1 {
549
+ font-size: 18px;
550
+ }
551
+
552
+ #chat-title p {
553
+ font-size: 12px;
554
+ }
555
+
556
+ .message {
557
+ padding: 12px 16px;
558
+ font-size: 14px;
559
+ max-width: 95%;
560
+ }
561
+
562
+ .wine-recommendation {
563
+ margin: 10px 0;
564
+ }
565
+
566
+ .wine-description {
567
+ padding: 10px 14px;
568
+ font-size: 13px;
569
+ }
570
+
571
+ #powered-by {
572
+ padding: 8px 16px;
573
+ display: block;
574
+ position: fixed;
575
+ width: 100%;
576
+ bottom: 0;
577
+ text-align: center;
578
+ box-sizing: border-box;
579
+ }
580
+
581
+ #powered-by a {
582
+ width: 100%;
583
+ justify-content: center;
584
+ }
585
+
586
+ #powered-by span {
587
+ font-size: 11px;
588
+ }
589
+
590
+ #chatbot-logo {
591
+ height: 14px;
592
+ }
593
+ }
594
+
595
+ /* Стили для планшетов */
596
+ @media screen and (min-width: 769px) and (max-width: 1024px) {
597
+ #chat-window {
598
+ width: 400px;
599
+ right: 30px;
600
+ bottom: 30px;
601
+ }
602
+
603
+ #message-container {
604
+ max-height: 450px;
605
+ }
606
+ }
607
+
608
+ /* Стили для ландшафтной ориентации на мобильных */
609
+ @media screen and (max-height: 600px) and (orientation: landscape) {
610
+ #chat-window {
611
+ height: 100vh;
612
+ }
613
+
614
+ #message-container {
615
+ height: calc(100vh - 140px);
616
+ }
617
+
618
+ .wine-item {
619
+ flex-direction: row;
620
+ }
621
+
622
+ .wine-item img {
623
+ width: 80px;
624
+ height: 80px;
625
+ }
626
+ }
627
+ `;
283
628
 
284
629
  const style = document.createElement('style');
285
630
  style.textContent = css;