@developpement/tp-chatbot-widget 0.0.6 → 0.0.8
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 +2 -2
- package/src/chatbot.js +512 -94
package/package.json
CHANGED
package/src/chatbot.css
CHANGED
package/src/chatbot.js
CHANGED
|
@@ -2,10 +2,90 @@
|
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
4
|
const CLIENT_THEMES = {
|
|
5
|
-
flix: {
|
|
6
|
-
|
|
5
|
+
flix: {
|
|
6
|
+
primary: '#73d700',
|
|
7
|
+
name: 'Flix Corporate',
|
|
8
|
+
position: 'left',
|
|
9
|
+
suggestions: [
|
|
10
|
+
{
|
|
11
|
+
FR: 'Comment réserver un billet ?',
|
|
12
|
+
EN: 'How to book a ticket?',
|
|
13
|
+
DE: 'Wie buche ich ein Ticket?',
|
|
14
|
+
ES: '¿Cómo reservar un billete?',
|
|
15
|
+
IT: 'Come prenotare un biglietto?',
|
|
16
|
+
NL: 'Hoe boek ik een ticket?',
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
FR: 'Annuler ma réservation',
|
|
20
|
+
EN: 'Cancel my booking',
|
|
21
|
+
DE: 'Meine Buchung stornieren',
|
|
22
|
+
ES: 'Cancelar mi reserva',
|
|
23
|
+
IT: 'Cancellare la mia prenotazione',
|
|
24
|
+
NL: 'Mijn boeking annuleren',
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
FR: 'Politique bagages',
|
|
28
|
+
EN: 'Baggage policy',
|
|
29
|
+
DE: 'Gepäckrichtlinien',
|
|
30
|
+
ES: 'Política de equipaje',
|
|
31
|
+
IT: 'Politica bagagli',
|
|
32
|
+
NL: 'Bagagebeleid',
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
FR: 'Mon compte entreprise',
|
|
36
|
+
EN: 'My corporate account',
|
|
37
|
+
DE: 'Mein Unternehmenskonto',
|
|
38
|
+
ES: 'Mi cuenta empresarial',
|
|
39
|
+
IT: 'Il mio account aziendale',
|
|
40
|
+
NL: 'Mijn zakelijk account',
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
},
|
|
44
|
+
sncf: {
|
|
45
|
+
primary: '#1a6b5a',
|
|
46
|
+
name: 'SNCF',
|
|
47
|
+
position: 'right',
|
|
48
|
+
suggestions: [
|
|
49
|
+
{
|
|
50
|
+
FR: 'Comment réserver ?',
|
|
51
|
+
EN: 'How to book?',
|
|
52
|
+
DE: 'Wie buche ich?',
|
|
53
|
+
ES: '¿Cómo reservar?',
|
|
54
|
+
IT: 'Come prenotare?',
|
|
55
|
+
NL: 'Hoe boek ik?',
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
FR: 'Annuler un billet',
|
|
59
|
+
EN: 'Cancel a ticket',
|
|
60
|
+
DE: 'Ticket stornieren',
|
|
61
|
+
ES: 'Cancelar un billete',
|
|
62
|
+
IT: 'Cancellare un biglietto',
|
|
63
|
+
NL: 'Ticket annuleren',
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
FR: 'Politique bagages',
|
|
67
|
+
EN: 'Baggage policy',
|
|
68
|
+
DE: 'Gepäckrichtlinien',
|
|
69
|
+
ES: 'Política de equipaje',
|
|
70
|
+
IT: 'Politica bagagli',
|
|
71
|
+
NL: 'Bagagebeleid',
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
FR: 'Retard ou incident',
|
|
75
|
+
EN: 'Delay or incident',
|
|
76
|
+
DE: 'Verspätung oder Vorfall',
|
|
77
|
+
ES: 'Retraso o incidente',
|
|
78
|
+
IT: 'Ritardo o incidente',
|
|
79
|
+
NL: 'Vertraging of incident',
|
|
80
|
+
},
|
|
81
|
+
],
|
|
82
|
+
},
|
|
7
83
|
};
|
|
8
|
-
|
|
84
|
+
|
|
85
|
+
const DEFAULT_THEME = { primary: '#7b1fa2', name: 'Support', position: 'right', suggestions: [] };
|
|
86
|
+
|
|
87
|
+
const WINDOW_WIDTH = '380px';
|
|
88
|
+
const WINDOW_HEIGHT = '580px';
|
|
9
89
|
|
|
10
90
|
function getClientTheme(client_id) {
|
|
11
91
|
return CLIENT_THEMES[client_id] || DEFAULT_THEME;
|
|
@@ -20,7 +100,7 @@
|
|
|
20
100
|
first_name: payload.firstName || '',
|
|
21
101
|
last_name: payload.lastName || '',
|
|
22
102
|
company_name: payload.companyName || '',
|
|
23
|
-
language: payload.language || '
|
|
103
|
+
language: payload.language || 'EN',
|
|
24
104
|
site_id: payload.siteId || '',
|
|
25
105
|
};
|
|
26
106
|
} catch {
|
|
@@ -63,6 +143,8 @@
|
|
|
63
143
|
this.sound_enabled = true;
|
|
64
144
|
this.client_id = 'flix';
|
|
65
145
|
this.access_token = '';
|
|
146
|
+
this.is_fullscreen = false;
|
|
147
|
+
this.is_minimized = false;
|
|
66
148
|
}
|
|
67
149
|
|
|
68
150
|
static get observedAttributes() {
|
|
@@ -98,6 +180,8 @@
|
|
|
98
180
|
this.polling_interval = null;
|
|
99
181
|
this.is_closed = false;
|
|
100
182
|
this.view = 'list';
|
|
183
|
+
this.is_fullscreen = false;
|
|
184
|
+
this.is_minimized = false;
|
|
101
185
|
|
|
102
186
|
this.render();
|
|
103
187
|
this.applyTheme();
|
|
@@ -127,6 +211,185 @@
|
|
|
127
211
|
if (this.polling_interval) clearInterval(this.polling_interval);
|
|
128
212
|
}
|
|
129
213
|
|
|
214
|
+
// ─── i18n ─────────────────────────────────────────────────────────────────
|
|
215
|
+
|
|
216
|
+
getMessages() {
|
|
217
|
+
const lang = (this.user_info?.language || 'EN').toUpperCase();
|
|
218
|
+
const msgs = {
|
|
219
|
+
FR: {
|
|
220
|
+
closed: '✅ Conversation clôturée',
|
|
221
|
+
csat_question: 'Cette conversation vous a-t-elle été utile ?',
|
|
222
|
+
csat_positive: '👍 Merci pour votre retour !',
|
|
223
|
+
csat_negative: '👎 Merci, nous allons nous améliorer.',
|
|
224
|
+
new_conv: '💬 Nouvelle conversation',
|
|
225
|
+
back_to_list: '← Voir toutes les conversations',
|
|
226
|
+
agent_taken: 'Un agent a pris en charge votre conversation. Vous pouvez lui écrire directement.',
|
|
227
|
+
agent_released: 'Notre assistant virtuel reprend la conversation. Comment puis-je vous aider ?',
|
|
228
|
+
close_confirm: 'Voulez-vous terminer cette conversation ?',
|
|
229
|
+
terminated: '✅ Conversation terminée',
|
|
230
|
+
virtual: '🤖 Assistant virtuel',
|
|
231
|
+
human_agent: '👤 Agent humain',
|
|
232
|
+
loading: 'Chargement...',
|
|
233
|
+
no_conv: 'Aucune conversation pour le moment.',
|
|
234
|
+
new_conv_btn: '💬 Nouvelle conversation',
|
|
235
|
+
error_load: 'Erreur de chargement.',
|
|
236
|
+
recent_conv: 'Vos conversations récentes',
|
|
237
|
+
hello: 'Bonjour',
|
|
238
|
+
close_btn: '✅ Terminer la conversation',
|
|
239
|
+
speak_agent: '👤 Parler à un agent',
|
|
240
|
+
write_msg: 'Écrivez votre message...',
|
|
241
|
+
limit_reached: 'Vous avez atteint la limite de 30 messages. Veuillez contacter un agent ou écrire à support@travelplanet.com.',
|
|
242
|
+
too_many: 'Trop de messages. Veuillez patienter avant de réessayer.',
|
|
243
|
+
error_occurred: 'Une erreur est survenue. Veuillez réessayer.',
|
|
244
|
+
},
|
|
245
|
+
EN: {
|
|
246
|
+
closed: '✅ Conversation closed',
|
|
247
|
+
csat_question: 'Was this conversation helpful?',
|
|
248
|
+
csat_positive: '👍 Thank you for your feedback!',
|
|
249
|
+
csat_negative: '👎 Thank you, we will improve.',
|
|
250
|
+
new_conv: '💬 New conversation',
|
|
251
|
+
back_to_list: '← Back to conversations',
|
|
252
|
+
agent_taken: 'An agent has taken over your conversation. You can now write to them directly.',
|
|
253
|
+
agent_released: 'Our virtual assistant is back. How can I help you?',
|
|
254
|
+
close_confirm: 'Do you want to end this conversation?',
|
|
255
|
+
terminated: '✅ Conversation ended',
|
|
256
|
+
virtual: '🤖 Virtual assistant',
|
|
257
|
+
human_agent: '👤 Human agent',
|
|
258
|
+
loading: 'Loading...',
|
|
259
|
+
no_conv: 'No conversations yet.',
|
|
260
|
+
new_conv_btn: '💬 New conversation',
|
|
261
|
+
error_load: 'Loading error.',
|
|
262
|
+
recent_conv: 'Your recent conversations',
|
|
263
|
+
hello: 'Hello',
|
|
264
|
+
close_btn: '✅ End conversation',
|
|
265
|
+
speak_agent: '👤 Talk to an agent',
|
|
266
|
+
write_msg: 'Write your message...',
|
|
267
|
+
limit_reached: 'You have reached the 30 message limit. Please contact an agent or write to support@travelplanet.com.',
|
|
268
|
+
too_many: 'Too many messages. Please wait before retrying.',
|
|
269
|
+
error_occurred: 'An error occurred. Please try again.',
|
|
270
|
+
},
|
|
271
|
+
DE: {
|
|
272
|
+
closed: '✅ Gespräch beendet',
|
|
273
|
+
csat_question: 'War dieses Gespräch hilfreich?',
|
|
274
|
+
csat_positive: '👍 Vielen Dank für Ihr Feedback!',
|
|
275
|
+
csat_negative: '👎 Danke, wir werden uns verbessern.',
|
|
276
|
+
new_conv: '💬 Neues Gespräch',
|
|
277
|
+
back_to_list: '← Zurück zur Liste',
|
|
278
|
+
agent_taken: 'Ein Agent hat Ihr Gespräch übernommen. Sie können ihm jetzt direkt schreiben.',
|
|
279
|
+
agent_released: 'Unser virtueller Assistent ist zurück. Wie kann ich Ihnen helfen?',
|
|
280
|
+
close_confirm: 'Möchten Sie das Gespräch beenden?',
|
|
281
|
+
terminated: '✅ Gespräch beendet',
|
|
282
|
+
virtual: '🤖 Virtueller Assistent',
|
|
283
|
+
human_agent: '👤 Menschlicher Agent',
|
|
284
|
+
loading: 'Laden...',
|
|
285
|
+
no_conv: 'Noch keine Gespräche.',
|
|
286
|
+
new_conv_btn: '💬 Neues Gespräch',
|
|
287
|
+
error_load: 'Ladefehler.',
|
|
288
|
+
recent_conv: 'Ihre letzten Gespräche',
|
|
289
|
+
hello: 'Hallo',
|
|
290
|
+
close_btn: '✅ Gespräch beenden',
|
|
291
|
+
speak_agent: '👤 Mit einem Agenten sprechen',
|
|
292
|
+
write_msg: 'Schreiben Sie Ihre Nachricht...',
|
|
293
|
+
limit_reached: 'Sie haben das Limit von 30 Nachrichten erreicht.',
|
|
294
|
+
too_many: 'Zu viele Nachrichten. Bitte warten Sie.',
|
|
295
|
+
error_occurred: 'Ein Fehler ist aufgetreten. Bitte versuchen Sie es erneut.',
|
|
296
|
+
},
|
|
297
|
+
ES: {
|
|
298
|
+
closed: '✅ Conversación cerrada',
|
|
299
|
+
csat_question: '¿Fue útil esta conversación?',
|
|
300
|
+
csat_positive: '👍 ¡Gracias por su opinión!',
|
|
301
|
+
csat_negative: '👎 Gracias, mejoraremos.',
|
|
302
|
+
new_conv: '💬 Nueva conversación',
|
|
303
|
+
back_to_list: '← Volver a las conversaciones',
|
|
304
|
+
agent_taken: 'Un agente ha tomado su conversación. Ahora puede escribirle directamente.',
|
|
305
|
+
agent_released: 'Nuestro asistente virtual ha vuelto. ¿En qué puedo ayudarle?',
|
|
306
|
+
close_confirm: '¿Desea finalizar esta conversación?',
|
|
307
|
+
terminated: '✅ Conversación terminada',
|
|
308
|
+
virtual: '🤖 Asistente virtual',
|
|
309
|
+
human_agent: '👤 Agente humano',
|
|
310
|
+
loading: 'Cargando...',
|
|
311
|
+
no_conv: 'No hay conversaciones por el momento.',
|
|
312
|
+
new_conv_btn: '💬 Nueva conversación',
|
|
313
|
+
error_load: 'Error de carga.',
|
|
314
|
+
recent_conv: 'Sus conversaciones recientes',
|
|
315
|
+
hello: 'Hola',
|
|
316
|
+
close_btn: '✅ Finalizar conversación',
|
|
317
|
+
speak_agent: '👤 Hablar con un agente',
|
|
318
|
+
write_msg: 'Escriba su mensaje...',
|
|
319
|
+
limit_reached: 'Ha alcanzado el límite de 30 mensajes.',
|
|
320
|
+
too_many: 'Demasiados mensajes. Por favor espere.',
|
|
321
|
+
error_occurred: 'Se produjo un error. Por favor, inténtelo de nuevo.',
|
|
322
|
+
},
|
|
323
|
+
IT: {
|
|
324
|
+
closed: '✅ Conversazione chiusa',
|
|
325
|
+
csat_question: 'Questa conversazione è stata utile?',
|
|
326
|
+
csat_positive: '👍 Grazie per il suo feedback!',
|
|
327
|
+
csat_negative: '👎 Grazie, miglioreremo.',
|
|
328
|
+
new_conv: '💬 Nuova conversazione',
|
|
329
|
+
back_to_list: '← Torna alle conversazioni',
|
|
330
|
+
agent_taken: 'Un agente ha preso in carico la sua conversazione.',
|
|
331
|
+
agent_released: 'Il nostro assistente virtuale è tornato. Come posso aiutarla?',
|
|
332
|
+
close_confirm: 'Vuole terminare questa conversazione?',
|
|
333
|
+
terminated: '✅ Conversazione terminata',
|
|
334
|
+
virtual: '🤖 Assistente virtuale',
|
|
335
|
+
human_agent: '👤 Agente umano',
|
|
336
|
+
loading: 'Caricamento...',
|
|
337
|
+
no_conv: 'Nessuna conversazione per il momento.',
|
|
338
|
+
new_conv_btn: '💬 Nuova conversazione',
|
|
339
|
+
error_load: 'Errore di caricamento.',
|
|
340
|
+
recent_conv: 'Le sue conversazioni recenti',
|
|
341
|
+
hello: 'Ciao',
|
|
342
|
+
close_btn: '✅ Termina conversazione',
|
|
343
|
+
speak_agent: '👤 Parla con un agente',
|
|
344
|
+
write_msg: 'Scrivi il tuo messaggio...',
|
|
345
|
+
limit_reached: 'Hai raggiunto il limite di 30 messaggi.',
|
|
346
|
+
too_many: 'Troppi messaggi. Attendere prego.',
|
|
347
|
+
error_occurred: 'Si è verificato un errore. Riprova.',
|
|
348
|
+
},
|
|
349
|
+
NL: {
|
|
350
|
+
closed: '✅ Gesprek gesloten',
|
|
351
|
+
csat_question: 'Was dit gesprek nuttig?',
|
|
352
|
+
csat_positive: '👍 Bedankt voor uw feedback!',
|
|
353
|
+
csat_negative: '👎 Bedankt, we zullen verbeteren.',
|
|
354
|
+
new_conv: '💬 Nieuw gesprek',
|
|
355
|
+
back_to_list: '← Terug naar gesprekken',
|
|
356
|
+
agent_taken: 'Een agent heeft uw gesprek overgenomen.',
|
|
357
|
+
agent_released: 'Onze virtuele assistent is terug. Hoe kan ik u helpen?',
|
|
358
|
+
close_confirm: 'Wilt u dit gesprek beëindigen?',
|
|
359
|
+
terminated: '✅ Gesprek beëindigd',
|
|
360
|
+
virtual: '🤖 Virtuele assistent',
|
|
361
|
+
human_agent: '👤 Menselijke agent',
|
|
362
|
+
loading: 'Laden...',
|
|
363
|
+
no_conv: 'Geen gesprekken op dit moment.',
|
|
364
|
+
new_conv_btn: '💬 Nieuw gesprek',
|
|
365
|
+
error_load: 'Laadфout.',
|
|
366
|
+
recent_conv: 'Uw recente gesprekken',
|
|
367
|
+
hello: 'Hallo',
|
|
368
|
+
close_btn: '✅ Gesprek beëindigen',
|
|
369
|
+
speak_agent: '👤 Praat met een agent',
|
|
370
|
+
write_msg: 'Schrijf uw bericht...',
|
|
371
|
+
limit_reached: 'U heeft de limiet van 30 berichten bereikt.',
|
|
372
|
+
too_many: 'Te veel berichten. Wacht even.',
|
|
373
|
+
error_occurred: 'Er is een fout opgetreden. Probeer het opnieuw.',
|
|
374
|
+
},
|
|
375
|
+
};
|
|
376
|
+
return msgs[lang] || msgs['EN'];
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
getWelcomeMessage(first_name = '') {
|
|
380
|
+
const lang = (this.user_info?.language || 'EN').toUpperCase();
|
|
381
|
+
const client_name = getClientTheme(this.client_id).name;
|
|
382
|
+
const msgs = {
|
|
383
|
+
FR: `Bonjour ${first_name} ! 👋 Je m'appelle Maria, votre assistante virtuelle ${client_name}. Comment puis-je vous aider aujourd'hui ?`,
|
|
384
|
+
EN: `Hello ${first_name}! 👋 My name is Maria, your ${client_name} virtual assistant. How can I help you today?`,
|
|
385
|
+
DE: `Hallo ${first_name}! 👋 Ich bin Maria, Ihre virtuelle Assistentin von ${client_name}. Wie kann ich Ihnen helfen?`,
|
|
386
|
+
ES: `¡Hola ${first_name}! 👋 Me llamo Maria, tu asistente virtual de ${client_name}. ¿En qué puedo ayudarte?`,
|
|
387
|
+
IT: `Ciao ${first_name}! 👋 Mi chiamo Maria, la tua assistente virtuale di ${client_name}. Come posso aiutarti?`,
|
|
388
|
+
NL: `Hallo ${first_name}! 👋 Ik ben Maria, uw virtuele assistent van ${client_name}. Hoe kan ik u helpen?`,
|
|
389
|
+
};
|
|
390
|
+
return msgs[lang] || msgs['EN'];
|
|
391
|
+
}
|
|
392
|
+
|
|
130
393
|
// ─── Theme ────────────────────────────────────────────────────────────────
|
|
131
394
|
|
|
132
395
|
getThemeColor() {
|
|
@@ -167,10 +430,9 @@
|
|
|
167
430
|
document.head.appendChild(style);
|
|
168
431
|
|
|
169
432
|
const position = getClientTheme(this.client_id).position || 'right';
|
|
170
|
-
|
|
171
433
|
const host = this.querySelector('.tp-chatbot-host');
|
|
172
434
|
if (host) {
|
|
173
|
-
host.style.width =
|
|
435
|
+
host.style.width = WINDOW_WIDTH;
|
|
174
436
|
host.style.left = position === 'left' ? '24px' : 'auto';
|
|
175
437
|
host.style.right = position === 'left' ? 'auto' : '24px';
|
|
176
438
|
}
|
|
@@ -200,22 +462,24 @@
|
|
|
200
462
|
<div class="tp-chatbot-avatar">M</div>
|
|
201
463
|
<div style="flex:1">
|
|
202
464
|
<div class="tp-chatbot-title">${theme.name} Support</div>
|
|
203
|
-
<div class="tp-chatbot-subtitle" id="tp-subtitle">🤖 Assistant
|
|
465
|
+
<div class="tp-chatbot-subtitle" id="tp-subtitle">🤖 Assistant</div>
|
|
204
466
|
</div>
|
|
205
|
-
<button id="tp-sound-btn" style="background:none;border:none;color:white;font-size:
|
|
206
|
-
<button id="tp-
|
|
467
|
+
<button id="tp-sound-btn" style="background:none;border:none;color:white;font-size:15px;cursor:pointer;padding:4px 6px;" title="Sound">🔔</button>
|
|
468
|
+
<button id="tp-minimize-btn" style="background:none;border:none;color:white;font-size:15px;cursor:pointer;padding:4px 6px;" title="Minimize">─</button>
|
|
469
|
+
<button id="tp-maximize-btn" style="background:none;border:none;color:white;font-size:15px;cursor:pointer;padding:4px 6px;" title="Fullscreen">□</button>
|
|
470
|
+
<button id="tp-back-btn" style="display:none;background:none;border:none;color:white;font-size:18px;cursor:pointer;padding:4px 6px;">←</button>
|
|
207
471
|
</div>
|
|
208
472
|
<div class="tp-chatbot-messages" id="tp-messages"></div>
|
|
209
|
-
<div class="tp-chatbot-agent-bar" id="tp-agent-bar">
|
|
210
|
-
<button class="tp-chatbot-agent-btn" id="tp-agent-btn">👤
|
|
473
|
+
<div class="tp-chatbot-agent-bar" id="tp-agent-bar" style="display:none;">
|
|
474
|
+
<button class="tp-chatbot-agent-btn" id="tp-agent-btn">👤 Talk to an agent</button>
|
|
211
475
|
</div>
|
|
212
|
-
<div class="tp-chatbot-input-bar" id="tp-chatbot-input-bar">
|
|
213
|
-
<textarea class="tp-chatbot-input" id="tp-input" placeholder="
|
|
476
|
+
<div class="tp-chatbot-input-bar" id="tp-chatbot-input-bar" style="display:none;">
|
|
477
|
+
<textarea class="tp-chatbot-input" id="tp-input" placeholder="Write your message..." rows="1"></textarea>
|
|
214
478
|
<button class="tp-chatbot-send" id="tp-send">➤</button>
|
|
215
479
|
</div>
|
|
216
480
|
<div id="tp-close-bar" style="padding:8px 12px;border-top:1px solid #ede8f5;text-align:center;display:none;">
|
|
217
481
|
<button id="tp-close-btn" style="background:none;border:1px solid #e5e7eb;border-radius:8px;padding:6px 16px;font-size:12px;color:#9ca3af;cursor:pointer;font-weight:600;">
|
|
218
|
-
✅
|
|
482
|
+
✅ End conversation
|
|
219
483
|
</button>
|
|
220
484
|
</div>
|
|
221
485
|
</div>
|
|
@@ -224,28 +488,115 @@
|
|
|
224
488
|
`;
|
|
225
489
|
}
|
|
226
490
|
|
|
491
|
+
// ─── Window controls ──────────────────────────────────────────────────────
|
|
492
|
+
|
|
493
|
+
minimize() {
|
|
494
|
+
const win = this.querySelector('#tp-window');
|
|
495
|
+
// Exit fullscreen first if needed
|
|
496
|
+
if (this.is_fullscreen) this.exitFullscreen(false);
|
|
497
|
+
this.is_minimized = true;
|
|
498
|
+
win.style.height = '56px';
|
|
499
|
+
win.style.overflow = 'hidden';
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
enterFullscreen() {
|
|
503
|
+
const win = this.querySelector('#tp-window');
|
|
504
|
+
const host = this.querySelector('.tp-chatbot-host');
|
|
505
|
+
this.is_fullscreen = true;
|
|
506
|
+
this.is_minimized = false;
|
|
507
|
+
win.style.position = 'fixed';
|
|
508
|
+
win.style.top = '0';
|
|
509
|
+
win.style.left = '0';
|
|
510
|
+
win.style.width = '100vw';
|
|
511
|
+
win.style.height = '100vh';
|
|
512
|
+
win.style.borderRadius = '0';
|
|
513
|
+
win.style.zIndex = '99999';
|
|
514
|
+
host.style.width = '100vw';
|
|
515
|
+
this.querySelector('#tp-maximize-btn').textContent = '⤡';
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
exitFullscreen(restore_height = true) {
|
|
519
|
+
const win = this.querySelector('#tp-window');
|
|
520
|
+
const position = getClientTheme(this.client_id).position || 'right';
|
|
521
|
+
const host = this.querySelector('.tp-chatbot-host');
|
|
522
|
+
this.is_fullscreen = false;
|
|
523
|
+
win.style.position = 'absolute';
|
|
524
|
+
win.style.top = '';
|
|
525
|
+
win.style.left = '0';
|
|
526
|
+
win.style.width = WINDOW_WIDTH;
|
|
527
|
+
win.style.borderRadius = '16px';
|
|
528
|
+
win.style.zIndex = '';
|
|
529
|
+
host.style.width = WINDOW_WIDTH;
|
|
530
|
+
host.style.left = position === 'left' ? '24px' : 'auto';
|
|
531
|
+
host.style.right = position === 'left' ? 'auto' : '24px';
|
|
532
|
+
if (restore_height) win.style.height = WINDOW_HEIGHT;
|
|
533
|
+
win.style.overflow = 'hidden';
|
|
534
|
+
this.querySelector('#tp-maximize-btn').textContent = '□';
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
// ─── Events ───────────────────────────────────────────────────────────────
|
|
538
|
+
|
|
227
539
|
attachEvents() {
|
|
228
540
|
this.querySelector('#tp-bubble').addEventListener('click', () => this.toggleChat());
|
|
229
541
|
this.querySelector('#tp-send').addEventListener('click', () => this.sendMessage());
|
|
230
542
|
this.querySelector('#tp-agent-btn').addEventListener('click', () => this.requestAgent());
|
|
231
543
|
this.querySelector('#tp-back-btn').addEventListener('click', () => this.showConversationList());
|
|
544
|
+
|
|
232
545
|
this.querySelector('#tp-input').addEventListener('keydown', e => {
|
|
233
546
|
if (e.key === 'Enter' && !e.shiftKey) {
|
|
234
547
|
e.preventDefault();
|
|
235
548
|
this.sendMessage();
|
|
236
549
|
}
|
|
237
550
|
});
|
|
551
|
+
|
|
238
552
|
this.querySelector('#tp-sound-btn').addEventListener('click', () => {
|
|
239
553
|
this.sound_enabled = !this.sound_enabled;
|
|
240
|
-
|
|
241
|
-
btn.textContent = this.sound_enabled ? '🔔' : '🔕';
|
|
242
|
-
btn.title = this.sound_enabled ? 'Désactiver le son' : 'Activer le son';
|
|
554
|
+
this.querySelector('#tp-sound-btn').textContent = this.sound_enabled ? '🔔' : '🔕';
|
|
243
555
|
});
|
|
556
|
+
|
|
244
557
|
this.querySelector('#tp-close-btn').addEventListener('click', () => {
|
|
245
|
-
if (window.confirm(
|
|
558
|
+
if (window.confirm(this.getMessages().close_confirm)) {
|
|
246
559
|
this.closeByUser();
|
|
247
560
|
}
|
|
248
561
|
});
|
|
562
|
+
|
|
563
|
+
this.querySelector('#tp-minimize-btn').addEventListener('click', () => {
|
|
564
|
+
if (this.is_minimized) {
|
|
565
|
+
// Restore
|
|
566
|
+
this.is_minimized = false;
|
|
567
|
+
const win = this.querySelector('#tp-window');
|
|
568
|
+
win.style.height = WINDOW_HEIGHT;
|
|
569
|
+
win.style.overflow = 'hidden';
|
|
570
|
+
} else {
|
|
571
|
+
this.minimize();
|
|
572
|
+
}
|
|
573
|
+
});
|
|
574
|
+
|
|
575
|
+
this.querySelector('#tp-maximize-btn').addEventListener('click', () => {
|
|
576
|
+
if (this.is_fullscreen) {
|
|
577
|
+
this.exitFullscreen(true);
|
|
578
|
+
} else {
|
|
579
|
+
if (this.is_minimized) {
|
|
580
|
+
this.is_minimized = false;
|
|
581
|
+
const win = this.querySelector('#tp-window');
|
|
582
|
+
win.style.height = WINDOW_HEIGHT;
|
|
583
|
+
win.style.overflow = 'hidden';
|
|
584
|
+
}
|
|
585
|
+
this.enterFullscreen();
|
|
586
|
+
}
|
|
587
|
+
});
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
updateUILanguage() {
|
|
591
|
+
const m = this.getMessages();
|
|
592
|
+
const agent_btn = this.querySelector('#tp-agent-btn');
|
|
593
|
+
if (agent_btn) agent_btn.textContent = m.speak_agent;
|
|
594
|
+
const close_btn = this.querySelector('#tp-close-btn');
|
|
595
|
+
if (close_btn) close_btn.textContent = m.close_btn;
|
|
596
|
+
const input = this.querySelector('#tp-input');
|
|
597
|
+
if (input) input.placeholder = m.write_msg;
|
|
598
|
+
const subtitle = this.querySelector('#tp-subtitle');
|
|
599
|
+
if (subtitle) subtitle.textContent = m.virtual;
|
|
249
600
|
}
|
|
250
601
|
|
|
251
602
|
toggleChat() {
|
|
@@ -255,9 +606,16 @@
|
|
|
255
606
|
if (this.is_open) {
|
|
256
607
|
window_el.classList.add('open');
|
|
257
608
|
bubble.textContent = '✕';
|
|
609
|
+
// Restore from minimized if needed
|
|
610
|
+
if (this.is_minimized) {
|
|
611
|
+
this.is_minimized = false;
|
|
612
|
+
window_el.style.height = WINDOW_HEIGHT;
|
|
613
|
+
window_el.style.overflow = 'hidden';
|
|
614
|
+
}
|
|
258
615
|
} else {
|
|
259
616
|
window_el.classList.remove('open');
|
|
260
617
|
bubble.textContent = '💬';
|
|
618
|
+
if (this.is_fullscreen) this.exitFullscreen(true);
|
|
261
619
|
}
|
|
262
620
|
}
|
|
263
621
|
|
|
@@ -282,10 +640,55 @@
|
|
|
282
640
|
return data.result?.conversation_id;
|
|
283
641
|
}
|
|
284
642
|
|
|
643
|
+
// ─── Suggestions ──────────────────────────────────────────────────────────
|
|
644
|
+
|
|
645
|
+
showSuggestions(suggestions) {
|
|
646
|
+
const existing = this.querySelector('#tp-suggestions');
|
|
647
|
+
if (existing) existing.remove();
|
|
648
|
+
if (!suggestions || suggestions.length === 0) return;
|
|
649
|
+
|
|
650
|
+
const lang = (this.user_info?.language || 'EN').toUpperCase();
|
|
651
|
+
const color = this.getThemeColor();
|
|
652
|
+
const container = this.querySelector('#tp-messages');
|
|
653
|
+
|
|
654
|
+
const wrap = document.createElement('div');
|
|
655
|
+
wrap.id = 'tp-suggestions';
|
|
656
|
+
wrap.style.cssText = 'padding:8px 12px 12px;display:flex;flex-direction:column;gap:6px;';
|
|
657
|
+
|
|
658
|
+
suggestions.forEach(s => {
|
|
659
|
+
const label = s[lang] || s['EN'];
|
|
660
|
+
const btn = document.createElement('button');
|
|
661
|
+
btn.style.cssText = `padding:8px 14px;border-radius:20px;border:1.5px solid ${color};background:white;color:${color};font-size:12px;font-weight:600;cursor:pointer;text-align:left;font-family:inherit;transition:all 0.12s;`;
|
|
662
|
+
btn.textContent = `→ ${label}`;
|
|
663
|
+
btn.addEventListener('mouseenter', () => {
|
|
664
|
+
btn.style.background = color;
|
|
665
|
+
btn.style.color = 'white';
|
|
666
|
+
});
|
|
667
|
+
btn.addEventListener('mouseleave', () => {
|
|
668
|
+
btn.style.background = 'white';
|
|
669
|
+
btn.style.color = color;
|
|
670
|
+
});
|
|
671
|
+
btn.addEventListener('click', () => {
|
|
672
|
+
const sugg_el = this.querySelector('#tp-suggestions');
|
|
673
|
+
if (sugg_el) sugg_el.remove();
|
|
674
|
+
const input = this.querySelector('#tp-input');
|
|
675
|
+
if (input) {
|
|
676
|
+
input.value = label;
|
|
677
|
+
this.sendMessage();
|
|
678
|
+
}
|
|
679
|
+
});
|
|
680
|
+
wrap.appendChild(btn);
|
|
681
|
+
});
|
|
682
|
+
|
|
683
|
+
container.appendChild(wrap);
|
|
684
|
+
container.scrollTop = container.scrollHeight;
|
|
685
|
+
}
|
|
686
|
+
|
|
285
687
|
// ─── Conversation List ────────────────────────────────────────────────────
|
|
286
688
|
|
|
287
689
|
async showConversationList() {
|
|
288
690
|
this.view = 'list';
|
|
691
|
+
const m = this.getMessages();
|
|
289
692
|
|
|
290
693
|
if (this.polling_interval) {
|
|
291
694
|
clearInterval(this.polling_interval);
|
|
@@ -293,7 +696,7 @@
|
|
|
293
696
|
}
|
|
294
697
|
|
|
295
698
|
const subtitle = this.querySelector('#tp-subtitle');
|
|
296
|
-
if (subtitle) subtitle.textContent =
|
|
699
|
+
if (subtitle) subtitle.textContent = m.virtual;
|
|
297
700
|
|
|
298
701
|
const back_btn = this.querySelector('#tp-back-btn');
|
|
299
702
|
if (back_btn) back_btn.style.display = 'none';
|
|
@@ -308,31 +711,29 @@
|
|
|
308
711
|
if (close_bar) close_bar.style.display = 'none';
|
|
309
712
|
|
|
310
713
|
const container = this.querySelector('#tp-messages');
|
|
311
|
-
container.innerHTML =
|
|
714
|
+
container.innerHTML = `<div style="padding:16px;text-align:center;color:#aaa;font-size:12px;">${m.loading}</div>`;
|
|
312
715
|
|
|
313
716
|
try {
|
|
314
717
|
const url = `${this.api_url}/chat/conversations?user_id=${encodeURIComponent(this.user_id)}&limit=5`;
|
|
315
718
|
const response = await fetch(url, { headers: this.getHeaders() });
|
|
316
719
|
const data = await response.json();
|
|
317
720
|
const conversations = data.result?.conversations || [];
|
|
318
|
-
|
|
319
721
|
const color = this.getThemeColor();
|
|
320
|
-
const dark = this.shadeColor(color, -20);
|
|
321
722
|
|
|
322
723
|
container.innerHTML = '';
|
|
323
724
|
|
|
324
725
|
const header_el = document.createElement('div');
|
|
325
726
|
header_el.style.cssText = 'padding:16px;border-bottom:1px solid #ede8f5;';
|
|
326
727
|
header_el.innerHTML = `
|
|
327
|
-
<div style="font-size:13px;font-weight:700;color:#1a1a2e;margin-bottom:4px;"
|
|
328
|
-
<div style="font-size:12px;color:#aaa;"
|
|
728
|
+
<div style="font-size:13px;font-weight:700;color:#1a1a2e;margin-bottom:4px;">${m.hello} ${this.user_info?.first_name || ''} 👋</div>
|
|
729
|
+
<div style="font-size:12px;color:#aaa;">${m.recent_conv}</div>
|
|
329
730
|
`;
|
|
330
731
|
container.appendChild(header_el);
|
|
331
732
|
|
|
332
733
|
if (conversations.length === 0) {
|
|
333
734
|
const empty = document.createElement('div');
|
|
334
735
|
empty.style.cssText = 'padding:24px 16px;text-align:center;color:#aaa;font-size:13px;';
|
|
335
|
-
empty.textContent =
|
|
736
|
+
empty.textContent = m.no_conv;
|
|
336
737
|
container.appendChild(empty);
|
|
337
738
|
} else {
|
|
338
739
|
conversations.forEach(conv => {
|
|
@@ -342,14 +743,13 @@
|
|
|
342
743
|
|
|
343
744
|
const is_closed = conv.status === 'closed';
|
|
344
745
|
const status_label = is_closed
|
|
345
|
-
? '✅
|
|
746
|
+
? '✅ Closed'
|
|
346
747
|
: conv.status === 'agent'
|
|
347
748
|
? '👤 Agent'
|
|
348
749
|
: conv.status === 'waiting_agent'
|
|
349
|
-
? '⏳
|
|
750
|
+
? '⏳ Waiting'
|
|
350
751
|
: '🤖 Bot';
|
|
351
752
|
const status_color = is_closed ? '#9ca3af' : conv.status === 'waiting_agent' ? '#f59e0b' : color;
|
|
352
|
-
|
|
353
753
|
const date = new Date(conv.updated_at).toLocaleDateString('fr-FR', {
|
|
354
754
|
day: '2-digit',
|
|
355
755
|
month: '2-digit',
|
|
@@ -364,7 +764,6 @@
|
|
|
364
764
|
</div>
|
|
365
765
|
<div style="font-size:11px;color:#aaa;">${conv.message_count} message${conv.message_count > 1 ? 's' : ''}</div>
|
|
366
766
|
`;
|
|
367
|
-
|
|
368
767
|
item.addEventListener('click', () => this.openConversation(conv.conversation_id, is_closed));
|
|
369
768
|
container.appendChild(item);
|
|
370
769
|
});
|
|
@@ -372,16 +771,12 @@
|
|
|
372
771
|
|
|
373
772
|
const new_btn_wrap = document.createElement('div');
|
|
374
773
|
new_btn_wrap.style.cssText = 'padding:16px;';
|
|
375
|
-
new_btn_wrap.innerHTML =
|
|
376
|
-
<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;">
|
|
377
|
-
💬 Nouvelle conversation
|
|
378
|
-
</button>
|
|
379
|
-
`;
|
|
774
|
+
new_btn_wrap.innerHTML = `<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;">${m.new_conv_btn}</button>`;
|
|
380
775
|
new_btn_wrap.querySelector('button').addEventListener('click', () => this.startNewConversation());
|
|
381
776
|
container.appendChild(new_btn_wrap);
|
|
382
777
|
} catch (e) {
|
|
383
778
|
console.error('showConversationList error:', e);
|
|
384
|
-
container.innerHTML =
|
|
779
|
+
container.innerHTML = `<div style="padding:16px;text-align:center;color:#ef4444;font-size:13px;">${m.error_load}</div>`;
|
|
385
780
|
}
|
|
386
781
|
}
|
|
387
782
|
|
|
@@ -394,6 +789,7 @@
|
|
|
394
789
|
this.agent_mode = false;
|
|
395
790
|
this.agent_requested = false;
|
|
396
791
|
|
|
792
|
+
const m = this.getMessages();
|
|
397
793
|
const back_btn = this.querySelector('#tp-back-btn');
|
|
398
794
|
if (back_btn) back_btn.style.display = 'block';
|
|
399
795
|
|
|
@@ -433,16 +829,17 @@
|
|
|
433
829
|
if (conv.status === 'agent') {
|
|
434
830
|
this.agent_mode = true;
|
|
435
831
|
const subtitle = this.querySelector('#tp-subtitle');
|
|
436
|
-
if (subtitle) subtitle.textContent =
|
|
832
|
+
if (subtitle) subtitle.textContent = m.human_agent;
|
|
437
833
|
if (agent_bar) agent_bar.style.display = 'none';
|
|
438
834
|
if (input_bar) input_bar.style.display = 'flex';
|
|
439
835
|
} else {
|
|
440
836
|
if (input_bar) input_bar.style.display = 'flex';
|
|
441
|
-
if (agent_bar) agent_bar.style.display = '
|
|
837
|
+
if (agent_bar) agent_bar.style.display = 'none';
|
|
442
838
|
const subtitle = this.querySelector('#tp-subtitle');
|
|
443
|
-
if (subtitle) subtitle.textContent =
|
|
839
|
+
if (subtitle) subtitle.textContent = m.virtual;
|
|
444
840
|
}
|
|
445
841
|
|
|
842
|
+
this.updateUILanguage();
|
|
446
843
|
this.polling_interval = setInterval(() => this.pollMessages(), 3000);
|
|
447
844
|
} catch (e) {
|
|
448
845
|
console.error('openConversation error:', e);
|
|
@@ -458,6 +855,8 @@
|
|
|
458
855
|
this.last_message_count = 0;
|
|
459
856
|
this.conversation_id = null;
|
|
460
857
|
|
|
858
|
+
const m = this.getMessages();
|
|
859
|
+
|
|
461
860
|
const back_btn = this.querySelector('#tp-back-btn');
|
|
462
861
|
if (back_btn) back_btn.style.display = 'block';
|
|
463
862
|
|
|
@@ -468,7 +867,7 @@
|
|
|
468
867
|
container.innerHTML = '';
|
|
469
868
|
|
|
470
869
|
const subtitle = this.querySelector('#tp-subtitle');
|
|
471
|
-
if (subtitle) subtitle.textContent =
|
|
870
|
+
if (subtitle) subtitle.textContent = m.virtual;
|
|
472
871
|
|
|
473
872
|
const input_bar = this.querySelector('#tp-chatbot-input-bar');
|
|
474
873
|
if (input_bar) input_bar.style.display = 'flex';
|
|
@@ -476,8 +875,16 @@
|
|
|
476
875
|
const agent_bar = this.querySelector('#tp-agent-bar');
|
|
477
876
|
if (agent_bar) agent_bar.style.display = 'none';
|
|
478
877
|
|
|
878
|
+
this.updateUILanguage();
|
|
479
879
|
this.conversation_id = await this.createConversation();
|
|
480
|
-
this.addMessage('assistant',
|
|
880
|
+
this.addMessage('assistant', this.getWelcomeMessage(this.user_info?.first_name || ''));
|
|
881
|
+
this.last_message_count = 1;
|
|
882
|
+
|
|
883
|
+
const theme = getClientTheme(this.client_id);
|
|
884
|
+
if (theme.suggestions && theme.suggestions.length > 0) {
|
|
885
|
+
this.showSuggestions(theme.suggestions);
|
|
886
|
+
}
|
|
887
|
+
|
|
481
888
|
this.polling_interval = setInterval(() => this.pollMessages(), 3000);
|
|
482
889
|
}
|
|
483
890
|
|
|
@@ -489,7 +896,6 @@
|
|
|
489
896
|
|
|
490
897
|
const input_bar = this.querySelector('#tp-chatbot-input-bar');
|
|
491
898
|
if (input_bar) input_bar.style.display = 'none';
|
|
492
|
-
|
|
493
899
|
const agent_bar = this.querySelector('#tp-agent-bar');
|
|
494
900
|
if (agent_bar) agent_bar.style.display = 'none';
|
|
495
901
|
|
|
@@ -499,18 +905,17 @@
|
|
|
499
905
|
const form_el = document.createElement('div');
|
|
500
906
|
form_el.id = 'tp-guest-form';
|
|
501
907
|
form_el.innerHTML = `
|
|
502
|
-
<div style="padding:
|
|
503
|
-
<p style="margin:
|
|
504
|
-
<input id="tp-guest-firstname" type="text" placeholder="
|
|
505
|
-
<input id="tp-guest-company" type="text" placeholder="
|
|
506
|
-
<input id="tp-guest-email" type="email" placeholder="Email *" style="padding:
|
|
507
|
-
<button id="tp-guest-submit" style="padding:
|
|
508
|
-
|
|
908
|
+
<div style="padding:16px;display:flex;flex-direction:column;gap:12px;">
|
|
909
|
+
<p style="margin:0;font-size:13px;color:#1a1a2e;font-weight:600;">Before we start, please identify yourself:</p>
|
|
910
|
+
<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;" />
|
|
911
|
+
<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;" />
|
|
912
|
+
<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;" />
|
|
913
|
+
<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;">
|
|
914
|
+
Start conversation →
|
|
509
915
|
</button>
|
|
510
|
-
<p id="tp-guest-error" style="margin:
|
|
916
|
+
<p id="tp-guest-error" style="margin:0;font-size:12px;color:#c0392b;display:none;"></p>
|
|
511
917
|
</div>
|
|
512
918
|
`;
|
|
513
|
-
|
|
514
919
|
container.appendChild(form_el);
|
|
515
920
|
this.querySelector('#tp-guest-submit').addEventListener('click', () => this.submitGuestForm());
|
|
516
921
|
}
|
|
@@ -522,36 +927,41 @@
|
|
|
522
927
|
const error_el = this.querySelector('#tp-guest-error');
|
|
523
928
|
|
|
524
929
|
if (!first_name || !company_name || !email) {
|
|
525
|
-
error_el.textContent = '
|
|
930
|
+
error_el.textContent = 'Please fill in all fields.';
|
|
526
931
|
error_el.style.display = 'block';
|
|
527
932
|
return;
|
|
528
933
|
}
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
if (!email_valid) {
|
|
532
|
-
error_el.textContent = 'Veuillez entrer un email valide.';
|
|
934
|
+
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
|
|
935
|
+
error_el.textContent = 'Please enter a valid email.';
|
|
533
936
|
error_el.style.display = 'block';
|
|
534
937
|
return;
|
|
535
938
|
}
|
|
536
939
|
|
|
537
|
-
this.user_info = { user_id: `guest-${email}`, first_name, last_name: '', company_name, email, language: '
|
|
940
|
+
this.user_info = { user_id: `guest-${email}`, first_name, last_name: '', company_name, email, language: 'EN', site_id: '' };
|
|
538
941
|
this.user_id = this.user_info.user_id;
|
|
539
942
|
|
|
540
943
|
const form_el = this.querySelector('#tp-guest-form');
|
|
541
944
|
if (form_el) form_el.remove();
|
|
945
|
+
|
|
542
946
|
this.view = 'chat';
|
|
543
947
|
this.conversation_id = await this.createConversation();
|
|
544
948
|
|
|
545
949
|
const input_bar = this.querySelector('#tp-chatbot-input-bar');
|
|
546
950
|
if (input_bar) input_bar.style.display = 'flex';
|
|
547
|
-
|
|
548
951
|
const agent_bar = this.querySelector('#tp-agent-bar');
|
|
549
|
-
if (agent_bar) agent_bar.style.display = '
|
|
550
|
-
|
|
952
|
+
if (agent_bar) agent_bar.style.display = 'none';
|
|
551
953
|
const close_bar = this.querySelector('#tp-close-bar');
|
|
552
954
|
if (close_bar) close_bar.style.display = 'block';
|
|
553
955
|
|
|
554
|
-
this.
|
|
956
|
+
this.updateUILanguage();
|
|
957
|
+
this.addMessage('assistant', this.getWelcomeMessage(first_name));
|
|
958
|
+
this.last_message_count = 1;
|
|
959
|
+
|
|
960
|
+
const theme = getClientTheme(this.client_id);
|
|
961
|
+
if (theme.suggestions && theme.suggestions.length > 0) {
|
|
962
|
+
this.showSuggestions(theme.suggestions);
|
|
963
|
+
}
|
|
964
|
+
|
|
555
965
|
this.polling_interval = setInterval(() => this.pollMessages(), 3000);
|
|
556
966
|
}
|
|
557
967
|
|
|
@@ -563,12 +973,12 @@
|
|
|
563
973
|
|
|
564
974
|
const role_label =
|
|
565
975
|
role === 'user'
|
|
566
|
-
? `👤 ${this.user_info?.first_name || '
|
|
976
|
+
? `👤 ${this.user_info?.first_name || 'You'}`
|
|
567
977
|
: role === 'agent'
|
|
568
978
|
? '👨💼 Agent'
|
|
569
979
|
: role === 'system'
|
|
570
980
|
? 'ℹ️ Info'
|
|
571
|
-
: '🤖
|
|
981
|
+
: '🤖 Maria';
|
|
572
982
|
|
|
573
983
|
const msg_el = document.createElement('div');
|
|
574
984
|
msg_el.className = `tp-chatbot-message ${role}`;
|
|
@@ -611,32 +1021,32 @@
|
|
|
611
1021
|
this.polling_interval = null;
|
|
612
1022
|
}
|
|
613
1023
|
|
|
1024
|
+
const m = this.getMessages();
|
|
614
1025
|
const subtitle = this.querySelector('#tp-subtitle');
|
|
615
|
-
if (subtitle) subtitle.textContent =
|
|
1026
|
+
if (subtitle) subtitle.textContent = m.terminated;
|
|
616
1027
|
|
|
617
1028
|
const input_bar = this.querySelector('#tp-chatbot-input-bar');
|
|
618
1029
|
if (input_bar) input_bar.style.display = 'none';
|
|
619
|
-
|
|
620
1030
|
const agent_bar = this.querySelector('#tp-agent-bar');
|
|
621
1031
|
if (agent_bar) agent_bar.style.display = 'none';
|
|
622
|
-
|
|
623
1032
|
const close_bar = this.querySelector('#tp-close-bar');
|
|
624
1033
|
if (close_bar) close_bar.style.display = 'none';
|
|
1034
|
+
const sugg_el = this.querySelector('#tp-suggestions');
|
|
1035
|
+
if (sugg_el) sugg_el.remove();
|
|
625
1036
|
|
|
626
1037
|
const container = this.querySelector('#tp-messages');
|
|
627
|
-
|
|
628
1038
|
const existing_banner = this.querySelector('#tp-closed-banner');
|
|
629
1039
|
if (existing_banner) {
|
|
630
|
-
if (existing_banner.querySelector('#tp-csat-block')) return;
|
|
631
|
-
existing_banner.remove();
|
|
1040
|
+
if (existing_banner.querySelector('#tp-csat-block')) return;
|
|
1041
|
+
existing_banner.remove();
|
|
632
1042
|
}
|
|
633
1043
|
|
|
634
1044
|
const color = this.getThemeColor();
|
|
635
1045
|
const dark = this.shadeColor(color, -20);
|
|
636
1046
|
|
|
637
1047
|
const csat_html = existing_csat
|
|
638
|
-
? `<div style="font-size:13px;color:#888;margin-bottom:14px;">${existing_csat === 'positive' ?
|
|
639
|
-
: `<div style="font-size:12px;color:#888;margin-bottom:10px;"
|
|
1048
|
+
? `<div style="font-size:13px;color:#888;margin-bottom:14px;">${existing_csat === 'positive' ? m.csat_positive : m.csat_negative}</div>`
|
|
1049
|
+
: `<div style="font-size:12px;color:#888;margin-bottom:10px;">${m.csat_question}</div>
|
|
640
1050
|
<div style="display:flex;gap:10px;justify-content:center;margin-bottom:14px;">
|
|
641
1051
|
<button id="tp-csat-positive" style="padding:8px 20px;border-radius:8px;border:1.5px solid #22c55e;background:white;color:#22c55e;font-size:18px;cursor:pointer;font-weight:700;">👍</button>
|
|
642
1052
|
<button id="tp-csat-negative" style="padding:8px 20px;border-radius:8px;border:1.5px solid #ef4444;background:white;color:#ef4444;font-size:18px;cursor:pointer;font-weight:700;">👎</button>
|
|
@@ -646,14 +1056,10 @@
|
|
|
646
1056
|
banner.id = 'tp-closed-banner';
|
|
647
1057
|
banner.innerHTML = `
|
|
648
1058
|
<div style="margin:16px;padding:16px;background:#f9f5ff;border:1px solid #ede8f5;border-radius:12px;text-align:center;">
|
|
649
|
-
<div style="font-size:13px;color:#1a1a2e;font-weight:600;margin-bottom:8px;"
|
|
1059
|
+
<div style="font-size:13px;color:#1a1a2e;font-weight:600;margin-bottom:8px;">${m.closed}</div>
|
|
650
1060
|
<div id="tp-csat-block">${csat_html}</div>
|
|
651
|
-
<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:600;cursor:pointer;margin-bottom:8px;width:100%;">
|
|
652
|
-
|
|
653
|
-
</button>
|
|
654
|
-
<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%;">
|
|
655
|
-
← Voir toutes les conversations
|
|
656
|
-
</button>
|
|
1061
|
+
<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:600;cursor:pointer;margin-bottom:8px;width:100%;">${m.new_conv}</button>
|
|
1062
|
+
<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>
|
|
657
1063
|
</div>
|
|
658
1064
|
`;
|
|
659
1065
|
container.appendChild(banner);
|
|
@@ -672,9 +1078,8 @@
|
|
|
672
1078
|
} catch (e) {
|
|
673
1079
|
console.error('CSAT error:', e);
|
|
674
1080
|
}
|
|
675
|
-
csat_block.innerHTML = `<div style="font-size:13px;color:#888;margin-bottom:14px;">${rating === 'positive' ?
|
|
1081
|
+
csat_block.innerHTML = `<div style="font-size:13px;color:#888;margin-bottom:14px;">${rating === 'positive' ? m.csat_positive : m.csat_negative}</div>`;
|
|
676
1082
|
};
|
|
677
|
-
|
|
678
1083
|
this.querySelector('#tp-csat-positive').addEventListener('click', () => submit_csat('positive'));
|
|
679
1084
|
this.querySelector('#tp-csat-negative').addEventListener('click', () => submit_csat('negative'));
|
|
680
1085
|
}
|
|
@@ -687,16 +1092,17 @@
|
|
|
687
1092
|
|
|
688
1093
|
async sendMessage() {
|
|
689
1094
|
if (this.is_closed || !this.conversation_id) return;
|
|
1095
|
+
const m = this.getMessages();
|
|
690
1096
|
const input = this.querySelector('#tp-input');
|
|
691
1097
|
const query = input.value.trim();
|
|
692
1098
|
if (!query || this.is_loading) return;
|
|
693
1099
|
|
|
694
|
-
const
|
|
1100
|
+
const sugg_el = this.querySelector('#tp-suggestions');
|
|
1101
|
+
if (sugg_el) sugg_el.remove();
|
|
1102
|
+
|
|
1103
|
+
const user_messages = this.messages.filter(msg => msg.role === 'user').length;
|
|
695
1104
|
if (user_messages >= 30) {
|
|
696
|
-
this.addMessage(
|
|
697
|
-
'system',
|
|
698
|
-
'Vous avez atteint la limite de 30 messages. Veuillez contacter un agent ou écrire à support@travelplanet.com.'
|
|
699
|
-
);
|
|
1105
|
+
this.addMessage('system', m.limit_reached);
|
|
700
1106
|
const input_bar = this.querySelector('#tp-chatbot-input-bar');
|
|
701
1107
|
if (input_bar) input_bar.style.display = 'none';
|
|
702
1108
|
return;
|
|
@@ -718,7 +1124,7 @@
|
|
|
718
1124
|
this.hideTyping();
|
|
719
1125
|
|
|
720
1126
|
if (response.status === 429) {
|
|
721
|
-
this.addMessage('system', data.error?.message ||
|
|
1127
|
+
this.addMessage('system', data.error?.message || m.too_many);
|
|
722
1128
|
this.is_loading = false;
|
|
723
1129
|
return;
|
|
724
1130
|
}
|
|
@@ -727,23 +1133,29 @@
|
|
|
727
1133
|
const source = data.result.source || null;
|
|
728
1134
|
|
|
729
1135
|
const subtitle = this.querySelector('#tp-subtitle');
|
|
730
|
-
if (subtitle) subtitle.textContent = this.agent_mode ?
|
|
1136
|
+
if (subtitle) subtitle.textContent = this.agent_mode ? m.human_agent : m.virtual;
|
|
731
1137
|
|
|
732
1138
|
if (data.result.reply) {
|
|
733
1139
|
this.addMessage(this.agent_mode ? 'agent' : 'assistant', data.result.reply);
|
|
1140
|
+
this.last_message_count += 2; // fix double message
|
|
734
1141
|
}
|
|
735
1142
|
|
|
736
|
-
|
|
737
|
-
const user_msg_count = this.messages.filter(m => m.role === 'user').length;
|
|
1143
|
+
const user_msg_count = this.messages.filter(msg => msg.role === 'user').length;
|
|
738
1144
|
const agent_bar = this.querySelector('#tp-agent-bar');
|
|
739
1145
|
if (agent_bar && !this.agent_requested && !this.agent_mode) {
|
|
740
|
-
if (
|
|
1146
|
+
if (
|
|
1147
|
+
source === 'fallback' ||
|
|
1148
|
+
source === 'clarification' ||
|
|
1149
|
+
source === 'no_match' ||
|
|
1150
|
+
source === 'out_of_scope' ||
|
|
1151
|
+
user_msg_count >= 5
|
|
1152
|
+
) {
|
|
741
1153
|
agent_bar.style.display = 'block';
|
|
742
1154
|
}
|
|
743
1155
|
}
|
|
744
1156
|
} catch {
|
|
745
1157
|
this.hideTyping();
|
|
746
|
-
this.addMessage('assistant',
|
|
1158
|
+
this.addMessage('assistant', m.error_occurred);
|
|
747
1159
|
}
|
|
748
1160
|
|
|
749
1161
|
this.is_loading = false;
|
|
@@ -763,6 +1175,7 @@
|
|
|
763
1175
|
});
|
|
764
1176
|
const data = await response.json();
|
|
765
1177
|
this.addMessage('system', data.result.message);
|
|
1178
|
+
this.last_message_count += 1;
|
|
766
1179
|
|
|
767
1180
|
if (data.result.status === 'no_agents' || data.result.status === 'outside_hours') {
|
|
768
1181
|
this.agent_requested = false;
|
|
@@ -802,6 +1215,7 @@
|
|
|
802
1215
|
const server_messages = conv.messages || [];
|
|
803
1216
|
const server_status = conv.status;
|
|
804
1217
|
const is_typing = conv.is_typing || false;
|
|
1218
|
+
const m = this.getMessages();
|
|
805
1219
|
|
|
806
1220
|
if (server_status === 'closed' && !this.is_closed) {
|
|
807
1221
|
this.showClosed();
|
|
@@ -830,6 +1244,10 @@
|
|
|
830
1244
|
const typing_el = this.querySelector('#tp-agent-typing');
|
|
831
1245
|
if (typing_el) typing_el.remove();
|
|
832
1246
|
this.addMessage('agent', msg.content);
|
|
1247
|
+
} else if (msg.role === 'assistant') {
|
|
1248
|
+
this.addMessage('assistant', msg.content);
|
|
1249
|
+
} else if (msg.role === 'system') {
|
|
1250
|
+
this.addMessage('system', msg.content);
|
|
833
1251
|
}
|
|
834
1252
|
});
|
|
835
1253
|
this.last_message_count = server_messages.length;
|
|
@@ -838,19 +1256,19 @@
|
|
|
838
1256
|
if (server_status === 'agent' && !this.agent_mode) {
|
|
839
1257
|
this.agent_mode = true;
|
|
840
1258
|
const subtitle = this.querySelector('#tp-subtitle');
|
|
841
|
-
if (subtitle) subtitle.textContent =
|
|
1259
|
+
if (subtitle) subtitle.textContent = m.human_agent;
|
|
842
1260
|
const agent_bar = this.querySelector('#tp-agent-bar');
|
|
843
1261
|
if (agent_bar) agent_bar.style.display = 'none';
|
|
844
|
-
this.addMessage('system',
|
|
1262
|
+
this.addMessage('system', m.agent_taken);
|
|
845
1263
|
this.last_message_count = server_messages.length;
|
|
846
1264
|
} else if (server_status === 'bot' && this.agent_mode) {
|
|
847
1265
|
this.agent_mode = false;
|
|
848
1266
|
this.agent_requested = false;
|
|
849
1267
|
const subtitle = this.querySelector('#tp-subtitle');
|
|
850
|
-
if (subtitle) subtitle.textContent =
|
|
1268
|
+
if (subtitle) subtitle.textContent = m.virtual;
|
|
851
1269
|
const agent_bar = this.querySelector('#tp-agent-bar');
|
|
852
1270
|
if (agent_bar) agent_bar.style.display = 'block';
|
|
853
|
-
this.addMessage('system',
|
|
1271
|
+
this.addMessage('system', m.agent_released);
|
|
854
1272
|
this.last_message_count = server_messages.length;
|
|
855
1273
|
}
|
|
856
1274
|
} catch (e) {
|