@developpement/tp-chatbot-widget 0.0.7 → 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 +508 -102
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,12 +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
|
-
|
|
481
|
-
this.
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
)
|
|
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
|
+
|
|
485
888
|
this.polling_interval = setInterval(() => this.pollMessages(), 3000);
|
|
486
889
|
}
|
|
487
890
|
|
|
@@ -493,7 +896,6 @@
|
|
|
493
896
|
|
|
494
897
|
const input_bar = this.querySelector('#tp-chatbot-input-bar');
|
|
495
898
|
if (input_bar) input_bar.style.display = 'none';
|
|
496
|
-
|
|
497
899
|
const agent_bar = this.querySelector('#tp-agent-bar');
|
|
498
900
|
if (agent_bar) agent_bar.style.display = 'none';
|
|
499
901
|
|
|
@@ -503,18 +905,17 @@
|
|
|
503
905
|
const form_el = document.createElement('div');
|
|
504
906
|
form_el.id = 'tp-guest-form';
|
|
505
907
|
form_el.innerHTML = `
|
|
506
|
-
<div style="padding:
|
|
507
|
-
<p style="margin:
|
|
508
|
-
<input id="tp-guest-firstname" type="text" placeholder="
|
|
509
|
-
<input id="tp-guest-company" type="text" placeholder="
|
|
510
|
-
<input id="tp-guest-email" type="email" placeholder="Email *" style="padding:
|
|
511
|
-
<button id="tp-guest-submit" style="padding:
|
|
512
|
-
|
|
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 →
|
|
513
915
|
</button>
|
|
514
|
-
<p id="tp-guest-error" style="margin:
|
|
916
|
+
<p id="tp-guest-error" style="margin:0;font-size:12px;color:#c0392b;display:none;"></p>
|
|
515
917
|
</div>
|
|
516
918
|
`;
|
|
517
|
-
|
|
518
919
|
container.appendChild(form_el);
|
|
519
920
|
this.querySelector('#tp-guest-submit').addEventListener('click', () => this.submitGuestForm());
|
|
520
921
|
}
|
|
@@ -526,40 +927,41 @@
|
|
|
526
927
|
const error_el = this.querySelector('#tp-guest-error');
|
|
527
928
|
|
|
528
929
|
if (!first_name || !company_name || !email) {
|
|
529
|
-
error_el.textContent = '
|
|
930
|
+
error_el.textContent = 'Please fill in all fields.';
|
|
530
931
|
error_el.style.display = 'block';
|
|
531
932
|
return;
|
|
532
933
|
}
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
if (!email_valid) {
|
|
536
|
-
error_el.textContent = 'Veuillez entrer un email valide.';
|
|
934
|
+
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
|
|
935
|
+
error_el.textContent = 'Please enter a valid email.';
|
|
537
936
|
error_el.style.display = 'block';
|
|
538
937
|
return;
|
|
539
938
|
}
|
|
540
939
|
|
|
541
|
-
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: '' };
|
|
542
941
|
this.user_id = this.user_info.user_id;
|
|
543
942
|
|
|
544
943
|
const form_el = this.querySelector('#tp-guest-form');
|
|
545
944
|
if (form_el) form_el.remove();
|
|
945
|
+
|
|
546
946
|
this.view = 'chat';
|
|
547
947
|
this.conversation_id = await this.createConversation();
|
|
548
948
|
|
|
549
949
|
const input_bar = this.querySelector('#tp-chatbot-input-bar');
|
|
550
950
|
if (input_bar) input_bar.style.display = 'flex';
|
|
551
|
-
|
|
552
951
|
const agent_bar = this.querySelector('#tp-agent-bar');
|
|
553
|
-
if (agent_bar) agent_bar.style.display = '
|
|
554
|
-
|
|
952
|
+
if (agent_bar) agent_bar.style.display = 'none';
|
|
555
953
|
const close_bar = this.querySelector('#tp-close-bar');
|
|
556
954
|
if (close_bar) close_bar.style.display = 'block';
|
|
557
955
|
|
|
558
|
-
|
|
559
|
-
this.addMessage(
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
);
|
|
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
|
+
|
|
563
965
|
this.polling_interval = setInterval(() => this.pollMessages(), 3000);
|
|
564
966
|
}
|
|
565
967
|
|
|
@@ -571,12 +973,12 @@
|
|
|
571
973
|
|
|
572
974
|
const role_label =
|
|
573
975
|
role === 'user'
|
|
574
|
-
? `👤 ${this.user_info?.first_name || '
|
|
976
|
+
? `👤 ${this.user_info?.first_name || 'You'}`
|
|
575
977
|
: role === 'agent'
|
|
576
978
|
? '👨💼 Agent'
|
|
577
979
|
: role === 'system'
|
|
578
980
|
? 'ℹ️ Info'
|
|
579
|
-
: '🤖
|
|
981
|
+
: '🤖 Maria';
|
|
580
982
|
|
|
581
983
|
const msg_el = document.createElement('div');
|
|
582
984
|
msg_el.className = `tp-chatbot-message ${role}`;
|
|
@@ -619,32 +1021,32 @@
|
|
|
619
1021
|
this.polling_interval = null;
|
|
620
1022
|
}
|
|
621
1023
|
|
|
1024
|
+
const m = this.getMessages();
|
|
622
1025
|
const subtitle = this.querySelector('#tp-subtitle');
|
|
623
|
-
if (subtitle) subtitle.textContent =
|
|
1026
|
+
if (subtitle) subtitle.textContent = m.terminated;
|
|
624
1027
|
|
|
625
1028
|
const input_bar = this.querySelector('#tp-chatbot-input-bar');
|
|
626
1029
|
if (input_bar) input_bar.style.display = 'none';
|
|
627
|
-
|
|
628
1030
|
const agent_bar = this.querySelector('#tp-agent-bar');
|
|
629
1031
|
if (agent_bar) agent_bar.style.display = 'none';
|
|
630
|
-
|
|
631
1032
|
const close_bar = this.querySelector('#tp-close-bar');
|
|
632
1033
|
if (close_bar) close_bar.style.display = 'none';
|
|
1034
|
+
const sugg_el = this.querySelector('#tp-suggestions');
|
|
1035
|
+
if (sugg_el) sugg_el.remove();
|
|
633
1036
|
|
|
634
1037
|
const container = this.querySelector('#tp-messages');
|
|
635
|
-
|
|
636
1038
|
const existing_banner = this.querySelector('#tp-closed-banner');
|
|
637
1039
|
if (existing_banner) {
|
|
638
|
-
if (existing_banner.querySelector('#tp-csat-block')) return;
|
|
639
|
-
existing_banner.remove();
|
|
1040
|
+
if (existing_banner.querySelector('#tp-csat-block')) return;
|
|
1041
|
+
existing_banner.remove();
|
|
640
1042
|
}
|
|
641
1043
|
|
|
642
1044
|
const color = this.getThemeColor();
|
|
643
1045
|
const dark = this.shadeColor(color, -20);
|
|
644
1046
|
|
|
645
1047
|
const csat_html = existing_csat
|
|
646
|
-
? `<div style="font-size:13px;color:#888;margin-bottom:14px;">${existing_csat === 'positive' ?
|
|
647
|
-
: `<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>
|
|
648
1050
|
<div style="display:flex;gap:10px;justify-content:center;margin-bottom:14px;">
|
|
649
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>
|
|
650
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>
|
|
@@ -654,14 +1056,10 @@
|
|
|
654
1056
|
banner.id = 'tp-closed-banner';
|
|
655
1057
|
banner.innerHTML = `
|
|
656
1058
|
<div style="margin:16px;padding:16px;background:#f9f5ff;border:1px solid #ede8f5;border-radius:12px;text-align:center;">
|
|
657
|
-
<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>
|
|
658
1060
|
<div id="tp-csat-block">${csat_html}</div>
|
|
659
|
-
<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%;">
|
|
660
|
-
|
|
661
|
-
</button>
|
|
662
|
-
<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%;">
|
|
663
|
-
← Voir toutes les conversations
|
|
664
|
-
</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>
|
|
665
1063
|
</div>
|
|
666
1064
|
`;
|
|
667
1065
|
container.appendChild(banner);
|
|
@@ -680,9 +1078,8 @@
|
|
|
680
1078
|
} catch (e) {
|
|
681
1079
|
console.error('CSAT error:', e);
|
|
682
1080
|
}
|
|
683
|
-
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>`;
|
|
684
1082
|
};
|
|
685
|
-
|
|
686
1083
|
this.querySelector('#tp-csat-positive').addEventListener('click', () => submit_csat('positive'));
|
|
687
1084
|
this.querySelector('#tp-csat-negative').addEventListener('click', () => submit_csat('negative'));
|
|
688
1085
|
}
|
|
@@ -695,16 +1092,17 @@
|
|
|
695
1092
|
|
|
696
1093
|
async sendMessage() {
|
|
697
1094
|
if (this.is_closed || !this.conversation_id) return;
|
|
1095
|
+
const m = this.getMessages();
|
|
698
1096
|
const input = this.querySelector('#tp-input');
|
|
699
1097
|
const query = input.value.trim();
|
|
700
1098
|
if (!query || this.is_loading) return;
|
|
701
1099
|
|
|
702
|
-
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;
|
|
703
1104
|
if (user_messages >= 30) {
|
|
704
|
-
this.addMessage(
|
|
705
|
-
'system',
|
|
706
|
-
'Vous avez atteint la limite de 30 messages. Veuillez contacter un agent ou écrire à support@travelplanet.com.'
|
|
707
|
-
);
|
|
1105
|
+
this.addMessage('system', m.limit_reached);
|
|
708
1106
|
const input_bar = this.querySelector('#tp-chatbot-input-bar');
|
|
709
1107
|
if (input_bar) input_bar.style.display = 'none';
|
|
710
1108
|
return;
|
|
@@ -726,7 +1124,7 @@
|
|
|
726
1124
|
this.hideTyping();
|
|
727
1125
|
|
|
728
1126
|
if (response.status === 429) {
|
|
729
|
-
this.addMessage('system', data.error?.message ||
|
|
1127
|
+
this.addMessage('system', data.error?.message || m.too_many);
|
|
730
1128
|
this.is_loading = false;
|
|
731
1129
|
return;
|
|
732
1130
|
}
|
|
@@ -735,23 +1133,29 @@
|
|
|
735
1133
|
const source = data.result.source || null;
|
|
736
1134
|
|
|
737
1135
|
const subtitle = this.querySelector('#tp-subtitle');
|
|
738
|
-
if (subtitle) subtitle.textContent = this.agent_mode ?
|
|
1136
|
+
if (subtitle) subtitle.textContent = this.agent_mode ? m.human_agent : m.virtual;
|
|
739
1137
|
|
|
740
1138
|
if (data.result.reply) {
|
|
741
1139
|
this.addMessage(this.agent_mode ? 'agent' : 'assistant', data.result.reply);
|
|
1140
|
+
this.last_message_count += 2; // fix double message
|
|
742
1141
|
}
|
|
743
1142
|
|
|
744
|
-
|
|
745
|
-
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;
|
|
746
1144
|
const agent_bar = this.querySelector('#tp-agent-bar');
|
|
747
1145
|
if (agent_bar && !this.agent_requested && !this.agent_mode) {
|
|
748
|
-
if (
|
|
1146
|
+
if (
|
|
1147
|
+
source === 'fallback' ||
|
|
1148
|
+
source === 'clarification' ||
|
|
1149
|
+
source === 'no_match' ||
|
|
1150
|
+
source === 'out_of_scope' ||
|
|
1151
|
+
user_msg_count >= 5
|
|
1152
|
+
) {
|
|
749
1153
|
agent_bar.style.display = 'block';
|
|
750
1154
|
}
|
|
751
1155
|
}
|
|
752
1156
|
} catch {
|
|
753
1157
|
this.hideTyping();
|
|
754
|
-
this.addMessage('assistant',
|
|
1158
|
+
this.addMessage('assistant', m.error_occurred);
|
|
755
1159
|
}
|
|
756
1160
|
|
|
757
1161
|
this.is_loading = false;
|
|
@@ -771,6 +1175,7 @@
|
|
|
771
1175
|
});
|
|
772
1176
|
const data = await response.json();
|
|
773
1177
|
this.addMessage('system', data.result.message);
|
|
1178
|
+
this.last_message_count += 1;
|
|
774
1179
|
|
|
775
1180
|
if (data.result.status === 'no_agents' || data.result.status === 'outside_hours') {
|
|
776
1181
|
this.agent_requested = false;
|
|
@@ -810,6 +1215,7 @@
|
|
|
810
1215
|
const server_messages = conv.messages || [];
|
|
811
1216
|
const server_status = conv.status;
|
|
812
1217
|
const is_typing = conv.is_typing || false;
|
|
1218
|
+
const m = this.getMessages();
|
|
813
1219
|
|
|
814
1220
|
if (server_status === 'closed' && !this.is_closed) {
|
|
815
1221
|
this.showClosed();
|
|
@@ -850,19 +1256,19 @@
|
|
|
850
1256
|
if (server_status === 'agent' && !this.agent_mode) {
|
|
851
1257
|
this.agent_mode = true;
|
|
852
1258
|
const subtitle = this.querySelector('#tp-subtitle');
|
|
853
|
-
if (subtitle) subtitle.textContent =
|
|
1259
|
+
if (subtitle) subtitle.textContent = m.human_agent;
|
|
854
1260
|
const agent_bar = this.querySelector('#tp-agent-bar');
|
|
855
1261
|
if (agent_bar) agent_bar.style.display = 'none';
|
|
856
|
-
this.addMessage('system',
|
|
1262
|
+
this.addMessage('system', m.agent_taken);
|
|
857
1263
|
this.last_message_count = server_messages.length;
|
|
858
1264
|
} else if (server_status === 'bot' && this.agent_mode) {
|
|
859
1265
|
this.agent_mode = false;
|
|
860
1266
|
this.agent_requested = false;
|
|
861
1267
|
const subtitle = this.querySelector('#tp-subtitle');
|
|
862
|
-
if (subtitle) subtitle.textContent =
|
|
1268
|
+
if (subtitle) subtitle.textContent = m.virtual;
|
|
863
1269
|
const agent_bar = this.querySelector('#tp-agent-bar');
|
|
864
1270
|
if (agent_bar) agent_bar.style.display = 'block';
|
|
865
|
-
this.addMessage('system',
|
|
1271
|
+
this.addMessage('system', m.agent_released);
|
|
866
1272
|
this.last_message_count = server_messages.length;
|
|
867
1273
|
}
|
|
868
1274
|
} catch (e) {
|