@developpement/tp-chatbot-widget 0.0.7 → 0.0.9
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 +2 -3
- package/src/chatbot.css +2 -2
- package/src/chatbot.js +558 -104
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@developpement/tp-chatbot-widget",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.9",
|
|
4
4
|
"description": "Chatbot widget for TravelPlanet / Makitizy",
|
|
5
5
|
"main": "src/chatbot.js",
|
|
6
6
|
"files": [
|
|
@@ -10,8 +10,7 @@
|
|
|
10
10
|
"keywords": [
|
|
11
11
|
"chatbot",
|
|
12
12
|
"widget",
|
|
13
|
-
"makitizy"
|
|
14
|
-
"travelplanet"
|
|
13
|
+
"makitizy"
|
|
15
14
|
],
|
|
16
15
|
"license": "ISC"
|
|
17
16
|
}
|
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,215 @@
|
|
|
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
|
+
outside_hours: next => `ℹ️ Notre support est actuellement fermé. Prochain créneau : ${next}.`,
|
|
245
|
+
no_agents: 'ℹ️ Aucun agent disponible pour le moment. Veuillez réessayer plus tard.',
|
|
246
|
+
waiting_agent: '✅ Votre demande a été transmise. Un agent va prendre en charge votre conversation.',
|
|
247
|
+
waiting_agent_already: "ℹ️ Vous êtes déjà en file d'attente. Merci de patienter.",
|
|
248
|
+
agent_already: 'ℹ️ Un agent gère déjà votre conversation.',
|
|
249
|
+
},
|
|
250
|
+
EN: {
|
|
251
|
+
closed: '✅ Conversation closed',
|
|
252
|
+
csat_question: 'Was this conversation helpful?',
|
|
253
|
+
csat_positive: '👍 Thank you for your feedback!',
|
|
254
|
+
csat_negative: '👎 Thank you, we will improve.',
|
|
255
|
+
new_conv: '💬 New conversation',
|
|
256
|
+
back_to_list: '← Back to conversations',
|
|
257
|
+
agent_taken: 'An agent has taken over your conversation. You can now write to them directly.',
|
|
258
|
+
agent_released: 'Our virtual assistant is back. How can I help you?',
|
|
259
|
+
close_confirm: 'Do you want to end this conversation?',
|
|
260
|
+
terminated: '✅ Conversation ended',
|
|
261
|
+
virtual: '🤖 Virtual assistant',
|
|
262
|
+
human_agent: '👤 Human agent',
|
|
263
|
+
loading: 'Loading...',
|
|
264
|
+
no_conv: 'No conversations yet.',
|
|
265
|
+
new_conv_btn: '💬 New conversation',
|
|
266
|
+
error_load: 'Loading error.',
|
|
267
|
+
recent_conv: 'Your recent conversations',
|
|
268
|
+
hello: 'Hello',
|
|
269
|
+
close_btn: '✅ End conversation',
|
|
270
|
+
speak_agent: '👤 Talk to an agent',
|
|
271
|
+
write_msg: 'Write your message...',
|
|
272
|
+
limit_reached: 'You have reached the 30 message limit. Please contact an agent or write to support@travelplanet.com.',
|
|
273
|
+
too_many: 'Too many messages. Please wait before retrying.',
|
|
274
|
+
error_occurred: 'An error occurred. Please try again.',
|
|
275
|
+
outside_hours: next => `ℹ️ Our support is currently closed. Next available slot: ${next}.`,
|
|
276
|
+
no_agents: 'ℹ️ No agent available at the moment. Please try again later.',
|
|
277
|
+
waiting_agent: '✅ Your request has been sent. An agent will take over shortly.',
|
|
278
|
+
waiting_agent_already: 'ℹ️ You are already in the queue. Please wait.',
|
|
279
|
+
agent_already: 'ℹ️ An agent is already handling your conversation.',
|
|
280
|
+
},
|
|
281
|
+
DE: {
|
|
282
|
+
closed: '✅ Gespräch beendet',
|
|
283
|
+
csat_question: 'War dieses Gespräch hilfreich?',
|
|
284
|
+
csat_positive: '👍 Vielen Dank für Ihr Feedback!',
|
|
285
|
+
csat_negative: '👎 Danke, wir werden uns verbessern.',
|
|
286
|
+
new_conv: '💬 Neues Gespräch',
|
|
287
|
+
back_to_list: '← Zurück zur Liste',
|
|
288
|
+
agent_taken: 'Ein Agent hat Ihr Gespräch übernommen. Sie können ihm jetzt direkt schreiben.',
|
|
289
|
+
agent_released: 'Unser virtueller Assistent ist zurück. Wie kann ich Ihnen helfen?',
|
|
290
|
+
close_confirm: 'Möchten Sie das Gespräch beenden?',
|
|
291
|
+
terminated: '✅ Gespräch beendet',
|
|
292
|
+
virtual: '🤖 Virtueller Assistent',
|
|
293
|
+
human_agent: '👤 Menschlicher Agent',
|
|
294
|
+
loading: 'Laden...',
|
|
295
|
+
no_conv: 'Noch keine Gespräche.',
|
|
296
|
+
new_conv_btn: '💬 Neues Gespräch',
|
|
297
|
+
error_load: 'Ladefehler.',
|
|
298
|
+
recent_conv: 'Ihre letzten Gespräche',
|
|
299
|
+
hello: 'Hallo',
|
|
300
|
+
close_btn: '✅ Gespräch beenden',
|
|
301
|
+
speak_agent: '👤 Mit einem Agenten sprechen',
|
|
302
|
+
write_msg: 'Schreiben Sie Ihre Nachricht...',
|
|
303
|
+
limit_reached: 'Sie haben das Limit von 30 Nachrichten erreicht.',
|
|
304
|
+
too_many: 'Zu viele Nachrichten. Bitte warten Sie.',
|
|
305
|
+
error_occurred: 'Ein Fehler ist aufgetreten. Bitte versuchen Sie es erneut.',
|
|
306
|
+
outside_hours: next => `ℹ️ Unser Support ist derzeit geschlossen. Nächster Termin: ${next}.`,
|
|
307
|
+
no_agents: 'ℹ️ Derzeit kein Agent verfügbar. Bitte versuchen Sie es später.',
|
|
308
|
+
waiting_agent: '✅ Ihre Anfrage wurde übermittelt. Ein Agent übernimmt gleich.',
|
|
309
|
+
waiting_agent_already: 'ℹ️ Sie sind bereits in der Warteschlange. Bitte warten.',
|
|
310
|
+
agent_already: 'ℹ️ Ein Agent betreut bereits Ihr Gespräch.',
|
|
311
|
+
},
|
|
312
|
+
ES: {
|
|
313
|
+
closed: '✅ Conversación cerrada',
|
|
314
|
+
csat_question: '¿Fue útil esta conversación?',
|
|
315
|
+
csat_positive: '👍 ¡Gracias por su opinión!',
|
|
316
|
+
csat_negative: '👎 Gracias, mejoraremos.',
|
|
317
|
+
new_conv: '💬 Nueva conversación',
|
|
318
|
+
back_to_list: '← Volver a las conversaciones',
|
|
319
|
+
agent_taken: 'Un agente ha tomado su conversación. Ahora puede escribirle directamente.',
|
|
320
|
+
agent_released: 'Nuestro asistente virtual ha vuelto. ¿En qué puedo ayudarle?',
|
|
321
|
+
close_confirm: '¿Desea finalizar esta conversación?',
|
|
322
|
+
terminated: '✅ Conversación terminada',
|
|
323
|
+
virtual: '🤖 Asistente virtual',
|
|
324
|
+
human_agent: '👤 Agente humano',
|
|
325
|
+
loading: 'Cargando...',
|
|
326
|
+
no_conv: 'No hay conversaciones por el momento.',
|
|
327
|
+
new_conv_btn: '💬 Nueva conversación',
|
|
328
|
+
error_load: 'Error de carga.',
|
|
329
|
+
recent_conv: 'Sus conversaciones recientes',
|
|
330
|
+
hello: 'Hola',
|
|
331
|
+
close_btn: '✅ Finalizar conversación',
|
|
332
|
+
speak_agent: '👤 Hablar con un agente',
|
|
333
|
+
write_msg: 'Escriba su mensaje...',
|
|
334
|
+
limit_reached: 'Ha alcanzado el límite de 30 mensajes.',
|
|
335
|
+
too_many: 'Demasiados mensajes. Por favor espere.',
|
|
336
|
+
error_occurred: 'Se produjo un error. Por favor, inténtelo de nuevo.',
|
|
337
|
+
outside_hours: next => `ℹ️ Nuestro soporte está cerrado. Próximo horario: ${next}.`,
|
|
338
|
+
no_agents: 'ℹ️ No hay agentes disponibles. Inténtelo más tarde.',
|
|
339
|
+
waiting_agent: '✅ Su solicitud ha sido enviada. Un agente le atenderá pronto.',
|
|
340
|
+
waiting_agent_already: 'ℹ️ Ya está en la cola de espera. Por favor, espere.',
|
|
341
|
+
agent_already: 'ℹ️ Un agente ya está gestionando su conversación.',
|
|
342
|
+
},
|
|
343
|
+
IT: {
|
|
344
|
+
closed: '✅ Conversazione chiusa',
|
|
345
|
+
csat_question: 'Questa conversazione è stata utile?',
|
|
346
|
+
csat_positive: '👍 Grazie per il suo feedback!',
|
|
347
|
+
csat_negative: '👎 Grazie, miglioreremo.',
|
|
348
|
+
new_conv: '💬 Nuova conversazione',
|
|
349
|
+
back_to_list: '← Torna alle conversazioni',
|
|
350
|
+
agent_taken: 'Un agente ha preso in carico la sua conversazione.',
|
|
351
|
+
agent_released: 'Il nostro assistente virtuale è tornato. Come posso aiutarla?',
|
|
352
|
+
close_confirm: 'Vuole terminare questa conversazione?',
|
|
353
|
+
terminated: '✅ Conversazione terminata',
|
|
354
|
+
virtual: '🤖 Assistente virtuale',
|
|
355
|
+
human_agent: '👤 Agente umano',
|
|
356
|
+
loading: 'Caricamento...',
|
|
357
|
+
no_conv: 'Nessuna conversazione per il momento.',
|
|
358
|
+
new_conv_btn: '💬 Nuova conversazione',
|
|
359
|
+
error_load: 'Errore di caricamento.',
|
|
360
|
+
recent_conv: 'Le sue conversazioni recenti',
|
|
361
|
+
hello: 'Ciao',
|
|
362
|
+
close_btn: '✅ Termina conversazione',
|
|
363
|
+
speak_agent: '👤 Parla con un agente',
|
|
364
|
+
write_msg: 'Scrivi il tuo messaggio...',
|
|
365
|
+
limit_reached: 'Hai raggiunto il limite di 30 messaggi.',
|
|
366
|
+
too_many: 'Troppi messaggi. Attendere prego.',
|
|
367
|
+
error_occurred: 'Si è verificato un errore. Riprova.',
|
|
368
|
+
outside_hours: next => `ℹ️ Il supporto è chiuso. Prossima disponibilità: ${next}.`,
|
|
369
|
+
no_agents: 'ℹ️ Nessun agente disponibile. Riprova più tardi.',
|
|
370
|
+
waiting_agent: '✅ Richiesta inviata. Un agente prenderà in carico la conversazione.',
|
|
371
|
+
waiting_agent_already: 'ℹ️ È già in coda. Attenda.',
|
|
372
|
+
agent_already: 'ℹ️ Un agente sta già gestendo la conversazione.',
|
|
373
|
+
},
|
|
374
|
+
NL: {
|
|
375
|
+
closed: '✅ Gesprek gesloten',
|
|
376
|
+
csat_question: 'Was dit gesprek nuttig?',
|
|
377
|
+
csat_positive: '👍 Bedankt voor uw feedback!',
|
|
378
|
+
csat_negative: '👎 Bedankt, we zullen verbeteren.',
|
|
379
|
+
new_conv: '💬 Nieuw gesprek',
|
|
380
|
+
back_to_list: '← Terug naar gesprekken',
|
|
381
|
+
agent_taken: 'Een agent heeft uw gesprek overgenomen.',
|
|
382
|
+
agent_released: 'Onze virtuele assistent is terug. Hoe kan ik u helpen?',
|
|
383
|
+
close_confirm: 'Wilt u dit gesprek beëindigen?',
|
|
384
|
+
terminated: '✅ Gesprek beëindigd',
|
|
385
|
+
virtual: '🤖 Virtuele assistent',
|
|
386
|
+
human_agent: '👤 Menselijke agent',
|
|
387
|
+
loading: 'Laden...',
|
|
388
|
+
no_conv: 'Geen gesprekken op dit moment.',
|
|
389
|
+
new_conv_btn: '💬 Nieuw gesprek',
|
|
390
|
+
error_load: 'Laadфout.',
|
|
391
|
+
recent_conv: 'Uw recente gesprekken',
|
|
392
|
+
hello: 'Hallo',
|
|
393
|
+
close_btn: '✅ Gesprek beëindigen',
|
|
394
|
+
speak_agent: '👤 Praat met een agent',
|
|
395
|
+
write_msg: 'Schrijf uw bericht...',
|
|
396
|
+
limit_reached: 'U heeft de limiet van 30 berichten bereikt.',
|
|
397
|
+
too_many: 'Te veel berichten. Wacht even.',
|
|
398
|
+
error_occurred: 'Er is een fout opgetreden. Probeer het opnieuw.',
|
|
399
|
+
outside_hours: next => `ℹ️ Onze support is gesloten. Volgende beschikbare tijd: ${next}.`,
|
|
400
|
+
no_agents: 'ℹ️ Geen agent beschikbaar. Probeer het later opnieuw.',
|
|
401
|
+
waiting_agent: '✅ Uw verzoek is verzonden. Een agent neemt het gesprek over.',
|
|
402
|
+
waiting_agent_already: 'ℹ️ U staat al in de wachtrij. Even geduld.',
|
|
403
|
+
agent_already: 'ℹ️ Een agent beheert uw gesprek al.',
|
|
404
|
+
},
|
|
405
|
+
};
|
|
406
|
+
return msgs[lang] || msgs['EN'];
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
getWelcomeMessage(first_name = '') {
|
|
410
|
+
const lang = (this.user_info?.language || 'EN').toUpperCase();
|
|
411
|
+
const client_name = getClientTheme(this.client_id).name;
|
|
412
|
+
const msgs = {
|
|
413
|
+
FR: `Bonjour ${first_name} ! 👋 Je m'appelle Maria, votre assistante virtuelle ${client_name}. Comment puis-je vous aider aujourd'hui ?`,
|
|
414
|
+
EN: `Hello ${first_name}! 👋 My name is Maria, your ${client_name} virtual assistant. How can I help you today?`,
|
|
415
|
+
DE: `Hallo ${first_name}! 👋 Ich bin Maria, Ihre virtuelle Assistentin von ${client_name}. Wie kann ich Ihnen helfen?`,
|
|
416
|
+
ES: `¡Hola ${first_name}! 👋 Me llamo Maria, tu asistente virtual de ${client_name}. ¿En qué puedo ayudarte?`,
|
|
417
|
+
IT: `Ciao ${first_name}! 👋 Mi chiamo Maria, la tua assistente virtuale di ${client_name}. Come posso aiutarti?`,
|
|
418
|
+
NL: `Hallo ${first_name}! 👋 Ik ben Maria, uw virtuele assistent van ${client_name}. Hoe kan ik u helpen?`,
|
|
419
|
+
};
|
|
420
|
+
return msgs[lang] || msgs['EN'];
|
|
421
|
+
}
|
|
422
|
+
|
|
130
423
|
// ─── Theme ────────────────────────────────────────────────────────────────
|
|
131
424
|
|
|
132
425
|
getThemeColor() {
|
|
@@ -167,10 +460,9 @@
|
|
|
167
460
|
document.head.appendChild(style);
|
|
168
461
|
|
|
169
462
|
const position = getClientTheme(this.client_id).position || 'right';
|
|
170
|
-
|
|
171
463
|
const host = this.querySelector('.tp-chatbot-host');
|
|
172
464
|
if (host) {
|
|
173
|
-
host.style.width =
|
|
465
|
+
host.style.width = WINDOW_WIDTH;
|
|
174
466
|
host.style.left = position === 'left' ? '24px' : 'auto';
|
|
175
467
|
host.style.right = position === 'left' ? 'auto' : '24px';
|
|
176
468
|
}
|
|
@@ -200,22 +492,24 @@
|
|
|
200
492
|
<div class="tp-chatbot-avatar">M</div>
|
|
201
493
|
<div style="flex:1">
|
|
202
494
|
<div class="tp-chatbot-title">${theme.name} Support</div>
|
|
203
|
-
<div class="tp-chatbot-subtitle" id="tp-subtitle">🤖 Assistant
|
|
495
|
+
<div class="tp-chatbot-subtitle" id="tp-subtitle">🤖 Assistant</div>
|
|
204
496
|
</div>
|
|
205
|
-
<button id="tp-sound-btn" style="background:none;border:none;color:white;font-size:
|
|
206
|
-
<button id="tp-
|
|
497
|
+
<button id="tp-sound-btn" style="background:none;border:none;color:white;font-size:15px;cursor:pointer;padding:4px 6px;" title="Sound">🔔</button>
|
|
498
|
+
<button id="tp-minimize-btn" style="background:none;border:none;color:white;font-size:15px;cursor:pointer;padding:4px 6px;" title="Minimize">─</button>
|
|
499
|
+
<button id="tp-maximize-btn" style="background:none;border:none;color:white;font-size:15px;cursor:pointer;padding:4px 6px;" title="Fullscreen">□</button>
|
|
500
|
+
<button id="tp-back-btn" style="display:none;background:none;border:none;color:white;font-size:18px;cursor:pointer;padding:4px 6px;">←</button>
|
|
207
501
|
</div>
|
|
208
502
|
<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">👤
|
|
503
|
+
<div class="tp-chatbot-agent-bar" id="tp-agent-bar" style="display:none;">
|
|
504
|
+
<button class="tp-chatbot-agent-btn" id="tp-agent-btn">👤 Talk to an agent</button>
|
|
211
505
|
</div>
|
|
212
|
-
<div class="tp-chatbot-input-bar" id="tp-chatbot-input-bar">
|
|
213
|
-
<textarea class="tp-chatbot-input" id="tp-input" placeholder="
|
|
506
|
+
<div class="tp-chatbot-input-bar" id="tp-chatbot-input-bar" style="display:none;">
|
|
507
|
+
<textarea class="tp-chatbot-input" id="tp-input" placeholder="Write your message..." rows="1"></textarea>
|
|
214
508
|
<button class="tp-chatbot-send" id="tp-send">➤</button>
|
|
215
509
|
</div>
|
|
216
510
|
<div id="tp-close-bar" style="padding:8px 12px;border-top:1px solid #ede8f5;text-align:center;display:none;">
|
|
217
511
|
<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
|
-
✅
|
|
512
|
+
✅ End conversation
|
|
219
513
|
</button>
|
|
220
514
|
</div>
|
|
221
515
|
</div>
|
|
@@ -224,28 +518,115 @@
|
|
|
224
518
|
`;
|
|
225
519
|
}
|
|
226
520
|
|
|
521
|
+
// ─── Window controls ──────────────────────────────────────────────────────
|
|
522
|
+
|
|
523
|
+
minimize() {
|
|
524
|
+
const win = this.querySelector('#tp-window');
|
|
525
|
+
// Exit fullscreen first if needed
|
|
526
|
+
if (this.is_fullscreen) this.exitFullscreen(false);
|
|
527
|
+
this.is_minimized = true;
|
|
528
|
+
win.style.height = '56px';
|
|
529
|
+
win.style.overflow = 'hidden';
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
enterFullscreen() {
|
|
533
|
+
const win = this.querySelector('#tp-window');
|
|
534
|
+
const host = this.querySelector('.tp-chatbot-host');
|
|
535
|
+
this.is_fullscreen = true;
|
|
536
|
+
this.is_minimized = false;
|
|
537
|
+
win.style.position = 'fixed';
|
|
538
|
+
win.style.top = '0';
|
|
539
|
+
win.style.left = '0';
|
|
540
|
+
win.style.width = '100vw';
|
|
541
|
+
win.style.height = '100vh';
|
|
542
|
+
win.style.borderRadius = '0';
|
|
543
|
+
win.style.zIndex = '99999';
|
|
544
|
+
host.style.width = '100vw';
|
|
545
|
+
this.querySelector('#tp-maximize-btn').textContent = '⤡';
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
exitFullscreen(restore_height = true) {
|
|
549
|
+
const win = this.querySelector('#tp-window');
|
|
550
|
+
const position = getClientTheme(this.client_id).position || 'right';
|
|
551
|
+
const host = this.querySelector('.tp-chatbot-host');
|
|
552
|
+
this.is_fullscreen = false;
|
|
553
|
+
win.style.position = 'absolute';
|
|
554
|
+
win.style.top = '';
|
|
555
|
+
win.style.left = '0';
|
|
556
|
+
win.style.width = WINDOW_WIDTH;
|
|
557
|
+
win.style.borderRadius = '16px';
|
|
558
|
+
win.style.zIndex = '';
|
|
559
|
+
host.style.width = WINDOW_WIDTH;
|
|
560
|
+
host.style.left = position === 'left' ? '24px' : 'auto';
|
|
561
|
+
host.style.right = position === 'left' ? 'auto' : '24px';
|
|
562
|
+
if (restore_height) win.style.height = WINDOW_HEIGHT;
|
|
563
|
+
win.style.overflow = 'hidden';
|
|
564
|
+
this.querySelector('#tp-maximize-btn').textContent = '□';
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
// ─── Events ───────────────────────────────────────────────────────────────
|
|
568
|
+
|
|
227
569
|
attachEvents() {
|
|
228
570
|
this.querySelector('#tp-bubble').addEventListener('click', () => this.toggleChat());
|
|
229
571
|
this.querySelector('#tp-send').addEventListener('click', () => this.sendMessage());
|
|
230
572
|
this.querySelector('#tp-agent-btn').addEventListener('click', () => this.requestAgent());
|
|
231
573
|
this.querySelector('#tp-back-btn').addEventListener('click', () => this.showConversationList());
|
|
574
|
+
|
|
232
575
|
this.querySelector('#tp-input').addEventListener('keydown', e => {
|
|
233
576
|
if (e.key === 'Enter' && !e.shiftKey) {
|
|
234
577
|
e.preventDefault();
|
|
235
578
|
this.sendMessage();
|
|
236
579
|
}
|
|
237
580
|
});
|
|
581
|
+
|
|
238
582
|
this.querySelector('#tp-sound-btn').addEventListener('click', () => {
|
|
239
583
|
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';
|
|
584
|
+
this.querySelector('#tp-sound-btn').textContent = this.sound_enabled ? '🔔' : '🔕';
|
|
243
585
|
});
|
|
586
|
+
|
|
244
587
|
this.querySelector('#tp-close-btn').addEventListener('click', () => {
|
|
245
|
-
if (window.confirm(
|
|
588
|
+
if (window.confirm(this.getMessages().close_confirm)) {
|
|
246
589
|
this.closeByUser();
|
|
247
590
|
}
|
|
248
591
|
});
|
|
592
|
+
|
|
593
|
+
this.querySelector('#tp-minimize-btn').addEventListener('click', () => {
|
|
594
|
+
if (this.is_minimized) {
|
|
595
|
+
// Restore
|
|
596
|
+
this.is_minimized = false;
|
|
597
|
+
const win = this.querySelector('#tp-window');
|
|
598
|
+
win.style.height = WINDOW_HEIGHT;
|
|
599
|
+
win.style.overflow = 'hidden';
|
|
600
|
+
} else {
|
|
601
|
+
this.minimize();
|
|
602
|
+
}
|
|
603
|
+
});
|
|
604
|
+
|
|
605
|
+
this.querySelector('#tp-maximize-btn').addEventListener('click', () => {
|
|
606
|
+
if (this.is_fullscreen) {
|
|
607
|
+
this.exitFullscreen(true);
|
|
608
|
+
} else {
|
|
609
|
+
if (this.is_minimized) {
|
|
610
|
+
this.is_minimized = false;
|
|
611
|
+
const win = this.querySelector('#tp-window');
|
|
612
|
+
win.style.height = WINDOW_HEIGHT;
|
|
613
|
+
win.style.overflow = 'hidden';
|
|
614
|
+
}
|
|
615
|
+
this.enterFullscreen();
|
|
616
|
+
}
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
updateUILanguage() {
|
|
621
|
+
const m = this.getMessages();
|
|
622
|
+
const agent_btn = this.querySelector('#tp-agent-btn');
|
|
623
|
+
if (agent_btn) agent_btn.textContent = m.speak_agent;
|
|
624
|
+
const close_btn = this.querySelector('#tp-close-btn');
|
|
625
|
+
if (close_btn) close_btn.textContent = m.close_btn;
|
|
626
|
+
const input = this.querySelector('#tp-input');
|
|
627
|
+
if (input) input.placeholder = m.write_msg;
|
|
628
|
+
const subtitle = this.querySelector('#tp-subtitle');
|
|
629
|
+
if (subtitle) subtitle.textContent = m.virtual;
|
|
249
630
|
}
|
|
250
631
|
|
|
251
632
|
toggleChat() {
|
|
@@ -255,9 +636,16 @@
|
|
|
255
636
|
if (this.is_open) {
|
|
256
637
|
window_el.classList.add('open');
|
|
257
638
|
bubble.textContent = '✕';
|
|
639
|
+
// Restore from minimized if needed
|
|
640
|
+
if (this.is_minimized) {
|
|
641
|
+
this.is_minimized = false;
|
|
642
|
+
window_el.style.height = WINDOW_HEIGHT;
|
|
643
|
+
window_el.style.overflow = 'hidden';
|
|
644
|
+
}
|
|
258
645
|
} else {
|
|
259
646
|
window_el.classList.remove('open');
|
|
260
647
|
bubble.textContent = '💬';
|
|
648
|
+
if (this.is_fullscreen) this.exitFullscreen(true);
|
|
261
649
|
}
|
|
262
650
|
}
|
|
263
651
|
|
|
@@ -282,10 +670,55 @@
|
|
|
282
670
|
return data.result?.conversation_id;
|
|
283
671
|
}
|
|
284
672
|
|
|
673
|
+
// ─── Suggestions ──────────────────────────────────────────────────────────
|
|
674
|
+
|
|
675
|
+
showSuggestions(suggestions) {
|
|
676
|
+
const existing = this.querySelector('#tp-suggestions');
|
|
677
|
+
if (existing) existing.remove();
|
|
678
|
+
if (!suggestions || suggestions.length === 0) return;
|
|
679
|
+
|
|
680
|
+
const lang = (this.user_info?.language || 'EN').toUpperCase();
|
|
681
|
+
const color = this.getThemeColor();
|
|
682
|
+
const container = this.querySelector('#tp-messages');
|
|
683
|
+
|
|
684
|
+
const wrap = document.createElement('div');
|
|
685
|
+
wrap.id = 'tp-suggestions';
|
|
686
|
+
wrap.style.cssText = 'padding:8px 12px 12px;display:flex;flex-direction:column;gap:6px;';
|
|
687
|
+
|
|
688
|
+
suggestions.forEach(s => {
|
|
689
|
+
const label = s[lang] || s['EN'];
|
|
690
|
+
const btn = document.createElement('button');
|
|
691
|
+
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;`;
|
|
692
|
+
btn.textContent = `→ ${label}`;
|
|
693
|
+
btn.addEventListener('mouseenter', () => {
|
|
694
|
+
btn.style.background = color;
|
|
695
|
+
btn.style.color = 'white';
|
|
696
|
+
});
|
|
697
|
+
btn.addEventListener('mouseleave', () => {
|
|
698
|
+
btn.style.background = 'white';
|
|
699
|
+
btn.style.color = color;
|
|
700
|
+
});
|
|
701
|
+
btn.addEventListener('click', () => {
|
|
702
|
+
const sugg_el = this.querySelector('#tp-suggestions');
|
|
703
|
+
if (sugg_el) sugg_el.remove();
|
|
704
|
+
const input = this.querySelector('#tp-input');
|
|
705
|
+
if (input) {
|
|
706
|
+
input.value = label;
|
|
707
|
+
this.sendMessage();
|
|
708
|
+
}
|
|
709
|
+
});
|
|
710
|
+
wrap.appendChild(btn);
|
|
711
|
+
});
|
|
712
|
+
|
|
713
|
+
container.appendChild(wrap);
|
|
714
|
+
container.scrollTop = container.scrollHeight;
|
|
715
|
+
}
|
|
716
|
+
|
|
285
717
|
// ─── Conversation List ────────────────────────────────────────────────────
|
|
286
718
|
|
|
287
719
|
async showConversationList() {
|
|
288
720
|
this.view = 'list';
|
|
721
|
+
const m = this.getMessages();
|
|
289
722
|
|
|
290
723
|
if (this.polling_interval) {
|
|
291
724
|
clearInterval(this.polling_interval);
|
|
@@ -293,7 +726,7 @@
|
|
|
293
726
|
}
|
|
294
727
|
|
|
295
728
|
const subtitle = this.querySelector('#tp-subtitle');
|
|
296
|
-
if (subtitle) subtitle.textContent =
|
|
729
|
+
if (subtitle) subtitle.textContent = m.virtual;
|
|
297
730
|
|
|
298
731
|
const back_btn = this.querySelector('#tp-back-btn');
|
|
299
732
|
if (back_btn) back_btn.style.display = 'none';
|
|
@@ -308,31 +741,29 @@
|
|
|
308
741
|
if (close_bar) close_bar.style.display = 'none';
|
|
309
742
|
|
|
310
743
|
const container = this.querySelector('#tp-messages');
|
|
311
|
-
container.innerHTML =
|
|
744
|
+
container.innerHTML = `<div style="padding:16px;text-align:center;color:#aaa;font-size:12px;">${m.loading}</div>`;
|
|
312
745
|
|
|
313
746
|
try {
|
|
314
747
|
const url = `${this.api_url}/chat/conversations?user_id=${encodeURIComponent(this.user_id)}&limit=5`;
|
|
315
748
|
const response = await fetch(url, { headers: this.getHeaders() });
|
|
316
749
|
const data = await response.json();
|
|
317
750
|
const conversations = data.result?.conversations || [];
|
|
318
|
-
|
|
319
751
|
const color = this.getThemeColor();
|
|
320
|
-
const dark = this.shadeColor(color, -20);
|
|
321
752
|
|
|
322
753
|
container.innerHTML = '';
|
|
323
754
|
|
|
324
755
|
const header_el = document.createElement('div');
|
|
325
756
|
header_el.style.cssText = 'padding:16px;border-bottom:1px solid #ede8f5;';
|
|
326
757
|
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;"
|
|
758
|
+
<div style="font-size:13px;font-weight:700;color:#1a1a2e;margin-bottom:4px;">${m.hello} ${this.user_info?.first_name || ''} 👋</div>
|
|
759
|
+
<div style="font-size:12px;color:#aaa;">${m.recent_conv}</div>
|
|
329
760
|
`;
|
|
330
761
|
container.appendChild(header_el);
|
|
331
762
|
|
|
332
763
|
if (conversations.length === 0) {
|
|
333
764
|
const empty = document.createElement('div');
|
|
334
765
|
empty.style.cssText = 'padding:24px 16px;text-align:center;color:#aaa;font-size:13px;';
|
|
335
|
-
empty.textContent =
|
|
766
|
+
empty.textContent = m.no_conv;
|
|
336
767
|
container.appendChild(empty);
|
|
337
768
|
} else {
|
|
338
769
|
conversations.forEach(conv => {
|
|
@@ -342,14 +773,13 @@
|
|
|
342
773
|
|
|
343
774
|
const is_closed = conv.status === 'closed';
|
|
344
775
|
const status_label = is_closed
|
|
345
|
-
? '✅
|
|
776
|
+
? '✅ Closed'
|
|
346
777
|
: conv.status === 'agent'
|
|
347
778
|
? '👤 Agent'
|
|
348
779
|
: conv.status === 'waiting_agent'
|
|
349
|
-
? '⏳
|
|
780
|
+
? '⏳ Waiting'
|
|
350
781
|
: '🤖 Bot';
|
|
351
782
|
const status_color = is_closed ? '#9ca3af' : conv.status === 'waiting_agent' ? '#f59e0b' : color;
|
|
352
|
-
|
|
353
783
|
const date = new Date(conv.updated_at).toLocaleDateString('fr-FR', {
|
|
354
784
|
day: '2-digit',
|
|
355
785
|
month: '2-digit',
|
|
@@ -364,7 +794,6 @@
|
|
|
364
794
|
</div>
|
|
365
795
|
<div style="font-size:11px;color:#aaa;">${conv.message_count} message${conv.message_count > 1 ? 's' : ''}</div>
|
|
366
796
|
`;
|
|
367
|
-
|
|
368
797
|
item.addEventListener('click', () => this.openConversation(conv.conversation_id, is_closed));
|
|
369
798
|
container.appendChild(item);
|
|
370
799
|
});
|
|
@@ -372,16 +801,12 @@
|
|
|
372
801
|
|
|
373
802
|
const new_btn_wrap = document.createElement('div');
|
|
374
803
|
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
|
-
`;
|
|
804
|
+
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
805
|
new_btn_wrap.querySelector('button').addEventListener('click', () => this.startNewConversation());
|
|
381
806
|
container.appendChild(new_btn_wrap);
|
|
382
807
|
} catch (e) {
|
|
383
808
|
console.error('showConversationList error:', e);
|
|
384
|
-
container.innerHTML =
|
|
809
|
+
container.innerHTML = `<div style="padding:16px;text-align:center;color:#ef4444;font-size:13px;">${m.error_load}</div>`;
|
|
385
810
|
}
|
|
386
811
|
}
|
|
387
812
|
|
|
@@ -394,6 +819,7 @@
|
|
|
394
819
|
this.agent_mode = false;
|
|
395
820
|
this.agent_requested = false;
|
|
396
821
|
|
|
822
|
+
const m = this.getMessages();
|
|
397
823
|
const back_btn = this.querySelector('#tp-back-btn');
|
|
398
824
|
if (back_btn) back_btn.style.display = 'block';
|
|
399
825
|
|
|
@@ -433,16 +859,17 @@
|
|
|
433
859
|
if (conv.status === 'agent') {
|
|
434
860
|
this.agent_mode = true;
|
|
435
861
|
const subtitle = this.querySelector('#tp-subtitle');
|
|
436
|
-
if (subtitle) subtitle.textContent =
|
|
862
|
+
if (subtitle) subtitle.textContent = m.human_agent;
|
|
437
863
|
if (agent_bar) agent_bar.style.display = 'none';
|
|
438
864
|
if (input_bar) input_bar.style.display = 'flex';
|
|
439
865
|
} else {
|
|
440
866
|
if (input_bar) input_bar.style.display = 'flex';
|
|
441
|
-
if (agent_bar) agent_bar.style.display = '
|
|
867
|
+
if (agent_bar) agent_bar.style.display = 'none';
|
|
442
868
|
const subtitle = this.querySelector('#tp-subtitle');
|
|
443
|
-
if (subtitle) subtitle.textContent =
|
|
869
|
+
if (subtitle) subtitle.textContent = m.virtual;
|
|
444
870
|
}
|
|
445
871
|
|
|
872
|
+
this.updateUILanguage();
|
|
446
873
|
this.polling_interval = setInterval(() => this.pollMessages(), 3000);
|
|
447
874
|
} catch (e) {
|
|
448
875
|
console.error('openConversation error:', e);
|
|
@@ -458,6 +885,8 @@
|
|
|
458
885
|
this.last_message_count = 0;
|
|
459
886
|
this.conversation_id = null;
|
|
460
887
|
|
|
888
|
+
const m = this.getMessages();
|
|
889
|
+
|
|
461
890
|
const back_btn = this.querySelector('#tp-back-btn');
|
|
462
891
|
if (back_btn) back_btn.style.display = 'block';
|
|
463
892
|
|
|
@@ -468,7 +897,7 @@
|
|
|
468
897
|
container.innerHTML = '';
|
|
469
898
|
|
|
470
899
|
const subtitle = this.querySelector('#tp-subtitle');
|
|
471
|
-
if (subtitle) subtitle.textContent =
|
|
900
|
+
if (subtitle) subtitle.textContent = m.virtual;
|
|
472
901
|
|
|
473
902
|
const input_bar = this.querySelector('#tp-chatbot-input-bar');
|
|
474
903
|
if (input_bar) input_bar.style.display = 'flex';
|
|
@@ -476,12 +905,16 @@
|
|
|
476
905
|
const agent_bar = this.querySelector('#tp-agent-bar');
|
|
477
906
|
if (agent_bar) agent_bar.style.display = 'none';
|
|
478
907
|
|
|
908
|
+
this.updateUILanguage();
|
|
479
909
|
this.conversation_id = await this.createConversation();
|
|
480
|
-
|
|
481
|
-
this.
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
)
|
|
910
|
+
this.addMessage('assistant', this.getWelcomeMessage(this.user_info?.first_name || ''));
|
|
911
|
+
this.last_message_count = 1;
|
|
912
|
+
|
|
913
|
+
const theme = getClientTheme(this.client_id);
|
|
914
|
+
if (theme.suggestions && theme.suggestions.length > 0) {
|
|
915
|
+
this.showSuggestions(theme.suggestions);
|
|
916
|
+
}
|
|
917
|
+
|
|
485
918
|
this.polling_interval = setInterval(() => this.pollMessages(), 3000);
|
|
486
919
|
}
|
|
487
920
|
|
|
@@ -493,7 +926,6 @@
|
|
|
493
926
|
|
|
494
927
|
const input_bar = this.querySelector('#tp-chatbot-input-bar');
|
|
495
928
|
if (input_bar) input_bar.style.display = 'none';
|
|
496
|
-
|
|
497
929
|
const agent_bar = this.querySelector('#tp-agent-bar');
|
|
498
930
|
if (agent_bar) agent_bar.style.display = 'none';
|
|
499
931
|
|
|
@@ -503,18 +935,17 @@
|
|
|
503
935
|
const form_el = document.createElement('div');
|
|
504
936
|
form_el.id = 'tp-guest-form';
|
|
505
937
|
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
|
-
|
|
938
|
+
<div style="padding:16px;display:flex;flex-direction:column;gap:12px;">
|
|
939
|
+
<p style="margin:0;font-size:13px;color:#1a1a2e;font-weight:600;">Before we start, please identify yourself:</p>
|
|
940
|
+
<input id="tp-guest-firstname" type="text" placeholder="First name *" style="padding:10px 14px;border-radius:8px;border:1.5px solid #ede8f5;font-size:13px;font-family:inherit;outline:none;" />
|
|
941
|
+
<input id="tp-guest-company" type="text" placeholder="Company name *" style="padding:10px 14px;border-radius:8px;border:1.5px solid #ede8f5;font-size:13px;font-family:inherit;outline:none;" />
|
|
942
|
+
<input id="tp-guest-email" type="email" placeholder="Email *" style="padding:10px 14px;border-radius:8px;border:1.5px solid #ede8f5;font-size:13px;font-family:inherit;outline:none;" />
|
|
943
|
+
<button id="tp-guest-submit" style="padding:10px;background:linear-gradient(135deg,${color},${dark});color:white;border:none;border-radius:8px;font-size:13px;font-weight:600;cursor:pointer;">
|
|
944
|
+
Start conversation →
|
|
513
945
|
</button>
|
|
514
|
-
<p id="tp-guest-error" style="margin:
|
|
946
|
+
<p id="tp-guest-error" style="margin:0;font-size:12px;color:#c0392b;display:none;"></p>
|
|
515
947
|
</div>
|
|
516
948
|
`;
|
|
517
|
-
|
|
518
949
|
container.appendChild(form_el);
|
|
519
950
|
this.querySelector('#tp-guest-submit').addEventListener('click', () => this.submitGuestForm());
|
|
520
951
|
}
|
|
@@ -526,40 +957,41 @@
|
|
|
526
957
|
const error_el = this.querySelector('#tp-guest-error');
|
|
527
958
|
|
|
528
959
|
if (!first_name || !company_name || !email) {
|
|
529
|
-
error_el.textContent = '
|
|
960
|
+
error_el.textContent = 'Please fill in all fields.';
|
|
530
961
|
error_el.style.display = 'block';
|
|
531
962
|
return;
|
|
532
963
|
}
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
if (!email_valid) {
|
|
536
|
-
error_el.textContent = 'Veuillez entrer un email valide.';
|
|
964
|
+
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
|
|
965
|
+
error_el.textContent = 'Please enter a valid email.';
|
|
537
966
|
error_el.style.display = 'block';
|
|
538
967
|
return;
|
|
539
968
|
}
|
|
540
969
|
|
|
541
|
-
this.user_info = { user_id: `guest-${email}`, first_name, last_name: '', company_name, email, language: '
|
|
970
|
+
this.user_info = { user_id: `guest-${email}`, first_name, last_name: '', company_name, email, language: 'EN', site_id: '' };
|
|
542
971
|
this.user_id = this.user_info.user_id;
|
|
543
972
|
|
|
544
973
|
const form_el = this.querySelector('#tp-guest-form');
|
|
545
974
|
if (form_el) form_el.remove();
|
|
975
|
+
|
|
546
976
|
this.view = 'chat';
|
|
547
977
|
this.conversation_id = await this.createConversation();
|
|
548
978
|
|
|
549
979
|
const input_bar = this.querySelector('#tp-chatbot-input-bar');
|
|
550
980
|
if (input_bar) input_bar.style.display = 'flex';
|
|
551
|
-
|
|
552
981
|
const agent_bar = this.querySelector('#tp-agent-bar');
|
|
553
|
-
if (agent_bar) agent_bar.style.display = '
|
|
554
|
-
|
|
982
|
+
if (agent_bar) agent_bar.style.display = 'none';
|
|
555
983
|
const close_bar = this.querySelector('#tp-close-bar');
|
|
556
984
|
if (close_bar) close_bar.style.display = 'block';
|
|
557
985
|
|
|
558
|
-
|
|
559
|
-
this.addMessage(
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
);
|
|
986
|
+
this.updateUILanguage();
|
|
987
|
+
this.addMessage('assistant', this.getWelcomeMessage(first_name));
|
|
988
|
+
this.last_message_count = 1;
|
|
989
|
+
|
|
990
|
+
const theme = getClientTheme(this.client_id);
|
|
991
|
+
if (theme.suggestions && theme.suggestions.length > 0) {
|
|
992
|
+
this.showSuggestions(theme.suggestions);
|
|
993
|
+
}
|
|
994
|
+
|
|
563
995
|
this.polling_interval = setInterval(() => this.pollMessages(), 3000);
|
|
564
996
|
}
|
|
565
997
|
|
|
@@ -571,12 +1003,12 @@
|
|
|
571
1003
|
|
|
572
1004
|
const role_label =
|
|
573
1005
|
role === 'user'
|
|
574
|
-
? `👤 ${this.user_info?.first_name || '
|
|
1006
|
+
? `👤 ${this.user_info?.first_name || 'You'}`
|
|
575
1007
|
: role === 'agent'
|
|
576
1008
|
? '👨💼 Agent'
|
|
577
1009
|
: role === 'system'
|
|
578
1010
|
? 'ℹ️ Info'
|
|
579
|
-
: '🤖
|
|
1011
|
+
: '🤖 Maria';
|
|
580
1012
|
|
|
581
1013
|
const msg_el = document.createElement('div');
|
|
582
1014
|
msg_el.className = `tp-chatbot-message ${role}`;
|
|
@@ -619,32 +1051,32 @@
|
|
|
619
1051
|
this.polling_interval = null;
|
|
620
1052
|
}
|
|
621
1053
|
|
|
1054
|
+
const m = this.getMessages();
|
|
622
1055
|
const subtitle = this.querySelector('#tp-subtitle');
|
|
623
|
-
if (subtitle) subtitle.textContent =
|
|
1056
|
+
if (subtitle) subtitle.textContent = m.terminated;
|
|
624
1057
|
|
|
625
1058
|
const input_bar = this.querySelector('#tp-chatbot-input-bar');
|
|
626
1059
|
if (input_bar) input_bar.style.display = 'none';
|
|
627
|
-
|
|
628
1060
|
const agent_bar = this.querySelector('#tp-agent-bar');
|
|
629
1061
|
if (agent_bar) agent_bar.style.display = 'none';
|
|
630
|
-
|
|
631
1062
|
const close_bar = this.querySelector('#tp-close-bar');
|
|
632
1063
|
if (close_bar) close_bar.style.display = 'none';
|
|
1064
|
+
const sugg_el = this.querySelector('#tp-suggestions');
|
|
1065
|
+
if (sugg_el) sugg_el.remove();
|
|
633
1066
|
|
|
634
1067
|
const container = this.querySelector('#tp-messages');
|
|
635
|
-
|
|
636
1068
|
const existing_banner = this.querySelector('#tp-closed-banner');
|
|
637
1069
|
if (existing_banner) {
|
|
638
|
-
if (existing_banner.querySelector('#tp-csat-block')) return;
|
|
639
|
-
existing_banner.remove();
|
|
1070
|
+
if (existing_banner.querySelector('#tp-csat-block')) return;
|
|
1071
|
+
existing_banner.remove();
|
|
640
1072
|
}
|
|
641
1073
|
|
|
642
1074
|
const color = this.getThemeColor();
|
|
643
1075
|
const dark = this.shadeColor(color, -20);
|
|
644
1076
|
|
|
645
1077
|
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;"
|
|
1078
|
+
? `<div style="font-size:13px;color:#888;margin-bottom:14px;">${existing_csat === 'positive' ? m.csat_positive : m.csat_negative}</div>`
|
|
1079
|
+
: `<div style="font-size:12px;color:#888;margin-bottom:10px;">${m.csat_question}</div>
|
|
648
1080
|
<div style="display:flex;gap:10px;justify-content:center;margin-bottom:14px;">
|
|
649
1081
|
<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
1082
|
<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 +1086,10 @@
|
|
|
654
1086
|
banner.id = 'tp-closed-banner';
|
|
655
1087
|
banner.innerHTML = `
|
|
656
1088
|
<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;"
|
|
1089
|
+
<div style="font-size:13px;color:#1a1a2e;font-weight:600;margin-bottom:8px;">${m.closed}</div>
|
|
658
1090
|
<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>
|
|
1091
|
+
<button id="tp-new-conversation" style="padding:10px 20px;background:linear-gradient(135deg,${color},${dark});color:white;border:none;border-radius:8px;font-size:13px;font-weight:600;cursor:pointer;margin-bottom:8px;width:100%;">${m.new_conv}</button>
|
|
1092
|
+
<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
1093
|
</div>
|
|
666
1094
|
`;
|
|
667
1095
|
container.appendChild(banner);
|
|
@@ -680,9 +1108,8 @@
|
|
|
680
1108
|
} catch (e) {
|
|
681
1109
|
console.error('CSAT error:', e);
|
|
682
1110
|
}
|
|
683
|
-
csat_block.innerHTML = `<div style="font-size:13px;color:#888;margin-bottom:14px;">${rating === 'positive' ?
|
|
1111
|
+
csat_block.innerHTML = `<div style="font-size:13px;color:#888;margin-bottom:14px;">${rating === 'positive' ? m.csat_positive : m.csat_negative}</div>`;
|
|
684
1112
|
};
|
|
685
|
-
|
|
686
1113
|
this.querySelector('#tp-csat-positive').addEventListener('click', () => submit_csat('positive'));
|
|
687
1114
|
this.querySelector('#tp-csat-negative').addEventListener('click', () => submit_csat('negative'));
|
|
688
1115
|
}
|
|
@@ -695,16 +1122,17 @@
|
|
|
695
1122
|
|
|
696
1123
|
async sendMessage() {
|
|
697
1124
|
if (this.is_closed || !this.conversation_id) return;
|
|
1125
|
+
const m = this.getMessages();
|
|
698
1126
|
const input = this.querySelector('#tp-input');
|
|
699
1127
|
const query = input.value.trim();
|
|
700
1128
|
if (!query || this.is_loading) return;
|
|
701
1129
|
|
|
702
|
-
const
|
|
1130
|
+
const sugg_el = this.querySelector('#tp-suggestions');
|
|
1131
|
+
if (sugg_el) sugg_el.remove();
|
|
1132
|
+
|
|
1133
|
+
const user_messages = this.messages.filter(msg => msg.role === 'user').length;
|
|
703
1134
|
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
|
-
);
|
|
1135
|
+
this.addMessage('system', m.limit_reached);
|
|
708
1136
|
const input_bar = this.querySelector('#tp-chatbot-input-bar');
|
|
709
1137
|
if (input_bar) input_bar.style.display = 'none';
|
|
710
1138
|
return;
|
|
@@ -726,7 +1154,7 @@
|
|
|
726
1154
|
this.hideTyping();
|
|
727
1155
|
|
|
728
1156
|
if (response.status === 429) {
|
|
729
|
-
this.addMessage('system', data.error?.message ||
|
|
1157
|
+
this.addMessage('system', data.error?.message || m.too_many);
|
|
730
1158
|
this.is_loading = false;
|
|
731
1159
|
return;
|
|
732
1160
|
}
|
|
@@ -735,23 +1163,29 @@
|
|
|
735
1163
|
const source = data.result.source || null;
|
|
736
1164
|
|
|
737
1165
|
const subtitle = this.querySelector('#tp-subtitle');
|
|
738
|
-
if (subtitle) subtitle.textContent = this.agent_mode ?
|
|
1166
|
+
if (subtitle) subtitle.textContent = this.agent_mode ? m.human_agent : m.virtual;
|
|
739
1167
|
|
|
740
1168
|
if (data.result.reply) {
|
|
741
1169
|
this.addMessage(this.agent_mode ? 'agent' : 'assistant', data.result.reply);
|
|
1170
|
+
this.last_message_count += 2; // fix double message
|
|
742
1171
|
}
|
|
743
1172
|
|
|
744
|
-
|
|
745
|
-
const user_msg_count = this.messages.filter(m => m.role === 'user').length;
|
|
1173
|
+
const user_msg_count = this.messages.filter(msg => msg.role === 'user').length;
|
|
746
1174
|
const agent_bar = this.querySelector('#tp-agent-bar');
|
|
747
1175
|
if (agent_bar && !this.agent_requested && !this.agent_mode) {
|
|
748
|
-
if (
|
|
1176
|
+
if (
|
|
1177
|
+
source === 'fallback' ||
|
|
1178
|
+
source === 'clarification' ||
|
|
1179
|
+
source === 'no_match' ||
|
|
1180
|
+
source === 'out_of_scope' ||
|
|
1181
|
+
user_msg_count >= 5
|
|
1182
|
+
) {
|
|
749
1183
|
agent_bar.style.display = 'block';
|
|
750
1184
|
}
|
|
751
1185
|
}
|
|
752
1186
|
} catch {
|
|
753
1187
|
this.hideTyping();
|
|
754
|
-
this.addMessage('assistant',
|
|
1188
|
+
this.addMessage('assistant', m.error_occurred);
|
|
755
1189
|
}
|
|
756
1190
|
|
|
757
1191
|
this.is_loading = false;
|
|
@@ -770,9 +1204,28 @@
|
|
|
770
1204
|
body: JSON.stringify({ user_id: this.user_id }),
|
|
771
1205
|
});
|
|
772
1206
|
const data = await response.json();
|
|
773
|
-
this.
|
|
1207
|
+
const m = this.getMessages();
|
|
1208
|
+
const status = data.result?.status;
|
|
1209
|
+
|
|
1210
|
+
let msg = '';
|
|
1211
|
+
if (status === 'outside_hours') {
|
|
1212
|
+
msg = m.outside_hours(data.result.next_opening);
|
|
1213
|
+
} else if (status === 'no_agents') {
|
|
1214
|
+
msg = m.no_agents;
|
|
1215
|
+
} else if (status === 'waiting_agent') {
|
|
1216
|
+
msg = m.waiting_agent;
|
|
1217
|
+
} else if (status === 'waiting_agent_already') {
|
|
1218
|
+
msg = m.waiting_agent_already;
|
|
1219
|
+
} else if (status === 'agent') {
|
|
1220
|
+
msg = m.agent_already;
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
if (msg) {
|
|
1224
|
+
this.addMessage('system', msg);
|
|
1225
|
+
this.last_message_count += 1;
|
|
1226
|
+
}
|
|
774
1227
|
|
|
775
|
-
if (
|
|
1228
|
+
if (status === 'no_agents' || status === 'outside_hours' || status === 'waiting_agent_already') {
|
|
776
1229
|
this.agent_requested = false;
|
|
777
1230
|
if (agent_bar) agent_bar.style.display = 'block';
|
|
778
1231
|
}
|
|
@@ -810,6 +1263,7 @@
|
|
|
810
1263
|
const server_messages = conv.messages || [];
|
|
811
1264
|
const server_status = conv.status;
|
|
812
1265
|
const is_typing = conv.is_typing || false;
|
|
1266
|
+
const m = this.getMessages();
|
|
813
1267
|
|
|
814
1268
|
if (server_status === 'closed' && !this.is_closed) {
|
|
815
1269
|
this.showClosed();
|
|
@@ -850,19 +1304,19 @@
|
|
|
850
1304
|
if (server_status === 'agent' && !this.agent_mode) {
|
|
851
1305
|
this.agent_mode = true;
|
|
852
1306
|
const subtitle = this.querySelector('#tp-subtitle');
|
|
853
|
-
if (subtitle) subtitle.textContent =
|
|
1307
|
+
if (subtitle) subtitle.textContent = m.human_agent;
|
|
854
1308
|
const agent_bar = this.querySelector('#tp-agent-bar');
|
|
855
1309
|
if (agent_bar) agent_bar.style.display = 'none';
|
|
856
|
-
this.addMessage('system',
|
|
1310
|
+
this.addMessage('system', m.agent_taken);
|
|
857
1311
|
this.last_message_count = server_messages.length;
|
|
858
1312
|
} else if (server_status === 'bot' && this.agent_mode) {
|
|
859
1313
|
this.agent_mode = false;
|
|
860
1314
|
this.agent_requested = false;
|
|
861
1315
|
const subtitle = this.querySelector('#tp-subtitle');
|
|
862
|
-
if (subtitle) subtitle.textContent =
|
|
1316
|
+
if (subtitle) subtitle.textContent = m.virtual;
|
|
863
1317
|
const agent_bar = this.querySelector('#tp-agent-bar');
|
|
864
1318
|
if (agent_bar) agent_bar.style.display = 'block';
|
|
865
|
-
this.addMessage('system',
|
|
1319
|
+
this.addMessage('system', m.agent_released);
|
|
866
1320
|
this.last_message_count = server_messages.length;
|
|
867
1321
|
}
|
|
868
1322
|
} catch (e) {
|