@developpement/tp-chatbot-widget 0.0.9 → 0.0.11
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/package.json +1 -1
- package/src/chatbot.css +254 -39
- package/src/chatbot.js +416 -297
package/src/chatbot.js
CHANGED
|
@@ -1,9 +1,42 @@
|
|
|
1
1
|
(function () {
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
+
// ─── SVG Icons ──────────────────────────────────────────────────────────────
|
|
5
|
+
// viewBox 0 0 20 20 | stroke-width 1.6 | round caps & joins | fill none | stroke currentColor
|
|
6
|
+
|
|
7
|
+
const ICONS = {
|
|
8
|
+
bell: `<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M10 2a6 6 0 0 0-6 6c0 3.5-1.5 5-1.5 5h15s-1.5-1.5-1.5-5a6 6 0 0 0-6-6Z"/><path d="M11.73 17a2 2 0 0 1-3.46 0"/></svg>`,
|
|
9
|
+
bell_off: `<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M4 4 16 16M8.27 3.05A6 6 0 0 1 16 8c0 2.2.5 3.7 1 4.7M6.15 6.17A5.97 5.97 0 0 0 4 8c0 3.5-1.5 5-1.5 5H14M11.73 17a2 2 0 0 1-3.46 0"/></svg>`,
|
|
10
|
+
minimize: `<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><line x1="4" y1="10" x2="16" y2="10"/></svg>`,
|
|
11
|
+
restore: `<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><rect x="7" y="3" width="10" height="10" rx="1.5"/><path d="M3 7v10h10"/></svg>`,
|
|
12
|
+
back: `<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M13 16 7 10l6-6"/></svg>`,
|
|
13
|
+
close: `<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M5 5l10 10M15 5 5 15"/></svg>`,
|
|
14
|
+
user: `<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><circle cx="10" cy="7" r="3.5"/><path d="M3 17c0-3.3 3.1-6 7-6s7 2.7 7 6"/></svg>`,
|
|
15
|
+
agent: `<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M3 10a7 7 0 1 1 14 0"/><path d="M2 10h2a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1v-2a1 1 0 0 1 1-1Z"/><path d="M16 10h1a1 1 0 0 1 1 1v2a1 1 0 0 1-1 1h-1a1 1 0 0 1-1-1v-2a1 1 0 0 1 1-1Z"/><path d="M16 13v1a3 3 0 0 1-3 3h-2"/></svg>`,
|
|
16
|
+
bot: `<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="7" width="14" height="10" rx="2"/><path d="M7 11h.01M13 11h.01M7 14.5h6"/><circle cx="10" cy="4" r="1.5"/><line x1="10" y1="5.5" x2="10" y2="7"/></svg>`,
|
|
17
|
+
send: `<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M17 3 3 9l5 2 2 5 7-13Z"/><path d="m8 11 4-4"/></svg>`,
|
|
18
|
+
attach: `<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M16.5 10.5 9 18a5 5 0 0 1-7-7l8.5-8.5a3 3 0 0 1 4.24 4.24L6 15.5a1 1 0 0 1-1.42-1.42L12 7"/></svg>`,
|
|
19
|
+
helpful: `<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M7 10V17M7 10l3-7a3 3 0 0 1 3 3v2h3.5a1 1 0 0 1 1 1.15l-.9 5A1 1 0 0 1 15.6 17H7"/><rect x="3" y="10" width="4" height="7" rx="1"/></svg>`,
|
|
20
|
+
not_helpful: `<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M13 10V3M13 10l-3 7a3 3 0 0 1-3-3v-2H3.5a1 1 0 0 1-1-1.15l.9-5A1 1 0 0 1 4.4 3H13"/><rect x="13" y="3" width="4" height="7" rx="1"/></svg>`,
|
|
21
|
+
check: `<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M4 10l5 5 7-8"/></svg>`,
|
|
22
|
+
msg_plus: `<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M14 3H6a2 2 0 0 0-2 2v7a2 2 0 0 0 2 2h2l2 3 2-3h2a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2Z"/><path d="M10 7v5M7.5 9.5h5"/></svg>`,
|
|
23
|
+
msg: `<svg viewBox="0 0 20 20" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M14 3H6a2 2 0 0 0-2 2v7a2 2 0 0 0 2 2h2l2 3 2-3h2a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2Z"/></svg>`,
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
function icon(name, size = 18, extra_style = '') {
|
|
27
|
+
const svg = ICONS[name];
|
|
28
|
+
if (!svg) return '';
|
|
29
|
+
return svg.replace(
|
|
30
|
+
'<svg ',
|
|
31
|
+
`<svg width="${size}" height="${size}" style="display:inline-block;vertical-align:middle;flex-shrink:0;${extra_style}" `
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// ─── Clients ─────────────────────────────────────────────────────────────────
|
|
36
|
+
|
|
4
37
|
const CLIENT_THEMES = {
|
|
5
38
|
flix: {
|
|
6
|
-
primary: '#
|
|
39
|
+
primary: '#97d700',
|
|
7
40
|
name: 'Flix Corporate',
|
|
8
41
|
position: 'left',
|
|
9
42
|
suggestions: [
|
|
@@ -83,7 +116,6 @@
|
|
|
83
116
|
};
|
|
84
117
|
|
|
85
118
|
const DEFAULT_THEME = { primary: '#7b1fa2', name: 'Support', position: 'right', suggestions: [] };
|
|
86
|
-
|
|
87
119
|
const WINDOW_WIDTH = '380px';
|
|
88
120
|
const WINDOW_HEIGHT = '580px';
|
|
89
121
|
|
|
@@ -111,19 +143,21 @@
|
|
|
111
143
|
function playSound() {
|
|
112
144
|
try {
|
|
113
145
|
const ctx = new (window.AudioContext || window.webkitAudioContext)();
|
|
114
|
-
const
|
|
146
|
+
const osc = ctx.createOscillator();
|
|
115
147
|
const gain = ctx.createGain();
|
|
116
|
-
|
|
148
|
+
osc.connect(gain);
|
|
117
149
|
gain.connect(ctx.destination);
|
|
118
|
-
|
|
119
|
-
|
|
150
|
+
osc.frequency.value = 880;
|
|
151
|
+
osc.type = 'sine';
|
|
120
152
|
gain.gain.setValueAtTime(0.2, ctx.currentTime);
|
|
121
153
|
gain.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + 0.4);
|
|
122
|
-
|
|
123
|
-
|
|
154
|
+
osc.start(ctx.currentTime);
|
|
155
|
+
osc.stop(ctx.currentTime + 0.4);
|
|
124
156
|
} catch {}
|
|
125
157
|
}
|
|
126
158
|
|
|
159
|
+
// ─── Component ───────────────────────────────────────────────────────────────
|
|
160
|
+
|
|
127
161
|
class TpChatbot extends HTMLElement {
|
|
128
162
|
constructor() {
|
|
129
163
|
super();
|
|
@@ -169,6 +203,32 @@
|
|
|
169
203
|
}
|
|
170
204
|
}
|
|
171
205
|
|
|
206
|
+
async fetchTheme() {
|
|
207
|
+
try {
|
|
208
|
+
const response = await fetch(`${this.api_url}/chat/config/theme/${this.client_id}`);
|
|
209
|
+
if (!response.ok) return;
|
|
210
|
+
const data = await response.json();
|
|
211
|
+
const t = data.result?.result;
|
|
212
|
+
console.log('[fetchTheme] open_by_default:', t.open_by_default, typeof t.open_by_default);
|
|
213
|
+
if (!t) return;
|
|
214
|
+
if (CLIENT_THEMES[this.client_id]) {
|
|
215
|
+
if (t.primary) CLIENT_THEMES[this.client_id].primary = t.primary;
|
|
216
|
+
if (t.position) CLIENT_THEMES[this.client_id].position = t.position;
|
|
217
|
+
if (t.name) CLIENT_THEMES[this.client_id].name = t.name;
|
|
218
|
+
if (t.open_by_default !== undefined) CLIENT_THEMES[this.client_id].open_by_default = t.open_by_default;
|
|
219
|
+
} else {
|
|
220
|
+
CLIENT_THEMES[this.client_id] = {
|
|
221
|
+
primary: t.primary || DEFAULT_THEME.primary,
|
|
222
|
+
position: t.position || DEFAULT_THEME.position,
|
|
223
|
+
name: t.name || DEFAULT_THEME.name,
|
|
224
|
+
suggestions: [],
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
} catch (e) {
|
|
228
|
+
console.warn('[tp-chatbot] fetchTheme failed, using defaults:', e.message);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
172
232
|
connectedCallback() {
|
|
173
233
|
this.client_id = this.getAttribute('client-id') || 'flix';
|
|
174
234
|
this.access_token = this.getAttribute('access-token') || '';
|
|
@@ -183,224 +243,269 @@
|
|
|
183
243
|
this.is_fullscreen = false;
|
|
184
244
|
this.is_minimized = false;
|
|
185
245
|
|
|
186
|
-
this.
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
246
|
+
this.fetchTheme().then(() => {
|
|
247
|
+
this.render();
|
|
248
|
+
this.applyTheme();
|
|
249
|
+
this.attachEvents();
|
|
250
|
+
this._initialized = true;
|
|
190
251
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
this.
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
252
|
+
const theme = getClientTheme(this.client_id);
|
|
253
|
+
console.log('[widget] theme after fetch:', theme);
|
|
254
|
+
|
|
255
|
+
if (theme.open_by_default) {
|
|
256
|
+
setTimeout(() => this.toggleChat(), 300);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (this.access_token) {
|
|
260
|
+
this.user_info = extractUserInfo(this.access_token);
|
|
261
|
+
this.user_id = this.user_info?.user_id;
|
|
262
|
+
this.showConversationList();
|
|
263
|
+
} else {
|
|
264
|
+
setTimeout(() => {
|
|
265
|
+
const token = this.getAttribute('access-token');
|
|
266
|
+
if (token) {
|
|
267
|
+
this.access_token = token;
|
|
268
|
+
this.user_info = extractUserInfo(token);
|
|
269
|
+
this.user_id = this.user_info?.user_id;
|
|
270
|
+
this.showConversationList();
|
|
271
|
+
} else {
|
|
272
|
+
this.showGuestForm();
|
|
273
|
+
}
|
|
274
|
+
}, 500);
|
|
275
|
+
}
|
|
276
|
+
});
|
|
208
277
|
}
|
|
209
278
|
|
|
210
279
|
disconnectedCallback() {
|
|
211
280
|
if (this.polling_interval) clearInterval(this.polling_interval);
|
|
212
281
|
}
|
|
213
282
|
|
|
214
|
-
// ─── i18n
|
|
283
|
+
// ─── i18n ──────────────────────────────────────────────────────────────────
|
|
215
284
|
|
|
216
285
|
getMessages() {
|
|
217
286
|
const lang = (this.user_info?.language || 'EN').toUpperCase();
|
|
218
287
|
const msgs = {
|
|
219
288
|
FR: {
|
|
220
|
-
closed: '
|
|
289
|
+
closed: 'Conversation clôturée',
|
|
221
290
|
csat_question: 'Cette conversation vous a-t-elle été utile ?',
|
|
222
|
-
csat_positive: '
|
|
223
|
-
csat_negative: '
|
|
224
|
-
new_conv: '
|
|
225
|
-
back_to_list: '
|
|
291
|
+
csat_positive: 'Merci pour votre retour !',
|
|
292
|
+
csat_negative: 'Merci, nous allons nous améliorer.',
|
|
293
|
+
new_conv: 'Nouvelle conversation',
|
|
294
|
+
back_to_list: 'Voir toutes les conversations',
|
|
226
295
|
agent_taken: 'Un agent a pris en charge votre conversation. Vous pouvez lui écrire directement.',
|
|
227
296
|
agent_released: 'Notre assistant virtuel reprend la conversation. Comment puis-je vous aider ?',
|
|
228
297
|
close_confirm: 'Voulez-vous terminer cette conversation ?',
|
|
229
|
-
terminated: '
|
|
230
|
-
virtual: '
|
|
231
|
-
human_agent: '
|
|
298
|
+
terminated: 'Conversation terminée',
|
|
299
|
+
virtual: 'Assistant virtuel',
|
|
300
|
+
human_agent: 'Agent humain',
|
|
232
301
|
loading: 'Chargement...',
|
|
233
302
|
no_conv: 'Aucune conversation pour le moment.',
|
|
234
|
-
new_conv_btn: '
|
|
303
|
+
new_conv_btn: 'Nouvelle conversation',
|
|
235
304
|
error_load: 'Erreur de chargement.',
|
|
236
305
|
recent_conv: 'Vos conversations récentes',
|
|
237
306
|
hello: 'Bonjour',
|
|
238
|
-
close_btn: '
|
|
239
|
-
speak_agent: '
|
|
307
|
+
close_btn: 'Terminer la conversation',
|
|
308
|
+
speak_agent: 'Parler à un agent',
|
|
240
309
|
write_msg: 'Écrivez votre message...',
|
|
241
310
|
limit_reached: 'Vous avez atteint la limite de 30 messages. Veuillez contacter un agent ou écrire à support@travelplanet.com.',
|
|
242
311
|
too_many: 'Trop de messages. Veuillez patienter avant de réessayer.',
|
|
243
312
|
error_occurred: 'Une erreur est survenue. Veuillez réessayer.',
|
|
244
|
-
outside_hours: next =>
|
|
245
|
-
no_agents: '
|
|
246
|
-
waiting_agent: '
|
|
247
|
-
waiting_agent_already: "
|
|
248
|
-
agent_already: '
|
|
313
|
+
outside_hours: next => `Notre support est actuellement fermé. Prochain créneau : ${next}.`,
|
|
314
|
+
no_agents: 'Aucun agent disponible pour le moment. Veuillez réessayer plus tard.',
|
|
315
|
+
waiting_agent: 'Votre demande a été transmise. Un agent va prendre en charge votre conversation.',
|
|
316
|
+
waiting_agent_already: "Vous êtes déjà en file d'attente. Merci de patienter.",
|
|
317
|
+
agent_already: 'Un agent gère déjà votre conversation.',
|
|
318
|
+
suggest_agent_escalation: "Je n'arrive pas à trouver une réponse adaptée. Souhaitez-vous parler à un agent ?",
|
|
319
|
+
status_closed: 'Fermé',
|
|
320
|
+
status_agent: 'Agent',
|
|
321
|
+
status_waiting: 'En attente',
|
|
322
|
+
status_bot: 'Bot',
|
|
323
|
+
messages: 'message',
|
|
249
324
|
},
|
|
250
325
|
EN: {
|
|
251
|
-
closed: '
|
|
326
|
+
closed: 'Conversation closed',
|
|
252
327
|
csat_question: 'Was this conversation helpful?',
|
|
253
|
-
csat_positive: '
|
|
254
|
-
csat_negative: '
|
|
255
|
-
new_conv: '
|
|
256
|
-
back_to_list: '
|
|
328
|
+
csat_positive: 'Thank you for your feedback!',
|
|
329
|
+
csat_negative: 'Thank you, we will improve.',
|
|
330
|
+
new_conv: 'New conversation',
|
|
331
|
+
back_to_list: 'Back to conversations',
|
|
257
332
|
agent_taken: 'An agent has taken over your conversation. You can now write to them directly.',
|
|
258
333
|
agent_released: 'Our virtual assistant is back. How can I help you?',
|
|
259
334
|
close_confirm: 'Do you want to end this conversation?',
|
|
260
|
-
terminated: '
|
|
261
|
-
virtual: '
|
|
262
|
-
human_agent: '
|
|
335
|
+
terminated: 'Conversation ended',
|
|
336
|
+
virtual: 'Virtual assistant',
|
|
337
|
+
human_agent: 'Human agent',
|
|
263
338
|
loading: 'Loading...',
|
|
264
339
|
no_conv: 'No conversations yet.',
|
|
265
|
-
new_conv_btn: '
|
|
340
|
+
new_conv_btn: 'New conversation',
|
|
266
341
|
error_load: 'Loading error.',
|
|
267
342
|
recent_conv: 'Your recent conversations',
|
|
268
343
|
hello: 'Hello',
|
|
269
|
-
close_btn: '
|
|
270
|
-
speak_agent: '
|
|
344
|
+
close_btn: 'End conversation',
|
|
345
|
+
speak_agent: 'Talk to an agent',
|
|
271
346
|
write_msg: 'Write your message...',
|
|
272
347
|
limit_reached: 'You have reached the 30 message limit. Please contact an agent or write to support@travelplanet.com.',
|
|
273
348
|
too_many: 'Too many messages. Please wait before retrying.',
|
|
274
349
|
error_occurred: 'An error occurred. Please try again.',
|
|
275
|
-
outside_hours: next =>
|
|
276
|
-
no_agents: '
|
|
277
|
-
waiting_agent: '
|
|
278
|
-
waiting_agent_already: '
|
|
279
|
-
agent_already: '
|
|
350
|
+
outside_hours: next => `Our support is currently closed. Next available slot: ${next}.`,
|
|
351
|
+
no_agents: 'No agent available at the moment. Please try again later.',
|
|
352
|
+
waiting_agent: 'Your request has been sent. An agent will take over shortly.',
|
|
353
|
+
waiting_agent_already: 'You are already in the queue. Please wait.',
|
|
354
|
+
agent_already: 'An agent is already handling your conversation.',
|
|
355
|
+
suggest_agent_escalation: "I'm having trouble finding a suitable answer. Would you like to speak to an agent?",
|
|
356
|
+
status_closed: 'Closed',
|
|
357
|
+
status_agent: 'Agent',
|
|
358
|
+
status_waiting: 'Waiting',
|
|
359
|
+
status_bot: 'Bot',
|
|
360
|
+
messages: 'message',
|
|
280
361
|
},
|
|
281
362
|
DE: {
|
|
282
|
-
closed: '
|
|
363
|
+
closed: 'Gespräch beendet',
|
|
283
364
|
csat_question: 'War dieses Gespräch hilfreich?',
|
|
284
|
-
csat_positive: '
|
|
285
|
-
csat_negative: '
|
|
286
|
-
new_conv: '
|
|
287
|
-
back_to_list: '
|
|
365
|
+
csat_positive: 'Vielen Dank für Ihr Feedback!',
|
|
366
|
+
csat_negative: 'Danke, wir werden uns verbessern.',
|
|
367
|
+
new_conv: 'Neues Gespräch',
|
|
368
|
+
back_to_list: 'Zurück zur Liste',
|
|
288
369
|
agent_taken: 'Ein Agent hat Ihr Gespräch übernommen. Sie können ihm jetzt direkt schreiben.',
|
|
289
370
|
agent_released: 'Unser virtueller Assistent ist zurück. Wie kann ich Ihnen helfen?',
|
|
290
371
|
close_confirm: 'Möchten Sie das Gespräch beenden?',
|
|
291
|
-
terminated: '
|
|
292
|
-
virtual: '
|
|
293
|
-
human_agent: '
|
|
372
|
+
terminated: 'Gespräch beendet',
|
|
373
|
+
virtual: 'Virtueller Assistent',
|
|
374
|
+
human_agent: 'Menschlicher Agent',
|
|
294
375
|
loading: 'Laden...',
|
|
295
376
|
no_conv: 'Noch keine Gespräche.',
|
|
296
|
-
new_conv_btn: '
|
|
377
|
+
new_conv_btn: 'Neues Gespräch',
|
|
297
378
|
error_load: 'Ladefehler.',
|
|
298
379
|
recent_conv: 'Ihre letzten Gespräche',
|
|
299
380
|
hello: 'Hallo',
|
|
300
|
-
close_btn: '
|
|
301
|
-
speak_agent: '
|
|
381
|
+
close_btn: 'Gespräch beenden',
|
|
382
|
+
speak_agent: 'Mit einem Agenten sprechen',
|
|
302
383
|
write_msg: 'Schreiben Sie Ihre Nachricht...',
|
|
303
384
|
limit_reached: 'Sie haben das Limit von 30 Nachrichten erreicht.',
|
|
304
385
|
too_many: 'Zu viele Nachrichten. Bitte warten Sie.',
|
|
305
386
|
error_occurred: 'Ein Fehler ist aufgetreten. Bitte versuchen Sie es erneut.',
|
|
306
|
-
outside_hours: next =>
|
|
307
|
-
no_agents: '
|
|
308
|
-
waiting_agent: '
|
|
309
|
-
waiting_agent_already: '
|
|
310
|
-
agent_already: '
|
|
387
|
+
outside_hours: next => `Unser Support ist derzeit geschlossen. Nächster Termin: ${next}.`,
|
|
388
|
+
no_agents: 'Derzeit kein Agent verfügbar. Bitte versuchen Sie es später.',
|
|
389
|
+
waiting_agent: 'Ihre Anfrage wurde übermittelt. Ein Agent übernimmt gleich.',
|
|
390
|
+
waiting_agent_already: 'Sie sind bereits in der Warteschlange. Bitte warten.',
|
|
391
|
+
agent_already: 'Ein Agent betreut bereits Ihr Gespräch.',
|
|
392
|
+
suggest_agent_escalation: 'Ich kann keine passende Antwort finden. Möchten Sie mit einem Agenten sprechen?',
|
|
393
|
+
status_closed: 'Geschlossen',
|
|
394
|
+
status_agent: 'Agent',
|
|
395
|
+
status_waiting: 'Wartend',
|
|
396
|
+
status_bot: 'Bot',
|
|
397
|
+
messages: 'Nachricht',
|
|
311
398
|
},
|
|
312
399
|
ES: {
|
|
313
|
-
closed: '
|
|
400
|
+
closed: 'Conversación cerrada',
|
|
314
401
|
csat_question: '¿Fue útil esta conversación?',
|
|
315
|
-
csat_positive: '
|
|
316
|
-
csat_negative: '
|
|
317
|
-
new_conv: '
|
|
318
|
-
back_to_list: '
|
|
402
|
+
csat_positive: '¡Gracias por su opinión!',
|
|
403
|
+
csat_negative: 'Gracias, mejoraremos.',
|
|
404
|
+
new_conv: 'Nueva conversación',
|
|
405
|
+
back_to_list: 'Volver a las conversaciones',
|
|
319
406
|
agent_taken: 'Un agente ha tomado su conversación. Ahora puede escribirle directamente.',
|
|
320
407
|
agent_released: 'Nuestro asistente virtual ha vuelto. ¿En qué puedo ayudarle?',
|
|
321
408
|
close_confirm: '¿Desea finalizar esta conversación?',
|
|
322
|
-
terminated: '
|
|
323
|
-
virtual: '
|
|
324
|
-
human_agent: '
|
|
409
|
+
terminated: 'Conversación terminada',
|
|
410
|
+
virtual: 'Asistente virtual',
|
|
411
|
+
human_agent: 'Agente humano',
|
|
325
412
|
loading: 'Cargando...',
|
|
326
413
|
no_conv: 'No hay conversaciones por el momento.',
|
|
327
|
-
new_conv_btn: '
|
|
414
|
+
new_conv_btn: 'Nueva conversación',
|
|
328
415
|
error_load: 'Error de carga.',
|
|
329
416
|
recent_conv: 'Sus conversaciones recientes',
|
|
330
417
|
hello: 'Hola',
|
|
331
|
-
close_btn: '
|
|
332
|
-
speak_agent: '
|
|
418
|
+
close_btn: 'Finalizar conversación',
|
|
419
|
+
speak_agent: 'Hablar con un agente',
|
|
333
420
|
write_msg: 'Escriba su mensaje...',
|
|
334
421
|
limit_reached: 'Ha alcanzado el límite de 30 mensajes.',
|
|
335
422
|
too_many: 'Demasiados mensajes. Por favor espere.',
|
|
336
423
|
error_occurred: 'Se produjo un error. Por favor, inténtelo de nuevo.',
|
|
337
|
-
outside_hours: next =>
|
|
338
|
-
no_agents: '
|
|
339
|
-
waiting_agent: '
|
|
340
|
-
waiting_agent_already: '
|
|
341
|
-
agent_already: '
|
|
424
|
+
outside_hours: next => `Nuestro soporte está cerrado. Próximo horario: ${next}.`,
|
|
425
|
+
no_agents: 'No hay agentes disponibles. Inténtelo más tarde.',
|
|
426
|
+
waiting_agent: 'Su solicitud ha sido enviada. Un agente le atenderá pronto.',
|
|
427
|
+
waiting_agent_already: 'Ya está en la cola de espera. Por favor, espere.',
|
|
428
|
+
agent_already: 'Un agente ya está gestionando su conversación.',
|
|
429
|
+
suggest_agent_escalation: 'No encuentro una respuesta adecuada. ¿Desea hablar con un agente?',
|
|
430
|
+
status_closed: 'Cerrado',
|
|
431
|
+
status_agent: 'Agente',
|
|
432
|
+
status_waiting: 'Esperando',
|
|
433
|
+
status_bot: 'Bot',
|
|
434
|
+
messages: 'mensaje',
|
|
342
435
|
},
|
|
343
436
|
IT: {
|
|
344
|
-
closed: '
|
|
437
|
+
closed: 'Conversazione chiusa',
|
|
345
438
|
csat_question: 'Questa conversazione è stata utile?',
|
|
346
|
-
csat_positive: '
|
|
347
|
-
csat_negative: '
|
|
348
|
-
new_conv: '
|
|
349
|
-
back_to_list: '
|
|
439
|
+
csat_positive: 'Grazie per il suo feedback!',
|
|
440
|
+
csat_negative: 'Grazie, miglioreremo.',
|
|
441
|
+
new_conv: 'Nuova conversazione',
|
|
442
|
+
back_to_list: 'Torna alle conversazioni',
|
|
350
443
|
agent_taken: 'Un agente ha preso in carico la sua conversazione.',
|
|
351
444
|
agent_released: 'Il nostro assistente virtuale è tornato. Come posso aiutarla?',
|
|
352
445
|
close_confirm: 'Vuole terminare questa conversazione?',
|
|
353
|
-
terminated: '
|
|
354
|
-
virtual: '
|
|
355
|
-
human_agent: '
|
|
446
|
+
terminated: 'Conversazione terminata',
|
|
447
|
+
virtual: 'Assistente virtuale',
|
|
448
|
+
human_agent: 'Agente umano',
|
|
356
449
|
loading: 'Caricamento...',
|
|
357
450
|
no_conv: 'Nessuna conversazione per il momento.',
|
|
358
|
-
new_conv_btn: '
|
|
451
|
+
new_conv_btn: 'Nuova conversazione',
|
|
359
452
|
error_load: 'Errore di caricamento.',
|
|
360
453
|
recent_conv: 'Le sue conversazioni recenti',
|
|
361
454
|
hello: 'Ciao',
|
|
362
|
-
close_btn: '
|
|
363
|
-
speak_agent: '
|
|
455
|
+
close_btn: 'Termina conversazione',
|
|
456
|
+
speak_agent: 'Parla con un agente',
|
|
364
457
|
write_msg: 'Scrivi il tuo messaggio...',
|
|
365
458
|
limit_reached: 'Hai raggiunto il limite di 30 messaggi.',
|
|
366
459
|
too_many: 'Troppi messaggi. Attendere prego.',
|
|
367
460
|
error_occurred: 'Si è verificato un errore. Riprova.',
|
|
368
|
-
outside_hours: next =>
|
|
369
|
-
no_agents: '
|
|
370
|
-
waiting_agent: '
|
|
371
|
-
waiting_agent_already: '
|
|
372
|
-
agent_already: '
|
|
461
|
+
outside_hours: next => `Il supporto è chiuso. Prossima disponibilità: ${next}.`,
|
|
462
|
+
no_agents: 'Nessun agente disponibile. Riprova più tardi.',
|
|
463
|
+
waiting_agent: 'Richiesta inviata. Un agente prenderà in carico la conversazione.',
|
|
464
|
+
waiting_agent_already: 'È già in coda. Attenda.',
|
|
465
|
+
agent_already: 'Un agente sta già gestendo la conversazione.',
|
|
466
|
+
suggest_agent_escalation: 'Non riesco a trovare una risposta adeguata. Desidera parlare con un agente?',
|
|
467
|
+
status_closed: 'Chiuso',
|
|
468
|
+
status_agent: 'Agente',
|
|
469
|
+
status_waiting: 'Attesa',
|
|
470
|
+
status_bot: 'Bot',
|
|
471
|
+
messages: 'messaggio',
|
|
373
472
|
},
|
|
374
473
|
NL: {
|
|
375
|
-
closed: '
|
|
474
|
+
closed: 'Gesprek gesloten',
|
|
376
475
|
csat_question: 'Was dit gesprek nuttig?',
|
|
377
|
-
csat_positive: '
|
|
378
|
-
csat_negative: '
|
|
379
|
-
new_conv: '
|
|
380
|
-
back_to_list: '
|
|
476
|
+
csat_positive: 'Bedankt voor uw feedback!',
|
|
477
|
+
csat_negative: 'Bedankt, we zullen verbeteren.',
|
|
478
|
+
new_conv: 'Nieuw gesprek',
|
|
479
|
+
back_to_list: 'Terug naar gesprekken',
|
|
381
480
|
agent_taken: 'Een agent heeft uw gesprek overgenomen.',
|
|
382
481
|
agent_released: 'Onze virtuele assistent is terug. Hoe kan ik u helpen?',
|
|
383
482
|
close_confirm: 'Wilt u dit gesprek beëindigen?',
|
|
384
|
-
terminated: '
|
|
385
|
-
virtual: '
|
|
386
|
-
human_agent: '
|
|
483
|
+
terminated: 'Gesprek beëindigd',
|
|
484
|
+
virtual: 'Virtuele assistent',
|
|
485
|
+
human_agent: 'Menselijke agent',
|
|
387
486
|
loading: 'Laden...',
|
|
388
487
|
no_conv: 'Geen gesprekken op dit moment.',
|
|
389
|
-
new_conv_btn: '
|
|
390
|
-
error_load: '
|
|
488
|
+
new_conv_btn: 'Nieuw gesprek',
|
|
489
|
+
error_load: 'Laadout.',
|
|
391
490
|
recent_conv: 'Uw recente gesprekken',
|
|
392
491
|
hello: 'Hallo',
|
|
393
|
-
close_btn: '
|
|
394
|
-
speak_agent: '
|
|
492
|
+
close_btn: 'Gesprek beëindigen',
|
|
493
|
+
speak_agent: 'Praat met een agent',
|
|
395
494
|
write_msg: 'Schrijf uw bericht...',
|
|
396
495
|
limit_reached: 'U heeft de limiet van 30 berichten bereikt.',
|
|
397
496
|
too_many: 'Te veel berichten. Wacht even.',
|
|
398
497
|
error_occurred: 'Er is een fout opgetreden. Probeer het opnieuw.',
|
|
399
|
-
outside_hours: next =>
|
|
400
|
-
no_agents: '
|
|
401
|
-
waiting_agent: '
|
|
402
|
-
waiting_agent_already: '
|
|
403
|
-
agent_already: '
|
|
498
|
+
outside_hours: next => `Onze support is gesloten. Volgende beschikbare tijd: ${next}.`,
|
|
499
|
+
no_agents: 'Geen agent beschikbaar. Probeer het later opnieuw.',
|
|
500
|
+
waiting_agent: 'Uw verzoek is verzonden. Een agent neemt het gesprek over.',
|
|
501
|
+
waiting_agent_already: 'U staat al in de wachtrij. Even geduld.',
|
|
502
|
+
agent_already: 'Een agent beheert uw gesprek al.',
|
|
503
|
+
suggest_agent_escalation: 'Ik kan geen passend antwoord vinden. Wilt u met een agent spreken?',
|
|
504
|
+
status_closed: 'Gesloten',
|
|
505
|
+
status_agent: 'Agent',
|
|
506
|
+
status_waiting: 'Wachten',
|
|
507
|
+
status_bot: 'Bot',
|
|
508
|
+
messages: 'bericht',
|
|
404
509
|
},
|
|
405
510
|
};
|
|
406
511
|
return msgs[lang] || msgs['EN'];
|
|
@@ -410,17 +515,17 @@
|
|
|
410
515
|
const lang = (this.user_info?.language || 'EN').toUpperCase();
|
|
411
516
|
const client_name = getClientTheme(this.client_id).name;
|
|
412
517
|
const msgs = {
|
|
413
|
-
FR: `Bonjour ${first_name} !
|
|
414
|
-
EN: `Hello ${first_name}!
|
|
415
|
-
DE: `Hallo ${first_name}!
|
|
416
|
-
ES: `¡Hola ${first_name}!
|
|
417
|
-
IT: `Ciao ${first_name}!
|
|
418
|
-
NL: `Hallo ${first_name}!
|
|
518
|
+
FR: `Bonjour ${first_name} ! Je m'appelle Maria, votre assistante virtuelle ${client_name}. Comment puis-je vous aider aujourd'hui ?`,
|
|
519
|
+
EN: `Hello ${first_name}! My name is Maria, your ${client_name} virtual assistant. How can I help you today?`,
|
|
520
|
+
DE: `Hallo ${first_name}! Ich bin Maria, Ihre virtuelle Assistentin von ${client_name}. Wie kann ich Ihnen helfen?`,
|
|
521
|
+
ES: `¡Hola ${first_name}! Me llamo Maria, tu asistente virtual de ${client_name}. ¿En qué puedo ayudarte?`,
|
|
522
|
+
IT: `Ciao ${first_name}! Mi chiamo Maria, la tua assistente virtuale di ${client_name}. Come posso aiutarti?`,
|
|
523
|
+
NL: `Hallo ${first_name}! Ik ben Maria, uw virtuele assistent van ${client_name}. Hoe kan ik u helpen?`,
|
|
419
524
|
};
|
|
420
525
|
return msgs[lang] || msgs['EN'];
|
|
421
526
|
}
|
|
422
527
|
|
|
423
|
-
// ─── Theme
|
|
528
|
+
// ─── Theme ─────────────────────────────────────────────────────────────────
|
|
424
529
|
|
|
425
530
|
getThemeColor() {
|
|
426
531
|
return getClientTheme(this.client_id).primary;
|
|
@@ -456,6 +561,9 @@
|
|
|
456
561
|
#tp-new-conversation { background: linear-gradient(135deg, ${color}, ${dark}) !important; }
|
|
457
562
|
.tp-conv-item:hover { background: ${color}11 !important; }
|
|
458
563
|
.tp-conv-new-btn { background: linear-gradient(135deg, ${color}, ${dark}) !important; }
|
|
564
|
+
.tp-badge-closed { background: ${color}22 !important; color: ${dark} !important; }
|
|
565
|
+
.tp-attach-btn { color: ${color} !important; }
|
|
566
|
+
.tp-attach-btn:hover { background: ${color}18 !important; }
|
|
459
567
|
`;
|
|
460
568
|
document.head.appendChild(style);
|
|
461
569
|
|
|
@@ -466,13 +574,11 @@
|
|
|
466
574
|
host.style.left = position === 'left' ? '24px' : 'auto';
|
|
467
575
|
host.style.right = position === 'left' ? 'auto' : '24px';
|
|
468
576
|
}
|
|
469
|
-
|
|
470
577
|
const bubble = this.querySelector('.tp-chatbot-bubble');
|
|
471
578
|
if (bubble) {
|
|
472
579
|
bubble.style.marginLeft = position === 'left' ? '0' : 'auto';
|
|
473
580
|
bubble.style.marginRight = position === 'left' ? 'auto' : '0';
|
|
474
581
|
}
|
|
475
|
-
|
|
476
582
|
const win = this.querySelector('.tp-chatbot-window');
|
|
477
583
|
if (win) {
|
|
478
584
|
win.style.left = '0';
|
|
@@ -481,7 +587,7 @@
|
|
|
481
587
|
}
|
|
482
588
|
}
|
|
483
589
|
|
|
484
|
-
// ─── Render
|
|
590
|
+
// ─── Render ────────────────────────────────────────────────────────────────
|
|
485
591
|
|
|
486
592
|
render() {
|
|
487
593
|
const theme = getClientTheme(this.client_id);
|
|
@@ -490,39 +596,40 @@
|
|
|
490
596
|
<div class="tp-chatbot-window" id="tp-window">
|
|
491
597
|
<div class="tp-chatbot-header" id="tp-header">
|
|
492
598
|
<div class="tp-chatbot-avatar">M</div>
|
|
493
|
-
<div style="flex:1">
|
|
599
|
+
<div style="flex:1;min-width:0;">
|
|
494
600
|
<div class="tp-chatbot-title">${theme.name} Support</div>
|
|
495
|
-
<div class="tp-chatbot-subtitle" id="tp-subtitle"
|
|
601
|
+
<div class="tp-chatbot-subtitle" id="tp-subtitle">${icon('bot', 12, 'margin-right:4px')} Assistant</div>
|
|
496
602
|
</div>
|
|
497
|
-
<button id="tp-sound-btn"
|
|
498
|
-
<button id="tp-minimize-btn"
|
|
499
|
-
<button id="tp-maximize-btn"
|
|
500
|
-
<button id="tp-back-btn" style="display:none;
|
|
603
|
+
<button id="tp-sound-btn" class="tp-header-btn" title="Sound">${icon('bell', 16)}</button>
|
|
604
|
+
<button id="tp-minimize-btn" class="tp-header-btn" title="Minimize">${icon('minimize', 16)}</button>
|
|
605
|
+
<button id="tp-maximize-btn" class="tp-header-btn" title="Fullscreen">${icon('restore', 16)}</button>
|
|
606
|
+
<button id="tp-back-btn" class="tp-header-btn" style="display:none;" title="Back">${icon('back', 16)}</button>
|
|
501
607
|
</div>
|
|
502
608
|
<div class="tp-chatbot-messages" id="tp-messages"></div>
|
|
503
609
|
<div class="tp-chatbot-agent-bar" id="tp-agent-bar" style="display:none;">
|
|
504
|
-
<button class="tp-chatbot-agent-btn" id="tp-agent-btn"
|
|
610
|
+
<button class="tp-chatbot-agent-btn" id="tp-agent-btn">
|
|
611
|
+
<span style="display:inline-flex;align-items:center;gap:6px;">${icon('agent', 16)} Talk to an agent</span>
|
|
612
|
+
</button>
|
|
505
613
|
</div>
|
|
506
614
|
<div class="tp-chatbot-input-bar" id="tp-chatbot-input-bar" style="display:none;">
|
|
507
615
|
<textarea class="tp-chatbot-input" id="tp-input" placeholder="Write your message..." rows="1"></textarea>
|
|
508
|
-
<button class="tp-chatbot-send" id="tp-send"
|
|
616
|
+
<button class="tp-chatbot-send" id="tp-send">${icon('send', 16, 'color:white')}</button>
|
|
509
617
|
</div>
|
|
510
618
|
<div id="tp-close-bar" style="padding:8px 12px;border-top:1px solid #ede8f5;text-align:center;display:none;">
|
|
511
|
-
<button id="tp-close-btn"
|
|
512
|
-
|
|
619
|
+
<button id="tp-close-btn" class="tp-close-btn">
|
|
620
|
+
<span style="display:inline-flex;align-items:center;gap:6px;">${icon('check', 14)} End conversation</span>
|
|
513
621
|
</button>
|
|
514
622
|
</div>
|
|
515
623
|
</div>
|
|
516
|
-
<div class="tp-chatbot-bubble" id="tp-bubble"
|
|
624
|
+
<div class="tp-chatbot-bubble" id="tp-bubble">${icon('msg', 24, 'color:white')}</div>
|
|
517
625
|
</div>
|
|
518
626
|
`;
|
|
519
627
|
}
|
|
520
628
|
|
|
521
|
-
// ─── Window controls
|
|
629
|
+
// ─── Window controls ───────────────────────────────────────────────────────
|
|
522
630
|
|
|
523
631
|
minimize() {
|
|
524
632
|
const win = this.querySelector('#tp-window');
|
|
525
|
-
// Exit fullscreen first if needed
|
|
526
633
|
if (this.is_fullscreen) this.exitFullscreen(false);
|
|
527
634
|
this.is_minimized = true;
|
|
528
635
|
win.style.height = '56px';
|
|
@@ -542,7 +649,8 @@
|
|
|
542
649
|
win.style.borderRadius = '0';
|
|
543
650
|
win.style.zIndex = '99999';
|
|
544
651
|
host.style.width = '100vw';
|
|
545
|
-
this.querySelector('#tp-maximize-btn')
|
|
652
|
+
const btn = this.querySelector('#tp-maximize-btn');
|
|
653
|
+
if (btn) btn.innerHTML = icon('restore', 16);
|
|
546
654
|
}
|
|
547
655
|
|
|
548
656
|
exitFullscreen(restore_height = true) {
|
|
@@ -561,10 +669,11 @@
|
|
|
561
669
|
host.style.right = position === 'left' ? 'auto' : '24px';
|
|
562
670
|
if (restore_height) win.style.height = WINDOW_HEIGHT;
|
|
563
671
|
win.style.overflow = 'hidden';
|
|
564
|
-
this.querySelector('#tp-maximize-btn')
|
|
672
|
+
const btn = this.querySelector('#tp-maximize-btn');
|
|
673
|
+
if (btn) btn.innerHTML = icon('restore', 16);
|
|
565
674
|
}
|
|
566
675
|
|
|
567
|
-
// ─── Events
|
|
676
|
+
// ─── Events ────────────────────────────────────────────────────────────────
|
|
568
677
|
|
|
569
678
|
attachEvents() {
|
|
570
679
|
this.querySelector('#tp-bubble').addEventListener('click', () => this.toggleChat());
|
|
@@ -581,18 +690,15 @@
|
|
|
581
690
|
|
|
582
691
|
this.querySelector('#tp-sound-btn').addEventListener('click', () => {
|
|
583
692
|
this.sound_enabled = !this.sound_enabled;
|
|
584
|
-
this.querySelector('#tp-sound-btn').
|
|
693
|
+
this.querySelector('#tp-sound-btn').innerHTML = icon(this.sound_enabled ? 'bell' : 'bell_off', 16);
|
|
585
694
|
});
|
|
586
695
|
|
|
587
696
|
this.querySelector('#tp-close-btn').addEventListener('click', () => {
|
|
588
|
-
if (window.confirm(this.getMessages().close_confirm))
|
|
589
|
-
this.closeByUser();
|
|
590
|
-
}
|
|
697
|
+
if (window.confirm(this.getMessages().close_confirm)) this.closeByUser();
|
|
591
698
|
});
|
|
592
699
|
|
|
593
700
|
this.querySelector('#tp-minimize-btn').addEventListener('click', () => {
|
|
594
701
|
if (this.is_minimized) {
|
|
595
|
-
// Restore
|
|
596
702
|
this.is_minimized = false;
|
|
597
703
|
const win = this.querySelector('#tp-window');
|
|
598
704
|
win.style.height = WINDOW_HEIGHT;
|
|
@@ -620,13 +726,15 @@
|
|
|
620
726
|
updateUILanguage() {
|
|
621
727
|
const m = this.getMessages();
|
|
622
728
|
const agent_btn = this.querySelector('#tp-agent-btn');
|
|
623
|
-
if (agent_btn)
|
|
729
|
+
if (agent_btn)
|
|
730
|
+
agent_btn.innerHTML = `<span style="display:inline-flex;align-items:center;gap:6px;">${icon('agent', 16)} ${m.speak_agent}</span>`;
|
|
624
731
|
const close_btn = this.querySelector('#tp-close-btn');
|
|
625
|
-
if (close_btn)
|
|
732
|
+
if (close_btn)
|
|
733
|
+
close_btn.innerHTML = `<span style="display:inline-flex;align-items:center;gap:6px;">${icon('check', 14)} ${m.close_btn}</span>`;
|
|
626
734
|
const input = this.querySelector('#tp-input');
|
|
627
735
|
if (input) input.placeholder = m.write_msg;
|
|
628
736
|
const subtitle = this.querySelector('#tp-subtitle');
|
|
629
|
-
if (subtitle) subtitle.
|
|
737
|
+
if (subtitle) subtitle.innerHTML = `${icon('bot', 12, 'margin-right:4px')} ${m.virtual}`;
|
|
630
738
|
}
|
|
631
739
|
|
|
632
740
|
toggleChat() {
|
|
@@ -635,8 +743,7 @@
|
|
|
635
743
|
const bubble = this.querySelector('#tp-bubble');
|
|
636
744
|
if (this.is_open) {
|
|
637
745
|
window_el.classList.add('open');
|
|
638
|
-
bubble.
|
|
639
|
-
// Restore from minimized if needed
|
|
746
|
+
bubble.innerHTML = icon('close', 20, 'color:white');
|
|
640
747
|
if (this.is_minimized) {
|
|
641
748
|
this.is_minimized = false;
|
|
642
749
|
window_el.style.height = WINDOW_HEIGHT;
|
|
@@ -644,7 +751,7 @@
|
|
|
644
751
|
}
|
|
645
752
|
} else {
|
|
646
753
|
window_el.classList.remove('open');
|
|
647
|
-
bubble.
|
|
754
|
+
bubble.innerHTML = icon('msg', 24, 'color:white');
|
|
648
755
|
if (this.is_fullscreen) this.exitFullscreen(true);
|
|
649
756
|
}
|
|
650
757
|
}
|
|
@@ -660,11 +767,7 @@
|
|
|
660
767
|
const response = await fetch(`${this.api_url}/chat/conversations`, {
|
|
661
768
|
method: 'POST',
|
|
662
769
|
headers: this.getHeaders(true),
|
|
663
|
-
body: JSON.stringify({
|
|
664
|
-
user_id: this.user_id,
|
|
665
|
-
user_info: this.user_info,
|
|
666
|
-
client_id: this.client_id,
|
|
667
|
-
}),
|
|
770
|
+
body: JSON.stringify({ user_id: this.user_id, user_info: this.user_info, client_id: this.client_id }),
|
|
668
771
|
});
|
|
669
772
|
const data = await response.json();
|
|
670
773
|
return data.result?.conversation_id;
|
|
@@ -676,15 +779,12 @@
|
|
|
676
779
|
const existing = this.querySelector('#tp-suggestions');
|
|
677
780
|
if (existing) existing.remove();
|
|
678
781
|
if (!suggestions || suggestions.length === 0) return;
|
|
679
|
-
|
|
680
782
|
const lang = (this.user_info?.language || 'EN').toUpperCase();
|
|
681
783
|
const color = this.getThemeColor();
|
|
682
784
|
const container = this.querySelector('#tp-messages');
|
|
683
|
-
|
|
684
785
|
const wrap = document.createElement('div');
|
|
685
786
|
wrap.id = 'tp-suggestions';
|
|
686
787
|
wrap.style.cssText = 'padding:8px 12px 12px;display:flex;flex-direction:column;gap:6px;';
|
|
687
|
-
|
|
688
788
|
suggestions.forEach(s => {
|
|
689
789
|
const label = s[lang] || s['EN'];
|
|
690
790
|
const btn = document.createElement('button');
|
|
@@ -709,38 +809,29 @@
|
|
|
709
809
|
});
|
|
710
810
|
wrap.appendChild(btn);
|
|
711
811
|
});
|
|
712
|
-
|
|
713
812
|
container.appendChild(wrap);
|
|
714
813
|
container.scrollTop = container.scrollHeight;
|
|
715
814
|
}
|
|
716
815
|
|
|
717
|
-
// ─── Conversation List
|
|
816
|
+
// ─── Conversation List ─────────────────────────────────────────────────────
|
|
718
817
|
|
|
719
818
|
async showConversationList() {
|
|
720
819
|
this.view = 'list';
|
|
721
820
|
const m = this.getMessages();
|
|
722
|
-
|
|
723
821
|
if (this.polling_interval) {
|
|
724
822
|
clearInterval(this.polling_interval);
|
|
725
823
|
this.polling_interval = null;
|
|
726
824
|
}
|
|
727
825
|
|
|
728
826
|
const subtitle = this.querySelector('#tp-subtitle');
|
|
729
|
-
if (subtitle) subtitle.
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
const input_bar = this.querySelector('#tp-chatbot-input-bar');
|
|
735
|
-
if (input_bar) input_bar.style.display = 'none';
|
|
736
|
-
|
|
737
|
-
const agent_bar = this.querySelector('#tp-agent-bar');
|
|
738
|
-
if (agent_bar) agent_bar.style.display = 'none';
|
|
739
|
-
|
|
740
|
-
const close_bar = this.querySelector('#tp-close-bar');
|
|
741
|
-
if (close_bar) close_bar.style.display = 'none';
|
|
827
|
+
if (subtitle) subtitle.innerHTML = `${icon('bot', 12, 'margin-right:4px')} ${m.virtual}`;
|
|
828
|
+
this.querySelector('#tp-back-btn').style.display = 'none';
|
|
829
|
+
this.querySelector('#tp-chatbot-input-bar').style.display = 'none';
|
|
830
|
+
this.querySelector('#tp-agent-bar').style.display = 'none';
|
|
831
|
+
this.querySelector('#tp-close-bar').style.display = 'none';
|
|
742
832
|
|
|
743
833
|
const container = this.querySelector('#tp-messages');
|
|
834
|
+
container.style.background = '#f5f5f7';
|
|
744
835
|
container.innerHTML = `<div style="padding:16px;text-align:center;color:#aaa;font-size:12px;">${m.loading}</div>`;
|
|
745
836
|
|
|
746
837
|
try {
|
|
@@ -751,35 +842,40 @@
|
|
|
751
842
|
const color = this.getThemeColor();
|
|
752
843
|
|
|
753
844
|
container.innerHTML = '';
|
|
845
|
+
container.style.background = 'white';
|
|
846
|
+
container.style.padding = '0';
|
|
754
847
|
|
|
848
|
+
// Header greeting
|
|
755
849
|
const header_el = document.createElement('div');
|
|
756
|
-
header_el.style.cssText = 'padding:16px;border-bottom:1px solid #
|
|
850
|
+
header_el.style.cssText = 'padding:16px;border-bottom:1px solid #f0f0f0;';
|
|
757
851
|
header_el.innerHTML = `
|
|
758
|
-
<div style="font-size:
|
|
852
|
+
<div style="font-size:14px;font-weight:700;color:#1a1a2e;margin-bottom:2px;">${m.hello} ${this.user_info?.first_name || ''} 👋</div>
|
|
759
853
|
<div style="font-size:12px;color:#aaa;">${m.recent_conv}</div>
|
|
760
854
|
`;
|
|
761
855
|
container.appendChild(header_el);
|
|
762
856
|
|
|
763
857
|
if (conversations.length === 0) {
|
|
764
858
|
const empty = document.createElement('div');
|
|
765
|
-
empty.style.cssText = 'padding:
|
|
766
|
-
empty.
|
|
859
|
+
empty.style.cssText = 'padding:32px 16px;text-align:center;color:#bbb;font-size:13px;';
|
|
860
|
+
empty.innerHTML = `<div style="margin-bottom:8px;opacity:0.4;">${icon('msg', 32)}</div>${m.no_conv}`;
|
|
767
861
|
container.appendChild(empty);
|
|
768
862
|
} else {
|
|
769
863
|
conversations.forEach(conv => {
|
|
770
864
|
const item = document.createElement('div');
|
|
771
865
|
item.className = 'tp-conv-item';
|
|
772
|
-
item.style.cssText =
|
|
866
|
+
item.style.cssText =
|
|
867
|
+
'padding:14px 16px;border-bottom:1px solid #f5f5f5;cursor:pointer;transition:background 0.15s;display:flex;justify-content:space-between;align-items:center;';
|
|
773
868
|
|
|
774
869
|
const is_closed = conv.status === 'closed';
|
|
775
870
|
const status_label = is_closed
|
|
776
|
-
?
|
|
871
|
+
? m.status_closed
|
|
777
872
|
: conv.status === 'agent'
|
|
778
|
-
?
|
|
873
|
+
? m.status_agent
|
|
779
874
|
: conv.status === 'waiting_agent'
|
|
780
|
-
?
|
|
781
|
-
:
|
|
782
|
-
|
|
875
|
+
? m.status_waiting
|
|
876
|
+
: m.status_bot;
|
|
877
|
+
|
|
878
|
+
const badge_color = is_closed ? color : conv.status === 'waiting_agent' ? '#f59e0b' : color;
|
|
783
879
|
const date = new Date(conv.updated_at).toLocaleDateString('fr-FR', {
|
|
784
880
|
day: '2-digit',
|
|
785
881
|
month: '2-digit',
|
|
@@ -788,20 +884,27 @@
|
|
|
788
884
|
});
|
|
789
885
|
|
|
790
886
|
item.innerHTML = `
|
|
791
|
-
<div
|
|
792
|
-
<
|
|
793
|
-
<
|
|
887
|
+
<div>
|
|
888
|
+
<div style="font-size:13px;font-weight:600;color:${is_closed ? '#888' : '#1a1a2e'};margin-bottom:3px;">${date}</div>
|
|
889
|
+
<div style="font-size:11px;color:#bbb;">${conv.message_count} ${m.messages}${conv.message_count > 1 ? 's' : ''}</div>
|
|
794
890
|
</div>
|
|
795
|
-
<
|
|
891
|
+
<span class="tp-badge-closed" style="display:inline-flex;align-items:center;gap:4px;font-size:11px;font-weight:700;padding:3px 10px;border-radius:20px;background:${badge_color}18;color:${badge_color};">
|
|
892
|
+
${is_closed ? icon('check', 11) : ''} ${status_label}
|
|
893
|
+
</span>
|
|
796
894
|
`;
|
|
797
895
|
item.addEventListener('click', () => this.openConversation(conv.conversation_id, is_closed));
|
|
798
896
|
container.appendChild(item);
|
|
799
897
|
});
|
|
800
898
|
}
|
|
801
899
|
|
|
900
|
+
// New conversation button
|
|
802
901
|
const new_btn_wrap = document.createElement('div');
|
|
803
902
|
new_btn_wrap.style.cssText = 'padding:16px;';
|
|
804
|
-
new_btn_wrap.innerHTML =
|
|
903
|
+
new_btn_wrap.innerHTML = `
|
|
904
|
+
<button class="tp-conv-new-btn" style="width:100%;padding:12px;color:white;border:none;border-radius:10px;font-size:13px;font-weight:700;cursor:pointer;display:flex;align-items:center;justify-content:center;gap:8px;">
|
|
905
|
+
${icon('msg_plus', 16, 'color:white')} ${m.new_conv_btn}
|
|
906
|
+
</button>
|
|
907
|
+
`;
|
|
805
908
|
new_btn_wrap.querySelector('button').addEventListener('click', () => this.startNewConversation());
|
|
806
909
|
container.appendChild(new_btn_wrap);
|
|
807
910
|
} catch (e) {
|
|
@@ -820,10 +923,10 @@
|
|
|
820
923
|
this.agent_requested = false;
|
|
821
924
|
|
|
822
925
|
const m = this.getMessages();
|
|
823
|
-
|
|
824
|
-
if (back_btn) back_btn.style.display = 'block';
|
|
825
|
-
|
|
926
|
+
this.querySelector('#tp-back-btn').style.display = 'block';
|
|
826
927
|
const container = this.querySelector('#tp-messages');
|
|
928
|
+
container.style.background = '#f5f5f7';
|
|
929
|
+
container.style.padding = '16px';
|
|
827
930
|
container.innerHTML = '';
|
|
828
931
|
|
|
829
932
|
const input_bar = this.querySelector('#tp-chatbot-input-bar');
|
|
@@ -839,10 +942,9 @@
|
|
|
839
942
|
|
|
840
943
|
conv.messages.forEach(msg => {
|
|
841
944
|
if (['user', 'assistant', 'agent', 'system'].includes(msg.role)) {
|
|
842
|
-
this.addMessage(msg.role, msg.content);
|
|
945
|
+
this.addMessage(msg.role, msg.content, msg.message_id || null);
|
|
843
946
|
}
|
|
844
947
|
});
|
|
845
|
-
|
|
846
948
|
this.last_message_count = conv.messages.length;
|
|
847
949
|
|
|
848
950
|
if (conv.status === 'closed') {
|
|
@@ -859,14 +961,14 @@
|
|
|
859
961
|
if (conv.status === 'agent') {
|
|
860
962
|
this.agent_mode = true;
|
|
861
963
|
const subtitle = this.querySelector('#tp-subtitle');
|
|
862
|
-
if (subtitle) subtitle.
|
|
964
|
+
if (subtitle) subtitle.innerHTML = `${icon('user', 12, 'margin-right:4px')} ${m.human_agent}`;
|
|
863
965
|
if (agent_bar) agent_bar.style.display = 'none';
|
|
864
966
|
if (input_bar) input_bar.style.display = 'flex';
|
|
865
967
|
} else {
|
|
866
968
|
if (input_bar) input_bar.style.display = 'flex';
|
|
867
969
|
if (agent_bar) agent_bar.style.display = 'none';
|
|
868
970
|
const subtitle = this.querySelector('#tp-subtitle');
|
|
869
|
-
if (subtitle) subtitle.
|
|
971
|
+
if (subtitle) subtitle.innerHTML = `${icon('bot', 12, 'margin-right:4px')} ${m.virtual}`;
|
|
870
972
|
}
|
|
871
973
|
|
|
872
974
|
this.updateUILanguage();
|
|
@@ -886,52 +988,40 @@
|
|
|
886
988
|
this.conversation_id = null;
|
|
887
989
|
|
|
888
990
|
const m = this.getMessages();
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
if (back_btn) back_btn.style.display = 'block';
|
|
892
|
-
|
|
893
|
-
const close_bar = this.querySelector('#tp-close-bar');
|
|
894
|
-
if (close_bar) close_bar.style.display = 'block';
|
|
991
|
+
this.querySelector('#tp-back-btn').style.display = 'block';
|
|
992
|
+
this.querySelector('#tp-close-bar').style.display = 'block';
|
|
895
993
|
|
|
896
994
|
const container = this.querySelector('#tp-messages');
|
|
995
|
+
container.style.background = '#f5f5f7';
|
|
996
|
+
container.style.padding = '16px';
|
|
897
997
|
container.innerHTML = '';
|
|
898
998
|
|
|
899
999
|
const subtitle = this.querySelector('#tp-subtitle');
|
|
900
|
-
if (subtitle) subtitle.
|
|
901
|
-
|
|
902
|
-
const input_bar = this.querySelector('#tp-chatbot-input-bar');
|
|
903
|
-
if (input_bar) input_bar.style.display = 'flex';
|
|
904
|
-
|
|
905
|
-
const agent_bar = this.querySelector('#tp-agent-bar');
|
|
906
|
-
if (agent_bar) agent_bar.style.display = 'none';
|
|
1000
|
+
if (subtitle) subtitle.innerHTML = `${icon('bot', 12, 'margin-right:4px')} ${m.virtual}`;
|
|
907
1001
|
|
|
1002
|
+
this.querySelector('#tp-chatbot-input-bar').style.display = 'flex';
|
|
1003
|
+
this.querySelector('#tp-agent-bar').style.display = 'none';
|
|
908
1004
|
this.updateUILanguage();
|
|
1005
|
+
|
|
909
1006
|
this.conversation_id = await this.createConversation();
|
|
910
1007
|
this.addMessage('assistant', this.getWelcomeMessage(this.user_info?.first_name || ''));
|
|
911
1008
|
this.last_message_count = 1;
|
|
912
1009
|
|
|
913
1010
|
const theme = getClientTheme(this.client_id);
|
|
914
|
-
if (theme.suggestions && theme.suggestions.length > 0)
|
|
915
|
-
this.showSuggestions(theme.suggestions);
|
|
916
|
-
}
|
|
1011
|
+
if (theme.suggestions && theme.suggestions.length > 0) this.showSuggestions(theme.suggestions);
|
|
917
1012
|
|
|
918
1013
|
this.polling_interval = setInterval(() => this.pollMessages(), 3000);
|
|
919
1014
|
}
|
|
920
1015
|
|
|
921
|
-
// ─── Guest form
|
|
1016
|
+
// ─── Guest form ────────────────────────────────────────────────────────────
|
|
922
1017
|
|
|
923
1018
|
showGuestForm() {
|
|
924
1019
|
const container = this.querySelector('#tp-messages');
|
|
925
1020
|
if (!container) return;
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
if (input_bar) input_bar.style.display = 'none';
|
|
929
|
-
const agent_bar = this.querySelector('#tp-agent-bar');
|
|
930
|
-
if (agent_bar) agent_bar.style.display = 'none';
|
|
931
|
-
|
|
1021
|
+
this.querySelector('#tp-chatbot-input-bar').style.display = 'none';
|
|
1022
|
+
this.querySelector('#tp-agent-bar').style.display = 'none';
|
|
932
1023
|
const color = this.getThemeColor();
|
|
933
1024
|
const dark = this.shadeColor(color, -20);
|
|
934
|
-
|
|
935
1025
|
const form_el = document.createElement('div');
|
|
936
1026
|
form_el.id = 'tp-guest-form';
|
|
937
1027
|
form_el.innerHTML = `
|
|
@@ -940,9 +1030,7 @@
|
|
|
940
1030
|
<input id="tp-guest-firstname" type="text" placeholder="First name *" style="padding:10px 14px;border-radius:8px;border:1.5px solid #ede8f5;font-size:13px;font-family:inherit;outline:none;" />
|
|
941
1031
|
<input id="tp-guest-company" type="text" placeholder="Company name *" style="padding:10px 14px;border-radius:8px;border:1.5px solid #ede8f5;font-size:13px;font-family:inherit;outline:none;" />
|
|
942
1032
|
<input id="tp-guest-email" type="email" placeholder="Email *" style="padding:10px 14px;border-radius:8px;border:1.5px solid #ede8f5;font-size:13px;font-family:inherit;outline:none;" />
|
|
943
|
-
<button id="tp-guest-submit" style="padding:10px;background:linear-gradient(135deg,${color},${dark});color:white;border:none;border-radius:8px;font-size:13px;font-weight:600;cursor:pointer;">
|
|
944
|
-
Start conversation →
|
|
945
|
-
</button>
|
|
1033
|
+
<button id="tp-guest-submit" style="padding:10px;background:linear-gradient(135deg,${color},${dark});color:white;border:none;border-radius:8px;font-size:13px;font-weight:600;cursor:pointer;">Start conversation →</button>
|
|
946
1034
|
<p id="tp-guest-error" style="margin:0;font-size:12px;color:#c0392b;display:none;"></p>
|
|
947
1035
|
</div>
|
|
948
1036
|
`;
|
|
@@ -955,7 +1043,6 @@
|
|
|
955
1043
|
const company_name = this.querySelector('#tp-guest-company')?.value.trim();
|
|
956
1044
|
const email = this.querySelector('#tp-guest-email')?.value.trim();
|
|
957
1045
|
const error_el = this.querySelector('#tp-guest-error');
|
|
958
|
-
|
|
959
1046
|
if (!first_name || !company_name || !email) {
|
|
960
1047
|
error_el.textContent = 'Please fill in all fields.';
|
|
961
1048
|
error_el.style.display = 'block';
|
|
@@ -976,57 +1063,86 @@
|
|
|
976
1063
|
this.view = 'chat';
|
|
977
1064
|
this.conversation_id = await this.createConversation();
|
|
978
1065
|
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
const
|
|
984
|
-
|
|
1066
|
+
this.querySelector('#tp-chatbot-input-bar').style.display = 'flex';
|
|
1067
|
+
this.querySelector('#tp-agent-bar').style.display = 'none';
|
|
1068
|
+
this.querySelector('#tp-close-bar').style.display = 'block';
|
|
1069
|
+
|
|
1070
|
+
const container = this.querySelector('#tp-messages');
|
|
1071
|
+
container.style.background = '#f5f5f7';
|
|
1072
|
+
container.style.padding = '16px';
|
|
985
1073
|
|
|
986
1074
|
this.updateUILanguage();
|
|
987
1075
|
this.addMessage('assistant', this.getWelcomeMessage(first_name));
|
|
988
1076
|
this.last_message_count = 1;
|
|
989
1077
|
|
|
990
1078
|
const theme = getClientTheme(this.client_id);
|
|
991
|
-
if (theme.suggestions && theme.suggestions.length > 0)
|
|
992
|
-
this.showSuggestions(theme.suggestions);
|
|
993
|
-
}
|
|
1079
|
+
if (theme.suggestions && theme.suggestions.length > 0) this.showSuggestions(theme.suggestions);
|
|
994
1080
|
|
|
995
1081
|
this.polling_interval = setInterval(() => this.pollMessages(), 3000);
|
|
996
1082
|
}
|
|
997
1083
|
|
|
998
|
-
// ─── Messages
|
|
1084
|
+
// ─── Messages ──────────────────────────────────────────────────────────────
|
|
999
1085
|
|
|
1000
|
-
addMessage(role, content) {
|
|
1001
|
-
this.messages.push({ role, content, created_at: new Date().toISOString() });
|
|
1086
|
+
addMessage(role, content, message_id = null) {
|
|
1087
|
+
this.messages.push({ role, content, message_id, created_at: new Date().toISOString() });
|
|
1002
1088
|
const container = this.querySelector('#tp-messages');
|
|
1003
1089
|
|
|
1004
1090
|
const role_label =
|
|
1005
1091
|
role === 'user'
|
|
1006
|
-
?
|
|
1092
|
+
? `${icon('user', 11, 'margin-right:3px')} ${this.user_info?.first_name || 'You'}`
|
|
1007
1093
|
: role === 'agent'
|
|
1008
|
-
? '
|
|
1094
|
+
? `${icon('agent', 11, 'margin-right:3px')} Agent`
|
|
1009
1095
|
: role === 'system'
|
|
1010
|
-
? '
|
|
1011
|
-
: '
|
|
1096
|
+
? 'Info'
|
|
1097
|
+
: `${icon('bot', 11, 'margin-right:3px')} Maria`;
|
|
1012
1098
|
|
|
1013
1099
|
const msg_el = document.createElement('div');
|
|
1014
1100
|
msg_el.className = `tp-chatbot-message ${role}`;
|
|
1015
1101
|
const rendered_content =
|
|
1016
1102
|
role === 'assistant' || role === 'agent' ? (typeof marked !== 'undefined' ? marked.parse(content) : content) : content;
|
|
1017
1103
|
|
|
1104
|
+
const feedback_html =
|
|
1105
|
+
role === 'assistant' && message_id
|
|
1106
|
+
? `<div class="tp-feedback-bar" data-message-id="${message_id}">
|
|
1107
|
+
<button class="tp-feedback-btn" data-rating="positive" title="Helpful">${icon('helpful', 14)}</button>
|
|
1108
|
+
<button class="tp-feedback-btn" data-rating="negative" title="Not helpful">${icon('not_helpful', 14)}</button>
|
|
1109
|
+
</div>`
|
|
1110
|
+
: '';
|
|
1111
|
+
|
|
1018
1112
|
msg_el.innerHTML = `
|
|
1019
1113
|
<div class="tp-chatbot-bubble-msg">
|
|
1020
1114
|
<div class="tp-chatbot-role">${role_label}</div>
|
|
1021
1115
|
<div class="tp-chatbot-content tp-chatbot-markdown">${rendered_content}</div>
|
|
1116
|
+
${feedback_html}
|
|
1022
1117
|
</div>
|
|
1023
1118
|
`;
|
|
1119
|
+
|
|
1120
|
+
if (role === 'assistant' && message_id) {
|
|
1121
|
+
const bar = msg_el.querySelector('.tp-feedback-bar');
|
|
1122
|
+
bar.querySelectorAll('.tp-feedback-btn').forEach(btn => {
|
|
1123
|
+
btn.addEventListener('click', async () => {
|
|
1124
|
+
if (bar.dataset.voted) return;
|
|
1125
|
+
bar.dataset.voted = '1';
|
|
1126
|
+
const rating = btn.dataset.rating;
|
|
1127
|
+
bar.querySelectorAll('.tp-feedback-btn').forEach(b => {
|
|
1128
|
+
b.style.opacity = b === btn ? '1' : '0.3';
|
|
1129
|
+
b.style.cursor = 'default';
|
|
1130
|
+
});
|
|
1131
|
+
try {
|
|
1132
|
+
await fetch(`${this.api_url}/chat/conversations/${this.conversation_id}/feedback`, {
|
|
1133
|
+
method: 'POST',
|
|
1134
|
+
headers: this.getHeaders(true),
|
|
1135
|
+
body: JSON.stringify({ message_id, rating, user_id: this.user_id }),
|
|
1136
|
+
});
|
|
1137
|
+
} catch {}
|
|
1138
|
+
});
|
|
1139
|
+
});
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1024
1142
|
container.appendChild(msg_el);
|
|
1025
1143
|
container.scrollTop = container.scrollHeight;
|
|
1026
1144
|
|
|
1027
|
-
if ((role === 'assistant' || role === 'agent') && this.sound_enabled)
|
|
1028
|
-
playSound();
|
|
1029
|
-
}
|
|
1145
|
+
if ((role === 'assistant' || role === 'agent') && this.sound_enabled) playSound();
|
|
1030
1146
|
}
|
|
1031
1147
|
|
|
1032
1148
|
showTyping() {
|
|
@@ -1040,8 +1156,8 @@
|
|
|
1040
1156
|
}
|
|
1041
1157
|
|
|
1042
1158
|
hideTyping() {
|
|
1043
|
-
const
|
|
1044
|
-
if (
|
|
1159
|
+
const t = this.querySelector('#tp-typing');
|
|
1160
|
+
if (t) t.remove();
|
|
1045
1161
|
}
|
|
1046
1162
|
|
|
1047
1163
|
showClosed(existing_csat = null) {
|
|
@@ -1053,14 +1169,11 @@
|
|
|
1053
1169
|
|
|
1054
1170
|
const m = this.getMessages();
|
|
1055
1171
|
const subtitle = this.querySelector('#tp-subtitle');
|
|
1056
|
-
if (subtitle) subtitle.
|
|
1172
|
+
if (subtitle) subtitle.innerHTML = `${icon('check', 12, 'margin-right:4px')} ${m.terminated}`;
|
|
1057
1173
|
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
if (agent_bar) agent_bar.style.display = 'none';
|
|
1062
|
-
const close_bar = this.querySelector('#tp-close-bar');
|
|
1063
|
-
if (close_bar) close_bar.style.display = 'none';
|
|
1174
|
+
this.querySelector('#tp-chatbot-input-bar').style.display = 'none';
|
|
1175
|
+
this.querySelector('#tp-agent-bar').style.display = 'none';
|
|
1176
|
+
this.querySelector('#tp-close-bar').style.display = 'none';
|
|
1064
1177
|
const sugg_el = this.querySelector('#tp-suggestions');
|
|
1065
1178
|
if (sugg_el) sugg_el.remove();
|
|
1066
1179
|
|
|
@@ -1078,17 +1191,21 @@
|
|
|
1078
1191
|
? `<div style="font-size:13px;color:#888;margin-bottom:14px;">${existing_csat === 'positive' ? m.csat_positive : m.csat_negative}</div>`
|
|
1079
1192
|
: `<div style="font-size:12px;color:#888;margin-bottom:10px;">${m.csat_question}</div>
|
|
1080
1193
|
<div style="display:flex;gap:10px;justify-content:center;margin-bottom:14px;">
|
|
1081
|
-
<button id="tp-csat-positive" style="
|
|
1082
|
-
<button id="tp-csat-negative" style="
|
|
1194
|
+
<button id="tp-csat-positive" class="tp-csat-btn" style="border-color:#22c55e;color:#22c55e;">${icon('helpful', 20)}</button>
|
|
1195
|
+
<button id="tp-csat-negative" class="tp-csat-btn" style="border-color:#ef4444;color:#ef4444;">${icon('not_helpful', 20)}</button>
|
|
1083
1196
|
</div>`;
|
|
1084
1197
|
|
|
1085
1198
|
const banner = document.createElement('div');
|
|
1086
1199
|
banner.id = 'tp-closed-banner';
|
|
1087
1200
|
banner.innerHTML = `
|
|
1088
|
-
<div style="margin:16px;padding:16px;background:#
|
|
1089
|
-
<div style="font-size:13px;color:#1a1a2e;font-weight:600;margin-bottom:
|
|
1201
|
+
<div style="margin:16px;padding:16px;background:#f9f9f9;border:1px solid #ede8f5;border-radius:12px;text-align:center;">
|
|
1202
|
+
<div style="display:inline-flex;align-items:center;gap:6px;font-size:13px;color:#1a1a2e;font-weight:600;margin-bottom:10px;">
|
|
1203
|
+
${icon('check', 14)} ${m.closed}
|
|
1204
|
+
</div>
|
|
1090
1205
|
<div id="tp-csat-block">${csat_html}</div>
|
|
1091
|
-
<button id="tp-new-conversation" style="padding:10px 20px;background:linear-gradient(135deg,${color},${dark});color:white;border:none;border-radius:8px;font-size:13px;font-weight:
|
|
1206
|
+
<button id="tp-new-conversation" style="padding:10px 20px;background:linear-gradient(135deg,${color},${dark});color:white;border:none;border-radius:8px;font-size:13px;font-weight:700;cursor:pointer;margin-bottom:8px;width:100%;display:flex;align-items:center;justify-content:center;gap:8px;">
|
|
1207
|
+
${icon('msg_plus', 15, 'color:white')} ${m.new_conv}
|
|
1208
|
+
</button>
|
|
1092
1209
|
<button id="tp-back-to-list" style="padding:8px 20px;background:none;color:${color};border:1px solid #ede8f5;border-radius:8px;font-size:12px;font-weight:600;cursor:pointer;width:100%;">${m.back_to_list}</button>
|
|
1093
1210
|
</div>
|
|
1094
1211
|
`;
|
|
@@ -1118,7 +1235,7 @@
|
|
|
1118
1235
|
this.querySelector('#tp-back-to-list').addEventListener('click', () => this.showConversationList());
|
|
1119
1236
|
}
|
|
1120
1237
|
|
|
1121
|
-
// ─── Send / Poll
|
|
1238
|
+
// ─── Send / Poll ───────────────────────────────────────────────────────────
|
|
1122
1239
|
|
|
1123
1240
|
async sendMessage() {
|
|
1124
1241
|
if (this.is_closed || !this.conversation_id) return;
|
|
@@ -1133,8 +1250,7 @@
|
|
|
1133
1250
|
const user_messages = this.messages.filter(msg => msg.role === 'user').length;
|
|
1134
1251
|
if (user_messages >= 30) {
|
|
1135
1252
|
this.addMessage('system', m.limit_reached);
|
|
1136
|
-
|
|
1137
|
-
if (input_bar) input_bar.style.display = 'none';
|
|
1253
|
+
this.querySelector('#tp-chatbot-input-bar').style.display = 'none';
|
|
1138
1254
|
return;
|
|
1139
1255
|
}
|
|
1140
1256
|
|
|
@@ -1149,7 +1265,6 @@
|
|
|
1149
1265
|
headers: this.getHeaders(true),
|
|
1150
1266
|
body: JSON.stringify({ query, user_id: this.user_id, user_info: this.user_info }),
|
|
1151
1267
|
});
|
|
1152
|
-
|
|
1153
1268
|
const data = await response.json();
|
|
1154
1269
|
this.hideTyping();
|
|
1155
1270
|
|
|
@@ -1161,19 +1276,24 @@
|
|
|
1161
1276
|
|
|
1162
1277
|
this.agent_mode = data.result.agent_mode;
|
|
1163
1278
|
const source = data.result.source || null;
|
|
1164
|
-
|
|
1165
1279
|
const subtitle = this.querySelector('#tp-subtitle');
|
|
1166
|
-
if (subtitle)
|
|
1280
|
+
if (subtitle)
|
|
1281
|
+
subtitle.innerHTML = this.agent_mode
|
|
1282
|
+
? `${icon('user', 12, 'margin-right:4px')} ${m.human_agent}`
|
|
1283
|
+
: `${icon('bot', 12, 'margin-right:4px')} ${m.virtual}`;
|
|
1167
1284
|
|
|
1168
1285
|
if (data.result.reply) {
|
|
1169
|
-
this.addMessage(this.agent_mode ? 'agent' : 'assistant', data.result.reply);
|
|
1170
|
-
this.last_message_count += 2;
|
|
1286
|
+
this.addMessage(this.agent_mode ? 'agent' : 'assistant', data.result.reply, data.result.message_id || null);
|
|
1287
|
+
this.last_message_count += 2;
|
|
1171
1288
|
}
|
|
1172
1289
|
|
|
1173
1290
|
const user_msg_count = this.messages.filter(msg => msg.role === 'user').length;
|
|
1174
1291
|
const agent_bar = this.querySelector('#tp-agent-bar');
|
|
1292
|
+
const suggest_agent = data.result.suggest_agent || false;
|
|
1293
|
+
|
|
1175
1294
|
if (agent_bar && !this.agent_requested && !this.agent_mode) {
|
|
1176
1295
|
if (
|
|
1296
|
+
suggest_agent ||
|
|
1177
1297
|
source === 'fallback' ||
|
|
1178
1298
|
source === 'clarification' ||
|
|
1179
1299
|
source === 'no_match' ||
|
|
@@ -1183,9 +1303,14 @@
|
|
|
1183
1303
|
agent_bar.style.display = 'block';
|
|
1184
1304
|
}
|
|
1185
1305
|
}
|
|
1306
|
+
|
|
1307
|
+
if (suggest_agent && !this.agent_requested) {
|
|
1308
|
+
this.addMessage('system', m.suggest_agent_escalation);
|
|
1309
|
+
this.last_message_count += 1;
|
|
1310
|
+
}
|
|
1186
1311
|
} catch {
|
|
1187
1312
|
this.hideTyping();
|
|
1188
|
-
this.addMessage('assistant',
|
|
1313
|
+
this.addMessage('assistant', this.getMessages().error_occurred);
|
|
1189
1314
|
}
|
|
1190
1315
|
|
|
1191
1316
|
this.is_loading = false;
|
|
@@ -1208,17 +1333,11 @@
|
|
|
1208
1333
|
const status = data.result?.status;
|
|
1209
1334
|
|
|
1210
1335
|
let msg = '';
|
|
1211
|
-
if (status === 'outside_hours')
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
msg = m.waiting_agent;
|
|
1217
|
-
} else if (status === 'waiting_agent_already') {
|
|
1218
|
-
msg = m.waiting_agent_already;
|
|
1219
|
-
} else if (status === 'agent') {
|
|
1220
|
-
msg = m.agent_already;
|
|
1221
|
-
}
|
|
1336
|
+
if (status === 'outside_hours') msg = m.outside_hours(data.result.next_opening);
|
|
1337
|
+
else if (status === 'no_agents') msg = m.no_agents;
|
|
1338
|
+
else if (status === 'waiting_agent') msg = m.waiting_agent;
|
|
1339
|
+
else if (status === 'waiting_agent_already') msg = m.waiting_agent_already;
|
|
1340
|
+
else if (status === 'agent') msg = m.agent_already;
|
|
1222
1341
|
|
|
1223
1342
|
if (msg) {
|
|
1224
1343
|
this.addMessage('system', msg);
|
|
@@ -1231,6 +1350,7 @@
|
|
|
1231
1350
|
}
|
|
1232
1351
|
} catch {
|
|
1233
1352
|
this.agent_requested = false;
|
|
1353
|
+
const agent_bar = this.querySelector('#tp-agent-bar');
|
|
1234
1354
|
if (agent_bar) agent_bar.style.display = 'block';
|
|
1235
1355
|
}
|
|
1236
1356
|
}
|
|
@@ -1255,7 +1375,6 @@
|
|
|
1255
1375
|
const url = `${this.api_url}/chat/conversations/${this.conversation_id}?user_id=${encodeURIComponent(this.user_id)}`;
|
|
1256
1376
|
const response = await fetch(url, { headers: this.getHeaders() });
|
|
1257
1377
|
if (!response.ok) return;
|
|
1258
|
-
|
|
1259
1378
|
const data = await response.json();
|
|
1260
1379
|
const conv = data.result?.conversation;
|
|
1261
1380
|
if (!conv) return;
|
|
@@ -1276,7 +1395,7 @@
|
|
|
1276
1395
|
const typing = document.createElement('div');
|
|
1277
1396
|
typing.className = 'tp-chatbot-message agent';
|
|
1278
1397
|
typing.id = 'tp-agent-typing';
|
|
1279
|
-
typing.innerHTML = `<div class="tp-chatbot-bubble-msg"><div class="tp-chatbot-role"
|
|
1398
|
+
typing.innerHTML = `<div class="tp-chatbot-bubble-msg"><div class="tp-chatbot-role">${icon('agent', 11, 'margin-right:3px')} Agent</div><div class="tp-chatbot-typing"><span></span><span></span><span></span></div></div>`;
|
|
1280
1399
|
container.appendChild(typing);
|
|
1281
1400
|
container.scrollTop = container.scrollHeight;
|
|
1282
1401
|
}
|
|
@@ -1291,9 +1410,9 @@
|
|
|
1291
1410
|
if (msg.role === 'agent') {
|
|
1292
1411
|
const typing_el = this.querySelector('#tp-agent-typing');
|
|
1293
1412
|
if (typing_el) typing_el.remove();
|
|
1294
|
-
this.addMessage('agent', msg.content);
|
|
1413
|
+
this.addMessage('agent', msg.content, msg.message_id || null);
|
|
1295
1414
|
} else if (msg.role === 'assistant') {
|
|
1296
|
-
this.addMessage('assistant', msg.content);
|
|
1415
|
+
this.addMessage('assistant', msg.content, msg.message_id || null);
|
|
1297
1416
|
} else if (msg.role === 'system') {
|
|
1298
1417
|
this.addMessage('system', msg.content);
|
|
1299
1418
|
}
|
|
@@ -1304,7 +1423,7 @@
|
|
|
1304
1423
|
if (server_status === 'agent' && !this.agent_mode) {
|
|
1305
1424
|
this.agent_mode = true;
|
|
1306
1425
|
const subtitle = this.querySelector('#tp-subtitle');
|
|
1307
|
-
if (subtitle) subtitle.
|
|
1426
|
+
if (subtitle) subtitle.innerHTML = `${icon('user', 12, 'margin-right:4px')} ${m.human_agent}`;
|
|
1308
1427
|
const agent_bar = this.querySelector('#tp-agent-bar');
|
|
1309
1428
|
if (agent_bar) agent_bar.style.display = 'none';
|
|
1310
1429
|
this.addMessage('system', m.agent_taken);
|
|
@@ -1313,7 +1432,7 @@
|
|
|
1313
1432
|
this.agent_mode = false;
|
|
1314
1433
|
this.agent_requested = false;
|
|
1315
1434
|
const subtitle = this.querySelector('#tp-subtitle');
|
|
1316
|
-
if (subtitle) subtitle.
|
|
1435
|
+
if (subtitle) subtitle.innerHTML = `${icon('bot', 12, 'margin-right:4px')} ${m.virtual}`;
|
|
1317
1436
|
const agent_bar = this.querySelector('#tp-agent-bar');
|
|
1318
1437
|
if (agent_bar) agent_bar.style.display = 'block';
|
|
1319
1438
|
this.addMessage('system', m.agent_released);
|