@developpement/tp-chatbot-widget 0.0.5 → 0.0.7

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@developpement/tp-chatbot-widget",
3
- "version": "0.0.5",
3
+ "version": "0.0.7",
4
4
  "description": "Chatbot widget for TravelPlanet / Makitizy",
5
5
  "main": "src/chatbot.js",
6
6
  "files": [
package/src/chatbot.css CHANGED
@@ -32,7 +32,6 @@
32
32
  .tp-chatbot-window {
33
33
  position: absolute;
34
34
  bottom: 70px;
35
- right: 0;
36
35
  width: 360px;
37
36
  height: 500px;
38
37
  background: white;
package/src/chatbot.js CHANGED
@@ -2,10 +2,10 @@
2
2
  'use strict';
3
3
 
4
4
  const CLIENT_THEMES = {
5
- flix: { primary: '#7b1fa2', name: 'Flix Corporate' },
6
- sncf: { primary: '#1a6b5a', name: 'SNCF' },
5
+ flix: { primary: '#5cc500', name: 'Flix Corporate', position: 'left' },
6
+ sncf: { primary: '#1a6b5a', name: 'SNCF', position: 'right' },
7
7
  };
8
- const DEFAULT_THEME = { primary: '#7b1fa2', name: 'Support' };
8
+ const DEFAULT_THEME = { primary: '#7b1fa2', name: 'Support', position: 'right' };
9
9
 
10
10
  function getClientTheme(client_id) {
11
11
  return CLIENT_THEMES[client_id] || DEFAULT_THEME;
@@ -100,7 +100,7 @@
100
100
  this.view = 'list';
101
101
 
102
102
  this.render();
103
- this.applyTheme(); // ← couleur appliquée immédiatement, avant le token
103
+ this.applyTheme();
104
104
  this.attachEvents();
105
105
  this._initialized = true;
106
106
 
@@ -109,7 +109,6 @@
109
109
  this.user_id = this.user_info?.user_id;
110
110
  this.showConversationList();
111
111
  } else {
112
- // Attend 500ms que le framework passe le token
113
112
  setTimeout(() => {
114
113
  const token = this.getAttribute('access-token');
115
114
  if (token) {
@@ -166,6 +165,28 @@
166
165
  .tp-conv-new-btn { background: linear-gradient(135deg, ${color}, ${dark}) !important; }
167
166
  `;
168
167
  document.head.appendChild(style);
168
+
169
+ const position = getClientTheme(this.client_id).position || 'right';
170
+
171
+ const host = this.querySelector('.tp-chatbot-host');
172
+ if (host) {
173
+ host.style.width = '360px';
174
+ host.style.left = position === 'left' ? '24px' : 'auto';
175
+ host.style.right = position === 'left' ? 'auto' : '24px';
176
+ }
177
+
178
+ const bubble = this.querySelector('.tp-chatbot-bubble');
179
+ if (bubble) {
180
+ bubble.style.marginLeft = position === 'left' ? '0' : 'auto';
181
+ bubble.style.marginRight = position === 'left' ? 'auto' : '0';
182
+ }
183
+
184
+ const win = this.querySelector('.tp-chatbot-window');
185
+ if (win) {
186
+ win.style.left = '0';
187
+ win.style.right = 'auto';
188
+ win.style.transformOrigin = position === 'left' ? 'bottom left' : 'bottom right';
189
+ }
169
190
  }
170
191
 
171
192
  // ─── Render ───────────────────────────────────────────────────────────────
@@ -399,7 +420,7 @@
399
420
  this.last_message_count = conv.messages.length;
400
421
 
401
422
  if (conv.status === 'closed') {
402
- this.showClosed();
423
+ this.showClosed(conv.csat || null);
403
424
  return;
404
425
  }
405
426
 
@@ -453,10 +474,14 @@
453
474
  if (input_bar) input_bar.style.display = 'flex';
454
475
 
455
476
  const agent_bar = this.querySelector('#tp-agent-bar');
456
- if (agent_bar) agent_bar.style.display = 'block';
477
+ if (agent_bar) agent_bar.style.display = 'none';
457
478
 
458
479
  this.conversation_id = await this.createConversation();
459
- this.addMessage('assistant', `Bonjour ${this.user_info?.first_name || ''} ! Comment puis-je vous aider ?`);
480
+ const client_name = this.client_id === 'flix' ? 'Flix Corporate' : this.client_id === 'sncf' ? 'SNCF' : this.client_id;
481
+ this.addMessage(
482
+ 'assistant',
483
+ `Bonjour ${this.user_info?.first_name || ''} ! 👋 Je m'appelle Maria, votre assistante virtuelle ${client_name}. Comment puis-je vous aider aujourd'hui ?`
484
+ );
460
485
  this.polling_interval = setInterval(() => this.pollMessages(), 3000);
461
486
  }
462
487
 
@@ -530,7 +555,11 @@
530
555
  const close_bar = this.querySelector('#tp-close-bar');
531
556
  if (close_bar) close_bar.style.display = 'block';
532
557
 
533
- this.addMessage('assistant', `Bonjour ${first_name} ! Comment puis-je vous aider aujourd'hui ?`);
558
+ const client_name = this.client_id === 'flix' ? 'Flix Corporate' : this.client_id === 'sncf' ? 'SNCF' : this.client_id;
559
+ this.addMessage(
560
+ 'assistant',
561
+ `Bonjour ${first_name} ! 👋 Je m'appelle Maria, votre assistante virtuelle ${client_name}. Comment puis-je vous aider aujourd'hui ?`
562
+ );
534
563
  this.polling_interval = setInterval(() => this.pollMessages(), 3000);
535
564
  }
536
565
 
@@ -583,7 +612,7 @@
583
612
  if (typing) typing.remove();
584
613
  }
585
614
 
586
- showClosed() {
615
+ showClosed(existing_csat = null) {
587
616
  this.is_closed = true;
588
617
  if (this.polling_interval) {
589
618
  clearInterval(this.polling_interval);
@@ -603,16 +632,30 @@
603
632
  if (close_bar) close_bar.style.display = 'none';
604
633
 
605
634
  const container = this.querySelector('#tp-messages');
606
- if (this.querySelector('#tp-closed-banner')) return;
635
+
636
+ const existing_banner = this.querySelector('#tp-closed-banner');
637
+ if (existing_banner) {
638
+ if (existing_banner.querySelector('#tp-csat-block')) return; // déjà complet
639
+ existing_banner.remove(); // incomplet, on recrée
640
+ }
607
641
 
608
642
  const color = this.getThemeColor();
609
643
  const dark = this.shadeColor(color, -20);
610
644
 
645
+ const csat_html = existing_csat
646
+ ? `<div style="font-size:13px;color:#888;margin-bottom:14px;">${existing_csat === 'positive' ? '👍 Merci pour votre retour !' : '👎 Merci, nous allons nous améliorer.'}</div>`
647
+ : `<div style="font-size:12px;color:#888;margin-bottom:10px;">Cette conversation vous a-t-elle été utile ?</div>
648
+ <div style="display:flex;gap:10px;justify-content:center;margin-bottom:14px;">
649
+ <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
+ <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>
651
+ </div>`;
652
+
611
653
  const banner = document.createElement('div');
612
654
  banner.id = 'tp-closed-banner';
613
655
  banner.innerHTML = `
614
- <div style="margin:16px;padding:16px;background:#fef2f2;border:1px solid #fecaca;border-radius:10px;text-align:center;">
615
- <div style="font-size:13px;color:#ef4444;font-weight:600;margin-bottom:12px;">✅ Cette conversation a été clôturée.</div>
656
+ <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;">✅ Conversation clôturée</div>
658
+ <div id="tp-csat-block">${csat_html}</div>
616
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%;">
617
660
  💬 Nouvelle conversation
618
661
  </button>
@@ -624,6 +667,26 @@
624
667
  container.appendChild(banner);
625
668
  container.scrollTop = container.scrollHeight;
626
669
 
670
+ if (!existing_csat) {
671
+ const submit_csat = async rating => {
672
+ const csat_block = this.querySelector('#tp-csat-block');
673
+ if (!csat_block) return;
674
+ try {
675
+ await fetch(`${this.api_url}/chat/conversations/${this.conversation_id}/csat`, {
676
+ method: 'POST',
677
+ headers: this.getHeaders(true),
678
+ body: JSON.stringify({ user_id: this.user_id, rating }),
679
+ });
680
+ } catch (e) {
681
+ console.error('CSAT error:', e);
682
+ }
683
+ csat_block.innerHTML = `<div style="font-size:13px;color:#888;margin-bottom:14px;">${rating === 'positive' ? '👍 Merci pour votre retour !' : '👎 Merci, nous allons nous améliorer.'}</div>`;
684
+ };
685
+
686
+ this.querySelector('#tp-csat-positive').addEventListener('click', () => submit_csat('positive'));
687
+ this.querySelector('#tp-csat-negative').addEventListener('click', () => submit_csat('negative'));
688
+ }
689
+
627
690
  this.querySelector('#tp-new-conversation').addEventListener('click', () => this.startNewConversation());
628
691
  this.querySelector('#tp-back-to-list').addEventListener('click', () => this.showConversationList());
629
692
  }
@@ -663,20 +726,29 @@
663
726
  this.hideTyping();
664
727
 
665
728
  if (response.status === 429) {
666
- this.addMessage('system', 'Vous avez atteint la limite de messages. Veuillez démarrer une nouvelle conversation.');
667
- const input_bar = this.querySelector('#tp-chatbot-input-bar');
668
- if (input_bar) input_bar.style.display = 'none';
729
+ this.addMessage('system', data.error?.message || 'Trop de messages. Veuillez patienter avant de réessayer.');
669
730
  this.is_loading = false;
670
731
  return;
671
732
  }
672
733
 
673
734
  this.agent_mode = data.result.agent_mode;
735
+ const source = data.result.source || null;
736
+
674
737
  const subtitle = this.querySelector('#tp-subtitle');
675
738
  if (subtitle) subtitle.textContent = this.agent_mode ? '👤 Agent humain' : '🤖 Assistant virtuel';
676
739
 
677
740
  if (data.result.reply) {
678
741
  this.addMessage(this.agent_mode ? 'agent' : 'assistant', data.result.reply);
679
742
  }
743
+
744
+ // Afficher bouton agent si fallback ou ≥ 3 messages user
745
+ const user_msg_count = this.messages.filter(m => m.role === 'user').length;
746
+ const agent_bar = this.querySelector('#tp-agent-bar');
747
+ if (agent_bar && !this.agent_requested && !this.agent_mode) {
748
+ if (source === 'fallback' || source === 'clarification' || source === 'no_match' || user_msg_count >= 5) {
749
+ agent_bar.style.display = 'block';
750
+ }
751
+ }
680
752
  } catch {
681
753
  this.hideTyping();
682
754
  this.addMessage('assistant', 'Une erreur est survenue. Veuillez réessayer.');
@@ -766,6 +838,10 @@
766
838
  const typing_el = this.querySelector('#tp-agent-typing');
767
839
  if (typing_el) typing_el.remove();
768
840
  this.addMessage('agent', msg.content);
841
+ } else if (msg.role === 'assistant') {
842
+ this.addMessage('assistant', msg.content);
843
+ } else if (msg.role === 'system') {
844
+ this.addMessage('system', msg.content);
769
845
  }
770
846
  });
771
847
  this.last_message_count = server_messages.length;